Merge Skia's chrome/m44 branch into mnc-dev.

bug:21108081
Change-Id: I2c8a4c8183d8449fe6e1c53fc69381b5c7a2e1aa
diff --git a/AUTHORS b/AUTHORS
index 915393b..76e2f44 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -31,6 +31,7 @@
 Samsung Open Source Group <*@osg.samsung.com>
 Skia <*@skia.org>
 Skia Buildbots <skia.buildbots@gmail.com>
+Sony Mobile Communications Inc. <*@sonymobile.com>
 Steve Singer <steve@ssinger.info>
 The Chromium Authors <*@chromium.org>
 Thiago Fransosi Farina <thiago.farina@gmail.com>
diff --git a/Android.mk b/Android.mk
index bdf11d1..789de12 100644
--- a/Android.mk
+++ b/Android.mk
@@ -62,13 +62,27 @@
 	-Wno-unused-parameter \
 	-U_FORTIFY_SOURCE \
 	-D_FORTIFY_SOURCE=1 \
-	-DSKIA_IMPLEMENTATION=1
+	-DSKIA_IMPLEMENTATION=1 \
+	-Wno-clobbered
 
 LOCAL_CPPFLAGS := \
 	-std=c++11 \
 	-Wno-invalid-offsetof
 
 LOCAL_SRC_FILES := \
+	src/codec/SkCodec.cpp \
+	src/codec/SkCodec_libbmp.cpp \
+	src/codec/SkCodec_libgif.cpp \
+	src/codec/SkCodec_libico.cpp \
+	src/codec/SkCodec_libpng.cpp \
+	src/codec/SkCodec_wbmp.cpp \
+	src/codec/SkGifInterlaceIter.cpp \
+	src/codec/SkJpegCodec.cpp \
+	src/codec/SkJpegDecoderMgr.cpp \
+	src/codec/SkJpegUtility.cpp \
+	src/codec/SkMaskSwizzler.cpp \
+	src/codec/SkMasks.cpp \
+	src/codec/SkSwizzler.cpp \
 	src/c/sk_paint.cpp \
 	src/c/sk_surface.cpp \
 	src/core/SkAAClip.cpp \
@@ -153,6 +167,7 @@
 	src/core/SkMaskGamma.cpp \
 	src/core/SkMath.cpp \
 	src/core/SkMatrix.cpp \
+	src/core/SkMatrixImageFilter.cpp \
 	src/core/SkMetaData.cpp \
 	src/core/SkMipMap.cpp \
 	src/core/SkMultiPictureDraw.cpp \
@@ -189,6 +204,7 @@
 	src/core/SkResourceCache.cpp \
 	src/core/SkRRect.cpp \
 	src/core/SkRTree.cpp \
+	src/core/SkRWBuffer.cpp \
 	src/core/SkScalar.cpp \
 	src/core/SkScalerContext.cpp \
 	src/core/SkScan.cpp \
@@ -207,6 +223,7 @@
 	src/core/SkStrokerPriv.cpp \
 	src/core/SkTaskGroup.cpp \
 	src/core/SkTextBlob.cpp \
+	src/core/SkTime.cpp \
 	src/core/SkTLS.cpp \
 	src/core/SkTSearch.cpp \
 	src/core/SkTypeface.cpp \
@@ -229,21 +246,24 @@
 	src/pipe/SkGPipeWrite.cpp \
 	src/lazy/SkCachingPixelRef.cpp \
 	src/pathops/SkAddIntersections.cpp \
-	src/pathops/SkDCubicIntersection.cpp \
+	src/pathops/SkDConicLineIntersection.cpp \
 	src/pathops/SkDCubicLineIntersection.cpp \
 	src/pathops/SkDCubicToQuads.cpp \
 	src/pathops/SkDLineIntersection.cpp \
-	src/pathops/SkDQuadImplicit.cpp \
-	src/pathops/SkDQuadIntersection.cpp \
 	src/pathops/SkDQuadLineIntersection.cpp \
 	src/pathops/SkIntersections.cpp \
 	src/pathops/SkOpAngle.cpp \
+	src/pathops/SkOpBuilder.cpp \
+	src/pathops/SkOpCoincidence.cpp \
 	src/pathops/SkOpContour.cpp \
+	src/pathops/SkOpCubicHull.cpp \
 	src/pathops/SkOpEdgeBuilder.cpp \
 	src/pathops/SkOpSegment.cpp \
-	src/pathops/SkPathOpsBounds.cpp \
+	src/pathops/SkOpSpan.cpp \
 	src/pathops/SkPathOpsCommon.cpp \
+	src/pathops/SkPathOpsConic.cpp \
 	src/pathops/SkPathOpsCubic.cpp \
+	src/pathops/SkPathOpsCurve.cpp \
 	src/pathops/SkPathOpsDebug.cpp \
 	src/pathops/SkPathOpsLine.cpp \
 	src/pathops/SkPathOpsOp.cpp \
@@ -251,11 +271,11 @@
 	src/pathops/SkPathOpsQuad.cpp \
 	src/pathops/SkPathOpsRect.cpp \
 	src/pathops/SkPathOpsSimplify.cpp \
+	src/pathops/SkPathOpsTSect.cpp \
 	src/pathops/SkPathOpsTightBounds.cpp \
-	src/pathops/SkPathOpsTriangle.cpp \
 	src/pathops/SkPathOpsTypes.cpp \
+	src/pathops/SkPathOpsWinding.cpp \
 	src/pathops/SkPathWriter.cpp \
-	src/pathops/SkQuarticRoot.cpp \
 	src/pathops/SkReduceOrder.cpp \
 	src/effects/Sk1DPathEffect.cpp \
 	src/effects/Sk2DPathEffect.cpp \
@@ -301,7 +321,6 @@
 	src/effects/SkTableMaskFilter.cpp \
 	src/effects/SkTestImageFilters.cpp \
 	src/effects/SkTileImageFilter.cpp \
-	src/effects/SkMatrixImageFilter.cpp \
 	src/effects/SkTransparentShader.cpp \
 	src/effects/SkXfermodeImageFilter.cpp \
 	src/effects/gradients/SkClampRange.cpp \
@@ -309,7 +328,6 @@
 	src/effects/gradients/SkGradientShader.cpp \
 	src/effects/gradients/SkLinearGradient.cpp \
 	src/effects/gradients/SkRadialGradient.cpp \
-	src/effects/gradients/SkTwoPointRadialGradient.cpp \
 	src/effects/gradients/SkTwoPointConicalGradient.cpp \
 	src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp \
 	src/effects/gradients/SkSweepGradient.cpp \
@@ -336,25 +354,19 @@
 	src/images/SkScaledBitmapSampler.cpp \
 	src/ports/SkImageGenerator_skia.cpp \
 	src/doc/SkDocument_PDF.cpp \
+	src/pdf/SkJpegInfo.cpp \
 	src/pdf/SkPDFBitmap.cpp \
 	src/pdf/SkPDFCanon.cpp \
-	src/pdf/SkPDFCatalog.cpp \
 	src/pdf/SkPDFDevice.cpp \
-	src/pdf/SkPDFDocument.cpp \
 	src/pdf/SkPDFFont.cpp \
 	src/pdf/SkPDFFormXObject.cpp \
 	src/pdf/SkPDFGraphicState.cpp \
-	src/pdf/SkPDFImage.cpp \
-	src/pdf/SkPDFPage.cpp \
 	src/pdf/SkPDFResourceDict.cpp \
 	src/pdf/SkPDFShader.cpp \
 	src/pdf/SkPDFStream.cpp \
 	src/pdf/SkPDFTypes.cpp \
 	src/pdf/SkPDFUtils.cpp \
 	src/core/SkForceCPlusPlusLinking.cpp \
-	src/codec/SkCodec.cpp \
-	src/codec/SkCodec_libpng.cpp \
-	src/codec/SkSwizzler.cpp \
 	src/fonts/SkFontMgr_indirect.cpp \
 	src/fonts/SkRemotableFontMgr.cpp \
 	src/ports/SkGlobalInitialization_default.cpp \
@@ -396,13 +408,13 @@
 	src/utils/SkNWayCanvas.cpp \
 	src/utils/SkNullCanvas.cpp \
 	src/utils/SkOSFile.cpp \
+	src/utils/SkPaintFilterCanvas.cpp \
 	src/utils/SkParse.cpp \
 	src/utils/SkParseColor.cpp \
 	src/utils/SkParsePath.cpp \
 	src/utils/SkPictureUtils.cpp \
 	src/utils/SkPatchGrid.cpp \
 	src/utils/SkPatchUtils.cpp \
-	src/utils/SkPathUtils.cpp \
 	src/utils/SkSHA1.cpp \
 	src/utils/SkRTConf.cpp \
 	src/utils/SkTextBox.cpp \
@@ -416,31 +428,35 @@
 	src/fonts/SkTestScalerContext.cpp \
 	src/gpu/GrAAHairLinePathRenderer.cpp \
 	src/gpu/GrAAConvexPathRenderer.cpp \
+	src/gpu/GrAAConvexTessellator.cpp \
 	src/gpu/GrAADistanceFieldPathRenderer.cpp \
 	src/gpu/GrAARectRenderer.cpp \
 	src/gpu/GrAddPathRenderers_default.cpp \
 	src/gpu/GrAtlas.cpp \
+	src/gpu/GrAtlasTextContext.cpp \
 	src/gpu/GrBatch.cpp \
+	src/gpu/GrBatchAtlas.cpp \
+	src/gpu/GrBatchFontCache.cpp \
 	src/gpu/GrBatchTarget.cpp \
-	src/gpu/GrBitmapTextContext.cpp \
+	src/gpu/GrBatchTest.cpp \
 	src/gpu/GrBlend.cpp \
 	src/gpu/GrBufferAllocPool.cpp \
 	src/gpu/GrClip.cpp \
 	src/gpu/GrClipMaskCache.cpp \
 	src/gpu/GrClipMaskManager.cpp \
 	src/gpu/GrContext.cpp \
+	src/gpu/GrCommandBuilder.cpp \
 	src/gpu/GrCoordTransform.cpp \
+	src/gpu/GrDashLinePathRenderer.cpp \
 	src/gpu/GrDefaultGeoProcFactory.cpp \
 	src/gpu/GrDefaultPathRenderer.cpp \
-	src/gpu/GrDistanceFieldTextContext.cpp \
 	src/gpu/GrDrawTarget.cpp \
-	src/gpu/GrFlushToGpuDrawTarget.cpp \
 	src/gpu/GrFontScaler.cpp \
-	src/gpu/GrGeometryProcessor.cpp \
 	src/gpu/GrGpu.cpp \
 	src/gpu/GrGpuResource.cpp \
 	src/gpu/GrGpuFactory.cpp \
 	src/gpu/GrInvariantOutput.cpp \
+	src/gpu/GrInOrderCommandBuilder.cpp \
 	src/gpu/GrInOrderDrawBuffer.cpp \
 	src/gpu/GrLayerCache.cpp \
 	src/gpu/GrLayerHoister.cpp \
@@ -464,25 +480,32 @@
 	src/gpu/GrRecordReplaceDraw.cpp \
 	src/gpu/GrRectanizer_pow2.cpp \
 	src/gpu/GrRectanizer_skyline.cpp \
+	src/gpu/GrRectBatch.cpp \
 	src/gpu/GrRenderTarget.cpp \
 	src/gpu/GrReducedClip.cpp \
+	src/gpu/GrReorderCommandBuilder.cpp \
 	src/gpu/GrResourceCache.cpp \
+	src/gpu/GrResourceProvider.cpp \
 	src/gpu/GrStencil.cpp \
 	src/gpu/GrStencilAndCoverPathRenderer.cpp \
 	src/gpu/GrStencilAndCoverTextContext.cpp \
-	src/gpu/GrStencilBuffer.cpp \
+	src/gpu/GrStencilAttachment.cpp \
+	src/gpu/GrStrokeInfo.cpp \
 	src/gpu/GrTargetCommands.cpp \
 	src/gpu/GrTraceMarker.cpp \
 	src/gpu/GrTessellatingPathRenderer.cpp \
+	src/gpu/GrTestUtils.cpp \
 	src/gpu/GrSWMaskHelper.cpp \
 	src/gpu/GrSoftwarePathRenderer.cpp \
 	src/gpu/GrSurface.cpp \
+	src/gpu/GrTextBlobCache.cpp \
 	src/gpu/GrTextContext.cpp \
-	src/gpu/GrFontCache.cpp \
 	src/gpu/GrTexture.cpp \
+	src/gpu/GrTextureProvider.cpp \
 	src/gpu/GrTextureAccess.cpp \
 	src/gpu/GrXferProcessor.cpp \
 	src/gpu/effects/GrConfigConversionEffect.cpp \
+	src/gpu/effects/GrConstColorProcessor.cpp \
 	src/gpu/effects/GrCoverageSetOpXP.cpp \
 	src/gpu/effects/GrCustomXfermode.cpp \
 	src/gpu/effects/GrBezierEffect.cpp \
@@ -492,7 +515,7 @@
 	src/gpu/effects/GrBitmapTextGeoProc.cpp \
 	src/gpu/effects/GrDashingEffect.cpp \
 	src/gpu/effects/GrDisableColorXP.cpp \
-	src/gpu/effects/GrDistanceFieldTextureEffect.cpp \
+	src/gpu/effects/GrDistanceFieldGeoProc.cpp \
 	src/gpu/effects/GrDitherEffect.cpp \
 	src/gpu/effects/GrMatrixConvolutionEffect.cpp \
 	src/gpu/effects/GrOvalEffect.cpp \
@@ -525,7 +548,7 @@
 	src/gpu/gl/GrGLProgramDataManager.cpp \
 	src/gpu/gl/GrGLRenderTarget.cpp \
 	src/gpu/gl/GrGLSL.cpp \
-	src/gpu/gl/GrGLStencilBuffer.cpp \
+	src/gpu/gl/GrGLStencilAttachment.cpp \
 	src/gpu/gl/GrGLTexture.cpp \
 	src/gpu/gl/GrGLUtil.cpp \
 	src/gpu/gl/GrGLVertexArray.cpp \
@@ -576,21 +599,26 @@
 	libft2
 
 LOCAL_STATIC_LIBRARIES := \
+	libgif \
 	libwebp-decode \
 	libwebp-encode \
-	libgif \
 	libsfntly
 
 LOCAL_C_INCLUDES := \
+	external/jpeg \
+	external/libpng \
+	$(LOCAL_PATH)/include/codec \
+	$(LOCAL_PATH)/src/codec \
+	$(LOCAL_PATH)/src/core \
 	$(LOCAL_PATH)/include/c \
 	$(LOCAL_PATH)/include/config \
 	$(LOCAL_PATH)/include/core \
 	$(LOCAL_PATH)/include/pathops \
 	$(LOCAL_PATH)/include/pipe \
+	external/giflib \
 	$(LOCAL_PATH)/include/ports \
 	$(LOCAL_PATH)/include/utils \
 	$(LOCAL_PATH)/include/images \
-	$(LOCAL_PATH)/src/core \
 	$(LOCAL_PATH)/src/sfnt \
 	$(LOCAL_PATH)/src/image \
 	$(LOCAL_PATH)/src/opts \
@@ -599,15 +627,10 @@
 	$(LOCAL_PATH)/src/gpu \
 	$(LOCAL_PATH)/include/effects \
 	$(LOCAL_PATH)/src/effects \
-	external/jpeg \
 	$(LOCAL_PATH)/src/lazy \
 	$(LOCAL_PATH)/third_party/etc1 \
 	$(LOCAL_PATH)/third_party/ktx \
 	external/webp/include \
-	external/giflib \
-	external/libpng \
-	$(LOCAL_PATH)/include/codec \
-	$(LOCAL_PATH)/src/codec \
 	external/expat/lib \
 	external/freetype/include \
 	$(LOCAL_PATH)/include/utils/win \
@@ -617,6 +640,7 @@
 	external/zlib
 
 LOCAL_EXPORT_C_INCLUDE_DIRS := \
+	$(LOCAL_PATH)/include/codec \
 	$(LOCAL_PATH)/include/c \
 	$(LOCAL_PATH)/include/config \
 	$(LOCAL_PATH)/include/core \
@@ -640,8 +664,7 @@
 	src/opts/SkMorphology_opts_arm.cpp \
 	src/opts/SkTextureCompression_opts_arm.cpp \
 	src/opts/SkUtils_opts_arm.cpp \
-	src/opts/SkXfermode_opts_arm.cpp \
-	src/opts/memset.arm.S
+	src/opts/SkXfermode_opts_arm.cpp
 
 ifeq ($(ARCH_ARM_HAVE_NEON), true)
 LOCAL_SRC_FILES_arm += \
@@ -652,9 +675,8 @@
 	src/opts/SkBlurImage_opts_neon.cpp \
 	src/opts/SkMorphology_opts_neon.cpp \
 	src/opts/SkTextureCompression_opts_neon.cpp \
-	src/opts/SkXfermode_opts_arm_neon.cpp \
-	src/opts/memset16_neon.S \
-	src/opts/memset32_neon.S
+	src/opts/SkUtils_opts_arm_neon.cpp \
+	src/opts/SkXfermode_opts_arm_neon.cpp
 
 LOCAL_CFLAGS_arm += \
 	-DSK_ARM_HAS_NEON
diff --git a/DEPS b/DEPS
index 15917a9..549c1bf 100644
--- a/DEPS
+++ b/DEPS
@@ -3,10 +3,9 @@
 # Dependencies on outside packages.
 #
 deps = {
-  "common": "https://skia.googlesource.com/common.git@d7c2e2b9464e70e0f3847a330b930d008dc8c8db",
+  "common": "https://skia.googlesource.com/common.git@c282fe0b6e392b14f88d647cbd86e1a3ef5498e0",
 
   # There is some duplication here that might be worth cleaning up:
-  #   - both Android and ChromeOS pull the same giflib;
   #   - can use use our existing t_p/e/libjpeg instead of pulling it for Android?
 
   "third_party/externals/angle2"  : "https://chromium.googlesource.com/angle/angle.git@c415283b2bcd786e1a8c55c19ef3511eb2b3928c",
@@ -14,19 +13,18 @@
   "third_party/externals/gyp"     : "https://chromium.googlesource.com/external/gyp.git@dd831fd86e7a254c696f53944333562466e453ad",
   "third_party/externals/harfbuzz": "https://skia.googlesource.com/third_party/harfbuzz.git@0.9.35",
   "third_party/externals/jsoncpp" : "https://chromium.googlesource.com/external/jsoncpp/jsoncpp.git@1afff032c83e26ddf7f2776e8b43de5ad666c1fa",
-  "third_party/externals/libjpeg" : "https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@82ce8a6d4ebe12a177c0c3597192f2b4f09e81c3",
+  "third_party/externals/libjpeg" : "https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@034e9a9747e0983bc19808ea70e469bc8342081f",
   "third_party/externals/libwebp" : "https://chromium.googlesource.com/webm/libwebp.git@3fe91635df8734b23f3c1b9d1f0c4fa8cfaf4e39",
   "third_party/externals/nanomsg" : "https://skia.googlesource.com/third_party/nanomsg.git@0.4-beta",
   "third_party/externals/zlib"    : "https://chromium.googlesource.com/chromium/src/third_party/zlib@4ba7cdd0e7bf49d671645264f839838fc56e1492",
   # NOTE: If we update libpng, we may need to update the generated file at third_party/libpng/pnglibconf.h
-  "third_party/externals/libpng"  : "git://git.code.sf.net/p/libpng/code@070a616b8275277e18ef8ee91e2ca23f7bdc67d5",
+  "third_party/externals/libpng"  : "https://skia.googlesource.com/third_party/libpng.git@070a616b8275277e18ef8ee91e2ca23f7bdc67d5",
+  "third_party/externals/giflib"  : "https://android.googlesource.com/platform/external/giflib.git@android-5.1.0_r3",
 
-  "platform_tools/android/third_party/externals/expat" : "https://android.googlesource.com/platform/external/expat.git@android-4.2.2_r1.2",
-  "platform_tools/android/third_party/externals/gif" : "https://android.googlesource.com/platform/external/giflib.git@android-4.2.2_r1.2",
-  "platform_tools/android/third_party/externals/jpeg" : "https://android.googlesource.com/platform/external/jpeg.git@ef1b83013e7814622a9d11579878d342e84580b7",
+  "platform_tools/android/third_party/externals/expat" : "https://android.googlesource.com/platform/external/expat.git@android-5.1.0_r3",
+  "platform_tools/android/third_party/externals/jpeg" : "https://android.googlesource.com/platform/external/jpeg.git@android-5.1.0_r3",
   "platform_tools/android/third_party/externals/png" : "https://android.googlesource.com/platform/external/libpng.git@android-4.2.2_r1.2",
 
-  "platform_tools/chromeos/third_party/externals/gif" : "https://android.googlesource.com/platform/external/giflib.git@android-4.2.2_r1.2",
   "platform_tools/chromeos/toolchain/src/third_party/chromite": "https://chromium.googlesource.com/chromiumos/chromite.git@d6a4c7e0ee4d53ddc5238dbddfc0417796a70e54",
   "platform_tools/chromeos/toolchain/src/third_party/pyelftools": "https://chromium.googlesource.com/chromiumos/third_party/pyelftools.git@bdc1d380acd88d4bfaf47265008091483b0d614e",
 
diff --git a/Doxyfile b/Doxyfile
index 3497e27..28dd00e 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -39,9 +39,9 @@
 # CLASS_GRAPH = YES
 # COLLABORATION_GRAPH = YES
 # UML_LOOK = YES
-# GRAPHICAL_HIERARCHY = YES 
+# GRAPHICAL_HIERARCHY = YES
 
-# Make SkDEBUGCODE disappear, but not SK_OVERRIDE.
+# Make SkDEBUGCODE disappear.
 ENABLE_PREPROCESSING = YES
 MACRO_EXPANSION = YES
 EXPAND_ONLY_PREDEF = YES
diff --git a/HASHTAGS b/HASHTAGS
new file mode 100644
index 0000000..45fd645
--- /dev/null
+++ b/HASHTAGS
@@ -0,0 +1,22 @@
+# This file is used by the post upload hook in the PRESUBMIT file to
+# automatically change a CL's description based on the specified hashtags.
+# Please see skia:3586 for more details.
+#
+# The format of this file is:
+# hashtag_name,mapped_text
+#
+# Here are some examples:
+# * "projectxyz, BUG=skia:123" would convert "#projectxyz" into "BUG=skia:123".
+# * "notry, NOTRY=true" would convert "#notry" into "NOTRY=true".
+
+dummyproject,BUG=skia:2139,BUG=skia:2812
+notry,NOTRY=true
+nocommit,COMMIT=false
+
+floats,BUG=skia:3592
+neon,#n5,#n9
+n5,CQ_INCLUDE_TRYBOTS=client.skia.android:Test-Android-Nexus5-Adreno330-Arm7-Debug-Trybot
+n7,CQ_INCLUDE_TRYBOTS=client.skia.android:Test-Android-Nexus7-Tegra3-Arm7-Debug-Trybot
+n9,CQ_INCLUDE_TRYBOTS=client.skia.android:Test-Android-Nexus9-TegraK1-Arm64-Release-Trybot
+
+crskps,BUG=skia:3574,NOTRY=true
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index a4d90c8..6d429df 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -9,6 +9,7 @@
 for more details about the presubmit API built into gcl.
 """
 
+import csv
 import fnmatch
 import os
 import re
@@ -21,6 +22,9 @@
 
 SKIA_TREE_STATUS_URL = 'http://skia-tree-status.appspot.com'
 
+CQ_KEYWORDS_THAT_NEED_APPENDING = ('CQ_INCLUDE_TRYBOTS', 'CQ_EXTRA_TRYBOTS',
+                                   'CQ_EXCLUDE_TRYBOTS', 'CQ_TRYBOTS')
+
 # Please add the complete email address here (and not just 'xyz@' or 'xyz').
 PUBLIC_API_OWNERS = (
     'reed@chromium.org',
@@ -77,6 +81,76 @@
       white_list=affected_python_files)
 
 
+def _IfDefChecks(input_api, output_api):
+  """Ensures if/ifdef are not before includes. See skbug/3362 for details."""
+  comment_block_start_pattern = re.compile('^\s*\/\*.*$')
+  comment_block_middle_pattern = re.compile('^\s+\*.*')
+  comment_block_end_pattern = re.compile('^\s+\*\/.*$')
+  single_line_comment_pattern = re.compile('^\s*//.*$')
+  def is_comment(line):
+    return (comment_block_start_pattern.match(line) or
+            comment_block_middle_pattern.match(line) or
+            comment_block_end_pattern.match(line) or
+            single_line_comment_pattern.match(line))
+
+  empty_line_pattern = re.compile('^\s*$')
+  def is_empty_line(line):
+    return empty_line_pattern.match(line)
+
+  failing_files = []
+  for affected_file in input_api.AffectedSourceFiles(None):
+    affected_file_path = affected_file.LocalPath()
+    if affected_file_path.endswith('.cpp') or affected_file_path.endswith('.h'):
+      f = open(affected_file_path)
+      for line in f.xreadlines():
+        if is_comment(line) or is_empty_line(line):
+          continue
+        # The below will be the first real line after comments and newlines.
+        if line.startswith('#if 0 '):
+          pass
+        elif line.startswith('#if ') or line.startswith('#ifdef '):
+          failing_files.append(affected_file_path)
+        break
+
+  results = []
+  if failing_files:
+    results.append(
+        output_api.PresubmitError(
+            'The following files have #if or #ifdef before includes:\n%s\n\n'
+            'See skbug.com/3362 for why this should be fixed.' %
+                '\n'.join(failing_files)))
+  return results
+
+
+def _CopyrightChecks(input_api, output_api, source_file_filter=None):
+  results = []
+  year_pattern = r'\d{4}'
+  year_range_pattern = r'%s(-%s)?' % (year_pattern, year_pattern)
+  years_pattern = r'%s(,%s)*,?' % (year_range_pattern, year_range_pattern)
+  copyright_pattern = (
+      r'Copyright (\([cC]\) )?%s \w+' % years_pattern)
+
+  for affected_file in input_api.AffectedSourceFiles(source_file_filter):
+    if 'third_party' in affected_file.LocalPath():
+      continue
+    contents = input_api.ReadFile(affected_file, 'rb')
+    if not re.search(copyright_pattern, contents):
+      results.append(output_api.PresubmitError(
+          '%s is missing a correct copyright header.' % affected_file))
+  return results
+
+
+def _ToolFlags(input_api, output_api):
+  """Make sure `{dm,nanobench}_flags.py test` passes if modified."""
+  results = []
+  sources = lambda x: ('dm_flags.py'        in x.LocalPath() or
+                       'nanobench_flags.py' in x.LocalPath())
+  for f in input_api.AffectedSourceFiles(sources):
+    if 0 != subprocess.call(['python', f.LocalPath(), 'test']):
+      results.append(output_api.PresubmitError('`python %s test` failed' % f))
+  return results
+
+
 def _CommonChecks(input_api, output_api):
   """Presubmit checks common to upload and commit."""
   results = []
@@ -85,11 +159,20 @@
                        x.LocalPath().endswith('.gyp') or
                        x.LocalPath().endswith('.py') or
                        x.LocalPath().endswith('.sh') or
+                       x.LocalPath().endswith('.m') or
+                       x.LocalPath().endswith('.mm') or
+                       x.LocalPath().endswith('.go') or
+                       x.LocalPath().endswith('.c') or
+                       x.LocalPath().endswith('.cc') or
                        x.LocalPath().endswith('.cpp'))
   results.extend(
       _CheckChangeHasEol(
           input_api, output_api, source_file_filter=sources))
   results.extend(_PythonChecks(input_api, output_api))
+  results.extend(_IfDefChecks(input_api, output_api))
+  results.extend(_CopyrightChecks(input_api, output_api,
+                                  source_file_filter=sources))
+  results.extend(_ToolFlags(input_api, output_api))
   return results
 
 
@@ -212,6 +295,14 @@
       # It is a revert CL, ignore the public api owners check.
       return results
 
+    # TODO(rmistry): Stop checking for COMMIT=false once crbug/470609 is
+    # resolved.
+    if issue_properties['cq_dry_run'] or re.search(
+        r'^COMMIT=false$', issue_properties['description'], re.M):
+      # Ignore public api owners check for dry run CLs since they are not
+      # going to be committed.
+      return results
+
     match = re.search(r'^TBR=(.*)$', issue_properties['description'], re.M)
     if match:
       tbr_entries = match.group(1).strip().split(',')
@@ -315,6 +406,34 @@
                 'Trybots do not yet work for non-master branches. '
                 'Automatically added \'NOTRY=true\' to the CL\'s description'))
 
+    # Read and process the HASHTAGS file.
+    hashtags_fullpath = os.path.join(change._local_root, 'HASHTAGS')
+    with open(hashtags_fullpath, 'rb') as hashtags_csv:
+      hashtags_reader = csv.reader(hashtags_csv, delimiter=',')
+      for row in hashtags_reader:
+        if not row or row[0].startswith('#'):
+          # Ignore empty lines and comments
+          continue
+        hashtag = row[0]
+        # Search for the hashtag in the description.
+        if re.search('#%s' % hashtag, new_description, re.M | re.I):
+          for mapped_text in row[1:]:
+            # Special case handling for CQ_KEYWORDS_THAT_NEED_APPENDING.
+            appended_description = _HandleAppendingCQKeywords(
+                hashtag, mapped_text, new_description, results, output_api)
+            if appended_description:
+              new_description = appended_description
+              continue
+
+            # Add the mapped text if it does not already exist in the
+            # CL's description.
+            if not re.search(
+                r'^%s$' % mapped_text, new_description, re.M | re.I):
+              new_description += '\n%s' % mapped_text
+              results.append(
+                  output_api.PresubmitNotifyResult(
+                      'Found \'#%s\', automatically added \'%s\' to the CL\'s '
+                      'description' % (hashtag, mapped_text)))
 
     # If the description has changed update it.
     if new_description != original_description:
@@ -323,6 +442,31 @@
     return results
 
 
+def _HandleAppendingCQKeywords(hashtag, keyword_and_value, description,
+                               results, output_api):
+  """Handles the CQ keywords that need appending if specified in hashtags."""
+  keyword = keyword_and_value.split('=')[0]
+  if keyword in CQ_KEYWORDS_THAT_NEED_APPENDING:
+    # If the keyword is already in the description then append to it.
+    match = re.search(
+        r'^%s=(.*)$' % keyword, description, re.M | re.I)
+    if match:
+      old_values = match.group(1).split(';')
+      new_value = keyword_and_value.split('=')[1]
+      if new_value in old_values:
+        # Do not need to do anything here.
+        return description
+      # Update the description with the new values.
+      new_description = description.replace(
+          match.group(0), "%s;%s" % (match.group(0), new_value))
+      results.append(
+          output_api.PresubmitNotifyResult(
+          'Found \'#%s\', automatically appended \'%s\' to %s in '
+          'the CL\'s description' % (hashtag, new_value, keyword)))
+      return new_description
+  return None
+
+
 def CheckChangeOnCommit(input_api, output_api):
   """Presubmit checks for the change on commit.
 
diff --git a/SKP_VERSION b/SKP_VERSION
index a36df4e..136c8ca 100644
--- a/SKP_VERSION
+++ b/SKP_VERSION
@@ -1 +1 @@
-269
\ No newline at end of file
+342
\ No newline at end of file
diff --git a/bench/AlternatingColorPatternBench.cpp b/bench/AlternatingColorPatternBench.cpp
index fccc494..7744d72 100644
--- a/bench/AlternatingColorPatternBench.cpp
+++ b/bench/AlternatingColorPatternBench.cpp
@@ -108,7 +108,7 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
@@ -146,10 +146,10 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(false);
-        paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        paint.setFilterQuality(kLow_SkFilterQuality);
 
         for (int i = 0; i < loops; ++i) {
             for (int j = 0; j < NUM_DRAWS; ++j) {
diff --git a/bench/Android.mk b/bench/Android.mk
index cfa8f5c..d6ac6ae 100644
--- a/bench/Android.mk
+++ b/bench/Android.mk
@@ -22,10 +22,12 @@
 
 LOCAL_SRC_FILES := \
 	../gm/gm.cpp \
+	CodecBench.cpp \
 	DecodingBench.cpp \
 	DecodingSubsetBench.cpp \
 	GMBench.cpp \
 	RecordingBench.cpp \
+	SKPAnimationBench.cpp \
 	SKPBench.cpp \
 	nanobench.cpp \
 	Benchmark.cpp \
@@ -48,6 +50,7 @@
 	ColorCubeBench.cpp \
 	ColorFilterBench.cpp \
 	ColorPrivBench.cpp \
+	ControlBench.cpp \
 	CoverageBench.cpp \
 	DashBench.cpp \
 	DeferredSurfaceCopyBench.cpp \
@@ -86,9 +89,9 @@
 	PatchGridBench.cpp \
 	PathBench.cpp \
 	PathIterBench.cpp \
-	PathUtilsBench.cpp \
 	PerlinNoiseBench.cpp \
 	PictureNestingBench.cpp \
+	PictureOverheadBench.cpp \
 	PicturePlaybackBench.cpp \
 	PremulAndUnpremulAlphaOpsBench.cpp \
 	RTreeBench.cpp \
@@ -108,18 +111,23 @@
 	StrokeBench.cpp \
 	TableBench.cpp \
 	TextBench.cpp \
+	TextBlobBench.cpp \
 	TileBench.cpp \
 	VertBench.cpp \
 	WritePixelsBench.cpp \
 	WriterBench.cpp \
 	XfermodeBench.cpp \
+	nanobenchAndroid.cpp \
 	../gm/aaclip.cpp \
 	../gm/aarectmodes.cpp \
 	../gm/addarc.cpp \
+	../gm/all_bitmap_configs.cpp \
 	../gm/alphagradients.cpp \
+	../gm/anisotropic.cpp \
 	../gm/arcofzorro.cpp \
 	../gm/arithmode.cpp \
 	../gm/astcbitmap.cpp \
+	../gm/badpaint.cpp \
 	../gm/beziereffects.cpp \
 	../gm/beziers.cpp \
 	../gm/bigblurs.cpp \
@@ -132,12 +140,15 @@
 	../gm/bitmapscroll.cpp \
 	../gm/bitmapshader.cpp \
 	../gm/bitmapsource.cpp \
+	../gm/bitmapsource2.cpp \
 	../gm/bleed.cpp \
+	../gm/blend.cpp \
 	../gm/blurcircles.cpp \
 	../gm/blurs.cpp \
 	../gm/blurquickreject.cpp \
 	../gm/blurrect.cpp \
 	../gm/blurroundrect.cpp \
+	../gm/bmpfilterqualityrepeat.cpp \
 	../gm/circles.cpp \
 	../gm/circularclips.cpp \
 	../gm/clipdrawdraw.cpp \
@@ -159,6 +170,8 @@
 	../gm/complexclip3.cpp \
 	../gm/composeshader.cpp \
 	../gm/conicpaths.cpp \
+	../gm/constcolorprocessor.cpp \
+	../gm/convex_all_line_paths.cpp \
 	../gm/convexpaths.cpp \
 	../gm/convexpolyclip.cpp \
 	../gm/convexpolyeffect.cpp \
@@ -183,6 +196,7 @@
 	../gm/extractbitmap.cpp \
 	../gm/emboss.cpp \
 	../gm/emptypath.cpp \
+	../gm/fadefilter.cpp \
 	../gm/fatpathfill.cpp \
 	../gm/factory.cpp \
 	../gm/filltypes.cpp \
@@ -219,18 +233,22 @@
 	../gm/lighting.cpp \
 	../gm/lumafilter.cpp \
 	../gm/image.cpp \
+	../gm/imagefilters.cpp \
 	../gm/imagefiltersbase.cpp \
 	../gm/imagefiltersclipped.cpp \
 	../gm/imagefilterscropped.cpp \
 	../gm/imagefiltersgraph.cpp \
 	../gm/imagefiltersscaled.cpp \
+	../gm/imagefilterstransformed.cpp \
 	../gm/internal_links.cpp \
+	../gm/largeglyphblur.cpp \
 	../gm/lcdtext.cpp \
 	../gm/linepaths.cpp \
 	../gm/matrixconvolution.cpp \
 	../gm/matriximagefilter.cpp \
 	../gm/megalooper.cpp \
 	../gm/mixedxfermodes.cpp \
+	../gm/mixedtextblobs.cpp \
 	../gm/mipmap.cpp \
 	../gm/modecolorfilters.cpp \
 	../gm/morphology.cpp \
@@ -254,6 +272,7 @@
 	../gm/pictureimagefilter.cpp \
 	../gm/pictureshader.cpp \
 	../gm/pictureshadertile.cpp \
+	../gm/pixelsnap.cpp \
 	../gm/points.cpp \
 	../gm/poly2poly.cpp \
 	../gm/polygons.cpp \
@@ -291,7 +310,11 @@
 	../gm/variedtext.cpp \
 	../gm/tallstretchedbitmaps.cpp \
 	../gm/textblob.cpp \
+	../gm/textbloblooper.cpp \
+	../gm/textblobcolortrans.cpp \
+	../gm/textblobgeometrychange.cpp \
 	../gm/textblobshader.cpp \
+	../gm/textblobtransforms.cpp \
 	../gm/texturedomaineffect.cpp \
 	../gm/thinrects.cpp \
 	../gm/thinstrokedrects.cpp \
@@ -301,7 +324,6 @@
 	../gm/tilemodes_scaled.cpp \
 	../gm/tinybitmap.cpp \
 	../gm/transparency.cpp \
-	../gm/twopointradial.cpp \
 	../gm/typeface.cpp \
 	../gm/vertices.cpp \
 	../gm/verttext.cpp \
@@ -325,6 +347,8 @@
 	../tools/Resources.cpp \
 	../tools/sk_tool_utils.cpp \
 	../tools/sk_tool_utils_font.cpp \
+	../src/utils/android/SkAndroidSDKCanvas.cpp \
+	../src/utils/android/SkHwuiRenderer.cpp \
 	../src/gpu/GrContextFactory.cpp \
 	../src/gpu/GrTest.cpp \
 	../tools/flags/SkCommandLineFlags.cpp
@@ -332,6 +356,10 @@
 LOCAL_SHARED_LIBRARIES := \
 	liblog \
 	libskia \
+	libandroid \
+	libgui \
+	libhwui \
+	libutils \
 	libGLESv2 \
 	libEGL
 
@@ -362,8 +390,11 @@
 	$(LOCAL_PATH)/../src/utils/debugger \
 	$(LOCAL_PATH)/../src/images \
 	$(LOCAL_PATH)/../src/lazy \
+	$(LOCAL_PATH)/../../../frameworks/base/libs/hwui \
+	$(LOCAL_PATH)/../../../frameworks/native/include \
 	$(LOCAL_PATH)/../tools/timer \
-	$(LOCAL_PATH)/../third_party/etc1
+	$(LOCAL_PATH)/../third_party/etc1 \
+	$(LOCAL_PATH)/../src/utils/android
 
 LOCAL_CFLAGS += \
 	-DSK_CRASH_HANDLER
diff --git a/bench/Benchmark.cpp b/bench/Benchmark.cpp
index 6040f91..bf622b0 100644
--- a/bench/Benchmark.cpp
+++ b/bench/Benchmark.cpp
@@ -53,7 +53,7 @@
 void Benchmark::setupPaint(SkPaint* paint) {
     paint->setAlpha(fForceAlpha);
     paint->setAntiAlias(true);
-    paint->setFilterLevel(SkPaint::kNone_FilterLevel);
+    paint->setFilterQuality(kNone_SkFilterQuality);
 
     paint->setFlags((paint->getFlags() & ~fClearMask) | fOrMask);
 
diff --git a/bench/Benchmark.h b/bench/Benchmark.h
index c30167e..22b52ab 100644
--- a/bench/Benchmark.h
+++ b/bench/Benchmark.h
@@ -13,11 +13,10 @@
 #include "SkString.h"
 #include "SkTRegistry.h"
 
-#define DEF_BENCH(code)                                                 \
-namespace {                                                             \
-static Benchmark* SK_MACRO_APPEND_LINE(factory)(void*) { code; }      \
-BenchRegistry SK_MACRO_APPEND_LINE(g_R_)(SK_MACRO_APPEND_LINE(factory)); \
-}
+#define DEF_BENCH3(code, N) \
+    static BenchRegistry gBench##N([](void*) -> Benchmark* { code; });
+#define DEF_BENCH2(code, N) DEF_BENCH3(code, N)
+#define DEF_BENCH(code) DEF_BENCH2(code, __COUNTER__)
 
 /*
  *  With the above macros, you can register benches as follows (at the bottom
@@ -57,6 +56,7 @@
         kRaster_Backend,
         kGPU_Backend,
         kPDF_Backend,
+        kHWUI_Backend,
     };
 
     // Call to determine whether the benchmark is intended for
diff --git a/bench/BigPathBench.cpp b/bench/BigPathBench.cpp
index c46296e..f18e3de 100644
--- a/bench/BigPathBench.cpp
+++ b/bench/BigPathBench.cpp
@@ -38,19 +38,19 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    SkIPoint onGetSize() SK_OVERRIDE {
+    SkIPoint onGetSize() override {
         return SkIPoint::Make(640, 100);
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         make_path(fPath);
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setStyle(SkPaint::kStroke_Style);
diff --git a/bench/BitmapBench.cpp b/bench/BitmapBench.cpp
index c5efe9e..6220247 100644
--- a/bench/BitmapBench.cpp
+++ b/bench/BitmapBench.cpp
@@ -247,7 +247,7 @@
         INHERITED::onDraw(loops, canvas);
     }
 
-    void setupPaint(SkPaint* paint) SK_OVERRIDE {
+    void setupPaint(SkPaint* paint) override {
         this->INHERITED::setupPaint(paint);
 
         int index = 0;
@@ -257,13 +257,13 @@
         if (fFlags & kBicubic_Flag) {
             index |= 2;
         }
-        static const SkPaint::FilterLevel gLevels[] = {
-            SkPaint::kNone_FilterLevel,
-            SkPaint::kLow_FilterLevel,
-            SkPaint::kMedium_FilterLevel,
-            SkPaint::kHigh_FilterLevel
+        static const SkFilterQuality gQualitys[] = {
+            kNone_SkFilterQuality,
+            kLow_SkFilterQuality,
+            kMedium_SkFilterQuality,
+            kHigh_SkFilterQuality
         };
-        paint->setFilterLevel(gLevels[index]);
+        paint->setFilterQuality(gQualitys[index]);
 }
 
 private:
@@ -303,7 +303,7 @@
         return fFullName.c_str();
     }
 
-    void onDrawIntoBitmap(const SkBitmap& bm) SK_OVERRIDE {
+    void onDrawIntoBitmap(const SkBitmap& bm) override {
         const int w = bm.width();
         const int h = bm.height();
 
diff --git a/bench/BitmapRectBench.cpp b/bench/BitmapRectBench.cpp
index fb3ebda..d3a7574 100644
--- a/bench/BitmapRectBench.cpp
+++ b/bench/BitmapRectBench.cpp
@@ -42,32 +42,32 @@
     SkBitmap                fBitmap;
     bool                    fSlightMatrix;
     uint8_t                 fAlpha;
-    SkPaint::FilterLevel    fFilterLevel;
+    SkFilterQuality         fFilterQuality;
     SkString                fName;
     SkRect                  fSrcR, fDstR;
 
     static const int kWidth = 128;
     static const int kHeight = 128;
 public:
-    BitmapRectBench(U8CPU alpha, SkPaint::FilterLevel filterLevel,
+    BitmapRectBench(U8CPU alpha, SkFilterQuality filterQuality,
                     bool slightMatrix)  {
         fAlpha = SkToU8(alpha);
-        fFilterLevel = filterLevel;
+        fFilterQuality = filterQuality;
         fSlightMatrix = slightMatrix;
 
         fBitmap.setInfo(SkImageInfo::MakeN32Premul(kWidth, kHeight));
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         fName.printf("bitmaprect_%02X_%sfilter_%s",
                      fAlpha,
-                     SkPaint::kNone_FilterLevel == fFilterLevel ? "no" : "",
+                     kNone_SkFilterQuality == fFilterQuality ? "no" : "",
                      fSlightMatrix ? "trans" : "identity");
         return fName.c_str();
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         fBitmap.allocPixels();
         fBitmap.setAlphaType(kOpaque_SkAlphaType);
         fBitmap.eraseColor(SK_ColorBLACK);
@@ -87,12 +87,12 @@
     }
 
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRandom rand;
 
         SkPaint paint;
         this->setupPaint(&paint);
-        paint.setFilterLevel(fFilterLevel);
+        paint.setFilterQuality(fFilterQuality);
         paint.setAlpha(fAlpha);
 
         for (int i = 0; i < loops; i++) {
@@ -104,10 +104,10 @@
     typedef Benchmark INHERITED;
 };
 
-DEF_BENCH(return new BitmapRectBench(0xFF, SkPaint::kNone_FilterLevel, false))
-DEF_BENCH(return new BitmapRectBench(0x80, SkPaint::kNone_FilterLevel, false))
-DEF_BENCH(return new BitmapRectBench(0xFF, SkPaint::kLow_FilterLevel, false))
-DEF_BENCH(return new BitmapRectBench(0x80, SkPaint::kLow_FilterLevel, false))
+DEF_BENCH(return new BitmapRectBench(0xFF, kNone_SkFilterQuality, false))
+DEF_BENCH(return new BitmapRectBench(0x80, kNone_SkFilterQuality, false))
+DEF_BENCH(return new BitmapRectBench(0xFF, kLow_SkFilterQuality, false))
+DEF_BENCH(return new BitmapRectBench(0x80, kLow_SkFilterQuality, false))
 
-DEF_BENCH(return new BitmapRectBench(0xFF, SkPaint::kNone_FilterLevel, true))
-DEF_BENCH(return new BitmapRectBench(0xFF, SkPaint::kLow_FilterLevel, true))
+DEF_BENCH(return new BitmapRectBench(0xFF, kNone_SkFilterQuality, true))
+DEF_BENCH(return new BitmapRectBench(0xFF, kLow_SkFilterQuality, true))
diff --git a/bench/BitmapScaleBench.cpp b/bench/BitmapScaleBench.cpp
index e5cd587..f71e10f 100644
--- a/bench/BitmapScaleBench.cpp
+++ b/bench/BitmapScaleBench.cpp
@@ -48,7 +48,7 @@
         return float(outputSize())/inputSize();
     }
 
-    SkIPoint onGetSize() SK_OVERRIDE {
+    SkIPoint onGetSize() override {
         return SkIPoint::Make( fOutputSize, fOutputSize );
     }
 
@@ -88,11 +88,11 @@
         setName( "filter" );
     }
 protected:
-    void doScaleImage() SK_OVERRIDE {
+    void doScaleImage() override {
         SkCanvas canvas( fOutputBitmap );
         SkPaint paint;
 
-        paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
+        paint.setFilterQuality(kHigh_SkFilterQuality);
         fInputBitmap.notifyPixelsChanged();
         canvas.concat(fMatrix);
         canvas.drawBitmap(fInputBitmap, 0, 0, &paint );
diff --git a/bench/BlurImageFilterBench.cpp b/bench/BlurImageFilterBench.cpp
index 68d0e6c..476f192 100644
--- a/bench/BlurImageFilterBench.cpp
+++ b/bench/BlurImageFilterBench.cpp
@@ -31,18 +31,18 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         if (!fInitialized) {
             make_checkerboard();
             fInitialized = true;
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         paint.setImageFilter(SkBlurImageFilter::Create(fSigmaX, fSigmaY))->unref();
 
diff --git a/bench/BlurRectBench.cpp b/bench/BlurRectBench.cpp
index 66aff22..8f8a2de 100644
--- a/bench/BlurRectBench.cpp
+++ b/bench/BlurRectBench.cpp
@@ -88,7 +88,7 @@
         this->setName(name);
     }
 protected:
-    void makeBlurryRect(const SkRect& r) SK_OVERRIDE {
+    void makeBlurryRect(const SkRect& r) override {
         SkMask mask;
         SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(this->radius()),
                              &mask, r, kNormal_SkBlurStyle);
@@ -110,7 +110,7 @@
     }
 
 protected:
-    void preBenchSetup(const SkRect& r) SK_OVERRIDE {
+    void preBenchSetup(const SkRect& r) override {
         SkMask::FreeImage(fSrcMask.fImage);
 
         r.roundOut(&fSrcMask.fBounds);
@@ -142,7 +142,7 @@
 
 protected:
 
-    void makeBlurryRect(const SkRect&) SK_OVERRIDE {
+    void makeBlurryRect(const SkRect&) override {
         SkMask mask;
         mask.fImage = NULL;
         SkBlurMask::BoxBlur(&mask, fSrcMask, SkBlurMask::ConvertRadiusToSigma(this->radius()),
@@ -169,7 +169,7 @@
 
 protected:
 
-    void makeBlurryRect(const SkRect&) SK_OVERRIDE {
+    void makeBlurryRect(const SkRect&) override {
         SkMask mask;
         mask.fImage = NULL;
         SkBlurMask::BlurGroundTruth(SkBlurMask::ConvertRadiusToSigma(this->radius()),
diff --git a/bench/BlurRectsBench.cpp b/bench/BlurRectsBench.cpp
index 553cf6b..8384771 100644
--- a/bench/BlurRectsBench.cpp
+++ b/bench/BlurRectsBench.cpp
@@ -21,7 +21,7 @@
         fInner = inner;
     }
 
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
@@ -29,7 +29,7 @@
         fName = name;
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         paint.setMaskFilter(SkBlurMaskFilter::Create(kNormal_SkBlurStyle, fRadius))->unref();
 
diff --git a/bench/BlurRoundRectBench.cpp b/bench/BlurRoundRectBench.cpp
index 7ec89b8..9f1e143 100644
--- a/bench/BlurRoundRectBench.cpp
+++ b/bench/BlurRoundRectBench.cpp
@@ -30,16 +30,16 @@
         fRRect.setRectXY(r, SkIntToScalar(cornerRadius), SkIntToScalar(cornerRadius));
     }
 
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    SkIPoint onGetSize() SK_OVERRIDE {
+    SkIPoint onGetSize() override {
         return SkIPoint::Make(SkScalarCeilToInt(fRRect.rect().width()),
                               SkScalarCeilToInt(fRRect.rect().height()));
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkLayerDrawLooper::Builder looperBuilder;
         {
             SkLayerDrawLooper::LayerInfo info;
diff --git a/bench/ChartBench.cpp b/bench/ChartBench.cpp
index 175e1dd..0cc02b0 100644
--- a/bench/ChartBench.cpp
+++ b/bench/ChartBench.cpp
@@ -94,7 +94,7 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         if (fAA) {
             return "chart_aa";
         } else {
@@ -102,7 +102,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         bool sizeChanged = false;
         if (canvas->getDeviceSize() != fSize) {
             fSize = canvas->getDeviceSize();
diff --git a/bench/ChecksumBench.cpp b/bench/ChecksumBench.cpp
index 661f9e6..9dd87c3 100644
--- a/bench/ChecksumBench.cpp
+++ b/bench/ChecksumBench.cpp
@@ -35,7 +35,7 @@
         }
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
diff --git a/bench/CmapBench.cpp b/bench/CmapBench.cpp
index ca0b25d..10591ed 100644
--- a/bench/CmapBench.cpp
+++ b/bench/CmapBench.cpp
@@ -81,11 +81,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         fProc(loops, fPaint, fText, sizeof(fText), NGLYPHS);
     }
 
diff --git a/bench/CodecBench.cpp b/bench/CodecBench.cpp
new file mode 100644
index 0000000..debda71
--- /dev/null
+++ b/bench/CodecBench.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "CodecBench.h"
+#include "SkBitmap.h"
+#include "SkCodec.h"
+#include "SkImageGenerator.h"
+#include "SkOSFile.h"
+
+CodecBench::CodecBench(SkString baseName, SkData* encoded, SkColorType colorType)
+    : fColorType(colorType)
+    , fData(SkRef(encoded))
+{
+    // Parse filename and the color type to give the benchmark a useful name
+    const char* colorName;
+    switch(colorType) {
+        case kN32_SkColorType:
+            colorName = "N32";
+            break;
+        case kRGB_565_SkColorType:
+            colorName = "565";
+            break;
+        case kAlpha_8_SkColorType:
+            colorName = "Alpha8";
+            break;
+        default:
+            colorName = "Unknown";
+    }
+    fName.printf("Codec_%s_%s", baseName.c_str(), colorName);
+#ifdef SK_DEBUG
+    // Ensure that we can create an SkCodec from this data.
+    SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(fData));
+    SkASSERT(codec);
+#endif
+}
+
+const char* CodecBench::onGetName() {
+    return fName.c_str();
+}
+
+bool CodecBench::isSuitableFor(Backend backend) {
+    return kNonRendering_Backend == backend;
+}
+
+void CodecBench::onPreDraw() {
+    SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(fData));
+
+    fInfo = codec->getInfo().makeColorType(fColorType);
+    SkAlphaType alphaType;
+    // Caller should not have created this CodecBench if the alpha type was
+    // invalid.
+    SkAssertResult(SkColorTypeValidateAlphaType(fColorType, fInfo.alphaType(),
+                                                &alphaType));
+    if (alphaType != fInfo.alphaType()) {
+        fInfo = fInfo.makeAlphaType(alphaType);
+    }
+
+    fPixelStorage.reset(fInfo.getSafeSize(fInfo.minRowBytes()));
+}
+
+void CodecBench::onDraw(const int n, SkCanvas* canvas) {
+    SkAutoTDelete<SkCodec> codec;
+    SkPMColor colorTable[256];
+    int colorCount;
+    for (int i = 0; i < n; i++) {
+        colorCount = 256;
+        codec.reset(SkCodec::NewFromData(fData));
+#ifdef SK_DEBUG
+        const SkImageGenerator::Result result =
+#endif
+        codec->getPixels(fInfo, fPixelStorage.get(), fInfo.minRowBytes(),
+                         NULL, colorTable, &colorCount);
+        SkASSERT(result == SkImageGenerator::kSuccess
+                 || result == SkImageGenerator::kIncompleteInput);
+    }
+}
diff --git a/bench/CodecBench.h b/bench/CodecBench.h
new file mode 100644
index 0000000..0675cca
--- /dev/null
+++ b/bench/CodecBench.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CodecBench_DEFINED
+#define CodecBench_DEFINED
+
+#include "Benchmark.h"
+#include "SkData.h"
+#include "SkImageInfo.h"
+#include "SkRefCnt.h"
+#include "SkString.h"
+
+/**
+ *  Time SkCodec.
+ */
+class CodecBench : public Benchmark {
+public:
+    // Calls encoded->ref()
+    CodecBench(SkString basename, SkData* encoded, SkColorType colorType);
+
+protected:
+    const char* onGetName() override;
+    bool isSuitableFor(Backend backend) override;
+    void onDraw(const int n, SkCanvas* canvas) override;
+    void onPreDraw() override;
+
+private:
+    SkString                fName;
+    const SkColorType       fColorType;
+    SkAutoTUnref<SkData>    fData;
+    SkImageInfo             fInfo;          // Set in onPreDraw.
+    SkAutoMalloc            fPixelStorage;
+    typedef Benchmark INHERITED;
+};
+#endif // CodecBench_DEFINED
diff --git a/bench/ColorCubeBench.cpp b/bench/ColorCubeBench.cpp
index 0d1e589..9326a12 100644
--- a/bench/ColorCubeBench.cpp
+++ b/bench/ColorCubeBench.cpp
@@ -27,22 +27,22 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "colorcube";
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         if (!SkToBool(fCubeData)) {
             this->makeCubeData();
             this->make_bitmap();
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         this->test(loops, canvas);
     }
 
-    SkIPoint onGetSize() SK_OVERRIDE {
+    SkIPoint onGetSize() override {
         return SkIPoint::Make(fSize.width(), fSize.height());
     }
 
diff --git a/bench/ColorFilterBench.cpp b/bench/ColorFilterBench.cpp
index b8cfbc3..4f7f151 100644
--- a/bench/ColorFilterBench.cpp
+++ b/bench/ColorFilterBench.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 "Benchmark.h"
 #include "SkCanvas.h"
 #include "SkColorFilterImageFilter.h"
@@ -19,7 +20,7 @@
 class ColorFilterBaseBench : public Benchmark {
 
 public:
-    ColorFilterBaseBench(bool small) : fIsSmall(small) { }
+    ColorFilterBaseBench(bool small) : fIsSmall(small) {}
 
 protected:
     SkRect getFilterRect() const {
@@ -55,6 +56,7 @@
     }
 
     inline bool isSmall() const { return fIsSmall; }
+
 private:
     bool fIsSmall;
 
@@ -68,11 +70,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return isSmall() ? "colorfilter_dim_bright_small" : "colorfilter_dim_bright_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRect r = getFilterRect();
         SkPaint paint;
         paint.setColor(SK_ColorRED);
@@ -98,11 +100,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return isSmall() ? "colorfilter_bright_gray_small" : "colorfilter_bright_gray_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRect r = getFilterRect();
         SkPaint paint;
         paint.setColor(SK_ColorRED);
@@ -125,11 +127,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return isSmall() ? "colorfilter_gray_bright_small" : "colorfilter_gray_bright_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRect r = getFilterRect();
         SkPaint paint;
         paint.setColor(SK_ColorRED);
@@ -152,11 +154,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return isSmall() ? "colorfilter_blue_bright_small" : "colorfilter_blue_bright_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRect r = getFilterRect();
         SkPaint paint;
         paint.setColor(SK_ColorRED);
@@ -179,11 +181,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return isSmall() ? "colorfilter_bright_blue_small" : "colorfilter_bright_blue_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRect r = getFilterRect();
         SkPaint paint;
         paint.setColor(SK_ColorRED);
@@ -206,11 +208,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return isSmall() ? "colorfilter_bright_small" : "colorfilter_bright_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRect r = getFilterRect();
         SkPaint paint;
         paint.setColor(SK_ColorRED);
@@ -232,11 +234,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return isSmall() ? "colorfilter_blue_small" : "colorfilter_blue_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRect r = getFilterRect();
         SkPaint paint;
         paint.setColor(SK_ColorRED);
@@ -254,15 +256,14 @@
 class ColorFilterGrayBench : public ColorFilterBaseBench {
 
 public:
-    ColorFilterGrayBench(bool small) : INHERITED(small) {
-    }
+    ColorFilterGrayBench(bool small) : INHERITED(small) {}
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return isSmall() ? "colorfilter_gray_small" : "colorfilter_gray_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRect r = getFilterRect();
         SkPaint paint;
         paint.setColor(SK_ColorRED);
@@ -277,71 +278,6 @@
     typedef ColorFilterBaseBench INHERITED;
 };
 
-class TableColorFilterBench : public ColorFilterBaseBench {
-
-public:
-    TableColorFilterBench(bool small) : INHERITED(small) {
-    }
-
-protected:
-    const char* onGetName() SK_OVERRIDE {
-        return isSmall() ? "table_colorfilter_small" : "table_colorfilter_large";
-    }
-
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
-        SkRect r = getFilterRect();
-        SkPaint paint;
-        paint.setColor(SK_ColorRED);
-        for (int i = 0; i < loops; i++) {
-            SkAutoTUnref<SkColorFilter> table_filter(make_table_filter());
-            paint.setColorFilter(table_filter);
-            canvas->drawRect(r, paint);
-        }
-    }
-
-private:
-    static void fill_table_data(uint8_t table[]) {
-        for (int i = 0; i < 256; ++i) {
-            int n = i >> 5;
-            table[i] = (n << 5) | (n << 2) | (n >> 1);
-        }
-    }
-
-    static SkColorFilter* make_table_filter() {
-        uint8_t table[256]; fill_table_data(table);
-        return SkTableColorFilter::Create(table);
-    }
-
-    typedef ColorFilterBaseBench INHERITED;
-};
-
-class LumaColorFilterBench : public ColorFilterBaseBench {
-
-public:
-    LumaColorFilterBench(bool small) : INHERITED(small) {
-    }
-
-protected:
-    const char* onGetName() SK_OVERRIDE {
-        return isSmall() ? "luma_colorfilter_small" : "luma_colorfilter_large";
-    }
-
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
-        SkRect r = getFilterRect();
-        SkPaint paint;
-        paint.setColor(SK_ColorRED);
-
-        for (int i = 0; i < loops; i++) {
-            SkAutoTUnref<SkColorFilter> luma_filter(SkLumaColorFilter::Create());
-            paint.setColorFilter(luma_filter);
-            canvas->drawRect(r, paint);
-        }
-    }
-
-private:
-    typedef ColorFilterBaseBench INHERITED;
-};
-
 ///////////////////////////////////////////////////////////////////////////////
 
 DEF_BENCH( return new ColorFilterDimBrightBench(true); )
@@ -352,8 +288,6 @@
 DEF_BENCH( return new ColorFilterBrightBench(true); )
 DEF_BENCH( return new ColorFilterBlueBench(true); )
 DEF_BENCH( return new ColorFilterGrayBench(true); )
-DEF_BENCH( return new TableColorFilterBench(true); )
-DEF_BENCH( return new LumaColorFilterBench(true); )
 
 DEF_BENCH( return new ColorFilterDimBrightBench(false); )
 DEF_BENCH( return new ColorFilterBrightGrayBench(false); )
@@ -363,5 +297,3 @@
 DEF_BENCH( return new ColorFilterBrightBench(false); )
 DEF_BENCH( return new ColorFilterBlueBench(false); )
 DEF_BENCH( return new ColorFilterGrayBench(false); )
-DEF_BENCH( return new TableColorFilterBench(false); )
-DEF_BENCH( return new LumaColorFilterBench(false); )
diff --git a/bench/ColorPrivBench.cpp b/bench/ColorPrivBench.cpp
index 4d04e89..e6c68b5 100644
--- a/bench/ColorPrivBench.cpp
+++ b/bench/ColorPrivBench.cpp
@@ -12,13 +12,13 @@
         fName.append(kScale ? "_255" : "_256");
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
-    const char* onGetName() SK_OVERRIDE { return fName.c_str(); }
+    const char* onGetName() override { return fName.c_str(); }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         // A handful of random srcs and dsts.
         SkRandom rand;
         for (int i = 0; i < kInputs; i++) {
@@ -33,7 +33,7 @@
         if (kScale) fScales[256] = 255;  // We'll just do 255 twice if we're limited to [0,255].
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         // We xor results of FourByteInterp into junk to make sure the function runs.
         volatile SkPMColor junk = 0;
 
diff --git a/bench/ControlBench.cpp b/bench/ControlBench.cpp
new file mode 100644
index 0000000..8c1177a
--- /dev/null
+++ b/bench/ControlBench.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Benchmark.h"
+
+// This benchmark's runtime should be fairly constant for a given machine,
+// so it can be used as a baseline to control for thermal or other throttling.
+
+struct ControlBench : public Benchmark {
+    const char* onGetName() override { return "control"; }
+    bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
+
+    void onDraw(const int loops, SkCanvas*) override {
+        // Nothing terribly useful: force a memory read, a memory write, and some math.
+        volatile uint32_t rand = 0;
+        for (int i = 0; i < 1000*loops; i++) {
+            rand *= 1664525;
+            rand += 1013904223;
+        }
+    }
+};
+DEF_BENCH(return new ControlBench;)
diff --git a/bench/CoverageBench.cpp b/bench/CoverageBench.cpp
index 097a9e1..96eafaa 100644
--- a/bench/CoverageBench.cpp
+++ b/bench/CoverageBench.cpp
@@ -44,11 +44,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         if (fDrawCoverage) {
             for (int i = 0; i < loops; ++i) {
                 fDraw.drawPathCoverage(fPath, fPaint);
diff --git a/bench/DashBench.cpp b/bench/DashBench.cpp
index 5f20969..bf0d456 100644
--- a/bench/DashBench.cpp
+++ b/bench/DashBench.cpp
@@ -57,11 +57,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         this->setupPaint(&paint);
         paint.setStyle(SkPaint::kStroke_Style);
@@ -106,7 +106,7 @@
 
 protected:
     virtual void handlePath(SkCanvas* canvas, const SkPath& path,
-                            const SkPaint& paint, int N) SK_OVERRIDE {
+                            const SkPaint& paint, int N) override {
         SkPoint pts[2];
         if (!path.isLine(pts) || pts[0].fY != pts[1].fY) {
             this->INHERITED::handlePath(canvas, path, paint, N);
@@ -153,8 +153,7 @@
 
 static void make_poly(SkPath* path) {
     make_unit_star(path, 9);
-    SkMatrix matrix;
-    matrix.setScale(SkIntToScalar(100), SkIntToScalar(100));
+    const SkMatrix matrix = SkMatrix::MakeScale(SkIntToScalar(100), SkIntToScalar(100));
     path->transform(matrix);
 }
 
@@ -190,11 +189,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         SkPath dst;
         for (int i = 0; i < loops; ++i) {
             SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle);
@@ -228,11 +227,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         this->setupPaint(&paint);
         paint.setStrokeWidth(fStrokeWidth);
@@ -267,11 +266,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint p;
         this->setupPaint(&p);
         p.setColor(SK_ColorBLACK);
@@ -353,11 +352,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint p;
         this->setupPaint(&p);
         p.setStyle(SkPaint::kStroke_Style);
@@ -393,11 +392,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint p;
         this->setupPaint(&p);
         p.setColor(SK_ColorBLACK);
diff --git a/bench/DecodingBench.cpp b/bench/DecodingBench.cpp
index c8490ca..6cf3c3a 100644
--- a/bench/DecodingBench.cpp
+++ b/bench/DecodingBench.cpp
@@ -6,8 +6,10 @@
  */
 
 #include "DecodingBench.h"
+#include "SkBitmap.h"
 #include "SkData.h"
 #include "SkImageDecoder.h"
+#include "SkMallocPixelRef.h"
 #include "SkOSFile.h"
 #include "SkStream.h"
 
@@ -19,6 +21,7 @@
  */
 DecodingBench::DecodingBench(SkString path, SkColorType colorType)
     : fColorType(colorType)
+    , fData(SkData::NewFromFileName(path.c_str()))
 {
     // Parse filename and the color type to give the benchmark a useful name
     SkString baseName = SkOSPath::Basename(path.c_str());
@@ -38,10 +41,12 @@
     }
     fName.printf("Decode_%s_%s", baseName.c_str(), colorName);
     
-    // Perform setup for the decode
-    SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
-    fStream.reset(new SkMemoryStream(encoded));
-    fDecoder.reset(SkImageDecoder::Factory(fStream.get()));
+#ifdef SK_DEBUG
+    // Ensure that we can create a decoder.
+    SkAutoTDelete<SkStreamRewindable> stream(new SkMemoryStream(fData));
+    SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
+    SkASSERT(decoder != NULL);
+#endif
 }
 
 const char* DecodingBench::onGetName() {
@@ -52,11 +57,54 @@
     return kNonRendering_Backend == backend;
 }
 
+void DecodingBench::onPreDraw() {
+    // Allocate the pixels now, to remove it from the loop.
+    SkAutoTDelete<SkStreamRewindable> stream(new SkMemoryStream(fData));
+    SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
+    SkBitmap bm;
+#ifdef SK_DEBUG
+    SkImageDecoder::Result result =
+#endif
+    decoder->decode(stream, &bm, fColorType, SkImageDecoder::kDecodeBounds_Mode);
+    SkASSERT(SkImageDecoder::kFailure != result);
+
+    const size_t rowBytes = bm.info().minRowBytes();
+    fPixelStorage.reset(bm.info().getSafeSize(rowBytes));
+}
+
+// Allocator which just uses an existing block of memory.
+class TargetAllocator : public SkBitmap::Allocator {
+public:
+    explicit TargetAllocator(void* storage)
+        : fPixelStorage(storage) {}
+
+    bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) override {
+        // We depend on the fact that this will only ever be used to
+        // decode to a bitmap with the same settings used to create
+        // fPixelStorage.
+        bm->setPixelRef(SkMallocPixelRef::NewDirect(bm->info(),
+                fPixelStorage, bm->rowBytes(), ct))->unref();
+        return true;
+    }
+
+private:
+    void* fPixelStorage; // Unowned. DecodingBench owns this.
+};
+
 void DecodingBench::onDraw(const int n, SkCanvas* canvas) {
     SkBitmap bitmap;
+    // Declare the allocator before the decoder, so it will outlive the
+    // decoder, which will unref it.
+    TargetAllocator allocator(fPixelStorage.get());
+    SkAutoTDelete<SkImageDecoder> decoder;
+    SkAutoTDelete<SkStreamRewindable> stream;
     for (int i = 0; i < n; i++) {
-        fStream->rewind();
-        fDecoder->decode(fStream, &bitmap, fColorType,
-                         SkImageDecoder::kDecodePixels_Mode);
+        // create a new stream and a new decoder to mimic the behavior of
+        // CodecBench.
+        stream.reset(new SkMemoryStream(fData));
+        decoder.reset(SkImageDecoder::Factory(stream));
+        decoder->setAllocator(&allocator);
+        decoder->decode(stream, &bitmap, fColorType,
+                        SkImageDecoder::kDecodePixels_Mode);
     }
 }
diff --git a/bench/DecodingBench.h b/bench/DecodingBench.h
index b41941b..a32bfea 100644
--- a/bench/DecodingBench.h
+++ b/bench/DecodingBench.h
@@ -5,10 +5,13 @@
  * found in the LICENSE file.
  */
 
+#ifndef DecodingBench_DEFINED
+#define DecodingBench_DEFINED
+
 #include "Benchmark.h"
+#include "SkData.h"
 #include "SkImageDecoder.h"
-#include "SkImageInfo.h"
-#include "SkStream.h"
+#include "SkRefCnt.h"
 #include "SkString.h"
 
 /*
@@ -22,14 +25,16 @@
     DecodingBench(SkString path, SkColorType colorType);
 
 protected:
-    const char* onGetName() SK_OVERRIDE;
-    bool isSuitableFor(Backend backend) SK_OVERRIDE;
-    void onDraw(const int n, SkCanvas* canvas) SK_OVERRIDE;
-    
+    const char* onGetName() override;
+    bool isSuitableFor(Backend backend) override;
+    void onDraw(const int n, SkCanvas* canvas) override;
+    void onPreDraw() override;
+
 private:
-    SkString fName;
-    SkColorType fColorType;
-    SkAutoTDelete<SkMemoryStream> fStream;
-    SkAutoTDelete<SkImageDecoder> fDecoder;
+    SkString                fName;
+    SkColorType             fColorType;
+    SkAutoTUnref<SkData>    fData;
+    SkAutoMalloc            fPixelStorage;
     typedef Benchmark INHERITED;
 };
+#endif // DecodingBench_DEFINED
diff --git a/bench/DecodingSubsetBench.h b/bench/DecodingSubsetBench.h
index 8034c8f..4941aa1 100644
--- a/bench/DecodingSubsetBench.h
+++ b/bench/DecodingSubsetBench.h
@@ -23,9 +23,9 @@
             const int divisor);
 
 protected:
-    const char* onGetName() SK_OVERRIDE;
-    bool isSuitableFor(Backend backend) SK_OVERRIDE;
-    void onDraw(const int n, SkCanvas* canvas) SK_OVERRIDE;
+    const char* onGetName() override;
+    bool isSuitableFor(Backend backend) override;
+    void onDraw(const int n, SkCanvas* canvas) override;
     
 private:
     SkString fName;
diff --git a/bench/DeferredSurfaceCopyBench.cpp b/bench/DeferredSurfaceCopyBench.cpp
index e0bf8e7..3644627 100644
--- a/bench/DeferredSurfaceCopyBench.cpp
+++ b/bench/DeferredSurfaceCopyBench.cpp
@@ -26,12 +26,12 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fDiscardableContents ? "DeferredSurfaceCopy_discardable" :
             "DeferredSurfaceCopy_nonDiscardable";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         // The canvas is not actually used for this test except to provide
         // configuration information: gpu, multisampling, size, etc?
         SkImageInfo info = SkImageInfo::MakeN32Premul(kSurfaceWidth, kSurfaceHeight);
diff --git a/bench/DisplacementBench.cpp b/bench/DisplacementBench.cpp
index 2f9a69e..b9070cb 100644
--- a/bench/DisplacementBench.cpp
+++ b/bench/DisplacementBench.cpp
@@ -22,7 +22,7 @@
     }
 
 protected:
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         if (!fInitialized) {
             this->makeBitmap();
             this->makeCheckerboard();
@@ -91,11 +91,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return this->isSmall() ? "displacement_zero_small" : "displacement_zero_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         SkAutoTUnref<SkImageFilter> displ(SkBitmapSource::Create(fCheckerboard));
         // No displacement effect
@@ -118,11 +118,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return isSmall() ? "displacement_alpha_small" : "displacement_alpha_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         SkAutoTUnref<SkImageFilter> displ(SkBitmapSource::Create(fCheckerboard));
         // Displacement, with 1 alpha component (which isn't pre-multiplied)
@@ -144,11 +144,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return isSmall() ? "displacement_full_small" : "displacement_full_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         SkAutoTUnref<SkImageFilter> displ(SkBitmapSource::Create(fCheckerboard));
         // Displacement, with 2 non-alpha components
diff --git a/bench/ETCBitmapBench.cpp b/bench/ETCBitmapBench.cpp
index 351b0ea..1cc61e6 100644
--- a/bench/ETCBitmapBench.cpp
+++ b/bench/ETCBitmapBench.cpp
@@ -122,12 +122,12 @@
     ETCBitmapBench(bool decompress, Backend backend)
         : fDecompress(decompress), fBackend(backend) { }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == this->fBackend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         if (kGPU_Backend == this->fBackend) {
             if (this->fDecompress) {
                 return "etc1bitmap_render_gpu_decompressed";
@@ -144,7 +144,7 @@
         }
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         if (NULL == fPKMData) {
             SkDebugf("Failed to load PKM data!\n");
             return;
@@ -162,7 +162,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         for (int i = 0; i < loops; ++i) {
             canvas->drawBitmap(this->fBitmap, 0, 0, NULL);
         }
@@ -187,7 +187,7 @@
         : ETCBitmapBench(decompress, backend) { }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         if (kGPU_Backend == this->backend()) {
             if (this->decompress()) {
                 return "etc1bitmap_upload_gpu_decompressed";
@@ -204,7 +204,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPixelRef* pr = fBitmap.pixelRef();
         for (int i = 0; i < loops; ++i) {
             if (pr) {
diff --git a/bench/FSRectBench.cpp b/bench/FSRectBench.cpp
index af99015..914b5b6 100644
--- a/bench/FSRectBench.cpp
+++ b/bench/FSRectBench.cpp
@@ -20,9 +20,9 @@
     FSRectBench() : fInit(false) { }
 
 protected:
-    const char* onGetName() SK_OVERRIDE { return "fullscreen_rects"; }
+    const char* onGetName() override { return "fullscreen_rects"; }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         if (!fInit) {
             SkRandom rand;
             static const SkScalar kMinOffset = 0;
@@ -39,7 +39,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         for (int i = 0; i < loops; ++i) {
             paint.setColor(fColors[i % N]);
diff --git a/bench/FontCacheBench.cpp b/bench/FontCacheBench.cpp
index 3bff063..304d8d3 100644
--- a/bench/FontCacheBench.cpp
+++ b/bench/FontCacheBench.cpp
@@ -28,11 +28,11 @@
     FontCacheBench()  {}
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "fontcache";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         this->setupPaint(&paint);
         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
@@ -104,11 +104,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "fontefficiency";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         static bool gDone;
         if (gDone) {
             return;
diff --git a/bench/GMBench.h b/bench/GMBench.h
index 43ca981..82ca3c9 100644
--- a/bench/GMBench.h
+++ b/bench/GMBench.h
@@ -21,10 +21,10 @@
     virtual ~GMBench();
 
 protected:
-    const char* onGetName() SK_OVERRIDE;
-    bool isSuitableFor(Backend backend) SK_OVERRIDE;
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE;
-    SkIPoint onGetSize() SK_OVERRIDE;
+    const char* onGetName() override;
+    bool isSuitableFor(Backend backend) override;
+    void onDraw(const int loops, SkCanvas* canvas) override;
+    SkIPoint onGetSize() override;
 
 private:
     skiagm::GM* fGM;
diff --git a/bench/GameBench.cpp b/bench/GameBench.cpp
index bf70b5c..d9a397e 100644
--- a/bench/GameBench.cpp
+++ b/bench/GameBench.cpp
@@ -74,11 +74,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         if (!fInitialized) {
             this->makeCheckerboard();
             this->makeAtlas();
@@ -86,7 +86,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRandom scaleRand;
         SkRandom transRand;
         SkRandom rotRand;
@@ -134,11 +134,11 @@
 
         SkPaint p;
         p.setColor(0xFF000000);
-        p.setFilterLevel(SkPaint::kLow_FilterLevel);
+        p.setFilterQuality(kLow_SkFilterQuality);
 
         SkPaint p2;         // for drawVertices path
         p2.setColor(0xFF000000);
-        p2.setFilterLevel(SkPaint::kLow_FilterLevel);
+        p2.setFilterQuality(kLow_SkFilterQuality);
         p2.setShader(SkShader::CreateBitmapShader(fAtlas,
                                                   SkShader::kClamp_TileMode,
                                                   SkShader::kClamp_TileMode))->unref();
diff --git a/bench/GeometryBench.cpp b/bench/GeometryBench.cpp
index fd86eff..6cd9ca2 100644
--- a/bench/GeometryBench.cpp
+++ b/bench/GeometryBench.cpp
@@ -16,11 +16,11 @@
         fName.printf("geo_%s", suffix);
     }
 
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return kNonRendering_Backend == backend;
     }
 
@@ -65,7 +65,7 @@
     GeoRectBench_intersect() : GeoRectBench("rect_intersect") {}
 
 protected:
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         for (int outer = 0; outer < loops; ++outer) {
             int count = 0;
             for (size_t i = 0; i < SK_ARRAY_COUNT(fRects); ++i) {
@@ -82,7 +82,7 @@
     GeoRectBench_intersect_rect() : GeoRectBench("rect_intersect_rect") {}
 
 protected:
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         for (int outer = 0; outer < loops; ++outer) {
             int count = 0;
             SkRect r;
@@ -99,7 +99,7 @@
     GeoRectBench_Intersects() : GeoRectBench("rect_Intersects") {}
     
 protected:
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         for (int outer = 0; outer < loops; ++outer) {
             int count = 0;
             for (size_t i = 0; i < SK_ARRAY_COUNT(fRects); ++i) {
@@ -115,7 +115,7 @@
     GeoRectBench_sort() : GeoRectBench("rect_sort") {}
     
 protected:
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         for (int outer = 0; outer < loops; ++outer) {
             for (size_t i = 0; i < SK_ARRAY_COUNT(fRects); ++i) {
                 fRects[i].sort();
@@ -129,3 +129,118 @@
 DEF_BENCH( return new GeoRectBench_Intersects; )
 
 DEF_BENCH( return new GeoRectBench_sort; )
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class QuadBenchBase : public GeometryBench {
+protected:
+    SkPoint fPts[4];
+public:
+    QuadBenchBase(const char name[]) : GeometryBench(name) {
+        SkRandom rand;
+        for (int i = 0; i < 4; ++i) {
+            fPts[i].set(rand.nextUScalar1(), rand.nextUScalar1());
+        }
+    }
+};
+
+class EvalQuadAt0 : public QuadBenchBase {
+public:
+    EvalQuadAt0() : QuadBenchBase("evalquadat0") {}
+protected:
+    void onDraw(const int loops, SkCanvas* canvas) override {
+        SkPoint result;
+        for (int outer = 0; outer < loops; ++outer) {
+            SkEvalQuadAt(fPts, 0.5f, &result);
+            SkEvalQuadAt(fPts, 0.5f, &result);
+            SkEvalQuadAt(fPts, 0.5f, &result);
+            SkEvalQuadAt(fPts, 0.5f, &result);
+        }
+    }
+};
+DEF_BENCH( return new EvalQuadAt0; )
+
+class EvalQuadAt1 : public QuadBenchBase {
+public:
+    EvalQuadAt1() : QuadBenchBase("evalquadat1") {}
+protected:
+    void onDraw(const int loops, SkCanvas* canvas) override {
+        SkPoint result;
+        for (int outer = 0; outer < loops; ++outer) {
+            result = SkEvalQuadAt(fPts, 0.5f);
+            result = SkEvalQuadAt(fPts, 0.5f);
+            result = SkEvalQuadAt(fPts, 0.5f);
+            result = SkEvalQuadAt(fPts, 0.5f);
+        }
+    }
+};
+DEF_BENCH( return new EvalQuadAt1; )
+
+////////
+
+class EvalQuadTangentAt0 : public QuadBenchBase {
+public:
+    EvalQuadTangentAt0() : QuadBenchBase("evalquadtangentat0") {}
+protected:
+    void onDraw(const int loops, SkCanvas* canvas) override {
+        SkPoint result;
+        for (int outer = 0; outer < loops; ++outer) {
+            SkEvalQuadAt(fPts, 0.5f, NULL, &result);
+            SkEvalQuadAt(fPts, 0.5f, NULL, &result);
+            SkEvalQuadAt(fPts, 0.5f, NULL, &result);
+            SkEvalQuadAt(fPts, 0.5f, NULL, &result);
+        }
+    }
+};
+DEF_BENCH( return new EvalQuadTangentAt0; )
+
+class EvalQuadTangentAt1 : public QuadBenchBase {
+public:
+    EvalQuadTangentAt1() : QuadBenchBase("evalquadtangentat1") {}
+protected:
+    void onDraw(const int loops, SkCanvas* canvas) override {
+        SkPoint result;
+        for (int outer = 0; outer < loops; ++outer) {
+            result = SkEvalQuadTangentAt(fPts, 0.5f);
+            result = SkEvalQuadTangentAt(fPts, 0.5f);
+            result = SkEvalQuadTangentAt(fPts, 0.5f);
+            result = SkEvalQuadTangentAt(fPts, 0.5f);
+        }
+    }
+};
+DEF_BENCH( return new EvalQuadTangentAt1; )
+
+////////
+
+class ChopQuadAt : public QuadBenchBase {
+public:
+    ChopQuadAt() : QuadBenchBase("chopquadat") {}
+protected:
+    void onDraw(const int loops, SkCanvas* canvas) override {
+        SkPoint dst[5];
+        for (int outer = 0; outer < loops; ++outer) {
+            SkChopQuadAt(fPts, dst, 0.5f);
+            SkChopQuadAt(fPts, dst, 0.5f);
+            SkChopQuadAt(fPts, dst, 0.5f);
+            SkChopQuadAt(fPts, dst, 0.5f);
+        }
+    }
+};
+DEF_BENCH( return new ChopQuadAt; )
+
+class ChopCubicAt : public QuadBenchBase {
+public:
+    ChopCubicAt() : QuadBenchBase("chopcubicat0") {}
+protected:
+    void onDraw(const int loops, SkCanvas* canvas) override {
+        SkPoint dst[7];
+        for (int outer = 0; outer < loops; ++outer) {
+            SkChopCubicAt(fPts, dst, 0.5f);
+            SkChopCubicAt(fPts, dst, 0.5f);
+            SkChopCubicAt(fPts, dst, 0.5f);
+            SkChopCubicAt(fPts, dst, 0.5f);
+        }
+    }
+};
+DEF_BENCH( return new ChopCubicAt; )
+
diff --git a/bench/GrMemoryPoolBench.cpp b/bench/GrMemoryPoolBench.cpp
index 320ff00..55690a4 100644
--- a/bench/GrMemoryPoolBench.cpp
+++ b/bench/GrMemoryPoolBench.cpp
@@ -32,7 +32,7 @@
  */
 class GrMemoryPoolBenchStack : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -94,7 +94,7 @@
  */
 class GrMemoryPoolBenchRandom : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -142,7 +142,7 @@
         M = 4 * (1 << 10),
     };
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
diff --git a/bench/GrOrderedSetBench.cpp b/bench/GrOrderedSetBench.cpp
index 280ed2c..f532a83 100644
--- a/bench/GrOrderedSetBench.cpp
+++ b/bench/GrOrderedSetBench.cpp
@@ -21,25 +21,25 @@
         fName.append("ordered_set_build");
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return kNonRendering_Backend == backend;
     }
 
     virtual ~GrOrderedSetBuildBench() {}
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         SkRandom rand;
         for (int j = 0; j < NUM_ELEMENTS; ++j) {
             fData[j] = rand.nextU() % NUM_ELEMENTS;
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         for (int i = 0; i < loops; ++i) {
             GrOrderedSet<int> set;
             for (int j = 0; j < NUM_ELEMENTS; ++j) {
@@ -62,18 +62,18 @@
         fName.append("ordered_set_find");
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return kNonRendering_Backend == backend;
     }
 
     virtual ~GrOrderedSetFindBench() {}
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         SkRandom rand;
         for (int j = 0; j < NUM_ELEMENTS; ++j) {
             fData[j] = rand.nextU() % 1500;
@@ -81,7 +81,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         for (int i = 0; i < loops; ++i) {
             for (int j = 0; j < NUM_ELEMENTS; ++j) {
                 fSet.find(fData[j]);
@@ -103,25 +103,25 @@
         fName.append("ordered_set_remove");
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return kNonRendering_Backend == backend;
     }
 
     virtual ~GrOrderedSetRemoveBench() {}
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         SkRandom rand;
         for (int j = 0; j < NUM_ELEMENTS; ++j) {
             fSet.insert(rand.nextU() % NUM_ELEMENTS);
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         typedef GrOrderedSet<int>::Iter SetIter;
         for (int i = 0; i < loops; ++i) {
             GrOrderedSet<int> testSet;
diff --git a/bench/GrResourceCacheBench.cpp b/bench/GrResourceCacheBench.cpp
index def736c..5807654 100644
--- a/bench/GrResourceCacheBench.cpp
+++ b/bench/GrResourceCacheBench.cpp
@@ -36,7 +36,7 @@
     }
 
 private:
-    size_t onGpuMemorySize() const SK_OVERRIDE { return 100; }
+    size_t onGpuMemorySize() const override { return 100; }
 
     typedef GrGpuResource INHERITED;
 };
@@ -53,16 +53,16 @@
 
 class GrResourceCacheBenchAdd : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "grresourcecache_add";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkAutoTUnref<GrContext> context(GrContext::CreateMockContext());
         if (NULL == context) {
             return;
@@ -90,16 +90,16 @@
 
 class GrResourceCacheBenchFind : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "grresourcecache_find";
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         fContext.reset(GrContext::CreateMockContext());
         if (!fContext) {
             return;
@@ -118,7 +118,7 @@
         populate_cache(gpu, CACHE_SIZE_COUNT);
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         if (!fContext) {
             return;
         }
diff --git a/bench/GradientBench.cpp b/bench/GradientBench.cpp
index e5e5fc3..21b7370 100644
--- a/bench/GradientBench.cpp
+++ b/bench/GradientBench.cpp
@@ -72,20 +72,6 @@
 }
 
 /// Ignores scale
-static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
-                             SkShader::TileMode tm, float scale) {
-    SkPoint center0, center1;
-    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
-                SkScalarAve(pts[0].fY, pts[1].fY));
-    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
-                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
-    return SkGradientShader::CreateTwoPointRadial(
-                                                  center1, (pts[1].fX - pts[0].fX) / 7,
-                                                  center0, (pts[1].fX - pts[0].fX) / 2,
-                                                  data.fColors, data.fPos, data.fCount, tm);
-}
-
-/// Ignores scale
 static SkShader* MakeConical(const SkPoint pts[2], const GradData& data,
                              SkShader::TileMode tm, float scale) {
     SkPoint center0, center1;
@@ -115,8 +101,8 @@
 static SkShader* MakeConicalOutside(const SkPoint pts[2], const GradData& data,
                                     SkShader::TileMode tm, float scale) {
     SkPoint center0, center1;
-    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
     return SkGradientShader::CreateTwoPointConical(center0, radius0,
@@ -129,8 +115,8 @@
 static SkShader* MakeConicalOutsideZeroRad(const SkPoint pts[2], const GradData& data,
                                            SkShader::TileMode tm, float scale) {
     SkPoint center0, center1;
-    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
     return SkGradientShader::CreateTwoPointConical(center0, 0.0,
@@ -149,7 +135,6 @@
     { MakeLinear,                 "linear"  },
     { MakeRadial,                 "radial1" },
     { MakeSweep,                  "sweep"   },
-    { Make2Radial,                "radial2" },
     { MakeConical,                "conical" },
     { MakeConicalZeroRad,         "conicalZero" },
     { MakeConicalOutside,         "conicalOut" },
@@ -160,7 +145,6 @@
     kLinear_GradType,
     kRadial_GradType,
     kSweep_GradType,
-    kRadial2_GradType,
     kConical_GradType,
     kConicalZero_GradType,
     kConicalOut_GradType,
@@ -313,9 +297,6 @@
 DEF_BENCH( return new GradientBench(kSweep_GradType); )
 DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[1]); )
 DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[2]); )
-DEF_BENCH( return new GradientBench(kRadial2_GradType); )
-DEF_BENCH( return new GradientBench(kRadial2_GradType, gGradData[1]); )
-DEF_BENCH( return new GradientBench(kRadial2_GradType, gGradData[0], SkShader::kMirror_TileMode); )
 DEF_BENCH( return new GradientBench(kConical_GradType); )
 DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[1]); )
 DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[2]); )
diff --git a/bench/HairlinePathBench.cpp b/bench/HairlinePathBench.cpp
index f52fc3b..e421ce0 100644
--- a/bench/HairlinePathBench.cpp
+++ b/bench/HairlinePathBench.cpp
@@ -42,7 +42,7 @@
     virtual void makePath(SkPath*) = 0;
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         fName.printf("path_hairline_%s_%s_",
                      fFlags & kBig_Flag ? "big" : "small",
                      fFlags & kAA_Flag ? "AA" : "noAA");
@@ -50,7 +50,7 @@
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint(fPaint);
         this->setupPaint(&paint);
 
@@ -59,13 +59,14 @@
         SkPath path;
         this->makePath(&path);
         if (fFlags & kBig_Flag) {
-            SkMatrix m;
-            m.setScale(SkIntToScalar(3), SkIntToScalar(3));
+            const SkMatrix m = SkMatrix::MakeScale(SkIntToScalar(3), SkIntToScalar(3));
             path.transform(m);
         }
 
         for (int i = 0; i < loops; i++) {
-            canvas->drawPath(path, paint);
+            for (int j = 0; j < 100; ++j) {
+                canvas->drawPath(path, paint);
+            }
         }
     }
 
@@ -80,10 +81,10 @@
 public:
     LinePathBench(Flags flags) : INHERITED(flags) {}
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("line");
     }
-    void makePath(SkPath* path) SK_OVERRIDE {
+    void makePath(SkPath* path) override {
         SkRandom rand;
         int size = SK_ARRAY_COUNT(points);
         int hSize = size / 2;
@@ -112,10 +113,10 @@
 public:
     QuadPathBench(Flags flags) : INHERITED(flags) {}
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("quad");
     }
-    void makePath(SkPath* path) SK_OVERRIDE {
+    void makePath(SkPath* path) override {
         SkRandom rand;
         int size = SK_ARRAY_COUNT(points);
         int hSize = size / 2;
@@ -144,10 +145,10 @@
 public:
     ConicPathBench(Flags flags) : INHERITED(flags) {}
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("conic");
     }
-    void makePath(SkPath* path) SK_OVERRIDE {
+    void makePath(SkPath* path) override {
         SkRandom rand;
         SkRandom randWeight;
         int size = SK_ARRAY_COUNT(points);
@@ -180,10 +181,10 @@
 public:
     CubicPathBench(Flags flags) : INHERITED(flags) {}
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("cubic");
     }
-    void makePath(SkPath* path) SK_OVERRIDE {
+    void makePath(SkPath* path) override {
         SkRandom rand;
         int size = SK_ARRAY_COUNT(points);
         int hSize = size / 2;
diff --git a/bench/ImageCacheBench.cpp b/bench/ImageCacheBench.cpp
index 75adc50..4a068b0 100644
--- a/bench/ImageCacheBench.cpp
+++ b/bench/ImageCacheBench.cpp
@@ -24,8 +24,8 @@
 
     TestRec(const TestKey& key, intptr_t value) : fKey(key), fValue(value) {}
 
-    const Key& getKey() const SK_OVERRIDE { return fKey; }
-    size_t bytesUsed() const SK_OVERRIDE { return sizeof(fKey) + sizeof(fValue); }
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override { return sizeof(fKey) + sizeof(fValue); }
 
     static bool Visitor(const SkResourceCache::Rec&, void*) {
         return true;
@@ -49,11 +49,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "imagecache";
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         if (fCache.getTotalBytesUsed() == 0) {
             this->populateCache();
         }
diff --git a/bench/ImageFilterCollapse.cpp b/bench/ImageFilterCollapse.cpp
index 6cda6ac..c631c99 100644
--- a/bench/ImageFilterCollapse.cpp
+++ b/bench/ImageFilterCollapse.cpp
@@ -33,13 +33,13 @@
         fImageFilter = NULL;
         for(int i = nFilters; i --> 0;) {
             SkAutoTUnref<SkImageFilter> filter(
-                        SkColorFilterImageFilter::Create(colorFilters[i], fImageFilter, NULL, 0)
+                        SkColorFilterImageFilter::Create(colorFilters[i], fImageFilter, NULL)
             );
             SkRefCnt_SafeAssign(fImageFilter, filter.get());
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         makeBitmap();
 
         for(int i = 0; i < loops; i++) {
@@ -79,11 +79,11 @@
     virtual ~TableCollapseBench() {}
 
 protected:
-    virtual const char* onGetName() SK_OVERRIDE {
+    virtual const char* onGetName() override {
         return "image_filter_collapse_table";
     }
 
-    virtual void onPreDraw() SK_OVERRIDE {
+    virtual void onPreDraw() override {
         for (int i = 0; i < 256; ++i) {
             int n = i >> 5;
             table1[i] = (n << 5) | (n << 2) | (n >> 1);
@@ -135,11 +135,11 @@
     virtual ~MatrixCollapseBench() {}
 
 protected:
-    virtual const char* onGetName() SK_OVERRIDE {
+    virtual const char* onGetName() override {
         return "image_filter_collapse_matrix";
     }
 
-    virtual void onPreDraw() SK_OVERRIDE {
+    virtual void onPreDraw() override {
         SkColorFilter* colorFilters[] = {
             make_brightness(0.1f),
             make_grayscale(),
diff --git a/bench/ImageFilterDAGBench.cpp b/bench/ImageFilterDAGBench.cpp
index 0e5d4d1..47d59bc 100644
--- a/bench/ImageFilterDAGBench.cpp
+++ b/bench/ImageFilterDAGBench.cpp
@@ -21,11 +21,11 @@
     ImageFilterDAGBench() {}
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "image_filter_dag";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         for (int j = 0; j < loops; j++) {
             SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(20.0f, 20.0f));
             SkImageFilter* inputs[kNumInputs];
diff --git a/bench/InterpBench.cpp b/bench/InterpBench.cpp
index c03da4b..9d8f32d 100644
--- a/bench/InterpBench.cpp
+++ b/bench/InterpBench.cpp
@@ -22,7 +22,7 @@
         fDx = 0.1257f;
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -51,7 +51,7 @@
     Fixed16D16Interp() : INHERITED("16.16") {}
 
 protected:
-    void performTest(int16_t dst[], float fx, float dx, int count) SK_OVERRIDE {
+    void performTest(int16_t dst[], float fx, float dx, int count) override {
         SkFixed curr = SkFloatToFixed(fx);
         SkFixed step = SkFloatToFixed(dx);
         for (int i = 0; i < count; i += 4) {
@@ -70,7 +70,7 @@
     Fixed32D32Interp() : INHERITED("32.32") {}
 
 protected:
-    void performTest(int16_t dst[], float fx, float dx, int count) SK_OVERRIDE {
+    void performTest(int16_t dst[], float fx, float dx, int count) override {
         int64_t curr = (int64_t)(fx * 65536 * 655536);
         int64_t step = (int64_t)(dx * 65536 * 655536);
         SkFixed tmp;
@@ -101,7 +101,7 @@
     Fixed16D48Interp() : INHERITED("16.48") {}
 
 protected:
-    void performTest(int16_t dst[], float fx, float dx, int count) SK_OVERRIDE {
+    void performTest(int16_t dst[], float fx, float dx, int count) override {
         int64_t curr = (int64_t)(fx * 65536 * 655536 * 65536);
         int64_t step = (int64_t)(dx * 65536 * 655536 * 65536);
         SkFixed tmp;
@@ -121,7 +121,7 @@
     FloatInterp() : INHERITED("float") {}
 
 protected:
-    void performTest(int16_t dst[], float fx, float dx, int count) SK_OVERRIDE {
+    void performTest(int16_t dst[], float fx, float dx, int count) override {
         SkFixed tmp;
         for (int i = 0; i < count; i += 4) {
             tmp = SkFloatToFixed(fx); dst[i + 0] = TILE(tmp, count); fx += dx;
@@ -139,7 +139,7 @@
     DoubleInterp() : INHERITED("double") {}
 
 protected:
-    void performTest(int16_t dst[], float fx, float dx, int count) SK_OVERRIDE {
+    void performTest(int16_t dst[], float fx, float dx, int count) override {
         double ffx = fx;
         double ddx = dx;
         SkFixed tmp;
diff --git a/bench/LightingBench.cpp b/bench/LightingBench.cpp
index ed0c985..a56ed22 100644
--- a/bench/LightingBench.cpp
+++ b/bench/LightingBench.cpp
@@ -101,11 +101,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fIsSmall ? "lightingpointlitdiffuse_small" : "lightingpointlitdiffuse_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         draw(loops, canvas, SkLightingImageFilter::CreatePointLitDiffuse(getPointLocation(),
                                                                          getWhite(),
                                                                          getSurfaceScale(),
@@ -122,11 +122,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fIsSmall ? "lightingdistantlitdiffuse_small" : "lightingdistantlitdiffuse_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         draw(loops, canvas, SkLightingImageFilter::CreateDistantLitDiffuse(getDistantDirection(),
                                                                            getWhite(),
                                                                            getSurfaceScale(),
@@ -143,11 +143,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fIsSmall ? "lightingspotlitdiffuse_small" : "lightingspotlitdiffuse_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         draw(loops, canvas, SkLightingImageFilter::CreateSpotLitDiffuse(getSpotLocation(),
                                                                         getSpotTarget(),
                                                                         getSpotExponent(),
@@ -167,11 +167,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fIsSmall ? "lightingpointlitspecular_small" : "lightingpointlitspecular_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         draw(loops, canvas, SkLightingImageFilter::CreatePointLitSpecular(getPointLocation(),
                                                                           getWhite(),
                                                                           getSurfaceScale(),
@@ -189,11 +189,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fIsSmall ? "lightingdistantlitspecular_small" : "lightingdistantlitspecular_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         draw(loops, canvas, SkLightingImageFilter::CreateDistantLitSpecular(getDistantDirection(),
                                                                             getWhite(),
                                                                             getSurfaceScale(),
@@ -211,11 +211,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fIsSmall ? "lightingspotlitspecular_small" : "lightingspotlitspecular_large";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         draw(loops, canvas, SkLightingImageFilter::CreateSpotLitSpecular(getSpotLocation(),
                                                                          getSpotTarget(),
                                                                          getSpotExponent(),
diff --git a/bench/LineBench.cpp b/bench/LineBench.cpp
index e5065d2..76231b8 100644
--- a/bench/LineBench.cpp
+++ b/bench/LineBench.cpp
@@ -38,11 +38,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         this->setupPaint(&paint);
 
diff --git a/bench/MagnifierBench.cpp b/bench/MagnifierBench.cpp
index 39bec98e..24bd53c 100644
--- a/bench/MagnifierBench.cpp
+++ b/bench/MagnifierBench.cpp
@@ -22,18 +22,18 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fIsSmall ? "magnifier_small" : "magnifier_large";
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         if (!fInitialized) {
             make_checkerboard();
             fInitialized = true;
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         const int w = fIsSmall ? FILTER_WIDTH_SMALL : FILTER_WIDTH_LARGE;
         const int h = fIsSmall ? FILTER_HEIGHT_SMALL : FILTER_HEIGHT_LARGE;
         SkPaint paint;
diff --git a/bench/MathBench.cpp b/bench/MathBench.cpp
index 5b46d1c..16a6a5a 100644
--- a/bench/MathBench.cpp
+++ b/bench/MathBench.cpp
@@ -31,7 +31,7 @@
         }
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -68,7 +68,7 @@
 
     virtual void performTest(float* SK_RESTRICT dst,
                              const float* SK_RESTRICT src,
-                             int count) SK_OVERRIDE {
+                             int count) override {
         uint32_t* d = SkTCast<uint32_t*>(dst);
         const uint32_t* s = SkTCast<const uint32_t*>(src);
         this->performITest(d, s, count);
@@ -166,7 +166,7 @@
 protected:
     virtual void performITest(uint32_t* SK_RESTRICT dst,
                               const uint32_t* SK_RESTRICT src,
-                              int count) SK_OVERRIDE {
+                              int count) override {
         for (int i = 0; i < count; ++i) {
             dst[i] = QMul64(src[i], (uint8_t)i);
         }
@@ -181,7 +181,7 @@
 protected:
     virtual void performITest(uint32_t* SK_RESTRICT dst,
                               const uint32_t* SK_RESTRICT src,
-                              int count) SK_OVERRIDE {
+                              int count) override {
         for (int i = 0; i < count; ++i) {
             dst[i] = SkAlphaMulQ(src[i], (uint8_t)i);
         }
@@ -285,7 +285,7 @@
         }
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -353,7 +353,7 @@
         }
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -414,7 +414,7 @@
         }
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -470,7 +470,7 @@
         fName = "point_normalize";
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -517,7 +517,7 @@
 
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -553,7 +553,7 @@
         fName.printf("divmod_%s", name);
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
diff --git a/bench/Matrix44Bench.cpp b/bench/Matrix44Bench.cpp
index 3739458..5093fd2 100644
--- a/bench/Matrix44Bench.cpp
+++ b/bench/Matrix44Bench.cpp
@@ -17,7 +17,7 @@
         fName.printf("matrix44_%s", name);
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
diff --git a/bench/MatrixBench.cpp b/bench/MatrixBench.cpp
index 1a35c2e..e3e5c70 100644
--- a/bench/MatrixBench.cpp
+++ b/bench/MatrixBench.cpp
@@ -18,7 +18,7 @@
         fName.printf("matrix_%s", name);
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -94,125 +94,6 @@
     }
 }
 
-// Test the performance of setConcat() non-perspective case:
-// using floating point precision only.
-class FloatConcatMatrixBench : public MatrixBench {
-public:
-    FloatConcatMatrixBench() : INHERITED("concat_floatfloat") {
-        init9(mya);
-        init9(myb);
-        init9(myr);
-    }
-protected:
-    virtual int mulLoopCount() const { return 4; }
-
-    static inline void muladdmul(float a, float b, float c, float d,
-                                   float* result) {
-      *result = a * b + c * d;
-    }
-    virtual void performTest() {
-        const float* a = mya;
-        const float* b = myb;
-        float* r = myr;
-        muladdmul(a[0], b[0], a[1], b[3], &r[0]);
-        muladdmul(a[0], b[1], a[1], b[4], &r[1]);
-        muladdmul(a[0], b[2], a[1], b[5], &r[2]);
-        r[2] += a[2];
-        muladdmul(a[3], b[0], a[4], b[3], &r[3]);
-        muladdmul(a[3], b[1], a[4], b[4], &r[4]);
-        muladdmul(a[3], b[2], a[4], b[5], &r[5]);
-        r[5] += a[5];
-        r[6] = r[7] = 0.0f;
-        r[8] = 1.0f;
-    }
-private:
-    float mya [9];
-    float myb [9];
-    float myr [9];
-    typedef MatrixBench INHERITED;
-};
-
-static inline float SkDoubleToFloat(double x) {
-    return static_cast<float>(x);
-}
-
-// Test the performance of setConcat() non-perspective case:
-// using floating point precision but casting up to float for
-// intermediate results during computations.
-class FloatDoubleConcatMatrixBench : public MatrixBench {
-public:
-    FloatDoubleConcatMatrixBench() : INHERITED("concat_floatdouble") {
-        init9(mya);
-        init9(myb);
-        init9(myr);
-    }
-protected:
-    virtual int mulLoopCount() const { return 4; }
-
-    static inline void muladdmul(float a, float b, float c, float d,
-                                   float* result) {
-      *result = SkDoubleToFloat((double)a * b + (double)c * d);
-    }
-    virtual void performTest() {
-        const float* a = mya;
-        const float* b = myb;
-        float* r = myr;
-        muladdmul(a[0], b[0], a[1], b[3], &r[0]);
-        muladdmul(a[0], b[1], a[1], b[4], &r[1]);
-        muladdmul(a[0], b[2], a[1], b[5], &r[2]);
-        r[2] += a[2];
-        muladdmul(a[3], b[0], a[4], b[3], &r[3]);
-        muladdmul(a[3], b[1], a[4], b[4], &r[4]);
-        muladdmul(a[3], b[2], a[4], b[5], &r[5]);
-        r[5] += a[5];
-        r[6] = r[7] = 0.0f;
-        r[8] = 1.0f;
-    }
-private:
-    float mya [9];
-    float myb [9];
-    float myr [9];
-    typedef MatrixBench INHERITED;
-};
-
-// Test the performance of setConcat() non-perspective case:
-// using double precision only.
-class DoubleConcatMatrixBench : public MatrixBench {
-public:
-    DoubleConcatMatrixBench() : INHERITED("concat_double") {
-        init9(mya);
-        init9(myb);
-        init9(myr);
-    }
-protected:
-    virtual int mulLoopCount() const { return 4; }
-
-    static inline void muladdmul(double a, double b, double c, double d,
-                                   double* result) {
-      *result = a * b + c * d;
-    }
-    virtual void performTest() {
-        const double* a = mya;
-        const double* b = myb;
-        double* r = myr;
-        muladdmul(a[0], b[0], a[1], b[3], &r[0]);
-        muladdmul(a[0], b[1], a[1], b[4], &r[1]);
-        muladdmul(a[0], b[2], a[1], b[5], &r[2]);
-        r[2] += a[2];
-        muladdmul(a[3], b[0], a[4], b[3], &r[3]);
-        muladdmul(a[3], b[1], a[4], b[4], &r[4]);
-        muladdmul(a[3], b[2], a[4], b[5], &r[5]);
-        r[5] += a[5];
-        r[6] = r[7] = 0.0;
-        r[8] = 1.0;
-    }
-private:
-    double mya [9];
-    double myb [9];
-    double myr [9];
-    typedef MatrixBench INHERITED;
-};
-
 class GetTypeMatrixBench : public MatrixBench {
 public:
     GetTypeMatrixBench()
@@ -260,87 +141,6 @@
     typedef MatrixBench INHERITED;
 };
 
-class ScaleTransMixedMatrixBench : public MatrixBench {
- public:
-    ScaleTransMixedMatrixBench() : INHERITED("scaletrans_mixed") {
-        fMatrix.setAll(fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1(),
-                       fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1(),
-                       fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1());
-        int i;
-        for (i = 0; i < kCount; i++) {
-            fSrc[i].fX = fRandom.nextSScalar1();
-            fSrc[i].fY = fRandom.nextSScalar1();
-            fDst[i].fX = fRandom.nextSScalar1();
-            fDst[i].fY = fRandom.nextSScalar1();
-        }
-    }
- protected:
-    virtual void performTest() {
-        SkPoint* dst = fDst;
-        const SkPoint* src = fSrc;
-        int count = kCount;
-        float mx = fMatrix[SkMatrix::kMScaleX];
-        float my = fMatrix[SkMatrix::kMScaleY];
-        float tx = fMatrix[SkMatrix::kMTransX];
-        float ty = fMatrix[SkMatrix::kMTransY];
-        do {
-            dst->fY = SkScalarMulAdd(src->fY, my, ty);
-            dst->fX = SkScalarMulAdd(src->fX, mx, tx);
-            src += 1;
-            dst += 1;
-        } while (--count);
-    }
- private:
-    enum {
-        kCount = 16
-    };
-    SkMatrix fMatrix;
-    SkPoint fSrc [kCount];
-    SkPoint fDst [kCount];
-    SkRandom fRandom;
-    typedef MatrixBench INHERITED;
-};
-
-class ScaleTransDoubleMatrixBench : public MatrixBench {
- public:
-    ScaleTransDoubleMatrixBench() : INHERITED("scaletrans_double") {
-        init9(fMatrix);
-        int i;
-        for (i = 0; i < kCount; i++) {
-            fSrc[i].fX = fRandom.nextSScalar1();
-            fSrc[i].fY = fRandom.nextSScalar1();
-            fDst[i].fX = fRandom.nextSScalar1();
-            fDst[i].fY = fRandom.nextSScalar1();
-        }
-    }
- protected:
-    virtual void performTest() {
-        SkPoint* dst = fDst;
-        const SkPoint* src = fSrc;
-        int count = kCount;
-        // As doubles, on Z600 Linux systems this is 2.5x as expensive as mixed mode
-        float mx = (float) fMatrix[SkMatrix::kMScaleX];
-        float my = (float) fMatrix[SkMatrix::kMScaleY];
-        float tx = (float) fMatrix[SkMatrix::kMTransX];
-        float ty = (float) fMatrix[SkMatrix::kMTransY];
-        do {
-            dst->fY = src->fY * my + ty;
-            dst->fX = src->fX * mx + tx;
-            src += 1;
-            dst += 1;
-        } while (--count);
-    }
- private:
-    enum {
-        kCount = 16
-    };
-    double fMatrix [9];
-    SkPoint fSrc [kCount];
-    SkPoint fDst [kCount];
-    SkRandom fRandom;
-    typedef MatrixBench INHERITED;
-};
-
 class DecomposeMatrixBench : public MatrixBench {
 public:
     DecomposeMatrixBench() : INHERITED("decompose") {}
@@ -428,9 +228,6 @@
 
 DEF_BENCH( return new EqualsMatrixBench(); )
 DEF_BENCH( return new ScaleMatrixBench(); )
-DEF_BENCH( return new FloatConcatMatrixBench(); )
-DEF_BENCH( return new FloatDoubleConcatMatrixBench(); )
-DEF_BENCH( return new DoubleConcatMatrixBench(); )
 DEF_BENCH( return new GetTypeMatrixBench(); )
 DEF_BENCH( return new DecomposeMatrixBench(); )
 
@@ -468,5 +265,37 @@
                            InvertMapRectMatrixBench::kRotate_Flag |
                            InvertMapRectMatrixBench::kTranslate_Flag); )
 
-DEF_BENCH( return new ScaleTransMixedMatrixBench(); )
-DEF_BENCH( return new ScaleTransDoubleMatrixBench(); )
+///////////////////////////////////////////////////////////////////////////////
+
+static SkMatrix make_trans() { return SkMatrix::MakeTrans(2, 3); }
+static SkMatrix make_scale() { SkMatrix m(make_trans()); m.postScale(1.5f, 0.5f); return m; }
+static SkMatrix make_afine() { SkMatrix m(make_trans()); m.postRotate(15); return m; }
+
+class MapPointsMatrixBench : public MatrixBench {
+protected:
+    SkMatrix fM;
+    enum {
+        N = 32
+    };
+    SkPoint fSrc[N], fDst[N];
+public:
+    MapPointsMatrixBench(const char name[], const SkMatrix& m)
+        : MatrixBench(name), fM(m)
+    {
+        SkRandom rand;
+        for (int i = 0; i < N; ++i) {
+            fSrc[i].set(rand.nextSScalar1(), rand.nextSScalar1());
+        }
+    }
+
+    void performTest() override {
+        for (int i = 0; i < 1000000; ++i) {
+            fM.mapPoints(fDst, fSrc, N);
+        }
+    }
+};
+DEF_BENCH( return new MapPointsMatrixBench("mappoints_identity", SkMatrix::I()); )
+DEF_BENCH( return new MapPointsMatrixBench("mappoints_trans", make_trans()); )
+DEF_BENCH( return new MapPointsMatrixBench("mappoints_scale", make_scale()); )
+DEF_BENCH( return new MapPointsMatrixBench("mappoints_affine", make_afine()); )
+
diff --git a/bench/MemcpyBench.cpp b/bench/MemcpyBench.cpp
index 4778d54..4bc1285 100644
--- a/bench/MemcpyBench.cpp
+++ b/bench/MemcpyBench.cpp
@@ -18,15 +18,15 @@
         , fMemcpy32(memcpy32)
         , fName(SkStringPrintf("%s_%d", name, count)) {}
 
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         fDst.reset(fCount);
         fSrc.reset(fCount);
 
@@ -36,7 +36,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         for (int i = 0; i < loops; i++) {
             fMemcpy32(fDst, fSrc, fCount);
         }
diff --git a/bench/MemoryBench.cpp b/bench/MemoryBench.cpp
index c2c9a1a..2b0f2ab 100644
--- a/bench/MemoryBench.cpp
+++ b/bench/MemoryBench.cpp
@@ -21,16 +21,16 @@
         fName.printf("chunkalloc_" SK_SIZE_T_SPECIFIER, minSize);
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         size_t inc = fMinSize >> 4;
         SkASSERT(inc > 0);
         size_t total = fMinSize * 64;
diff --git a/bench/MemsetBench.cpp b/bench/MemsetBench.cpp
index 41b802c..d1682bd 100644
--- a/bench/MemsetBench.cpp
+++ b/bench/MemsetBench.cpp
@@ -6,110 +6,79 @@
  */
 
 #include "Benchmark.h"
-#include "SkCanvas.h"
-#include "SkString.h"
+#include "SkTemplates.h"
 #include "SkUtils.h"
 
+template <typename T, bool kInline>
 class MemsetBench : public Benchmark {
-    SkString    fName;
-
-protected:
-    int      fMinSize;
-    int      fMaxSize;
-    enum {
-        kBufferSize = 10000,
-        VALUE32 = 0x12345678,
-        VALUE16 = 0x1234
-    };
-
-    enum MemsetType {
-        MEMSET16 = 16,
-        MEMSET32 = 32
-    };
-
 public:
-    MemsetBench(MemsetType type, int minSize, int maxSize)  {
-        SkASSERT((minSize < maxSize) && (maxSize <= kBufferSize));
-        fMinSize = minSize;
-        fMaxSize = maxSize;
-        fName.printf("memset%d_%d_%d", type, minSize, maxSize);
-    }
+    explicit MemsetBench(int n)
+        : fN(n)
+        , fBuffer(n)
+        , fName(SkStringPrintf("memset%d_%d%s", sizeof(T)*8, n, kInline ? "_inline" : "")) {}
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
-        return backend == kNonRendering_Backend;
-    }
+    bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
+    const char* onGetName() override { return fName.c_str(); }
 
-    virtual void performTest() = 0;
-
-protected:
-    const char* onGetName() SK_OVERRIDE {
-        return fName.c_str();
-    }
-
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
-        for (int i = 0; i < loops; ++i) {
-            this->performTest();
-        }
-    }
+    void onDraw(const int loops, SkCanvas*) override;
 
 private:
-    typedef Benchmark INHERITED;
+    int fN;
+    SkAutoTMalloc<T> fBuffer;
+    SkString fName;
 };
 
-class Memset32Bench : public MemsetBench {
-    uint32_t kBuffer[kBufferSize + 3];
-public:
-    Memset32Bench(int minSize, int maxSize)
-        : INHERITED(MEMSET32, minSize, maxSize) {}
-
-protected:
-    void performTest() SK_OVERRIDE {
-        for(int j = fMinSize; j < fMaxSize; ++j){
-            sk_memset32(kBuffer, VALUE32, j);
-            sk_memset32(kBuffer + 1, VALUE32, j);
-            sk_memset32(kBuffer + 2, VALUE32, j);
-            sk_memset32(kBuffer + 3, VALUE32, j);
-        }
+template <> void MemsetBench<uint32_t, false>::onDraw(const int loops, SkCanvas*) {
+    for (int i = 0; i < 1000*loops; i++) {
+        sk_memset32(fBuffer.get(), 0xFACEB004, fN);
     }
-private:
-    typedef MemsetBench INHERITED;
-};
+}
 
-class Memset16Bench : public MemsetBench {
-    uint16_t kBuffer[kBufferSize + 7];
-public:
-    Memset16Bench(int minSize, int maxSize)
-        : INHERITED(MEMSET16, minSize, maxSize) {}
-
-protected:
-    void performTest() SK_OVERRIDE {
-        for(int j = fMinSize; j < fMaxSize; ++j){
-            sk_memset16(kBuffer, VALUE16, j);
-            sk_memset16(kBuffer + 1, VALUE16, j);
-            sk_memset16(kBuffer + 2, VALUE16, j);
-            sk_memset16(kBuffer + 3, VALUE16, j);
-            sk_memset16(kBuffer + 4, VALUE16, j);
-            sk_memset16(kBuffer + 5, VALUE16, j);
-            sk_memset16(kBuffer + 6, VALUE16, j);
-            sk_memset16(kBuffer + 7, VALUE16, j);
-        }
+template <> void MemsetBench<uint16_t, false>::onDraw(const int loops, SkCanvas*) {
+    for (int i = 0; i < 1000*loops; i++) {
+        sk_memset16(fBuffer.get(), 0x4973, fN);
     }
-private:
-    typedef MemsetBench INHERITED;
-};
+}
 
-DEF_BENCH(return new Memset32Bench(1, 600);)
-DEF_BENCH(return new Memset32Bench(600, 800);)
-DEF_BENCH(return new Memset32Bench(800, 1000);)
-DEF_BENCH(return new Memset32Bench(1000, 2000);)
-DEF_BENCH(return new Memset32Bench(2000, 3000);)
-DEF_BENCH(return new Memset32Bench(3000, 4000);)
-DEF_BENCH(return new Memset32Bench(4000, 5000);)
+template <typename T>
+static void memsetT(T* dst, T val, int n) {
+    for (int i = 0; i < n; i++) { dst[i] = val; }
+}
 
-DEF_BENCH(return new Memset16Bench(1, 600);)
-DEF_BENCH(return new Memset16Bench(600, 800);)
-DEF_BENCH(return new Memset16Bench(800, 1000);)
-DEF_BENCH(return new Memset16Bench(1000, 2000);)
-DEF_BENCH(return new Memset16Bench(2000, 3000);)
-DEF_BENCH(return new Memset16Bench(3000, 4000);)
-DEF_BENCH(return new Memset16Bench(4000, 5000);)
+template <> void MemsetBench<uint32_t, true>::onDraw(const int loops, SkCanvas*) {
+    for (int i = 0; i < 1000*loops; i++) {
+        memsetT<uint32_t>(fBuffer.get(), 0xFACEB004, fN);
+    }
+}
+
+template <> void MemsetBench<uint16_t, true>::onDraw(const int loops, SkCanvas*) {
+    for (int i = 0; i < 1000*loops; i++) {
+        memsetT<uint16_t>(fBuffer.get(), 0x4973, fN);
+    }
+}
+
+DEF_BENCH(return (new MemsetBench<uint32_t,  true>(1)));
+DEF_BENCH(return (new MemsetBench<uint32_t, false>(1)));
+DEF_BENCH(return (new MemsetBench<uint32_t,  true>(10)));
+DEF_BENCH(return (new MemsetBench<uint32_t, false>(10)));
+DEF_BENCH(return (new MemsetBench<uint32_t,  true>(100)));
+DEF_BENCH(return (new MemsetBench<uint32_t, false>(100)));
+DEF_BENCH(return (new MemsetBench<uint32_t,  true>(1000)));
+DEF_BENCH(return (new MemsetBench<uint32_t, false>(1000)));
+DEF_BENCH(return (new MemsetBench<uint32_t,  true>(10000)));
+DEF_BENCH(return (new MemsetBench<uint32_t, false>(10000)));
+DEF_BENCH(return (new MemsetBench<uint32_t,  true>(100000)));
+DEF_BENCH(return (new MemsetBench<uint32_t, false>(100000)));
+
+DEF_BENCH(return (new MemsetBench<uint16_t,  true>(1)));
+DEF_BENCH(return (new MemsetBench<uint16_t, false>(1)));
+DEF_BENCH(return (new MemsetBench<uint16_t,  true>(10)));
+DEF_BENCH(return (new MemsetBench<uint16_t, false>(10)));
+DEF_BENCH(return (new MemsetBench<uint16_t,  true>(100)));
+DEF_BENCH(return (new MemsetBench<uint16_t, false>(100)));
+DEF_BENCH(return (new MemsetBench<uint16_t,  true>(1000)));
+DEF_BENCH(return (new MemsetBench<uint16_t, false>(1000)));
+DEF_BENCH(return (new MemsetBench<uint16_t,  true>(10000)));
+DEF_BENCH(return (new MemsetBench<uint16_t, false>(10000)));
+DEF_BENCH(return (new MemsetBench<uint16_t,  true>(100000)));
+DEF_BENCH(return (new MemsetBench<uint16_t, false>(100000)));
diff --git a/bench/MergeBench.cpp b/bench/MergeBench.cpp
index 1e8069e..05d6822 100644
--- a/bench/MergeBench.cpp
+++ b/bench/MergeBench.cpp
@@ -20,11 +20,11 @@
     MergeBench(bool small) : fIsSmall(small), fInitialized(false) { }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fIsSmall ? "merge_small" : "merge_large";
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         if (!fInitialized) {
             make_bitmap();
             make_checkerboard();
@@ -32,7 +32,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRect r = fIsSmall ? SkRect::MakeWH(FILTER_WIDTH_SMALL, FILTER_HEIGHT_SMALL) :
                               SkRect::MakeWH(FILTER_WIDTH_LARGE, FILTER_HEIGHT_LARGE);
         SkPaint paint;
diff --git a/bench/MipMapBench.cpp b/bench/MipMapBench.cpp
index cb4658a..a8156ed 100644
--- a/bench/MipMapBench.cpp
+++ b/bench/MipMapBench.cpp
@@ -16,18 +16,18 @@
     MipMapBench() {}
 
 protected:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return kNonRendering_Backend == backend;
     }
 
-    const char* onGetName() SK_OVERRIDE { return "mipmap_build"; }
+    const char* onGetName() override { return "mipmap_build"; }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         fBitmap.allocN32Pixels(1000, 1000, true);
         fBitmap.eraseColor(SK_ColorWHITE);  // so we don't read uninitialized memory
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         for (int i = 0; i < loops; i++) {
             SkMipMap::Build(fBitmap, NULL)->unref();
         }
diff --git a/bench/MutexBench.cpp b/bench/MutexBench.cpp
index b9d54f1..7fd8fab 100644
--- a/bench/MutexBench.cpp
+++ b/bench/MutexBench.cpp
@@ -9,7 +9,7 @@
 
 class MutexBench : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
diff --git a/bench/PMFloatBench.cpp b/bench/PMFloatBench.cpp
index 1da667f..14ccb33 100644
--- a/bench/PMFloatBench.cpp
+++ b/bench/PMFloatBench.cpp
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "Benchmark.h"
 #include "SkPMFloat.h"
 
@@ -14,10 +21,10 @@
 
 // I'm having better luck getting these to constant-propagate away as template parameters.
 template <bool kClamp, bool kWide>
-struct PMFloatBench : public Benchmark {
-    PMFloatBench() {}
+struct PMFloatGetSetBench : public Benchmark {
+    PMFloatGetSetBench() {}
 
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         switch (kClamp << 1 | kWide) {
             case 0: return "SkPMFloat_get_1x";
             case 1: return "SkPMFloat_get_4x";
@@ -27,9 +34,9 @@
         SkFAIL("unreachable");
         return "oh bother";
     }
-    bool isSuitableFor(Backend backend) SK_OVERRIDE { return backend == kNonRendering_Backend; }
+    bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         // Unlike blackhole, junk can and probably will be a register.
         uint32_t junk = 0;
         uint32_t seed = 0;
@@ -49,21 +56,32 @@
             colors[3] = seed + 3;
         #endif
 
-            SkPMFloat floats[4];
+            SkPMFloat fa,fb,fc,fd;
             if (kWide) {
-                SkPMFloat::From4PMColors(floats, colors);
+                SkPMFloat::From4PMColors(colors, &fa, &fb, &fc, &fd);
             } else {
-                for (int i = 0; i < 4; i++) {
-                    floats[i] = SkPMFloat::FromPMColor(colors[i]);
-                }
+                fa = SkPMFloat::FromPMColor(colors[0]);
+                fb = SkPMFloat::FromPMColor(colors[1]);
+                fc = SkPMFloat::FromPMColor(colors[2]);
+                fd = SkPMFloat::FromPMColor(colors[3]);
             }
 
             SkPMColor back[4];
             switch (kClamp << 1 | kWide) {
-                case 0: for (int i = 0; i < 4; i++) { back[i] = floats[i].get(); }     break;
-                case 1: SkPMFloat::To4PMColors(back, floats);                          break;
-                case 2: for (int i = 0; i < 4; i++) { back[i] = floats[i].clamped(); } break;
-                case 3: SkPMFloat::ClampTo4PMColors(back, floats);                     break;
+                case 0: {
+                    back[0] = fa.round();
+                    back[1] = fb.round();
+                    back[2] = fc.round();
+                    back[3] = fd.round();
+                } break;
+                case 1: SkPMFloat::RoundTo4PMColors(fa, fb, fc, fd, back); break;
+                case 2: {
+                    back[0] = fa.roundClamp();
+                    back[1] = fb.roundClamp();
+                    back[2] = fc.roundClamp();
+                    back[3] = fd.roundClamp();
+                } break;
+                case 3: SkPMFloat::RoundClampTo4PMColors(fa, fb, fc, fd, back); break;
             }
             for (int i = 0; i < 4; i++) {
                 junk ^= back[i];
@@ -74,7 +92,42 @@
 };
 
 // Extra () help DEF_BENCH not get confused by the comma inside the <>.
-DEF_BENCH(return (new PMFloatBench< true,  true>);)
-DEF_BENCH(return (new PMFloatBench<false,  true>);)
-DEF_BENCH(return (new PMFloatBench< true, false>);)
-DEF_BENCH(return (new PMFloatBench<false, false>);)
+DEF_BENCH(return (new PMFloatGetSetBench< true,  true>);)
+DEF_BENCH(return (new PMFloatGetSetBench<false,  true>);)
+DEF_BENCH(return (new PMFloatGetSetBench< true, false>);)
+DEF_BENCH(return (new PMFloatGetSetBench<false, false>);)
+
+struct PMFloatGradientBench : public Benchmark {
+    const char* onGetName() override { return "PMFloat_gradient"; }
+    bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
+
+    SkPMColor fDevice[100];
+    void onDraw(const int loops, SkCanvas*) override {
+        Sk4f c0 = SkPMFloat::FromARGB(255, 255, 0, 0),
+             c1 = SkPMFloat::FromARGB(255, 0, 0, 255),
+             dc = c1 - c0,
+             fx(0.1f),
+             dx(0.002f),
+             dcdx(dc*dx),
+             dcdx4(dcdx+dcdx+dcdx+dcdx);
+
+        for (int n = 0; n < loops; n++) {
+            Sk4f a = c0 + dc*fx + Sk4f(0.5f),  // The +0.5f lets us call trunc() instead of get().
+                 b = a + dcdx,
+                 c = b + dcdx,
+                 d = c + dcdx;
+            for (size_t i = 0; i < SK_ARRAY_COUNT(fDevice); i += 4) {
+                fDevice[i+0] = SkPMFloat(a).trunc();
+                fDevice[i+1] = SkPMFloat(b).trunc();
+                fDevice[i+2] = SkPMFloat(c).trunc();
+                fDevice[i+3] = SkPMFloat(d).trunc();
+                a += dcdx4;
+                b += dcdx4;
+                c += dcdx4;
+                d += dcdx4;
+            }
+        }
+    }
+};
+
+DEF_BENCH(return new PMFloatGradientBench;)
diff --git a/bench/PatchBench.cpp b/bench/PatchBench.cpp
index cc9cb84..097e68e 100644
--- a/bench/PatchBench.cpp
+++ b/bench/PatchBench.cpp
@@ -82,7 +82,7 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         SkString vertexMode;
         switch (fVertexMode) {
             case kNone_VertexMode:
@@ -107,7 +107,7 @@
         return fName.c_str();
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         this->setCubics();
         this->setColors();
         this->setTexCoords();
@@ -123,7 +123,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         canvas->scale(fScale.x(), fScale.y());
         for (int i = 0; i < loops; i++) {
             switch (fVertexMode) {
@@ -161,11 +161,11 @@
     SquarePatchBench(SkPoint scale, VertexMode vertexMode)
     : INHERITED(scale, vertexMode) { }
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("square");
     }
 
-    void setCubics() SK_OVERRIDE {
+    void setCubics() override {
         const SkPoint points[SkPatchUtils::kNumCtrlPts] = {
             //top points
             {100,100},{150,100},{250,100}, {300,100},
@@ -187,11 +187,11 @@
     LODDiffPatchBench(SkPoint scale, VertexMode vertexMode)
     : INHERITED(scale, vertexMode) { }
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("LOD_Diff");
     }
 
-    void setCubics() SK_OVERRIDE {
+    void setCubics() override {
         const SkPoint points[SkPatchUtils::kNumCtrlPts] = {
             //top points
             {100,175},{150,100},{250,100}, {300,0},
@@ -213,11 +213,11 @@
     LoopPatchBench(SkPoint scale, VertexMode vertexMode)
     : INHERITED(scale, vertexMode) { }
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("loop");
     }
 
-    void setCubics() SK_OVERRIDE {
+    void setCubics() override {
         const SkPoint points[SkPatchUtils::kNumCtrlPts] = {
             //top points
             {100,100},{300,200},{100,200}, {300,100},
diff --git a/bench/PatchGridBench.cpp b/bench/PatchGridBench.cpp
index 0fe12e2..610d8e7 100644
--- a/bench/PatchGridBench.cpp
+++ b/bench/PatchGridBench.cpp
@@ -163,7 +163,7 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         SkString vertexMode;
         switch (fVertexMode) {
             case kNone_VertexMode:
@@ -200,7 +200,7 @@
         return fName.c_str();
     }
     
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         this->setGrid();
         switch (fVertexMode) {
             case kTexCoords_VertexMode:
@@ -214,7 +214,7 @@
         this->setupPaint(&fPaint);
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         this->setScale(canvas);
         for (int i = 0; i < loops; i++) {
             fGrid.draw(canvas, fPaint);
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp
index b597f91..710179a 100644
--- a/bench/PathBench.cpp
+++ b/bench/PathBench.cpp
@@ -42,7 +42,7 @@
     virtual int complexity() { return 0; }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         fName.printf("path_%s_%s_",
                      fFlags & kStroke_Flag ? "stroke" : "fill",
                      fFlags & kBig_Flag ? "big" : "small");
@@ -50,15 +50,14 @@
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint(fPaint);
         this->setupPaint(&paint);
 
         SkPath path;
         this->makePath(&path);
         if (fFlags & kBig_Flag) {
-            SkMatrix m;
-            m.setScale(SkIntToScalar(10), SkIntToScalar(10));
+            const SkMatrix m = SkMatrix::MakeScale(SkIntToScalar(10), SkIntToScalar(10));
             path.transform(m);
         }
 
@@ -81,10 +80,10 @@
 public:
     TrianglePathBench(Flags flags) : INHERITED(flags) {}
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("triangle");
     }
-    void makePath(SkPath* path) SK_OVERRIDE {
+    void makePath(SkPath* path) override {
         static const int gCoord[] = {
             10, 10, 15, 5, 20, 20
         };
@@ -101,10 +100,10 @@
 public:
     RectPathBench(Flags flags) : INHERITED(flags) {}
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("rect");
     }
-    void makePath(SkPath* path) SK_OVERRIDE {
+    void makePath(SkPath* path) override {
         SkRect r = { 10, 10, 20, 20 };
         path->addRect(r);
     }
@@ -116,10 +115,10 @@
 public:
     OvalPathBench(Flags flags) : INHERITED(flags) {}
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("oval");
     }
-    void makePath(SkPath* path) SK_OVERRIDE {
+    void makePath(SkPath* path) override {
         SkRect r = { 10, 10, 23, 20 };
         path->addOval(r);
     }
@@ -131,10 +130,10 @@
 public:
     CirclePathBench(Flags flags) : INHERITED(flags) {}
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("circle");
     }
-    void makePath(SkPath* path) SK_OVERRIDE {
+    void makePath(SkPath* path) override {
         path->addCircle(SkIntToScalar(20), SkIntToScalar(20),
                         SkIntToScalar(10));
     }
@@ -146,7 +145,7 @@
 public:
     SawToothPathBench(Flags flags) : INHERITED(flags) {}
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("sawtooth");
     }
     virtual void makePath(SkPath* path) {
@@ -167,7 +166,7 @@
         path->lineTo(x0, y + 2 * dy);
         path->close();
     }
-    int complexity() SK_OVERRIDE { return 1; }
+    int complexity() override { return 1; }
 private:
     typedef PathBench INHERITED;
 };
@@ -176,10 +175,10 @@
 public:
     LongCurvedPathBench(Flags flags) : INHERITED(flags) {}
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("long_curved");
     }
-    void makePath(SkPath* path) SK_OVERRIDE {
+    void makePath(SkPath* path) override {
         SkRandom rand (12);
         int i;
         for (i = 0; i < 100; i++) {
@@ -190,7 +189,7 @@
         }
         path->close();
     }
-    int complexity() SK_OVERRIDE { return 2; }
+    int complexity() override { return 2; }
 private:
     typedef PathBench INHERITED;
 };
@@ -199,24 +198,24 @@
 public:
     LongLinePathBench(Flags flags) : INHERITED(flags) {}
 
-    void appendName(SkString* name) SK_OVERRIDE {
+    void appendName(SkString* name) override {
         name->append("long_line");
     }
-    void makePath(SkPath* path) SK_OVERRIDE {
+    void makePath(SkPath* path) override {
         SkRandom rand;
         path->moveTo(rand.nextUScalar1() * 640, rand.nextUScalar1() * 480);
         for (size_t i = 1; i < 100; i++) {
             path->lineTo(rand.nextUScalar1() * 640, rand.nextUScalar1() * 480);
         }
     }
-    int complexity() SK_OVERRIDE { return 2; }
+    int complexity() override { return 2; }
 private:
     typedef PathBench INHERITED;
 };
 
 class RandomPathBench : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -321,15 +320,15 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "path_create";
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         this->createData(10, 100);
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         for (int i = 0; i < loops; ++i) {
             if (i % 1000 == 0) {
                 fPath.reset();  // PathRef memory can grow without bound otherwise.
@@ -351,10 +350,10 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "path_copy";
     }
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         this->createData(10, 100);
         fPaths.reset(kPathCnt);
         fCopies.reset(kPathCnt);
@@ -363,7 +362,7 @@
         }
         this->finishedMakingPaths();
     }
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         for (int i = 0; i < loops; ++i) {
             int idx = i & (kPathCnt - 1);
             fCopies[idx] = fPaths[idx];
@@ -386,11 +385,11 @@
     PathTransformBench(bool inPlace) : fInPlace(inPlace) {}
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fInPlace ? "path_transform_in_place" : "path_transform_copy";
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         fMatrix.setScale(5 * SK_Scalar1, 6 * SK_Scalar1);
         this->createData(10, 100);
         fPaths.reset(kPathCnt);
@@ -403,7 +402,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         if (fInPlace) {
             for (int i = 0; i < loops; ++i) {
                 fPaths[i & (kPathCnt - 1)].transform(fMatrix);
@@ -434,11 +433,11 @@
     PathEqualityBench() { }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "path_equality_50%";
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         fParity = 0;
         this->createData(10, 100);
         fPaths.reset(kPathCnt);
@@ -450,7 +449,7 @@
         this->finishedMakingPaths();
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         for (int i = 0; i < loops; ++i) {
             int idx = i & (kPathCnt - 1);
             fParity ^= (fPaths[idx] == fCopies[idx & ~0x1]);
@@ -483,7 +482,7 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         switch (fType) {
             case kAdd_AddType:
                 return "path_add_path";
@@ -501,7 +500,7 @@
         }
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         // reversePathTo assumes a single contour path.
         bool allowMoves = kReversePathTo_AddType != fType;
         this->createData(10, 100, allowMoves);
@@ -514,7 +513,7 @@
         this->finishedMakingPaths();
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         switch (fType) {
             case kAdd_AddType:
                 for (int i = 0; i < loops; ++i) {
@@ -578,11 +577,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
 
         paint.setColor(SK_ColorBLACK);
@@ -642,7 +641,7 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
@@ -688,7 +687,7 @@
         SkASSERT(path->isConvex());
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRandom rand;
         SkRect r;
 
@@ -753,23 +752,23 @@
         }
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 private:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         for (int i = 0; i < loops; ++i) {
             const SkRect& rect = fQueryRects[i % kQueryRectCnt];
             fParity = fParity != fPath.conservativelyContainsRect(rect);
         }
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         fQueryRects.setCount(kQueryRectCnt);
 
         SkRandom rand;
@@ -806,55 +805,84 @@
 
 #include "SkGeometry.h"
 
-class ConicBench_Chop5 : public Benchmark {
-    SkConic fRQ;
+class ConicBench_Chop : public Benchmark {
+protected:
+    SkConic fRQ, fDst[2];
+    SkString fName;
 public:
-    ConicBench_Chop5()  {
+    ConicBench_Chop() : fName("conic-chop") {
         fRQ.fPts[0].set(0, 0);
         fRQ.fPts[1].set(100, 0);
         fRQ.fPts[2].set(100, 100);
         fRQ.fW = SkScalarCos(SK_ScalarPI/4);
     }
 
-private:
-    const char* onGetName() SK_OVERRIDE {
-        return "ratquad-chop-0.5";
+    bool isSuitableFor(Backend backend) override {
+        return backend == kNonRendering_Backend;
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
-        SkConic dst[2];
+private:
+    const char* onGetName() override { return fName.c_str(); }
+
+    void onDraw(const int loops, SkCanvas*) override {
         for (int i = 0; i < loops; ++i) {
-            fRQ.chopAt(0.5f, dst);
+            fRQ.chop(fDst);
         }
     }
 
     typedef Benchmark INHERITED;
 };
+DEF_BENCH( return new ConicBench_Chop; )
 
-class ConicBench_ChopHalf : public Benchmark {
-    SkConic fRQ;
+class ConicBench_EvalPos : public ConicBench_Chop {
+    const bool fUseV2;
 public:
-    ConicBench_ChopHalf()  {
-        fRQ.fPts[0].set(0, 0);
-        fRQ.fPts[1].set(100, 0);
-        fRQ.fPts[2].set(100, 100);
-        fRQ.fW = SkScalarCos(SK_ScalarPI/4);
+    ConicBench_EvalPos(bool useV2) : fUseV2(useV2) {
+        fName.printf("conic-eval-pos%d", useV2);
     }
-
-private:
-    const char* onGetName() SK_OVERRIDE {
-        return "ratquad-chop-half";
-    }
-
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
-        SkConic dst[2];
-        for (int i = 0; i < loops; ++i) {
-            fRQ.chop(dst);
+    void onDraw(const int loops, SkCanvas*) override {
+        if (fUseV2) {
+            for (int i = 0; i < loops; ++i) {
+                for (int j = 0; j < 1000; ++j) {
+                    fDst[0].fPts[0] = fRQ.evalAt(0.4f);
+                }
+            }
+        } else {
+            for (int i = 0; i < loops; ++i) {
+                for (int j = 0; j < 1000; ++j) {
+                    fRQ.evalAt(0.4f, &fDst[0].fPts[0], NULL);
+                }
+            }
         }
     }
-
-    typedef Benchmark INHERITED;
 };
+DEF_BENCH( return new ConicBench_EvalPos(false); )
+DEF_BENCH( return new ConicBench_EvalPos(true); )
+
+class ConicBench_EvalTan : public ConicBench_Chop {
+    const bool fUseV2;
+public:
+    ConicBench_EvalTan(bool useV2) : fUseV2(useV2) {
+        fName.printf("conic-eval-tan%d", useV2);
+    }
+    void onDraw(const int loops, SkCanvas*) override {
+        if (fUseV2) {
+            for (int i = 0; i < loops; ++i) {
+                for (int j = 0; j < 1000; ++j) {
+                    fDst[0].fPts[0] = fRQ.evalTangentAt(0.4f);
+                }
+            }
+        } else {
+            for (int i = 0; i < loops; ++i) {
+                for (int j = 0; j < 1000; ++j) {
+                    fRQ.evalAt(0.4f, NULL, &fDst[0].fPts[0]);
+                }
+            }
+        }
+    }
+};
+DEF_BENCH( return new ConicBench_EvalTan(false); )
+DEF_BENCH( return new ConicBench_EvalTan(true); )
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -878,7 +906,7 @@
         }
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -897,11 +925,11 @@
     ConicBench_ComputeError()  {}
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "conic-compute-error";
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         SkVector err;
         for (int i = 0; i < loops; ++i) {
             for (int j = 0; j < CONICS; ++j) {
@@ -919,11 +947,11 @@
     ConicBench_asQuadTol()  {}
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "conic-asQuadTol";
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         for (int i = 0; i < loops; ++i) {
             for (int j = 0; j < CONICS; ++j) {
                 fConics[j].asQuadTol(SK_ScalarHalf);
@@ -940,11 +968,11 @@
     ConicBench_quadPow2()  {}
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "conic-quadPow2";
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         for (int i = 0; i < loops; ++i) {
             for (int j = 0; j < CONICS; ++j) {
                 fConics[j].computeQuadPOW2(SK_ScalarHalf);
@@ -1012,10 +1040,10 @@
 DEF_BENCH( return new ConservativelyContainsBench(ConservativelyContainsBench::kRoundRect_Type); )
 DEF_BENCH( return new ConservativelyContainsBench(ConservativelyContainsBench::kOval_Type); )
 
+
 // These seem to be optimized away, which is troublesome for timing.
 /*
 DEF_BENCH( return new ConicBench_Chop5() )
-DEF_BENCH( return new ConicBench_ChopHalf() )
 DEF_BENCH( return new ConicBench_ComputeError() )
 DEF_BENCH( return new ConicBench_asQuadTol() )
 DEF_BENCH( return new ConicBench_quadPow2() )
diff --git a/bench/PathIterBench.cpp b/bench/PathIterBench.cpp
index c7a04bd..bbc3c03 100644
--- a/bench/PathIterBench.cpp
+++ b/bench/PathIterBench.cpp
@@ -56,16 +56,16 @@
         }
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         if (fRaw) {
             for (int i = 0; i < loops; ++i) {
                 SkPath::RawIter iter(fPath);
diff --git a/bench/PathUtilsBench.cpp b/bench/PathUtilsBench.cpp
deleted file mode 100644
index 6c8086f..0000000
--- a/bench/PathUtilsBench.cpp
+++ /dev/null
@@ -1,72 +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 "Benchmark.h"
-#include "SkCanvas.h"
-#include "SkPathUtils.h"
-#include "SkRandom.h"
-#include "SkString.h"
-#include "SkTime.h"
-
-#define H 16
-#define W 16
-#define STRIDE 2
-
-//this function is redefined for sample, test, and bench. is there anywhere
-// I can put it to avoid code duplcation?
-static void fillRandomBits( int chars, char* bits ){
-    SkRandom rand(SkTime::GetMSecs());
-
-    for (int i = 0; i < chars; ++i){
-        bits[i] = rand.nextU();
-    }
-}
-
-static void path_proc(char* bits, SkPath* path) {
-    SkPathUtils::BitsToPath_Path(path, bits, H, W, STRIDE);
-}
-
-static void region_proc(char* bits, SkPath* path) {
-    SkPathUtils::BitsToPath_Region(path, bits, H, W, STRIDE);
-}
-
-/// Emulates the mix of rects blitted by gmail during scrolling
-class PathUtilsBench : public Benchmark {
-    typedef void (*Proc)(char*, SkPath*);
-
-    Proc fProc;
-    SkString fName;
-    char* bits[H * STRIDE];
-
-public:
-    PathUtilsBench(Proc proc, const char name[])  {
-        fProc = proc;
-        fName.printf("pathUtils_%s", name);
-
-
-    }
-
-protected:
-    virtual const char* onGetName() { return fName.c_str(); }
-
-    virtual void onDraw(const int loops, SkCanvas* canvas) {
-
-        for (int i = 0; i < loops; ++i){
-            //create a random 16x16 bitmap
-            fillRandomBits(H * STRIDE, (char*) &bits);
-
-            //use passed function pointer to handle it
-            SkPath path;
-            fProc( (char*) &bits, &path);
-        }
-    }
-
-private:
-    typedef Benchmark INHERITED;
-};
-
-DEF_BENCH( return SkNEW_ARGS(PathUtilsBench, (path_proc, "path")); )
-DEF_BENCH( return SkNEW_ARGS(PathUtilsBench, (region_proc, "region")); )
diff --git a/bench/PerlinNoiseBench.cpp b/bench/PerlinNoiseBench.cpp
index 17260a9..b8237c0 100644
--- a/bench/PerlinNoiseBench.cpp
+++ b/bench/PerlinNoiseBench.cpp
@@ -17,11 +17,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "perlinnoise";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         this->test(loops, canvas, 0, 0, SkPerlinNoiseShader::kFractalNoise_Type,
                    0.1f, 0.1f, 3, 0, false);
     }
diff --git a/bench/PictureNestingBench.cpp b/bench/PictureNestingBench.cpp
index b2220b7..a649dcc 100644
--- a/bench/PictureNestingBench.cpp
+++ b/bench/PictureNestingBench.cpp
@@ -26,7 +26,7 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
@@ -139,7 +139,7 @@
         : INHERITED("playback", maxLevel, maxPictureLevel) {
     }
 protected:
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         this->INHERITED::onPreDraw();
 
         SkIPoint canvasSize = onGetSize();
diff --git a/bench/PictureOverheadBench.cpp b/bench/PictureOverheadBench.cpp
new file mode 100644
index 0000000..c9dedf4
--- /dev/null
+++ b/bench/PictureOverheadBench.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// A benchmark designed to isolate the constant overheads of picture recording.
+// We record an empty picture and a picture with one draw op to force memory allocation.
+
+#include "Benchmark.h"
+#include "SkCanvas.h"
+#include "SkPictureRecorder.h"
+
+template <bool kDraw>
+struct PictureOverheadBench : public Benchmark {
+    const char* onGetName() override {
+        return kDraw ? "picture_overhead_draw" : "picture_overhead_nodraw";
+    }
+    bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
+
+    void onDraw(const int loops, SkCanvas*) override {
+        SkPictureRecorder rec;
+        for (int i = 0; i < loops; i++) {
+            rec.beginRecording(SkRect::MakeWH(2000,3000));
+            if (kDraw) {
+                rec.getRecordingCanvas()->drawRect(SkRect::MakeXYWH(10, 10, 1000, 1000), SkPaint());
+            }
+            SkAutoTUnref<SkPicture> pic(rec.endRecordingAsPicture());
+        }
+    }
+};
+
+DEF_BENCH(return (new PictureOverheadBench<false>);)
+DEF_BENCH(return (new PictureOverheadBench< true>);)
diff --git a/bench/PicturePlaybackBench.cpp b/bench/PicturePlaybackBench.cpp
index 67945ce..e11d7c8 100644
--- a/bench/PicturePlaybackBench.cpp
+++ b/bench/PicturePlaybackBench.cpp
@@ -72,7 +72,7 @@
 public:
     TextPlaybackBench() : INHERITED("drawText") { }
 protected:
-    void recordCanvas(SkCanvas* canvas) SK_OVERRIDE {
+    void recordCanvas(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setTextSize(fTextSize);
         paint.setColor(SK_ColorBLACK);
@@ -97,7 +97,7 @@
         : INHERITED(drawPosH ? "drawPosTextH" : "drawPosText")
         , fDrawPosH(drawPosH) { }
 protected:
-    void recordCanvas(SkCanvas* canvas) SK_OVERRIDE {
+    void recordCanvas(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setTextSize(fTextSize);
         paint.setColor(SK_ColorBLACK);
@@ -159,10 +159,10 @@
         }
     }
 
-    const char* onGetName() SK_OVERRIDE { return fName.c_str(); }
-    SkIPoint onGetSize() SK_OVERRIDE { return SkIPoint::Make(1024,1024); }
+    const char* onGetName() override { return fName.c_str(); }
+    SkIPoint onGetSize() override { return SkIPoint::Make(1024,1024); }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         SkAutoTDelete<SkBBHFactory> factory;
         switch (fBBH) {
             case kNone:                                                 break;
@@ -185,7 +185,7 @@
         fPic.reset(recorder.endRecording());
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         for (int i = 0; i < loops; i++) {
             // This inner loop guarantees we make the same choices for all bench variants.
             SkRandom rand;
diff --git a/bench/PremulAndUnpremulAlphaOpsBench.cpp b/bench/PremulAndUnpremulAlphaOpsBench.cpp
index 4d9dbcd..6e79541 100644
--- a/bench/PremulAndUnpremulAlphaOpsBench.cpp
+++ b/bench/PremulAndUnpremulAlphaOpsBench.cpp
@@ -25,11 +25,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         SkImageInfo info = SkImageInfo::Make(W, H, fColorType, kUnpremul_SkAlphaType);
         fBmp1.allocPixels(info);   // used in writePixels
 
@@ -43,7 +43,7 @@
         fBmp2.allocPixels(info);    // used in readPixels()
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         canvas->clear(SK_ColorBLACK);
 
         for (int loop = 0; loop < loops; ++loop) {
diff --git a/bench/RTreeBench.cpp b/bench/RTreeBench.cpp
index cdbdf35..6f76bd1 100644
--- a/bench/RTreeBench.cpp
+++ b/bench/RTreeBench.cpp
@@ -26,15 +26,15 @@
         fName.printf("rtree_%s_build", name);
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRandom rand;
         SkAutoTMalloc<SkRect> rects(NUM_BUILD_RECTS);
         for (int i = 0; i < NUM_BUILD_RECTS; ++i) {
@@ -60,14 +60,14 @@
         fName.printf("rtree_%s_query", name);
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         SkRandom rand;
         SkAutoTMalloc<SkRect> rects(NUM_QUERY_RECTS);
         for (int i = 0; i < NUM_QUERY_RECTS; ++i) {
@@ -76,7 +76,7 @@
         fTree.insert(rects.get(), NUM_QUERY_RECTS);
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRandom rand;
         for (int i = 0; i < loops; ++i) {
             SkTDArray<unsigned> hits;
diff --git a/bench/ReadPixBench.cpp b/bench/ReadPixBench.cpp
index f019401..917b917 100644
--- a/bench/ReadPixBench.cpp
+++ b/bench/ReadPixBench.cpp
@@ -20,11 +20,11 @@
     ReadPixBench() {}
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "readpix";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         canvas->clear(SK_ColorBLACK);
 
         SkISize size = canvas->getDeviceSize();
diff --git a/bench/RecordingBench.h b/bench/RecordingBench.h
index 783596d..6cb5d51 100644
--- a/bench/RecordingBench.h
+++ b/bench/RecordingBench.h
@@ -16,10 +16,10 @@
     RecordingBench(const char* name, const SkPicture*, bool useBBH);
 
 protected:
-    const char* onGetName() SK_OVERRIDE;
-    bool isSuitableFor(Backend) SK_OVERRIDE;
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE;
-    SkIPoint onGetSize() SK_OVERRIDE;
+    const char* onGetName() override;
+    bool isSuitableFor(Backend) override;
+    void onDraw(const int loops, SkCanvas*) override;
+    SkIPoint onGetSize() override;
 
 private:
     SkAutoTUnref<const SkPicture> fSrc;
diff --git a/bench/RectBench.cpp b/bench/RectBench.cpp
index 6228e00..4aa6b8b 100644
--- a/bench/RectBench.cpp
+++ b/bench/RectBench.cpp
@@ -92,14 +92,14 @@
     }
 
 protected:
-    void setupPaint(SkPaint* paint) SK_OVERRIDE {
+    void setupPaint(SkPaint* paint) override {
         this->INHERITED::setupPaint(paint);
         // srcmode is most interesting when we're not opaque
         paint->setAlpha(0x80);
         paint->setXfermode(fMode);
     }
 
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         fName.set(this->INHERITED::onGetName());
         fName.prepend("srcmode_");
         return fName.c_str();
diff --git a/bench/RectanizerBench.cpp b/bench/RectanizerBench.cpp
index a5fad06..f7e1f04 100644
--- a/bench/RectanizerBench.cpp
+++ b/bench/RectanizerBench.cpp
@@ -64,15 +64,15 @@
     }
 
 protected:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return kNonRendering_Backend == backend;
     }
 
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         SkASSERT(NULL == fRectanizer.get());
 
         if (kPow2_RectanizerType == fRectanizerType) {
@@ -83,7 +83,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRandom rand;
         SkIPoint16 loc;
         SkISize size;
diff --git a/bench/RectoriBench.cpp b/bench/RectoriBench.cpp
index de725a7..b777fbe 100644
--- a/bench/RectoriBench.cpp
+++ b/bench/RectoriBench.cpp
@@ -20,11 +20,11 @@
 
 protected:
 
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "rectori";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRandom Random;
 
         for (int i = 0; i < loops; i++) {
diff --git a/bench/RefCntBench.cpp b/bench/RefCntBench.cpp
index 7aa5746..aacdf18 100644
--- a/bench/RefCntBench.cpp
+++ b/bench/RefCntBench.cpp
@@ -18,7 +18,7 @@
 public:
     AtomicInc32() : fX(0) {}
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -42,7 +42,7 @@
 public:
     AtomicInc64() : fX(0) {}
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -64,7 +64,7 @@
 
 class RefCntBench_Stack : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -100,7 +100,7 @@
 
 class RefCntBench_Heap : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -127,7 +127,7 @@
 
 class RefCntBench_New : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -155,7 +155,7 @@
 
 class WeakRefCntBench_Stack : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -186,16 +186,16 @@
 
 class WeakRefCntBench_Heap : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "ref_cnt_heap_weak";
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         char memory[sizeof(PlacedWeakRefCnt)];
         for (int i = 0; i < loops; ++i) {
             PlacedWeakRefCnt* ref = new (memory) PlacedWeakRefCnt();
@@ -213,16 +213,16 @@
 
 class WeakRefCntBench_New : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "ref_cnt_new_weak";
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         for (int i = 0; i < loops; ++i) {
             SkWeakRefCnt* ref = new SkWeakRefCnt();
             for (int j = 0; j < M; ++j) {
diff --git a/bench/RegionBench.cpp b/bench/RegionBench.cpp
index 5cd6779..84e8d1b 100644
--- a/bench/RegionBench.cpp
+++ b/bench/RegionBench.cpp
@@ -99,7 +99,7 @@
         }
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
diff --git a/bench/RegionContainBench.cpp b/bench/RegionContainBench.cpp
index 3a09809..dfee956 100644
--- a/bench/RegionContainBench.cpp
+++ b/bench/RegionContainBench.cpp
@@ -45,14 +45,14 @@
         fB.setRect(0, 0, H, W);
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE { return fName.c_str(); }
+    const char* onGetName() override { return fName.c_str(); }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         Proc proc = fProc;
 
         for (int i = 0; i < loops; ++i) {
diff --git a/bench/RepeatTileBench.cpp b/bench/RepeatTileBench.cpp
index 0e63336..afbc5b2 100644
--- a/bench/RepeatTileBench.cpp
+++ b/bench/RepeatTileBench.cpp
@@ -104,11 +104,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         fBitmap.allocPixels();
         fBitmap.eraseColor(kOpaque_SkAlphaType == fAlphaType ? SK_ColorWHITE : 0);
 
@@ -127,7 +127,7 @@
     }
 
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint(fPaint);
         this->setupPaint(&paint);
 
diff --git a/bench/RotatedRectBench.cpp b/bench/RotatedRectBench.cpp
index 87b4ec8..c37a6be 100644
--- a/bench/RotatedRectBench.cpp
+++ b/bench/RotatedRectBench.cpp
@@ -75,16 +75,16 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE { return fName.c_str(); }
+    const char* onGetName() override { return fName.c_str(); }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(fAA);
         paint.setXfermodeMode(fMode);
         SkColor color = start_color(fColorType);
 
-        int w = canvas->getBaseLayerSize().width();
-        int h = canvas->getBaseLayerSize().height();
+        int w = this->getSize().x();
+        int h = this->getSize().y();
 
         static const SkScalar kRectW = 25.1f;
         static const SkScalar kRectH = 25.9f;
diff --git a/bench/SKPAnimationBench.cpp b/bench/SKPAnimationBench.cpp
new file mode 100644
index 0000000..24506d8
--- /dev/null
+++ b/bench/SKPAnimationBench.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SKPAnimationBench.h"
+#include "SkCommandLineFlags.h"
+#include "SkMultiPictureDraw.h"
+#include "SkSurface.h"
+
+SKPAnimationBench::SKPAnimationBench(const char* name, const SkPicture* pic,
+                                     const SkIRect& clip, SkMatrix animationMatrix, int steps)
+    : INHERITED(name, pic, clip, 1.0, false)
+    , fSteps(steps)
+    , fAnimationMatrix(animationMatrix)
+    , fName(name) {
+    fUniqueName.printf("%s_animation", name);
+}
+
+const char* SKPAnimationBench::onGetName() {
+    return fName.c_str();
+}
+
+const char* SKPAnimationBench::onGetUniqueName() {
+    return fUniqueName.c_str();
+}
+
+void SKPAnimationBench::onPerCanvasPreDraw(SkCanvas* canvas) {
+    INHERITED::onPerCanvasPreDraw(canvas);
+    SkIRect bounds;
+    SkAssertResult(canvas->getClipDeviceBounds(&bounds));
+
+    fCenter.set((bounds.fRight - bounds.fLeft) / 2.0f,
+                (bounds.fBottom - bounds.fTop) / 2.0f);
+}
+
+void SKPAnimationBench::drawPicture() {
+    SkMatrix frameMatrix = SkMatrix::MakeTrans(-fCenter.fX, -fCenter.fY);
+    frameMatrix.postConcat(fAnimationMatrix);
+    SkMatrix reverseTranslate = SkMatrix::MakeTrans(fCenter.fX, fCenter.fY);
+    frameMatrix.postConcat(reverseTranslate);
+
+    SkMatrix currentMatrix = frameMatrix;
+    for (int i = 0; i < fSteps; i++) {
+        for (int j = 0; j < this->tileRects().count(); ++j) {
+            SkMatrix trans = SkMatrix::MakeTrans(-1.f * this->tileRects()[j].fLeft,
+                                                 -1.f * this->tileRects()[j].fTop);
+            SkMatrix tileMatrix = currentMatrix;
+            tileMatrix.postConcat(trans);
+            this->surfaces()[j]->getCanvas()->drawPicture(this->picture(), &tileMatrix, NULL);
+        }
+
+        for (int j = 0; j < this->tileRects().count(); ++j) {
+           this->surfaces()[j]->getCanvas()->flush();
+        }
+        currentMatrix.postConcat(frameMatrix);
+    }
+}
diff --git a/bench/SKPAnimationBench.h b/bench/SKPAnimationBench.h
new file mode 100644
index 0000000..f6e9fa4
--- /dev/null
+++ b/bench/SKPAnimationBench.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKPAnimationBench_DEFINED
+#define SKPAnimationBench_DEFINED
+
+#include "SKPBench.h"
+
+/**
+ * Runs an SkPicture as a benchmark by repeatedly drawing it, first centering the picture and
+ * for each step it concats the passed in matrix
+ */
+class SKPAnimationBench : public SKPBench {
+public:
+    SKPAnimationBench(const char* name, const SkPicture*, const SkIRect& devClip,
+                      SkMatrix viewMatrix, int steps);
+
+protected:
+    const char* onGetName() override;
+    const char* onGetUniqueName() override;
+    void onPerCanvasPreDraw(SkCanvas* canvas) override;
+
+    void drawMPDPicture() override {
+        SkFAIL("MPD not supported\n");
+    }
+    void drawPicture() override;
+
+private:
+    int fSteps;
+    SkMatrix fAnimationMatrix;
+    SkString fName;
+    SkString fUniqueName;
+    SkPoint fCenter;
+
+    typedef SKPBench INHERITED;
+};
+
+#endif
diff --git a/bench/SKPBench.cpp b/bench/SKPBench.cpp
index 3f52179..592d042 100644
--- a/bench/SKPBench.cpp
+++ b/bench/SKPBench.cpp
@@ -98,33 +98,40 @@
 void SKPBench::onDraw(const int loops, SkCanvas* canvas) {
     if (fUseMultiPictureDraw) {
         for (int i = 0; i < loops; i++) {
-            SkMultiPictureDraw mpd;
-
-            for (int j = 0; j < fTileRects.count(); ++j) {
-                SkMatrix trans;
-                trans.setTranslate(-fTileRects[j].fLeft/fScale,
-                                   -fTileRects[j].fTop/fScale);
-                mpd.add(fSurfaces[j]->getCanvas(), fPic, &trans);
-            }
-
-            mpd.draw();
-
-            for (int j = 0; j < fTileRects.count(); ++j) {
-                fSurfaces[j]->getCanvas()->flush();
-            }
+            this->drawMPDPicture();
         }
     } else {
         for (int i = 0; i < loops; i++) {
-            for (int j = 0; j < fTileRects.count(); ++j) {
-                SkMatrix trans;
-                trans.setTranslate(-fTileRects[j].fLeft / fScale,
-                                   -fTileRects[j].fTop / fScale);
-                fSurfaces[j]->getCanvas()->drawPicture(fPic, &trans, NULL);
-            }
-
-            for (int j = 0; j < fTileRects.count(); ++j) {
-                fSurfaces[j]->getCanvas()->flush();
-            }
+            this->drawPicture();
         }
     }
 }
+
+void SKPBench::drawMPDPicture() {
+    SkMultiPictureDraw mpd;
+
+    for (int j = 0; j < fTileRects.count(); ++j) {
+        SkMatrix trans;
+        trans.setTranslate(-fTileRects[j].fLeft/fScale,
+                           -fTileRects[j].fTop/fScale);
+        mpd.add(fSurfaces[j]->getCanvas(), fPic, &trans);
+    }
+
+    mpd.draw();
+
+    for (int j = 0; j < fTileRects.count(); ++j) {
+        fSurfaces[j]->getCanvas()->flush();
+    }
+}
+
+void SKPBench::drawPicture() {
+    for (int j = 0; j < fTileRects.count(); ++j) {
+        const SkMatrix trans = SkMatrix::MakeTrans(-fTileRects[j].fLeft / fScale,
+                                                   -fTileRects[j].fTop / fScale);
+        fSurfaces[j]->getCanvas()->drawPicture(fPic, &trans, NULL);
+    }
+
+    for (int j = 0; j < fTileRects.count(); ++j) {
+        fSurfaces[j]->getCanvas()->flush();
+    }
+}
diff --git a/bench/SKPBench.h b/bench/SKPBench.h
index 36d7dfd..3b645e9 100644
--- a/bench/SKPBench.h
+++ b/bench/SKPBench.h
@@ -19,16 +19,23 @@
 public:
     SKPBench(const char* name, const SkPicture*, const SkIRect& devClip, SkScalar scale,
              bool useMultiPictureDraw);
-    ~SKPBench() SK_OVERRIDE;
+    ~SKPBench() override;
 
 protected:
-    const char* onGetName() SK_OVERRIDE;
-    const char* onGetUniqueName() SK_OVERRIDE;
-    void onPerCanvasPreDraw(SkCanvas*) SK_OVERRIDE;
-    void onPerCanvasPostDraw(SkCanvas*) SK_OVERRIDE;
-    bool isSuitableFor(Backend backend) SK_OVERRIDE;
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE;
-    SkIPoint onGetSize() SK_OVERRIDE;
+    const char* onGetName() override;
+    const char* onGetUniqueName() override;
+    void onPerCanvasPreDraw(SkCanvas*) override;
+    void onPerCanvasPostDraw(SkCanvas*) override;
+    bool isSuitableFor(Backend backend) override;
+    void onDraw(const int loops, SkCanvas* canvas) override;
+    SkIPoint onGetSize() override;
+
+    virtual void drawMPDPicture();
+    virtual void drawPicture();
+
+    const SkPicture* picture() const { return fPic; }
+    const SkTDArray<SkSurface*>& surfaces() const { return fSurfaces; }
+    const SkTDArray<SkIRect>& tileRects() const { return fTileRects; }
 
 private:
     SkAutoTUnref<const SkPicture> fPic;
diff --git a/bench/ScalarBench.cpp b/bench/ScalarBench.cpp
index 21f9264..2c59fa5 100644
--- a/bench/ScalarBench.cpp
+++ b/bench/ScalarBench.cpp
@@ -18,7 +18,7 @@
         fName.printf("scalar_%s", name);
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
@@ -27,11 +27,11 @@
 protected:
     virtual int mulLoopCount() const { return 1; }
 
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         for (int i = 0; i < loops; i++) {
             this->performTest();
         }
@@ -101,8 +101,8 @@
         }
     }
 protected:
-    int mulLoopCount() const SK_OVERRIDE { return 1; }
-    void performTest() SK_OVERRIDE {
+    int mulLoopCount() const override { return 1; }
+    void performTest() override {
         int sum = 0;
         for (size_t i = 0; i < ARRAY_N; ++i) {
             // We pass -fArray[i], so the compiler can't cheat and treat the
@@ -140,19 +140,21 @@
         }
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "rect_bounds";
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkRect r;
         for (int i = 0; i < loops; ++i) {
-            r.set(fPts, PTS);
+            for (int i = 0; i < 1000; ++i) {
+                r.set(fPts, PTS);
+            }
         }
     }
 
diff --git a/bench/SkipZeroesBench.cpp b/bench/SkipZeroesBench.cpp
index 1587e01..7840a8a 100644
--- a/bench/SkipZeroesBench.cpp
+++ b/bench/SkipZeroesBench.cpp
@@ -36,16 +36,16 @@
         }
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         SkString resourcePath = GetResourcePath();
         if (resourcePath.isEmpty()) {
             fValid = false;
@@ -73,7 +73,7 @@
         }
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         if (!fValid) {
 #ifdef SK_DEBUG
             SkDebugf("stream was invalid: %s\n", fFilename.c_str());
diff --git a/bench/SortBench.cpp b/bench/SortBench.cpp
index b46b805..4cf91ba 100644
--- a/bench/SortBench.cpp
+++ b/bench/SortBench.cpp
@@ -107,22 +107,22 @@
         fName.printf("sort_%s_%s", gSorts[s].fName, gRec[t].fName);
     }
 
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
     // Delayed initialization only done if onDraw will be called.
-    void onPreDraw() SK_OVERRIDE {
+    void onPreDraw() override {
         fUnsorted.reset(N);
         gRec[fType].fProc(fUnsorted.get());
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         SkAutoTMalloc<int> sorted(N);
         for (int i = 0; i < loops; i++) {
             memcpy(sorted.get(), fUnsorted.get(), N*sizeof(int));
diff --git a/bench/TextBench.cpp b/bench/TextBench.cpp
index 39484a2..4ee0fd3 100644
--- a/bench/TextBench.cpp
+++ b/bench/TextBench.cpp
@@ -66,13 +66,7 @@
 
         if (doColorEmoji) {
             SkASSERT(kBW == fFQ);
-            SkString filename = GetResourcePath("/Funkster.ttf");
-            SkAutoTDelete<SkFILEStream> stream(new SkFILEStream(filename.c_str()));
-            if (stream->isValid()) {
-                fColorEmojiTypeface.reset(SkTypeface::CreateFromStream(stream.detach()));
-            } else {
-                SkDebugf("Could not find Funkster.ttf, please set --resourcePath correctly.\n");
-            }
+            fColorEmojiTypeface.reset(GetResourceAsTypeface("/fonts/Funkster.ttf"));
         }
 
         if (doPos) {
diff --git a/bench/TextBlobBench.cpp b/bench/TextBlobBench.cpp
new file mode 100644
index 0000000..1f4b2b7
--- /dev/null
+++ b/bench/TextBlobBench.cpp
@@ -0,0 +1,71 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Benchmark.h"
+#include "Resources.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkTemplates.h"
+#include "SkTextBlob.h"
+#include "SkTypeface.h"
+
+#include "sk_tool_utils.h"
+
+/*
+ * A trivial test which benchmarks the performance of a textblob with a single run.
+ */
+class TextBlobBench : public Benchmark {
+public:
+    TextBlobBench()
+        : fTypeface(sk_tool_utils::create_portable_typeface("Times", SkTypeface::kNormal)) {
+        // make textblob
+        SkPaint paint;
+        paint.setTypeface(fTypeface);
+        const char* text = "Hello blob!";
+        SkTDArray<uint16_t> glyphs;
+        size_t len = strlen(text);
+        glyphs.append(paint.textToGlyphs(text, len, NULL));
+        paint.textToGlyphs(text, len, glyphs.begin());
+
+        SkTextBlobBuilder builder;
+
+        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(paint, glyphs.count(), 10, 10,
+                                                                   NULL);
+        memcpy(run.glyphs, glyphs.begin(), glyphs.count() * sizeof(uint16_t));
+
+        fBlob.reset(builder.build());
+    }
+
+protected:
+    const char* onGetName() {
+        return "TextBlobBench";
+    }
+
+    void onDraw(const int loops, SkCanvas* canvas) {
+        SkPaint paint;
+
+        // To ensure maximum caching, we just redraw the blob at the same place everytime
+        for (int i = 0; i < loops; i++) {
+            canvas->drawTextBlob(fBlob, 0, 0, paint);
+        }
+    }
+
+private:
+
+    SkAutoTUnref<const SkTextBlob> fBlob;
+    SkTDArray<uint16_t>      fGlyphs;
+    SkAutoTUnref<SkTypeface> fTypeface;
+
+    typedef Benchmark INHERITED;
+};
+
+DEF_BENCH( return new TextBlobBench(); )
diff --git a/bench/TileBench.cpp b/bench/TileBench.cpp
index 9acef8c..cf35760 100644
--- a/bench/TileBench.cpp
+++ b/bench/TileBench.cpp
@@ -83,8 +83,8 @@
     virtual void onDraw(const int loops, SkCanvas* canvas) {
         SkPaint paint(fPaint);
         this->setupPaint(&paint);
-        paint.setFilterLevel(fDoFilter ? SkPaint::kLow_FilterLevel
-                                       : SkPaint::kNone_FilterLevel);
+        paint.setFilterQuality(fDoFilter ? kLow_SkFilterQuality
+                                         : kNone_SkFilterQuality);
         if (fDoTrans) {
             paint.setColor(SkColorSetARGBMacro(0x80, 0xFF, 0xFF, 0xFF));
         }
diff --git a/bench/WritePixelsBench.cpp b/bench/WritePixelsBench.cpp
index 3353ba2..3ee9034 100644
--- a/bench/WritePixelsBench.cpp
+++ b/bench/WritePixelsBench.cpp
@@ -41,11 +41,11 @@
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return fName.c_str();
     }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         SkISize size = canvas->getDeviceSize();
 
         canvas->clear(0xFFFF0000);
diff --git a/bench/WriterBench.cpp b/bench/WriterBench.cpp
index 4dcd3a3..6db1cf1 100644
--- a/bench/WriterBench.cpp
+++ b/bench/WriterBench.cpp
@@ -12,16 +12,16 @@
 
 class WriterBench : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE {
+    const char* onGetName() override {
         return "writer";
     }
 
-    void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas*) override {
         static const char gStr[] = "abcdefghimjklmnopqrstuvwxyz";
         static const size_t gLen = strlen(gStr);
         SkWriter32 writer;
diff --git a/bench/XfermodeBench.cpp b/bench/XfermodeBench.cpp
index 4afd06b..7e61826 100644
--- a/bench/XfermodeBench.cpp
+++ b/bench/XfermodeBench.cpp
@@ -13,64 +13,79 @@
 #include "SkString.h"
 #include "SkXfermode.h"
 
-// Benchmark that draws non-AA rects with an SkXfermode::Mode
+// Benchmark that draws non-AA rects or AA text with an SkXfermode::Mode.
 class XfermodeBench : public Benchmark {
 public:
-    XfermodeBench(SkXfermode::Mode mode) {
+    XfermodeBench(SkXfermode::Mode mode, bool aa) {
         fXfermode.reset(SkXfermode::Create(mode));
+        fAA = aa;
         SkASSERT(fXfermode.get() || SkXfermode::kSrcOver_Mode == mode);
-        fName.printf("Xfermode_%s", SkXfermode::ModeName(mode));
+        fName.printf("Xfermode_%s%s", SkXfermode::ModeName(mode), aa ? "_aa" : "");
     }
 
-    XfermodeBench(SkXfermode* xferMode, const char* name) {
+    XfermodeBench(SkXfermode* xferMode, const char* name, bool aa) {
         SkASSERT(xferMode);
         fXfermode.reset(xferMode);
-        fName.printf("Xfermode_%s", name);
+        fAA = aa;
+        fName.printf("Xfermode_%s%s", name, aa ? "_aa" : "");
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE { return fName.c_str(); }
+    const char* onGetName() override { return fName.c_str(); }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
+        const char* text = "Hamburgefons";
+        size_t len = strlen(text);
         SkISize size = canvas->getDeviceSize();
         SkRandom random;
         for (int i = 0; i < loops; ++i) {
             SkPaint paint;
             paint.setXfermode(fXfermode.get());
             paint.setColor(random.nextU());
-            SkScalar w = random.nextRangeScalar(SkIntToScalar(kMinSize), SkIntToScalar(kMaxSize));
-            SkScalar h = random.nextRangeScalar(SkIntToScalar(kMinSize), SkIntToScalar(kMaxSize));
-            SkRect rect = SkRect::MakeXYWH(
-                random.nextUScalar1() * (size.fWidth - w),
-                random.nextUScalar1() * (size.fHeight - h),
-                w,
-                h
-            );
-            canvas->drawRect(rect, paint);
+            if (fAA) {
+                // Draw text to exercise AA code paths.
+                paint.setAntiAlias(true);
+                paint.setTextSize(random.nextRangeScalar(12, 96));
+                SkScalar x = random.nextRangeScalar(0, (SkScalar)size.fWidth),
+                         y = random.nextRangeScalar(0, (SkScalar)size.fHeight);
+                for (int j = 0; j < 1000; ++j) {
+                    canvas->drawText(text, len, x, y, paint);
+                }
+            } else {
+                // Draw rects to exercise non-AA code paths.
+                SkScalar w = random.nextRangeScalar(50, 100);
+                SkScalar h = random.nextRangeScalar(50, 100);
+                SkRect rect = SkRect::MakeXYWH(
+                    random.nextUScalar1() * (size.fWidth - w),
+                    random.nextUScalar1() * (size.fHeight - h),
+                    w,
+                    h
+                );
+                for (int j = 0; j < 1000; ++j) {
+                    canvas->drawRect(rect, paint);
+                }
+            }
         }
     }
 
 private:
-    enum {
-        kMinSize = 50,
-        kMaxSize = 100,
-    };
     SkAutoTUnref<SkXfermode> fXfermode;
     SkString fName;
+    bool fAA;
 
     typedef Benchmark INHERITED;
 };
 
 class XferCreateBench : public Benchmark {
 public:
-    bool isSuitableFor(Backend backend) SK_OVERRIDE {
+    bool isSuitableFor(Backend backend) override {
         return backend == kNonRendering_Backend;
     }
 
 protected:
-    const char* onGetName() SK_OVERRIDE { return "xfermode_create"; }
+    const char* onGetName() override { return "xfermode_create"; }
 
-    void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(const int loops, SkCanvas* canvas) override {
         for (int outer = 0; outer < loops * 10; ++outer) {
             for (int i = 0; i <= SkXfermode::kLastMode; ++i) {
                 SkXfermode* xfer = SkXfermode::Create(SkXfermode::Mode(i));
@@ -85,11 +100,9 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-#define CONCAT_I(x, y) x ## y
-#define CONCAT(x, y) CONCAT_I(x, y) // allow for macro expansion
-#define BENCH(...) \
-    DEF_BENCH( return new XfermodeBench(__VA_ARGS__); );\
-
+#define BENCH(...)                                             \
+    DEF_BENCH( return new XfermodeBench(__VA_ARGS__, true); )  \
+    DEF_BENCH( return new XfermodeBench(__VA_ARGS__, false); )
 
 BENCH(SkXfermode::kClear_Mode)
 BENCH(SkXfermode::kSrc_Mode)
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index d66b308..85685fb 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -7,7 +7,10 @@
 
 #include <ctype.h>
 
+#include "nanobench.h"
+
 #include "Benchmark.h"
+#include "CodecBench.h"
 #include "CrashHandler.h"
 #include "DecodingBench.h"
 #include "DecodingSubsetBench.h"
@@ -15,12 +18,14 @@
 #include "ProcStats.h"
 #include "ResultsWriter.h"
 #include "RecordingBench.h"
+#include "SKPAnimationBench.h"
 #include "SKPBench.h"
 #include "Stats.h"
 #include "Timer.h"
 
 #include "SkBBoxHierarchy.h"
 #include "SkCanvas.h"
+#include "SkCodec.h"
 #include "SkCommonFlags.h"
 #include "SkData.h"
 #include "SkForceLinking.h"
@@ -32,6 +37,10 @@
 #include "SkSurface.h"
 #include "SkTaskGroup.h"
 
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+    #include "nanobenchAndroid.h"
+#endif
+
 #if SK_SUPPORT_GPU
     #include "gl/GrGLDefines.h"
     #include "GrContextFactory.h"
@@ -74,6 +83,7 @@
 DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this.");
 DEFINE_string(clip, "0,0,1000,1000", "Clip for SKPs.");
 DEFINE_string(scales, "1.0", "Space-separated scales for SKPs.");
+DEFINE_string(zoom, "1.0,1", "Comma-separated scale,step zoom factors for SKPs.");
 DEFINE_bool(bbh, true, "Build a BBH for SKPs?");
 DEFINE_bool(mpd, true, "Use MultiPictureDraw for the SKPs?");
 DEFINE_int32(flushEvery, 10, "Flush --outResultsFile every Nth run.");
@@ -86,24 +96,92 @@
 }
 #define HUMANIZE(ms) humanize(ms).c_str()
 
-static double time(int loops, Benchmark* bench, SkCanvas* canvas, SkGLContext* gl) {
+bool Target::init(SkImageInfo info, Benchmark* bench) {
+    if (Benchmark::kRaster_Backend == config.backend) {
+        this->surface.reset(SkSurface::NewRaster(info));
+        if (!this->surface.get()) {
+            return false;
+        }
+    }
+    return true;
+}
+bool Target::capturePixels(SkBitmap* bmp) {
+    SkCanvas* canvas = this->getCanvas();
+    if (!canvas) {
+        return false;
+    }
+    bmp->setInfo(canvas->imageInfo());
+    if (!canvas->readPixels(bmp, 0, 0)) {
+        SkDebugf("Can't read canvas pixels.\n");
+        return false;
+    }
+    return true;
+}
+
+#if SK_SUPPORT_GPU
+struct GPUTarget : public Target {
+    explicit GPUTarget(const Config& c) : Target(c), gl(NULL) { }
+    SkGLContext* gl;
+
+    void setup() override {
+        this->gl->makeCurrent();
+        // Make sure we're done with whatever came before.
+        SK_GL(*this->gl, Finish());
+    }
+    void endTiming() override {
+        if (this->gl) {
+            SK_GL(*this->gl, Flush());
+            this->gl->swapBuffers();
+        }
+    }
+    void fence() override {
+        SK_GL(*this->gl, Finish());
+    }
+
+    bool needsFrameTiming() const override { return true; }
+    bool init(SkImageInfo info, Benchmark* bench) override {
+        uint32_t flags = this->config.useDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0;
+        SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
+        this->surface.reset(SkSurface::NewRenderTarget(gGrFactory->get(this->config.ctxType),
+                                                         SkSurface::kNo_Budgeted, info,
+                                                         this->config.samples, &props));
+        this->gl = gGrFactory->getGLContext(this->config.ctxType);
+        if (!this->surface.get()) {
+            return false;
+        }
+        return true;
+    }
+    void fillOptions(ResultsWriter* log) override {
+        const GrGLubyte* version;
+        SK_GL_RET(*this->gl, version, GetString(GR_GL_VERSION));
+        log->configOption("GL_VERSION", (const char*)(version));
+
+        SK_GL_RET(*this->gl, version, GetString(GR_GL_RENDERER));
+        log->configOption("GL_RENDERER", (const char*) version);
+
+        SK_GL_RET(*this->gl, version, GetString(GR_GL_VENDOR));
+        log->configOption("GL_VENDOR", (const char*) version);
+
+        SK_GL_RET(*this->gl, version, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
+        log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version);
+    }
+};
+
+#endif
+
+static double time(int loops, Benchmark* bench, Target* target) {
+    SkCanvas* canvas = target->getCanvas();
     if (canvas) {
         canvas->clear(SK_ColorWHITE);
     }
     WallTimer timer;
     timer.start();
-    if (bench) {
-        bench->draw(loops, canvas);
-    }
+    canvas = target->beginTiming(canvas);
+    bench->draw(loops, canvas);
     if (canvas) {
         canvas->flush();
     }
-#if SK_SUPPORT_GPU
-    if (gl) {
-        SK_GL(*gl, Flush());
-        gl->swapBuffers();
-    }
-#endif
+    target->endTiming();
     timer.end();
     return timer.fWall;
 }
@@ -111,7 +189,10 @@
 static double estimate_timer_overhead() {
     double overhead = 0;
     for (int i = 0; i < FLAGS_overheadLoops; i++) {
-        overhead += time(1, NULL, NULL, NULL);
+        WallTimer timer;
+        timer.start();
+        timer.end();
+        overhead += timer.fWall;
     }
     return overhead / FLAGS_overheadLoops;
 }
@@ -137,19 +218,22 @@
     return loops;
 }
 
-static bool write_canvas_png(SkCanvas* canvas, const SkString& filename) {
+static bool write_canvas_png(Target* target, const SkString& filename) {
+
     if (filename.isEmpty()) {
         return false;
     }
-    if (kUnknown_SkColorType == canvas->imageInfo().colorType()) {
+    if (target->getCanvas() &&
+        kUnknown_SkColorType == target->getCanvas()->imageInfo().colorType()) {
         return false;
     }
+
     SkBitmap bmp;
-    bmp.setInfo(canvas->imageInfo());
-    if (!canvas->readPixels(&bmp, 0, 0)) {
-        SkDebugf("Can't read canvas pixels.\n");
+
+    if (!target->capturePixels(&bmp)) {
         return false;
     }
+
     SkString dir = SkOSPath::Dirname(filename.c_str());
     if (!sk_mkdir(dir.c_str())) {
         SkDebugf("Can't make dir %s.\n", dir.c_str());
@@ -168,7 +252,7 @@
 }
 
 static int kFailedLoops = -2;
-static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas, double* samples) {
+static int cpu_bench(const double overhead, Target* target, Benchmark* bench, double* samples) {
     // First figure out approximately how many loops of bench it takes to make overhead negligible.
     double bench_plus_overhead = 0.0;
     int round = 0;
@@ -179,7 +263,7 @@
                          bench->getUniqueName(), HUMANIZE(bench_plus_overhead), HUMANIZE(overhead));
                 return kFailedLoops;
             }
-            bench_plus_overhead = time(1, bench, canvas, NULL);
+            bench_plus_overhead = time(1, bench, target);
         }
     }
 
@@ -210,21 +294,13 @@
     }
 
     for (int i = 0; i < FLAGS_samples; i++) {
-        samples[i] = time(loops, bench, canvas, NULL) / loops;
+        samples[i] = time(loops, bench, target) / loops;
     }
     return loops;
 }
 
-#if SK_SUPPORT_GPU
-static void setup_gl(SkGLContext* gl) {
-    gl->makeCurrent();
-    // Make sure we're done with whatever came before.
-    SK_GL(*gl, Finish());
-}
-
-static int gpu_bench(SkGLContext* gl,
+static int gpu_bench(Target* target,
                      Benchmark* bench,
-                     SkCanvas* canvas,
                      double* samples) {
     // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs.
     int loops = FLAGS_loops;
@@ -242,7 +318,7 @@
             // _this_ round, not still timing last round.  We force this by looping
             // more times than any reasonable GPU will allow frames to lag.
             for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
-                elapsed = time(loops, bench, canvas, gl);
+                elapsed = time(loops, bench, target);
             }
         } while (elapsed < FLAGS_gpuMs);
 
@@ -250,8 +326,8 @@
         loops = (int)ceil(loops * FLAGS_gpuMs / elapsed);
         loops = clamp_loops(loops);
 
-        // Might as well make sure we're not still timing our calibration.
-        SK_GL(*gl, Finish());
+        // Make sure we're not still timing our calibration.
+        target->fence();
     } else {
         loops = detect_forever_loops(loops);
     }
@@ -259,16 +335,16 @@
     // Pretty much the same deal as the calibration: do some warmup to make
     // sure we're timing steady-state pipelined frames.
     for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
-        time(loops, bench, canvas, gl);
+        time(loops, bench, target);
     }
 
     // Now, actually do the timing!
     for (int i = 0; i < FLAGS_samples; i++) {
-        samples[i] = time(loops, bench, canvas, gl) / loops;
+        samples[i] = time(loops, bench, target) / loops;
     }
+
     return loops;
 }
-#endif
 
 static SkString to_lower(const char* str) {
     SkString lower(str);
@@ -278,30 +354,6 @@
     return lower;
 }
 
-struct Config {
-    const char* name;
-    Benchmark::Backend backend;
-    SkColorType color;
-    SkAlphaType alpha;
-    int samples;
-#if SK_SUPPORT_GPU
-    GrContextFactory::GLContextType ctxType;
-    bool useDFText;
-#else
-    int bogusInt;
-    bool bogusBool;
-#endif
-};
-
-struct Target {
-    explicit Target(const Config& c) : config(c) {}
-    const Config config;
-    SkAutoTDelete<SkSurface> surface;
-#if SK_SUPPORT_GPU
-    SkGLContext* gl;
-#endif
-};
-
 static bool is_cpu_config_allowed(const char* name) {
     for (int i = 0; i < FLAGS_config.count(); i++) {
         if (to_lower(FLAGS_config[i]).equals(name)) {
@@ -373,6 +425,14 @@
 #endif
     }
 #endif
+
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+    if (is_cpu_config_allowed("hwui")) {
+        Config config = { "hwui", Benchmark::kHWUI_Backend, kRGBA_8888_SkColorType,
+                          kPremul_SkAlphaType, 0, kBogusGLContextType, false };
+        configs->push(config);
+    }
+#endif
 }
 
 // If bench is enabled for config, returns a Target* for it, otherwise NULL.
@@ -384,23 +444,25 @@
     SkImageInfo info = SkImageInfo::Make(bench->getSize().fX, bench->getSize().fY,
                                          config.color, config.alpha);
 
-    Target* target = new Target(config);
+    Target* target = NULL;
 
-    if (Benchmark::kRaster_Backend == config.backend) {
-        target->surface.reset(SkSurface::NewRaster(info));
-    }
+    switch (config.backend) {
 #if SK_SUPPORT_GPU
-    else if (Benchmark::kGPU_Backend == config.backend) {
-        uint32_t flags = config.useDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0;
-        SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
-        target->surface.reset(SkSurface::NewRenderTarget(gGrFactory->get(config.ctxType),
-                                                         SkSurface::kNo_Budgeted, info,
-                                                         config.samples, &props));
-        target->gl = gGrFactory->getGLContext(config.ctxType);
-    }
+    case Benchmark::kGPU_Backend:
+        target = new GPUTarget(config);
+        break;
 #endif
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+    case Benchmark::kHWUI_Backend:
+        target = new HWUITarget(config, bench);
+        break;
+#endif
+    default:
+        target = new Target(config);
+        break;
+    }
 
-    if (Benchmark::kNonRendering_Backend != config.backend && !target->surface.get()) {
+    if (!target->init(info, bench)) {
         delete target;
         return NULL;
     }
@@ -418,22 +480,6 @@
     }
 }
 
-#if SK_SUPPORT_GPU
-static void fill_gpu_options(ResultsWriter* log, SkGLContext* ctx) {
-    const GrGLubyte* version;
-    SK_GL_RET(*ctx, version, GetString(GR_GL_VERSION));
-    log->configOption("GL_VERSION", (const char*)(version));
-
-    SK_GL_RET(*ctx, version, GetString(GR_GL_RENDERER));
-    log->configOption("GL_RENDERER", (const char*) version);
-
-    SK_GL_RET(*ctx, version, GetString(GR_GL_VENDOR));
-    log->configOption("GL_VENDOR", (const char*) version);
-
-    SK_GL_RET(*ctx, version, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
-    log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version);
-}
-#endif
 
 class BenchmarkStream {
 public:
@@ -443,9 +489,11 @@
                       , fCurrentScale(0)
                       , fCurrentSKP(0)
                       , fCurrentUseMPD(0)
+                      , fCurrentCodec(0)
                       , fCurrentImage(0)
                       , fCurrentSubsetImage(0)
                       , fCurrentColorType(0)
+                      , fCurrentAnimSKP(0)
                       , fDivisor(2) {
         for (int i = 0; i < FLAGS_skps.count(); i++) {
             if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
@@ -472,11 +520,16 @@
             }
         }
 
+        if (2 != sscanf(FLAGS_zoom[0], "%f,%d", &fZoomScale, &fZoomSteps)) {
+            SkDebugf("Can't parse %s from --zoom as a scale,step.\n", FLAGS_zoom[0]);
+            exit(1);
+        }
+
         fUseMPDs.push_back() = false;
         if (FLAGS_mpd) {
             fUseMPDs.push_back() = true;
         }
-        
+
         // Prepare the images for decoding
         for (int i = 0; i < FLAGS_images.count(); i++) {
             const char* flag = FLAGS_images[i];
@@ -492,10 +545,10 @@
                 fImages.push_back() = flag;
             }
         }
-        
+
         // Choose the candidate color types for image decoding
         const SkColorType colorTypes[] =
-            { kN32_SkColorType, kRGB_565_SkColorType, kAlpha_8_SkColorType };
+            { kN32_SkColorType, kRGB_565_SkColorType, kAlpha_8_SkColorType, kIndex_8_SkColorType };
         fColorTypes.push_back_n(SK_ARRAY_COUNT(colorTypes), colorTypes);
     }
 
@@ -580,8 +633,9 @@
                     fSourceType = "skp";
                     fBenchType = "playback";
                     return SkNEW_ARGS(SKPBench,
-                            (name.c_str(), pic.get(), fClip,
-                             fScales[fCurrentScale], fUseMPDs[fCurrentUseMPD++]));
+                                      (name.c_str(), pic.get(), fClip,
+                                       fScales[fCurrentScale], fUseMPDs[fCurrentUseMPD++]));
+
                 }
                 fCurrentUseMPD = 0;
                 fCurrentSKP++;
@@ -590,16 +644,90 @@
             fCurrentScale++;
         }
 
+        // Now loop over each skp again if we have an animation
+        if (fZoomScale != 1.0f && fZoomSteps != 1) {
+            while (fCurrentAnimSKP < fSKPs.count()) {
+                const SkString& path = fSKPs[fCurrentAnimSKP];
+                SkAutoTUnref<SkPicture> pic;
+                if (!ReadPicture(path.c_str(), &pic)) {
+                    fCurrentAnimSKP++;
+                    continue;
+                }
+
+                fCurrentAnimSKP++;
+                SkString name = SkOSPath::Basename(path.c_str());
+                SkMatrix anim = SkMatrix::I();
+                anim.setScale(fZoomScale, fZoomScale);
+                return SkNEW_ARGS(SKPAnimationBench, (name.c_str(), pic.get(), fClip, anim,
+                                  fZoomSteps));
+            }
+        }
+
+
+        for (; fCurrentCodec < fImages.count(); fCurrentCodec++) {
+            const SkString& path = fImages[fCurrentCodec];
+            SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
+            SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
+            if (!codec) {
+                // Nothing to time.
+                SkDebugf("Cannot find codec for %s\n", path.c_str());
+                continue;
+            }
+
+            while (fCurrentColorType < fColorTypes.count()) {
+                const SkColorType colorType = fColorTypes[fCurrentColorType];
+                fCurrentColorType++;
+
+                // Make sure we can decode to this color type.
+                SkImageInfo info = codec->getInfo().makeColorType(colorType);
+                SkAlphaType alphaType;
+                if (!SkColorTypeValidateAlphaType(colorType, info.alphaType(),
+                                                  &alphaType)) {
+                    continue;
+                }
+                if (alphaType != info.alphaType()) {
+                    info = info.makeAlphaType(alphaType);
+                }
+
+                const size_t rowBytes = info.minRowBytes();
+                SkAutoMalloc storage(info.getSafeSize(rowBytes));
+
+                // Used if fCurrentColorType is kIndex_8_SkColorType
+                int colorCount = 256;
+                SkPMColor colors[256];
+
+                const SkImageGenerator::Result result = codec->getPixels(
+                        info, storage.get(), rowBytes, NULL, colors,
+                        &colorCount);
+                switch (result) {
+                    case SkImageGenerator::kSuccess:
+                    case SkImageGenerator::kIncompleteInput:
+                        return new CodecBench(SkOSPath::Basename(path.c_str()),
+                                encoded, colorType);
+                    case SkImageGenerator::kInvalidConversion:
+                        // This is okay. Not all conversions are valid.
+                        break;
+                    default:
+                        // This represents some sort of failure.
+                        SkASSERT(false);
+                        break;
+                }
+            }
+            fCurrentColorType = 0;
+        }
+
         // Run the DecodingBenches
         while (fCurrentImage < fImages.count()) {
             while (fCurrentColorType < fColorTypes.count()) {
                 const SkString& path = fImages[fCurrentImage];
                 SkColorType colorType = fColorTypes[fCurrentColorType];
                 fCurrentColorType++;
-                // Check if the image decodes before creating the benchmark
+                // Check if the image decodes to the right color type
+                // before creating the benchmark
                 SkBitmap bitmap;
                 if (SkImageDecoder::DecodeFile(path.c_str(), &bitmap,
-                        colorType, SkImageDecoder::kDecodePixels_Mode)) {
+                        colorType, SkImageDecoder::kDecodePixels_Mode)
+                        && bitmap.colorType() == colorType) {
                     return new DecodingBench(path, colorType);
                 }
             }
@@ -690,6 +818,8 @@
     SkTArray<bool>     fUseMPDs;
     SkTArray<SkString> fImages;
     SkTArray<SkColorType> fColorTypes;
+    SkScalar           fZoomScale;
+    int                fZoomSteps;
 
     double fSKPBytes, fSKPOps;
 
@@ -699,9 +829,11 @@
     int fCurrentScale;
     int fCurrentSKP;
     int fCurrentUseMPD;
+    int fCurrentCodec;
     int fCurrentImage;
     int fCurrentSubsetImage;
     int fCurrentColorType;
+    int fCurrentAnimSKP;
     const int fDivisor;
 };
 
@@ -767,7 +899,7 @@
     } else if (FLAGS_quiet) {
         SkDebugf("median\tbench\tconfig\n");
     } else {
-        SkDebugf("maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n",
+        SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n",
                  FLAGS_samples, "samples");
     }
 
@@ -790,32 +922,26 @@
             bench->preDraw();
         }
         for (int j = 0; j < targets.count(); j++) {
-            SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface->getCanvas() : NULL;
+            // During HWUI output this canvas may be NULL.
+            SkCanvas* canvas = targets[j]->getCanvas();
             const char* config = targets[j]->config.name;
 
-#if SK_SUPPORT_GPU
-            if (Benchmark::kGPU_Backend == targets[j]->config.backend) {
-                setup_gl(targets[j]->gl);
-            }
-#endif
-
+            targets[j]->setup();
             bench->perCanvasPreDraw(canvas);
 
             const int loops =
-#if SK_SUPPORT_GPU
-                Benchmark::kGPU_Backend == targets[j]->config.backend
-                ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get())
-                :
-#endif
-                 cpu_bench(       overhead, bench.get(), canvas, samples.get());
+                targets[j]->needsFrameTiming()
+                ? gpu_bench(targets[j], bench.get(), samples.get())
+                : cpu_bench(overhead, targets[j], bench.get(), samples.get());
 
             bench->perCanvasPostDraw(canvas);
 
-            if (canvas && !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) {
+            if (Benchmark::kNonRendering_Backend != targets[j]->config.backend &&
+                !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) {
                 SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config);
                 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName());
                 pngFilename.append(".png");
-                write_canvas_png(canvas, pngFilename);
+                write_canvas_png(targets[j], pngFilename);
             }
 
             if (kFailedLoops == loops) {
@@ -827,11 +953,7 @@
             log->config(config);
             log->configOption("name", bench->getName());
             benchStream.fillCurrentOptions(log.get());
-#if SK_SUPPORT_GPU
-            if (Benchmark::kGPU_Backend == targets[j]->config.backend) {
-                fill_gpu_options(log.get(), targets[j]->gl);
-            }
-#endif
+            targets[j]->fillOptions(log.get());
             log->metric("min_ms",    stats.min);
             if (runs++ % FLAGS_flushEvery == 0) {
                 log->flush();
@@ -841,7 +963,8 @@
                 if (targets.count() == 1) {
                     config = ""; // Only print the config if we run the same bench on more than one.
                 }
-                SkDebugf("%4dM\t%s\t%s\n"
+                SkDebugf("%4d/%-4dMB\t%s\t%s\n"
+                         , sk_tools::getCurrResidentSetSizeMB()
                          , sk_tools::getMaxResidentSetSizeMB()
                          , bench->getUniqueName()
                          , config);
@@ -857,7 +980,8 @@
                 SkDebugf("%s\t%s\t%s\n", HUMANIZE(stats.median), bench->getUniqueName(), config);
             } else {
                 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
-                SkDebugf("%4dM\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n"
+                SkDebugf("%4d/%-4dMB\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n"
+                        , sk_tools::getCurrResidentSetSizeMB()
                         , sk_tools::getMaxResidentSetSizeMB()
                         , loops
                         , HUMANIZE(stats.min)
@@ -894,6 +1018,12 @@
     log->config("meta");
     log->metric("max_rss_mb", sk_tools::getMaxResidentSetSizeMB());
 
+#if SK_SUPPORT_GPU
+    // Make sure we clean up the global GrContextFactory here, otherwise we might race with the
+    // SkEventTracer destructor
+    gGrFactory.reset(NULL);
+#endif
+
     return 0;
 }
 
diff --git a/bench/nanobench.h b/bench/nanobench.h
new file mode 100644
index 0000000..1dc0b8b
--- /dev/null
+++ b/bench/nanobench.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef nanobench_DEFINED
+#define nanobench_DEFINED
+
+#include "Benchmark.h"
+#include "SkImageInfo.h"
+#include "SkSurface.h"
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContextFactory.h"
+#endif
+
+class ResultsWriter;
+class SkBitmap;
+class SkCanvas;
+
+struct Config {
+    const char* name;
+    Benchmark::Backend backend;
+    SkColorType color;
+    SkAlphaType alpha;
+    int samples;
+#if SK_SUPPORT_GPU
+    GrContextFactory::GLContextType ctxType;
+    bool useDFText;
+#else
+    int bogusInt;
+    bool bogusBool;
+#endif
+};
+
+struct Target {
+    explicit Target(const Config& c) : config(c) { }
+    virtual ~Target() { }
+
+    const Config config;
+    SkAutoTDelete<SkSurface> surface;
+
+    /** Called once per target, immediately before any timing or drawing. */
+    virtual void setup() { }
+
+    /** Called *after* the clock timer is started, before the benchmark
+        is drawn. Most back ends just return the canvas passed in,
+        but some may replace it. */
+    virtual SkCanvas* beginTiming(SkCanvas* canvas) { return canvas; }
+
+    /** Called *after* a benchmark is drawn, but before the clock timer
+        is stopped.  */
+    virtual void endTiming() { }
+
+    /** Called between benchmarks (or between calibration and measured
+        runs) to make sure all pending work in drivers / threads is
+        complete. */
+    virtual void fence() { }
+
+    /** CPU-like targets can just be timed, but GPU-like
+        targets need to pay attention to frame boundaries
+        or other similar details. */
+    virtual bool needsFrameTiming() const { return false; }
+
+    /** Called once per target, during program initialization.
+        Returns false if initialization fails. */
+    virtual bool init(SkImageInfo info, Benchmark* bench);
+
+    /** Stores any pixels drawn to the screen in the bitmap.
+        Returns false on error. */
+    virtual bool capturePixels(SkBitmap* bmp);
+
+    /** Writes any config-specific data to the log. */
+    virtual void fillOptions(ResultsWriter*) { }
+
+    SkCanvas* getCanvas() const {
+        if (!surface.get()) {
+            return NULL;
+        }
+        return surface->getCanvas();
+    }
+};
+
+#endif  // nanobench_DEFINED
diff --git a/bench/nanobenchAndroid.cpp b/bench/nanobenchAndroid.cpp
new file mode 100644
index 0000000..3d5cda4
--- /dev/null
+++ b/bench/nanobenchAndroid.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "nanobenchAndroid.h"
+
+#include "AnimationContext.h"
+#include "IContextFactory.h"
+#include "SkiaCanvasProxy.h"
+#include "android/rect.h"
+#include "android/native_window.h"
+#include "renderthread/TimeLord.h"
+
+/* These functions are only compiled in the Android Framework. */
+
+HWUITarget::HWUITarget(const Config& c, Benchmark* bench) : Target(c) { }
+
+void HWUITarget::setup() {
+    this->renderer.proxy->fence();
+}
+
+SkCanvas* HWUITarget::beginTiming(SkCanvas* canvas) {
+    SkCanvas* targetCanvas = this->renderer.prepareToDraw();
+    if (targetCanvas) {
+        this->fc.reset(targetCanvas);
+        canvas = &this->fc;
+        // This might minimally distort timing, but canvas isn't valid outside the timer.
+        canvas->clear(SK_ColorWHITE);
+    }
+
+    return canvas;
+}
+
+void HWUITarget::endTiming() {
+    this->renderer.finishDrawing();
+}
+
+void HWUITarget::fence() {
+    this->renderer.proxy->fence();
+}
+
+bool HWUITarget::needsFrameTiming() const {
+    return true;
+}
+
+bool HWUITarget::init(SkImageInfo info, Benchmark* bench) {
+    this->renderer.initialize({bench->getSize().x(), bench->getSize().y()});
+    return true;
+}
+
+bool HWUITarget::capturePixels(SkBitmap* bmp) {
+    return this->renderer.capturePixels(bmp);
+}
+
+
diff --git a/bench/nanobenchAndroid.h b/bench/nanobenchAndroid.h
new file mode 100644
index 0000000..7f6ff44
--- /dev/null
+++ b/bench/nanobenchAndroid.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef nanobenchAndroid_DEFINED
+#define nanobenchAndroid_DEFINED
+
+#include "SkAndroidSDKCanvas.h"
+#include "SkHwuiRenderer.h"
+
+#include "nanobench.h"
+
+struct HWUITarget : public Target {
+    explicit HWUITarget(const Config& c, Benchmark* bench);
+
+    SkHwuiRenderer renderer;
+    SkAndroidSDKCanvas fc;
+
+    void setup() override;
+    SkCanvas* beginTiming(SkCanvas* canvas) override;
+    void endTiming() override;
+    void fence() override;
+    bool needsFrameTiming() const override;
+
+    bool init(SkImageInfo info, Benchmark* bench) override;
+    bool capturePixels(SkBitmap* bmp) override;
+};
+
+#endif  // nanobenchAndroid_DEFINED
diff --git a/bin/ac b/bin/ac
index 05fd605..30e9534 100755
--- a/bin/ac
+++ b/bin/ac
@@ -6,7 +6,7 @@
 CLEAN=${CLEAN-clean}
 SAMPLES=100
 
-if [ $BRANCH == $CLEAN ]; then
+if [ "$BRANCH" = "$CLEAN" ]; then
     echo "Comparing $BRANCH to itself."
     exit 1
 fi
@@ -23,4 +23,4 @@
 platform_tools/android/bin/android_ninja -t Release nanobench
 platform_tools/android/bin/android_run_skia -t Release nanobench $@ --skps /data/local/tmp/skps -i /data/local/tmp/resources --samples $SAMPLES -v > $BRANCH.log
 
-compare $CLEAN.log $BRANCH.log
+./bin/compare $CLEAN.log $BRANCH.log
diff --git a/bin/c b/bin/c
index 129a4e1..6e3bd6e 100755
--- a/bin/c
+++ b/bin/c
@@ -6,7 +6,7 @@
 CLEAN=${CLEAN-clean}
 SAMPLES=100
 
-if [ $BRANCH == $CLEAN ]; then
+if [ "$BRANCH" = "$CLEAN" ]; then
     echo "Comparing $BRANCH to itself."
     exit 1
 fi
@@ -23,4 +23,4 @@
 ninja -C out/Release nanobench
 out/Release/nanobench $@ --samples $SAMPLES -v 2> $BRANCH.log
 
-compare $CLEAN.log $BRANCH.log
+./bin/compare $CLEAN.log $BRANCH.log
diff --git a/bin/sync-and-gyp b/bin/sync-and-gyp
new file mode 100755
index 0000000..825f316
--- /dev/null
+++ b/bin/sync-and-gyp
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script will update Skia's dependenciess as necessary and run
+# gyp if needed.
+
+# Example usage (assumes Posix-standard shell, git installed):
+#
+#   git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
+#   export PATH="${PWD}/depot_tools:${PATH}"
+#   git clone https://skia.googlesource.com/skia
+#   cd skia
+#   bin/sync-and-gyp && ninja -C out/Debug
+#   out/Debug/dm
+#
+# Once changes are made to DEPS or gyp/ or the source, recompile Skia with:
+#
+#   ${skiadir}/bin/sync-and-gyp && ninja -C ${skiadir}/out/Debug
+
+cd "$(dirname "$0")/.."
+
+if ! [ -f .gclient ] ; then
+    gclient config --name . --unmanaged 'https://skia.googlesource.com/skia'
+fi
+
+if ! [ -f DEPS ]; then
+    echo DEPS file missing >&2
+    exit 1
+fi
+
+if [ "$(git hash-object DEPS)" != "$(git config sync-deps.last)" ] ; then
+    gclient sync || exit
+    git config sync-deps.last "$(git hash-object DEPS)"
+fi
+
+function catifexists() { if [ -f "$1" ]; then cat "$1"; fi; }
+
+function gyp_hasher() {
+    {
+        echo "$GYP_GENERATORS"
+        echo "$GYP_DEFINES"
+        find gyp -type f -print -exec git hash-object {} \;
+    } | git hash-object --stdin
+}
+
+: ${SKIA_OUT:=out}
+GYP_HASH=$(gyp_hasher)
+HASH_PATH="${SKIA_OUT}/gyp_hash"
+if [ "$GYP_HASH" != "$(catifexists "$HASH_PATH")" ]; then
+    python ./gyp_skia || exit
+    echo "$GYP_HASH" > "$HASH_PATH"
+fi
diff --git a/debugger/QT/SkDebuggerGUI.cpp b/debugger/QT/SkDebuggerGUI.cpp
index 01d8c66..237f701 100644
--- a/debugger/QT/SkDebuggerGUI.cpp
+++ b/debugger/QT/SkDebuggerGUI.cpp
@@ -12,6 +12,7 @@
 #include "SkPictureRecord.h"
 #include <QListWidgetItem>
 #include <QtGui>
+#include "sk_tool_utils.h"
 
 #if defined(SK_BUILD_FOR_WIN32)
     #include "SysTimer_windows.h"
@@ -31,7 +32,6 @@
     , fToolBar(this)
     , fActionOpen(this)
     , fActionBreakpoint(this)
-    , fActionToggleIndexStyle(this)
     , fActionProfile(this)
     , fActionCancel(this)
     , fActionClearBreakpoints(this)
@@ -78,7 +78,6 @@
     connect(&fActionStepBack, SIGNAL(triggered()), this, SLOT(actionStepBack()));
     connect(&fActionStepForward, SIGNAL(triggered()), this, SLOT(actionStepForward()));
     connect(&fActionBreakpoint, SIGNAL(triggered()), this, SLOT(actionBreakpoints()));
-    connect(&fActionToggleIndexStyle, SIGNAL(triggered()), this, SLOT(actionToggleIndexStyle()));
     connect(&fActionInspector, SIGNAL(triggered()), this, SLOT(actionInspector()));
     connect(&fActionSettings, SIGNAL(triggered()), this, SLOT(actionSettings()));
     connect(&fFilter, SIGNAL(activated(QString)), this, SLOT(toggleFilter(QString)));
@@ -125,14 +124,6 @@
     }
 }
 
-void SkDebuggerGUI::actionToggleIndexStyle() {
-    bool indexStyleToggle = fActionToggleIndexStyle.isChecked();
-    SkListWidget* list = (SkListWidget*) fListWidget.itemDelegate();
-    list->setIndexStyle(indexStyleToggle ? SkListWidget::kOffset_IndexStyle
-                                         : SkListWidget::kIndex_IndexStyle);
-    fListWidget.update();
-}
-
 void SkDebuggerGUI::showDeletes() {
     bool deletesActivated = fActionShowDeletes.isChecked();
     for (int row = 0; row < fListWidget.count(); row++) {
@@ -292,9 +283,9 @@
 }
 
 void SkDebuggerGUI::actionTextureFilter() {
-    SkPaint::FilterLevel level;
-    bool enabled = fSettingsWidget.getFilterOverride(&level);
-    fDebugger.setTexFilterOverride(enabled, level);
+    SkFilterQuality quality;
+    bool enabled = fSettingsWidget.getFilterOverride(&quality);
+    fDebugger.setTexFilterOverride(enabled, quality);
     fCanvasWidget.update();
 }
 
@@ -359,7 +350,8 @@
     SkFILEWStream file(filename.c_str());
     SkAutoTUnref<SkPicture> copy(fDebugger.copyPicture());
 
-    copy->serialize(&file);
+    sk_tool_utils::PngPixelSerializer serializer;
+    copy->serialize(&file, &serializer);
 }
 
 void SkDebuggerGUI::loadFile(QListWidgetItem *item) {
@@ -481,10 +473,6 @@
     fActionBreakpoint.setText("Breakpoints");
     fActionBreakpoint.setCheckable(true);
 
-    fActionToggleIndexStyle.setShortcut(QKeySequence(tr("Ctrl+T")));
-    fActionToggleIndexStyle.setText("Toggle Index Style");
-    fActionToggleIndexStyle.setCheckable(true);
-
     QIcon cancel;
     cancel.addFile(QString::fromUtf8(":/reload.png"), QSize(),
             QIcon::Normal, QIcon::Off);
@@ -702,7 +690,6 @@
     fMenuView.setTitle("View");
     fMenuView.addAction(&fActionBreakpoint);
     fMenuView.addAction(&fActionShowDeletes);
-    fMenuView.addAction(&fActionToggleIndexStyle);
     fMenuView.addAction(&fActionZoomIn);
     fMenuView.addAction(&fActionZoomOut);
 
@@ -785,6 +772,22 @@
 }
 
 void SkDebuggerGUI::setupListWidget() {
+
+    SkASSERT(!strcmp("Save",
+                     SkDrawCommand::GetCommandString(SkDrawCommand::kSave_OpType)));
+    SkASSERT(!strcmp("SaveLayer",
+                     SkDrawCommand::GetCommandString(SkDrawCommand::kSaveLayer_OpType)));
+    SkASSERT(!strcmp("Restore",
+                     SkDrawCommand::GetCommandString(SkDrawCommand::kRestore_OpType)));
+    SkASSERT(!strcmp("BeginCommentGroup",
+                     SkDrawCommand::GetCommandString(SkDrawCommand::kBeginCommentGroup_OpType)));
+    SkASSERT(!strcmp("EndCommentGroup",
+                     SkDrawCommand::GetCommandString(SkDrawCommand::kEndCommentGroup_OpType)));
+    SkASSERT(!strcmp("BeginDrawPicture",
+                     SkDrawCommand::GetCommandString(SkDrawCommand::kBeginDrawPicture_OpType)));
+    SkASSERT(!strcmp("EndDrawPicture",
+                     SkDrawCommand::GetCommandString(SkDrawCommand::kEndDrawPicture_OpType)));
+
     fListWidget.clear();
     int counter = 0;
     int indent = 0;
@@ -796,20 +799,21 @@
         item->setData(Qt::UserRole + 1, counter++);
 
         if (0 == strcmp("Restore", commandString.c_str()) ||
-            0 == strcmp("EndCommentGroup", commandString.c_str())) {
+            0 == strcmp("EndCommentGroup", commandString.c_str()) ||
+            0 == strcmp("EndDrawPicture", commandString.c_str())) {
             indent -= 10;
         }
 
         item->setData(Qt::UserRole + 3, indent);
 
         if (0 == strcmp("Save", commandString.c_str()) ||
-            0 == strcmp("Save Layer", commandString.c_str()) ||
-            0 == strcmp("BeginCommentGroup", commandString.c_str())) {
+            0 == strcmp("SaveLayer", commandString.c_str()) ||
+            0 == strcmp("BeginCommentGroup", commandString.c_str()) ||
+            0 == strcmp("BeginDrawPicture", commandString.c_str())) {
             indent += 10;
         }
 
         item->setData(Qt::UserRole + 4, -1);
-        item->setData(Qt::UserRole + 5, (int) command->offset());
 
         fListWidget.addItem(item);
     }
diff --git a/debugger/QT/SkDebuggerGUI.h b/debugger/QT/SkDebuggerGUI.h
index d6c3f49..6ff4d4d 100644
--- a/debugger/QT/SkDebuggerGUI.h
+++ b/debugger/QT/SkDebuggerGUI.h
@@ -78,11 +78,6 @@
     void actionBreakpoints();
 
     /**
-        Toggles between count and offset style of command indexing in GUI
-     */
-    void actionToggleIndexStyle();
-
-    /**
         Profile the commands
      */
     void actionProfile();
@@ -243,7 +238,6 @@
 
     QAction fActionOpen;
     QAction fActionBreakpoint;
-    QAction fActionToggleIndexStyle;
     QAction fActionProfile;
     QAction fActionCancel;
     QAction fActionClearBreakpoints;
diff --git a/debugger/QT/SkGLWidget.cpp b/debugger/QT/SkGLWidget.cpp
index 6720239..cd9350a 100644
--- a/debugger/QT/SkGLWidget.cpp
+++ b/debugger/QT/SkGLWidget.cpp
@@ -60,7 +60,8 @@
 
     GrBackendRenderTargetDesc desc = this->getDesc(this->width(), this->height());
     desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
-    SkAutoTUnref<GrRenderTarget> curRenderTarget(fCurContext->wrapBackendRenderTarget(desc));
+    SkAutoTUnref<GrRenderTarget> curRenderTarget(
+            fCurContext->textureProvider()->wrapBackendRenderTarget(desc));
     SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
     fGpuDevice.reset(SkGpuDevice::Create(curRenderTarget, &props));
     fCanvas.reset(new SkCanvas(fGpuDevice));
diff --git a/debugger/QT/SkListWidget.cpp b/debugger/QT/SkListWidget.cpp
index b5f9038..a027778 100644
--- a/debugger/QT/SkListWidget.cpp
+++ b/debugger/QT/SkListWidget.cpp
@@ -61,11 +61,7 @@
 
     QString drawCommandText = index.data(Qt::DisplayRole).toString();
     QString drawCommandNumber;
-    if (kIndex_IndexStyle == fIndexStyle) {
-        drawCommandNumber = index.data(Qt::UserRole + 1).toString();
-    } else {
-        drawCommandNumber = index.data(Qt::UserRole + 5).toString();
-    }
+    drawCommandNumber = index.data(Qt::UserRole + 1).toString();
     float time = index.data(Qt::UserRole + 4).toFloat();
     QString drawTime;
     drawTime.setNum(time, 'f', 2);
diff --git a/debugger/QT/SkListWidget.h b/debugger/QT/SkListWidget.h
index d6e797a..4a799a0 100644
--- a/debugger/QT/SkListWidget.h
+++ b/debugger/QT/SkListWidget.h
@@ -19,16 +19,11 @@
  */
 class SkListWidget : public QAbstractItemDelegate {
 public:
-    enum IndexStyle {
-        kIndex_IndexStyle,
-        kOffset_IndexStyle,
-    };
-
     /**
         Constructs the list widget with the specified parent for layout purposes.
         @param parent  The parent container of this widget
      */
-    SkListWidget(QObject* parent = NULL) : fIndexStyle(kIndex_IndexStyle) {}
+    SkListWidget(QObject* parent = NULL) {}
 
     virtual ~SkListWidget() {}
 
@@ -43,14 +38,6 @@
      */
     QSize sizeHint(const QStyleOptionViewItem& option,
                    const QModelIndex& index) const;
-
-
-    void setIndexStyle(IndexStyle indexStyle) {
-        fIndexStyle = indexStyle;
-    }
-
-protected:
-    IndexStyle fIndexStyle;
 };
 
 #endif
diff --git a/debugger/QT/SkSettingsWidget.cpp b/debugger/QT/SkSettingsWidget.cpp
index 90e358b..59b7920 100644
--- a/debugger/QT/SkSettingsWidget.cpp
+++ b/debugger/QT/SkSettingsWidget.cpp
@@ -64,11 +64,11 @@
     fVerticalLayout.addRow(&fGLGroup);
 #endif
 
-    fFilterCombo.addItem("As encoded", QVariant(SkPaint::kNone_FilterLevel));
-    fFilterCombo.addItem("None", QVariant(SkPaint::kNone_FilterLevel));
-    fFilterCombo.addItem("Low", QVariant(SkPaint::kLow_FilterLevel));
-    fFilterCombo.addItem("Medium", QVariant(SkPaint::kMedium_FilterLevel));
-    fFilterCombo.addItem("High", QVariant(SkPaint::kHigh_FilterLevel));
+    fFilterCombo.addItem("As encoded", QVariant(kNone_SkFilterQuality));
+    fFilterCombo.addItem("None", QVariant(kNone_SkFilterQuality));
+    fFilterCombo.addItem("Low", QVariant(kLow_SkFilterQuality));
+    fFilterCombo.addItem("Medium", QVariant(kMedium_SkFilterQuality));
+    fFilterCombo.addItem("High", QVariant(kHigh_SkFilterQuality));
     connect(&fFilterCombo, SIGNAL(activated(int)), this, SIGNAL(texFilterSettingsChanged()));
 
     fVerticalLayout.addRow("Filtering", &fFilterCombo);
diff --git a/debugger/QT/SkSettingsWidget.h b/debugger/QT/SkSettingsWidget.h
index 70e8965..012abc0 100644
--- a/debugger/QT/SkSettingsWidget.h
+++ b/debugger/QT/SkSettingsWidget.h
@@ -51,9 +51,9 @@
 
 #endif
 
-    bool getFilterOverride(SkPaint::FilterLevel* filterLevel) const {
+    bool getFilterOverride(SkFilterQuality* filterQuality) const {
         int index = fFilterCombo.currentIndex();
-        *filterLevel = (SkPaint::FilterLevel)fFilterCombo.itemData(index).toUInt();
+        *filterQuality = (SkFilterQuality)fFilterCombo.itemData(index).toUInt();
 
         return index > 0;
     }
diff --git a/debugger/SkDebugger.h b/debugger/SkDebugger.h
index 76bd71b..230a1ff 100644
--- a/debugger/SkDebugger.h
+++ b/debugger/SkDebugger.h
@@ -111,9 +111,9 @@
         }
     }
 
-    void setTexFilterOverride(bool texFilterOverride, SkPaint::FilterLevel level) {
+    void setTexFilterOverride(bool texFilterOverride, SkFilterQuality quality) {
         if (fDebugCanvas) {
-            fDebugCanvas->overrideTexFiltering(texFilterOverride, level);
+            fDebugCanvas->overrideTexFiltering(texFilterOverride, quality);
         }
     }
 
diff --git a/dm/Android.mk b/dm/Android.mk
index 30f63e5..704c4cb 100644
--- a/dm/Android.mk
+++ b/dm/Android.mk
@@ -32,24 +32,27 @@
 	../tests/Test.cpp \
 	../tests/PathOpsAngleTest.cpp \
 	../tests/PathOpsBoundsTest.cpp \
+	../tests/PathOpsBuilderTest.cpp \
+	../tests/PathOpsBuildUseTest.cpp \
+	../tests/PathOpsConicIntersectionTest.cpp \
+	../tests/PathOpsConicLineIntersectionTest.cpp \
 	../tests/PathOpsCubicIntersectionTest.cpp \
 	../tests/PathOpsCubicIntersectionTestData.cpp \
 	../tests/PathOpsCubicLineIntersectionTest.cpp \
 	../tests/PathOpsCubicQuadIntersectionTest.cpp \
 	../tests/PathOpsCubicReduceOrderTest.cpp \
-	../tests/PathOpsCubicToQuadsTest.cpp \
 	../tests/PathOpsDCubicTest.cpp \
 	../tests/PathOpsDLineTest.cpp \
 	../tests/PathOpsDPointTest.cpp \
-	../tests/PathOpsDQuadTest.cpp \
 	../tests/PathOpsDRectTest.cpp \
-	../tests/PathOpsDTriangleTest.cpp \
 	../tests/PathOpsDVectorTest.cpp \
 	../tests/PathOpsExtendedTest.cpp \
 	../tests/PathOpsFuzz763Test.cpp \
 	../tests/PathOpsInverseTest.cpp \
+	../tests/PathOpsIssue3651.cpp \
 	../tests/PathOpsLineIntersectionTest.cpp \
 	../tests/PathOpsLineParametetersTest.cpp \
+	../tests/PathOpsOpCircleThreadedTest.cpp \
 	../tests/PathOpsOpCubicThreadedTest.cpp \
 	../tests/PathOpsOpRectThreadedTest.cpp \
 	../tests/PathOpsOpTest.cpp \
@@ -57,7 +60,6 @@
 	../tests/PathOpsQuadIntersectionTestData.cpp \
 	../tests/PathOpsQuadLineIntersectionTest.cpp \
 	../tests/PathOpsQuadLineIntersectionThreadedTest.cpp \
-	../tests/PathOpsQuadParameterizationTest.cpp \
 	../tests/PathOpsQuadReduceOrderTest.cpp \
 	../tests/PathOpsSimplifyDegenerateThreadedTest.cpp \
 	../tests/PathOpsSimplifyFailTest.cpp \
@@ -69,7 +71,9 @@
 	../tests/PathOpsSkpTest.cpp \
 	../tests/PathOpsTestCommon.cpp \
 	../tests/PathOpsThreadedCommon.cpp \
+	../tests/PathOpsThreeWayTest.cpp \
 	../tests/PathOpsTightBoundsTest.cpp \
+	../tests/PathOpsTypesTest.cpp \
 	../tests/AAClipTest.cpp \
 	../tests/ARGBImageEncoderTest.cpp \
 	../tests/AnnotationTest.cpp \
@@ -97,6 +101,7 @@
 	../tests/ClipCubicTest.cpp \
 	../tests/ClipStackTest.cpp \
 	../tests/ClipperTest.cpp \
+	../tests/CodexTest.cpp \
 	../tests/ColorFilterTest.cpp \
 	../tests/ColorPrivTest.cpp \
 	../tests/ColorTest.cpp \
@@ -111,6 +116,7 @@
 	../tests/DiscardableMemoryTest.cpp \
 	../tests/DocumentTest.cpp \
 	../tests/DrawBitmapRectTest.cpp \
+	../tests/DrawFilterTest.cpp \
 	../tests/DrawPathTest.cpp \
 	../tests/DrawTextTest.cpp \
 	../tests/DynamicHashTest.cpp \
@@ -126,6 +132,7 @@
 	../tests/FontNamesTest.cpp \
 	../tests/FontObjTest.cpp \
 	../tests/FrontBufferedStreamTest.cpp \
+	../tests/FunctionTest.cpp \
 	../tests/GLInterfaceValidationTest.cpp \
 	../tests/GLProgramsTest.cpp \
 	../tests/GeometryTest.cpp \
@@ -152,6 +159,7 @@
 	../tests/ImageGeneratorTest.cpp \
 	../tests/ImageIsOpaqueTest.cpp \
 	../tests/ImageNewShaderTest.cpp \
+	../tests/IndexedPngOverflowTest.cpp \
 	../tests/InfRectTest.cpp \
 	../tests/InterpolatorTest.cpp \
 	../tests/InvalidIndexedPngTest.cpp \
@@ -186,7 +194,6 @@
 	../tests/PathCoverageTest.cpp \
 	../tests/PathMeasureTest.cpp \
 	../tests/PathTest.cpp \
-	../tests/PathUtilsTest.cpp \
 	../tests/PictureBBHTest.cpp \
 	../tests/PictureShaderTest.cpp \
 	../tests/PictureTest.cpp \
@@ -220,7 +227,7 @@
 	../tests/ShaderImageFilterTest.cpp \
 	../tests/ShaderOpacityTest.cpp \
 	../tests/SizeTest.cpp \
-	../tests/Sk4xTest.cpp \
+	../tests/SkNxTest.cpp \
 	../tests/SkBase64Test.cpp \
 	../tests/SkImageTest.cpp \
 	../tests/SkResourceCacheTest.cpp \
@@ -233,12 +240,13 @@
 	../tests/StrokerTest.cpp \
 	../tests/SurfaceTest.cpp \
 	../tests/SVGDeviceTest.cpp \
+	../tests/SwizzlerTest.cpp \
 	../tests/TessellatingPathRendererTests.cpp \
 	../tests/TArrayTest.cpp \
+	../tests/TemplatesTest.cpp \
 	../tests/TDPQueueTest.cpp \
 	../tests/Time.cpp \
 	../tests/TLSTest.cpp \
-	../tests/TSetTest.cpp \
 	../tests/TextBlobTest.cpp \
 	../tests/TextureCompressionTest.cpp \
 	../tests/ToUnicodeTest.cpp \
@@ -258,10 +266,13 @@
 	../gm/aaclip.cpp \
 	../gm/aarectmodes.cpp \
 	../gm/addarc.cpp \
+	../gm/all_bitmap_configs.cpp \
 	../gm/alphagradients.cpp \
+	../gm/anisotropic.cpp \
 	../gm/arcofzorro.cpp \
 	../gm/arithmode.cpp \
 	../gm/astcbitmap.cpp \
+	../gm/badpaint.cpp \
 	../gm/beziereffects.cpp \
 	../gm/beziers.cpp \
 	../gm/bigblurs.cpp \
@@ -274,12 +285,15 @@
 	../gm/bitmapscroll.cpp \
 	../gm/bitmapshader.cpp \
 	../gm/bitmapsource.cpp \
+	../gm/bitmapsource2.cpp \
 	../gm/bleed.cpp \
+	../gm/blend.cpp \
 	../gm/blurcircles.cpp \
 	../gm/blurs.cpp \
 	../gm/blurquickreject.cpp \
 	../gm/blurrect.cpp \
 	../gm/blurroundrect.cpp \
+	../gm/bmpfilterqualityrepeat.cpp \
 	../gm/circles.cpp \
 	../gm/circularclips.cpp \
 	../gm/clipdrawdraw.cpp \
@@ -301,6 +315,8 @@
 	../gm/complexclip3.cpp \
 	../gm/composeshader.cpp \
 	../gm/conicpaths.cpp \
+	../gm/constcolorprocessor.cpp \
+	../gm/convex_all_line_paths.cpp \
 	../gm/convexpaths.cpp \
 	../gm/convexpolyclip.cpp \
 	../gm/convexpolyeffect.cpp \
@@ -325,6 +341,7 @@
 	../gm/extractbitmap.cpp \
 	../gm/emboss.cpp \
 	../gm/emptypath.cpp \
+	../gm/fadefilter.cpp \
 	../gm/fatpathfill.cpp \
 	../gm/factory.cpp \
 	../gm/filltypes.cpp \
@@ -361,18 +378,22 @@
 	../gm/lighting.cpp \
 	../gm/lumafilter.cpp \
 	../gm/image.cpp \
+	../gm/imagefilters.cpp \
 	../gm/imagefiltersbase.cpp \
 	../gm/imagefiltersclipped.cpp \
 	../gm/imagefilterscropped.cpp \
 	../gm/imagefiltersgraph.cpp \
 	../gm/imagefiltersscaled.cpp \
+	../gm/imagefilterstransformed.cpp \
 	../gm/internal_links.cpp \
+	../gm/largeglyphblur.cpp \
 	../gm/lcdtext.cpp \
 	../gm/linepaths.cpp \
 	../gm/matrixconvolution.cpp \
 	../gm/matriximagefilter.cpp \
 	../gm/megalooper.cpp \
 	../gm/mixedxfermodes.cpp \
+	../gm/mixedtextblobs.cpp \
 	../gm/mipmap.cpp \
 	../gm/modecolorfilters.cpp \
 	../gm/morphology.cpp \
@@ -396,6 +417,7 @@
 	../gm/pictureimagefilter.cpp \
 	../gm/pictureshader.cpp \
 	../gm/pictureshadertile.cpp \
+	../gm/pixelsnap.cpp \
 	../gm/points.cpp \
 	../gm/poly2poly.cpp \
 	../gm/polygons.cpp \
@@ -433,7 +455,11 @@
 	../gm/variedtext.cpp \
 	../gm/tallstretchedbitmaps.cpp \
 	../gm/textblob.cpp \
+	../gm/textbloblooper.cpp \
+	../gm/textblobcolortrans.cpp \
+	../gm/textblobgeometrychange.cpp \
 	../gm/textblobshader.cpp \
+	../gm/textblobtransforms.cpp \
 	../gm/texturedomaineffect.cpp \
 	../gm/thinrects.cpp \
 	../gm/thinstrokedrects.cpp \
@@ -443,7 +469,6 @@
 	../gm/tilemodes_scaled.cpp \
 	../gm/tinybitmap.cpp \
 	../gm/transparency.cpp \
-	../gm/twopointradial.cpp \
 	../gm/typeface.cpp \
 	../gm/vertices.cpp \
 	../gm/verttext.cpp \
@@ -477,6 +502,8 @@
 	../experimental/SkSetPoly3To3_D.cpp \
 	../tools/flags/SkCommonFlags.cpp \
 	../tools/picture_utils.cpp \
+	../src/utils/android/SkAndroidSDKCanvas.cpp \
+	../src/utils/android/SkHwuiRenderer.cpp \
 	../src/gpu/GrContextFactory.cpp \
 	../src/gpu/GrTest.cpp
 
@@ -524,6 +551,7 @@
 	$(LOCAL_PATH)/../src/utils/debugger \
 	$(LOCAL_PATH)/../tests \
 	$(LOCAL_PATH)/../src/pathops \
+	$(LOCAL_PATH)/../src/codec \
 	$(LOCAL_PATH)/../src/image \
 	$(LOCAL_PATH)/../src/pdf \
 	$(LOCAL_PATH)/../experimental/PdfViewer \
@@ -533,7 +561,8 @@
 	$(LOCAL_PATH)/../src/ports \
 	$(LOCAL_PATH)/../third_party/etc1 \
 	$(LOCAL_PATH)/../tools/timer \
-	$(LOCAL_PATH)/../experimental
+	$(LOCAL_PATH)/../experimental \
+	$(LOCAL_PATH)/../src/utils/android
 
 LOCAL_CFLAGS += \
 	-DSK_CRASH_HANDLER
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 9f3dbab..139e9eb 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -1,3 +1,10 @@
+/*
+ * 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 "CrashHandler.h"
 #include "DMJsonWriter.h"
 #include "DMSrcSink.h"
@@ -14,13 +21,14 @@
 #include "SkOSFile.h"
 #include "SkTHash.h"
 #include "SkTaskGroup.h"
+#include "SkThreadUtils.h"
 #include "Test.h"
 #include "Timer.h"
 
-DEFINE_string(src, "tests gm skp image subset", "Source types to test.");
+DEFINE_string(src, "tests gm skp image", "Source types to test.");
 DEFINE_bool(nameByHash, false,
             "If true, write to FLAGS_writePath[0]/<hash>.png instead of "
-            "to FLAGS_writePath[0]/<config>/<sourceType>/<name>.png");
+            "to FLAGS_writePath[0]/<config>/<sourceType>/<sourceOptions>/<name>.png");
 DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests.");
 DEFINE_string(matrix, "1 0 0 1",
               "2x2 scale+skew matrix to apply or upright when using "
@@ -28,12 +36,19 @@
 DEFINE_bool(gpu_threading, false, "Allow GPU work to run on multiple threads?");
 
 DEFINE_string(blacklist, "",
-        "Space-separated config/src/name triples to blacklist.  '_' matches anything.  E.g. \n"
-        "'--blacklist gpu skp _' will blacklist all SKPs drawn into the gpu config.\n"
-        "'--blacklist gpu skp _ 8888 gm aarects' will also blacklist the aarects GM on 8888.");
+        "Space-separated config/src/srcOptions/name quadruples to blacklist.  '_' matches anything.  E.g. \n"
+        "'--blacklist gpu skp _ _' will blacklist all SKPs drawn into the gpu config.\n"
+        "'--blacklist gpu skp _ _ 8888 gm _ aarects' will also blacklist the aarects GM on 8888.");
 
 DEFINE_string2(readPath, r, "", "If set check for equality with golden results in this directory.");
 
+DEFINE_string(uninterestingHashesFile, "",
+        "File containing a list of uninteresting hashes. If a result hashes to something in "
+        "this list, no image is written for that result.");
+
+DEFINE_int32(shards, 1, "We're splitting source data into this many shards.");
+DEFINE_int32(shard,  0, "Which shard do I run?");
+
 __SK_FORCE_IMAGE_DECODER_LINKING;
 using namespace DM;
 
@@ -48,23 +63,40 @@
     gFailures.push_back(err);
 }
 
-static int32_t gPending = 0;  // Atomic.
+static int32_t gPending = 0;  // Atomic.  Total number of running and queued tasks.
+
+SK_DECLARE_STATIC_MUTEX(gRunningMutex);
+static SkTArray<SkString> gRunning;
 
 static void done(double ms,
-                 ImplicitString config, ImplicitString src, ImplicitString name,
-                 ImplicitString log) {
+                 ImplicitString config, ImplicitString src, ImplicitString srcOptions,
+                 ImplicitString name, ImplicitString note, ImplicitString log) {
+    SkString id = SkStringPrintf("%s %s %s %s", config.c_str(), src.c_str(),
+                                                srcOptions.c_str(), name.c_str());
+    {
+        SkAutoMutexAcquire lock(gRunningMutex);
+        for (int i = 0; i < gRunning.count(); i++) {
+            if (gRunning[i] == id) {
+                gRunning.removeShuffle(i);
+                break;
+            }
+        }
+    }
+    if (!FLAGS_verbose) {
+        note = "";
+    }
     if (!log.isEmpty()) {
         log.prepend("\n");
     }
     auto pending = sk_atomic_dec(&gPending)-1;
-    SkDebugf("%s(%4dMB %5d) %s\t%s %s %s%s", FLAGS_verbose ? "\n" : kSkOverwriteLine
-                                           , sk_tools::getMaxResidentSetSizeMB()
-                                           , pending
-                                           , HumanizeMs(ms).c_str()
-                                           , config.c_str()
-                                           , src.c_str()
-                                           , name.c_str()
-                                           , log.c_str());
+    SkDebugf("%s(%4d/%-4dMB %5d) %s\t%s%s%s", FLAGS_verbose ? "\n" : kSkOverwriteLine
+                                       , sk_tools::getCurrResidentSetSizeMB()
+                                       , sk_tools::getMaxResidentSetSizeMB()
+                                       , pending
+                                       , HumanizeMs(ms).c_str()
+                                       , id.c_str()
+                                       , note.c_str()
+                                       , log.c_str());
     // We write our dm.json file every once in a while in case we crash.
     // Notice this also handles the final dm.json when pending == 0.
     if (pending % 500 == 0) {
@@ -72,28 +104,33 @@
     }
 }
 
+static void start(ImplicitString config, ImplicitString src,
+                  ImplicitString srcOptions, ImplicitString name) {
+    SkString id = SkStringPrintf("%s %s %s %s", config.c_str(), src.c_str(),
+                                                srcOptions.c_str(), name.c_str());
+    SkAutoMutexAcquire lock(gRunningMutex);
+    gRunning.push_back(id);
+}
+
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
 struct Gold : public SkString {
     Gold() : SkString("") {}
-    Gold(ImplicitString sink, ImplicitString src, ImplicitString name, ImplicitString md5)
+    Gold(ImplicitString sink, ImplicitString src, ImplicitString srcOptions,
+         ImplicitString name, ImplicitString md5)
         : SkString("") {
         this->append(sink);
         this->append(src);
+        this->append(srcOptions);
         this->append(name);
         this->append(md5);
-        while (this->size() % 4) {
-            this->append("!");  // Pad out if needed so we can pass this to Murmur3.
-        }
     }
-    static uint32_t Hash(const Gold& g) {
-        return SkChecksum::Murmur3((const uint32_t*)g.c_str(), g.size());
-    }
+    static uint32_t Hash(const Gold& g) { return SkGoodHash((const SkString&)g); }
 };
 static SkTHashSet<Gold, Gold::Hash> gGold;
 
 static void add_gold(JsonWriter::BitmapResult r) {
-    gGold.add(Gold(r.config, r.sourceType, r.name, r.md5));
+    gGold.add(Gold(r.config, r.sourceType, r.sourceOptions, r.name, r.md5));
 }
 
 static void gather_gold() {
@@ -108,37 +145,118 @@
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
+static SkTHashSet<SkString> gUninterestingHashes;
+
+static void gather_uninteresting_hashes() {
+    if (!FLAGS_uninterestingHashesFile.isEmpty()) {
+        SkAutoTUnref<SkData> data(SkData::NewFromFileName(FLAGS_uninterestingHashesFile[0]));
+        SkTArray<SkString> hashes;
+        SkStrSplit((const char*)data->data(), "\n", &hashes);
+        for (const SkString& hash : hashes) {
+            gUninterestingHashes.add(hash);
+        }
+    }
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
 template <typename T>
-struct Tagged : public SkAutoTDelete<T> { const char* tag; };
+struct Tagged : public SkAutoTDelete<T> {
+  const char* tag;
+  const char* options;
+};
 
 static const bool kMemcpyOK = true;
 
 static SkTArray<Tagged<Src>,  kMemcpyOK>  gSrcs;
 static SkTArray<Tagged<Sink>, kMemcpyOK> gSinks;
 
-static void push_src(const char* tag, Src* s) {
+static bool in_shard() {
+    static int N = 0;
+    return N++ % FLAGS_shards == FLAGS_shard;
+}
+
+static void push_src(const char* tag, const char* options, Src* s) {
     SkAutoTDelete<Src> src(s);
-    if (FLAGS_src.contains(tag) &&
+    if (in_shard() &&
+        FLAGS_src.contains(tag) &&
         !SkCommandLineFlags::ShouldSkip(FLAGS_match, src->name().c_str())) {
         Tagged<Src>& s = gSrcs.push_back();
         s.reset(src.detach());
         s.tag = tag;
+        s.options = options;
     }
 }
 
+static void push_codec_srcs(Path path) {
+    SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
+    if (!encoded) {
+        SkDebugf("Couldn't read %s.", path.c_str());
+        return;
+    }
+    SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
+    if (NULL == codec.get()) {
+        SkDebugf("Couldn't create codec for %s.", path.c_str());
+        return;
+    }
+
+    // Build additional test cases for images that decode natively to non-canvas types
+    switch(codec->getInfo().colorType()) {
+        case kGray_8_SkColorType:
+            push_src("image", "codec_kGray8", new CodecSrc(path, CodecSrc::kNormal_Mode,
+                    CodecSrc::kGrayscale_Always_DstColorType));
+            push_src("image", "scanline_kGray8", new CodecSrc(path, CodecSrc::kScanline_Mode,
+                    CodecSrc::kGrayscale_Always_DstColorType));
+            // Intentional fall through
+            // FIXME: Is this a long term solution for testing wbmps decodes to kIndex8?
+            // Further discussion on this topic is at skbug.com/3683
+      case kIndex_8_SkColorType:
+          push_src("image", "codec_kIndex8", new CodecSrc(path, CodecSrc::kNormal_Mode,
+                  CodecSrc::kIndex8_Always_DstColorType));
+          push_src("image", "scanline_kIndex8", new CodecSrc(path, CodecSrc::kScanline_Mode,
+                  CodecSrc::kIndex8_Always_DstColorType));
+        break;
+      default:
+        // Do nothing
+        break;
+    }
+
+    // Decode all images to the canvas color type
+    push_src("image", "codec", new CodecSrc(path, CodecSrc::kNormal_Mode,
+            CodecSrc::kGetFromCanvas_DstColorType));
+    push_src("image", "scanline", new CodecSrc(path, CodecSrc::kScanline_Mode,
+            CodecSrc::kGetFromCanvas_DstColorType));
+}
+
+static bool codec_supported(const char* ext) {
+    // FIXME: Once other versions of SkCodec are available, we can add them to this
+    // list (and eventually we can remove this check once they are all supported).
+    static const char* const exts[] = {
+        "bmp", "gif", "jpg", "jpeg", "png", "ico", "wbmp",
+        "BMP", "GIF", "JPG", "JPEG", "PNG", "ICO", "WBMP"
+    };
+
+    for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) {
+        if (0 == strcmp(exts[i], ext)) {
+            return true;
+        }
+    }
+    return false;
+}
+
 static void gather_srcs() {
     for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) {
-        push_src("gm", new GMSrc(r->factory()));
+        push_src("gm", "", new GMSrc(r->factory()));
     }
     for (int i = 0; i < FLAGS_skps.count(); i++) {
         const char* path = FLAGS_skps[i];
         if (sk_isdir(path)) {
             SkOSFile::Iter it(path, "skp");
             for (SkString file; it.next(&file); ) {
-                push_src("skp", new SKPSrc(SkOSPath::Join(path, file.c_str())));
+                push_src("skp", "", new SKPSrc(SkOSPath::Join(path, file.c_str())));
             }
         } else {
-            push_src("skp", new SKPSrc(path));
+            push_src("skp", "", new SKPSrc(path));
         }
     }
     static const char* const exts[] = {
@@ -152,14 +270,18 @@
                 SkOSFile::Iter it(flag, exts[j]);
                 for (SkString file; it.next(&file); ) {
                     SkString path = SkOSPath::Join(flag, file.c_str());
-                    push_src("image", new ImageSrc(path));     // Decode entire image.
-                    push_src("subset", new ImageSrc(path, 2)); // Decode into 2 x 2 subsets
+                    push_src("image", "decode", new ImageSrc(path)); // Decode entire image
+                    push_src("image", "subset", new ImageSrc(path, 2)); // Decode into 2x2 subsets
+                    if (codec_supported(exts[j])) {
+                        push_codec_srcs(path);
+                    }
                 }
             }
         } else if (sk_exists(flag)) {
             // assume that FLAGS_images[i] is a valid image if it is a file.
-            push_src("image", new ImageSrc(flag));     // Decode entire image.
-            push_src("subset", new ImageSrc(flag, 2)); // Decode into 2 x 2 subsets
+            push_src("image", "decode", new ImageSrc(flag)); // Decode entire image.
+            push_src("image", "subset", new ImageSrc(flag, 2)); // Decode into 2 x 2 subsets
+            push_codec_srcs(flag);
         }
     }
 }
@@ -177,9 +299,9 @@
     }
     // Try a noop Src as a canary.  If it fails, skip this sink.
     struct : public Src {
-        Error draw(SkCanvas*) const SK_OVERRIDE { return ""; }
-        SkISize size() const SK_OVERRIDE { return SkISize::Make(16, 16); }
-        Name name() const SK_OVERRIDE { return "noop"; }
+        Error draw(SkCanvas*) const override { return ""; }
+        SkISize size() const override { return SkISize::Make(16, 16); }
+        Name name() const override { return "noop"; }
     } noop;
 
     SkBitmap bitmap;
@@ -187,8 +309,8 @@
     SkString log;
     Error err = sink->draw(noop, &bitmap, &stream, &log);
     if (err.isFatal()) {
-        SkDebugf("Skipping %s: %s\n", tag, err.c_str());
-        return;
+        SkDebugf("Could not run %s: %s\n", tag, err.c_str());
+        exit(1);
     }
 
     Tagged<Sink>& ts = gSinks.push_back();
@@ -244,8 +366,12 @@
 
 static Sink* create_via(const char* tag, Sink* wrapped) {
 #define VIA(t, via, ...) if (0 == strcmp(t, tag)) { return new via(__VA_ARGS__); }
+    VIA("twice",     ViaTwice,         wrapped);
     VIA("pipe",      ViaPipe,          wrapped);
     VIA("serialize", ViaSerialization, wrapped);
+    VIA("deferred",  ViaDeferred,      wrapped);
+    VIA("2ndpic",    ViaSecondPicture, wrapped);
+    VIA("sp",        ViaSingletonPictures, wrapped);
     VIA("tiles",     ViaTiles, 256, 256,               NULL, wrapped);
     VIA("tiles_rt",  ViaTiles, 256, 256, new SkRTreeFactory, wrapped);
 
@@ -296,13 +422,16 @@
     return 0 == strcmp("_", needle) || NULL != strstr(haystack, needle);
 }
 
-static ImplicitString is_blacklisted(const char* sink, const char* src, const char* name) {
-    for (int i = 0; i < FLAGS_blacklist.count() - 2; i += 3) {
+static ImplicitString is_blacklisted(const char* sink, const char* src,
+                                     const char* srcOptions, const char* name) {
+    for (int i = 0; i < FLAGS_blacklist.count() - 3; i += 4) {
         if (match(FLAGS_blacklist[i+0], sink) &&
-            match(FLAGS_blacklist[i+1],  src) &&
-            match(FLAGS_blacklist[i+2], name)) {
-            return SkStringPrintf("%s %s %s",
-                                  FLAGS_blacklist[i+0], FLAGS_blacklist[i+1], FLAGS_blacklist[i+2]);
+            match(FLAGS_blacklist[i+1], src) &&
+            match(FLAGS_blacklist[i+2], srcOptions) &&
+            match(FLAGS_blacklist[i+3], name)) {
+            return SkStringPrintf("%s %s %s %s",
+                                  FLAGS_blacklist[i+0], FLAGS_blacklist[i+1],
+                                  FLAGS_blacklist[i+2], FLAGS_blacklist[i+3]);
         }
     }
     return "";
@@ -317,26 +446,34 @@
 
     static void Run(Task* task) {
         SkString name = task->src->name();
-        SkString whyBlacklisted = is_blacklisted(task->sink.tag, task->src.tag, name.c_str());
+        SkString note;
+        SkString whyBlacklisted = is_blacklisted(task->sink.tag, task->src.tag,
+                                                 task->src.options, name.c_str());
+        if (!whyBlacklisted.isEmpty()) {
+            note.appendf(" (--blacklist %s)", whyBlacklisted.c_str());
+        }
         SkString log;
         WallTimer timer;
         timer.start();
         if (!FLAGS_dryRun && whyBlacklisted.isEmpty()) {
             SkBitmap bitmap;
             SkDynamicMemoryWStream stream;
+            start(task->sink.tag, task->src.tag, task->src.options, name.c_str());
             Error err = task->sink->draw(*task->src, &bitmap, &stream, &log);
             if (!err.isEmpty()) {
                 timer.end();
                 if (err.isFatal()) {
-                    fail(SkStringPrintf("%s %s %s: %s",
+                    fail(SkStringPrintf("%s %s %s %s: %s",
                                         task->sink.tag,
                                         task->src.tag,
+                                        task->src.options,
                                         name.c_str(),
                                         err.c_str()));
-                } else if (FLAGS_verbose) {
-                    name.appendf(" (skipped: %s)", err.c_str());
+                } else {
+                    note.appendf(" (skipped: %s)", err.c_str());
                 }
-                done(timer.fWall, task->sink.tag, task->src.tag, name, log);
+                done(timer.fWall, task->sink.tag, task->src.tag, task->src.options,
+                     name, note, log);
                 return;
             }
             SkAutoTDelete<SkStreamAsset> data(stream.detachAsStream());
@@ -358,11 +495,13 @@
             }
 
             if (!FLAGS_readPath.isEmpty() &&
-                !gGold.contains(Gold(task->sink.tag, task->src.tag, name, md5))) {
-                fail(SkStringPrintf("%s not found for %s %s %s in %s",
+                !gGold.contains(Gold(task->sink.tag, task->src.tag,
+                                     task->src.options, name, md5))) {
+                fail(SkStringPrintf("%s not found for %s %s %s %s in %s",
                                     md5.c_str(),
                                     task->sink.tag,
                                     task->src.tag,
+                                    task->src.options,
                                     name.c_str(),
                                     FLAGS_readPath[0]));
             }
@@ -378,10 +517,7 @@
             }
         }
         timer.end();
-        if (FLAGS_verbose && !whyBlacklisted.isEmpty()) {
-            name.appendf(" (--blacklist, %s)", whyBlacklisted.c_str());
-        }
-        done(timer.fWall, task->sink.tag, task->src.tag, name, log);
+        done(timer.fWall, task->sink.tag, task->src.tag, task->src.options, name, note, log);
     }
 
     static void WriteToDisk(const Task& task,
@@ -390,13 +526,20 @@
                             SkStream* data, size_t len,
                             const SkBitmap* bitmap) {
         JsonWriter::BitmapResult result;
-        result.name       = task.src->name();
-        result.config     = task.sink.tag;
-        result.sourceType = task.src.tag;
-        result.ext        = ext;
-        result.md5        = md5;
+        result.name          = task.src->name();
+        result.config        = task.sink.tag;
+        result.sourceType    = task.src.tag;
+        result.sourceOptions = task.src.options;
+        result.ext           = ext;
+        result.md5           = md5;
         JsonWriter::AddBitmapResult(result);
 
+        // If an MD5 is uninteresting, we want it noted in the JSON file,
+        // but don't want to dump it out as a .png (or whatever ext is).
+        if (gUninterestingHashes.contains(md5)) {
+            return;
+        }
+
         const char* dir = FLAGS_writePath[0];
         if (0 == strcmp(dir, "@")) {  // Needed for iOS.
             dir = FLAGS_resourcePath[0];
@@ -416,6 +559,10 @@
             sk_mkdir(path.c_str());
             path = SkOSPath::Join(path.c_str(), task.src.tag);
             sk_mkdir(path.c_str());
+            if (strcmp(task.src.options, "") != 0) {
+              path = SkOSPath::Join(path.c_str(), task.src.options);
+              sk_mkdir(path.c_str());
+            }
             path = SkOSPath::Join(path.c_str(), task.src->name().c_str());
             path.append(".");
             path.append(ext);
@@ -468,8 +615,10 @@
     if (!FLAGS_src.contains("tests")) {
         return;
     }
-    for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r;
-         r = r->next()) {
+    for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r = r->next()) {
+        if (!in_shard()) {
+            continue;
+        }
         // Despite its name, factory() is returning a reference to
         // link-time static const POD data.
         const skiatest::Test& test = r->factory();
@@ -486,23 +635,24 @@
 
 static void run_test(skiatest::Test* test) {
     struct : public skiatest::Reporter {
-        void reportFailed(const skiatest::Failure& failure) SK_OVERRIDE {
+        void reportFailed(const skiatest::Failure& failure) override {
             fail(failure.toString());
             JsonWriter::AddTestFailure(failure);
         }
-        bool allowExtendedTest() const SK_OVERRIDE {
+        bool allowExtendedTest() const override {
             return FLAGS_pathOpsExtended;
         }
-        bool verbose() const SK_OVERRIDE { return FLAGS_veryVerbose; }
+        bool verbose() const override { return FLAGS_veryVerbose; }
     } reporter;
     WallTimer timer;
     timer.start();
     if (!FLAGS_dryRun) {
+        start("unit", "test", "", test->name);
         GrContextFactory factory;
         test->proc(&reporter, &factory);
     }
     timer.end();
-    done(timer.fWall, "unit", "test", test->name, "");
+    done(timer.fWall, "unit", "test", "", test->name, "", "");
 }
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@@ -515,6 +665,33 @@
     }
 }
 
+// Some runs (mostly, Valgrind) are so slow that the bot framework thinks we've hung.
+// This prints something every once in a while so that it knows we're still working.
+static void start_keepalive() {
+    struct Loop {
+        static void forever(void*) {
+            for (;;) {
+                static const int kSec = 300;
+            #if defined(SK_BUILD_FOR_WIN)
+                Sleep(kSec * 1000);
+            #else
+                sleep(kSec);
+            #endif
+                SkString running;
+                {
+                    SkAutoMutexAcquire lock(gRunningMutex);
+                    for (int i = 0; i < gRunning.count(); i++) {
+                        running.appendf("\n\t%s", gRunning[i].c_str());
+                    }
+                }
+                SkDebugf("\nCurrently running:%s\n", running.c_str());
+            }
+        }
+    };
+    static SkThread* intentionallyLeaked = new SkThread(Loop::forever);
+    intentionallyLeaked->start();
+}
+
 int dm_main();
 int dm_main() {
     SetupCrashHandler();
@@ -524,7 +701,10 @@
         SkInstCountPrintLeaksOnExit();
     }
 
+    start_keepalive();
+
     gather_gold();
+    gather_uninteresting_hashes();
 
     gather_srcs();
     gather_sinks();
@@ -579,7 +759,7 @@
     return 0;
 }
 
-#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
+#if !defined(SK_BUILD_FOR_IOS)
 int main(int argc, char** argv) {
     SkCommandLineFlags::Parse(argc, argv);
     return dm_main();
diff --git a/dm/DMGpuSupport.h b/dm/DMGpuSupport.h
index 1ced019..65fbc53 100644
--- a/dm/DMGpuSupport.h
+++ b/dm/DMGpuSupport.h
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #ifndef DMGpuSupport_DEFINED
 #define DMGpuSupport_DEFINED
 
diff --git a/dm/DMJsonWriter.cpp b/dm/DMJsonWriter.cpp
index e4a3d12..04ae2ce 100644
--- a/dm/DMJsonWriter.cpp
+++ b/dm/DMJsonWriter.cpp
@@ -7,6 +7,7 @@
 
 #include "DMJsonWriter.h"
 
+#include "ProcStats.h"
 #include "SkCommonFlags.h"
 #include "SkData.h"
 #include "SkJSONCPP.h"
@@ -57,6 +58,12 @@
             result["options"]["ext"]     = gBitmapResults[i].ext.c_str();
             result["md5"]                = gBitmapResults[i].md5.c_str();
 
+            // Source options only need to be part of the key if they exist.
+            // Source type by source type, we either always set options or never set options.
+            if (!gBitmapResults[i].sourceOptions.isEmpty()) {
+                result["key"]["source_options"] = gBitmapResults[i].sourceOptions.c_str();
+            }
+
             root["results"].append(result);
         }
     }
@@ -74,6 +81,11 @@
         }
     }
 
+    int maxResidentSetSizeMB = sk_tools::getMaxResidentSetSizeMB();
+    if (maxResidentSetSizeMB != -1) {
+        root["max_rss_MB"] = sk_tools::getMaxResidentSetSizeMB();
+    }
+
     SkString path = SkOSPath::Join(FLAGS_writePath[0], "dm.json");
     sk_mkdir(FLAGS_writePath[0]);
     SkFILEWStream stream(path.c_str());
@@ -103,6 +115,10 @@
         br.sourceType = r["key"]["source_type"].asCString();
         br.ext        = r["options"]["ext"].asCString();
         br.md5        = r["md5"].asCString();
+
+        if (!r["key"]["source_options"].isNull()) {
+            br.sourceOptions = r["key"]["source_options"].asCString();
+        }
         callback(br);
     }
     return true;
diff --git a/dm/DMJsonWriter.h b/dm/DMJsonWriter.h
index 5934846..67c9cf6 100644
--- a/dm/DMJsonWriter.h
+++ b/dm/DMJsonWriter.h
@@ -26,6 +26,7 @@
         SkString name;            // E.g. "ninepatch-stretch", "desk_gws.skp"
         SkString config;          //      "gpu", "8888", "serialize", "pipe"
         SkString sourceType;      //      "gm", "skp", "image"
+        SkString sourceOptions;   //      "image", "codec", "subset", "scanline"
         SkString md5;             // In ASCII, so 32 bytes long.
         SkString ext;             // Extension of file we wrote: "png", "pdf", ...
     };
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 5ca66c5..090a24d 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -1,19 +1,40 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "DMSrcSink.h"
 #include "SamplePipeControllers.h"
-#include "SkCommonFlags.h"
 #include "SkCodec.h"
+#include "SkCommonFlags.h"
+#include "SkData.h"
+#include "SkDeferredCanvas.h"
 #include "SkDocument.h"
 #include "SkError.h"
+#include "SkFunction.h"
+#include "SkImageGenerator.h"
 #include "SkMultiPictureDraw.h"
 #include "SkNullCanvas.h"
 #include "SkOSFile.h"
+#include "SkPictureData.h"
 #include "SkPictureRecorder.h"
 #include "SkRandom.h"
+#include "SkRecordDraw.h"
+#include "SkRecorder.h"
 #include "SkSVGCanvas.h"
+#include "SkScanlineDecoder.h"
 #include "SkStream.h"
 #include "SkXMLWriter.h"
 
-DEFINE_bool(codec, false, "Use SkCodec instead of SkImageDecoder");
+DEFINE_bool(multiPage, false, "For document-type backends, render the source"
+            " into multiple pages");
+
+static bool lazy_decode_bitmap(const void* src, size_t size, SkBitmap* dst) {
+    SkAutoTUnref<SkData> encoded(SkData::NewWithCopy(src, size));
+    return encoded && SkInstallDiscardablePixelRef(encoded, dst);
+}
 
 namespace DM {
 
@@ -38,42 +59,80 @@
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
-ImageSrc::ImageSrc(Path path, int divisor) : fPath(path), fDivisor(divisor) {}
+CodecSrc::CodecSrc(Path path, Mode mode, DstColorType dstColorType)
+    : fPath(path)
+    , fMode(mode)
+    , fDstColorType(dstColorType)
+{}
 
-Error ImageSrc::draw(SkCanvas* canvas) const {
+Error CodecSrc::draw(SkCanvas* canvas) const {
+    SkImageInfo canvasInfo;
+    if (NULL == canvas->peekPixels(&canvasInfo, NULL)) {
+        // TODO: Once we implement GPU paths (e.g. JPEG YUV), we should use a deferred decode to
+        // let the GPU handle it.
+        return Error::Nonfatal("No need to test decoding to non-raster backend.");
+    }
+
     SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
     if (!encoded) {
         return SkStringPrintf("Couldn't read %s.", fPath.c_str());
     }
-    const SkColorType dstColorType = canvas->imageInfo().colorType();
-    if (fDivisor == 0) {
-        // Decode the full image.
-        SkBitmap bitmap;
-        if (FLAGS_codec) {
-            SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
-            if (!codec) {
-                return SkStringPrintf("Couldn't decode %s.", fPath.c_str());
+    SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
+    if (NULL == codec.get()) {
+        return SkStringPrintf("Couldn't create codec for %s.", fPath.c_str());
+    }
+
+    // Choose the color type to decode to
+    SkImageInfo decodeInfo = codec->getInfo();
+    SkColorType canvasColorType = canvasInfo.colorType();
+    switch (fDstColorType) {
+        case kIndex8_Always_DstColorType:
+            decodeInfo = codec->getInfo().makeColorType(kIndex_8_SkColorType);
+            if (kRGB_565_SkColorType == canvasColorType) {
+                return Error::Nonfatal("Testing non-565 to 565 is uninteresting.");
             }
-            SkImageInfo info;
-            if (!codec->getInfo(&info)) {
-                return SkStringPrintf("Couldn't getInfo %s.", fPath.c_str());
+            break;
+        case kGrayscale_Always_DstColorType:
+            decodeInfo = codec->getInfo().makeColorType(kGray_8_SkColorType);
+            if (kRGB_565_SkColorType == canvasColorType) {
+                return Error::Nonfatal("Testing non-565 to 565 is uninteresting.");
             }
-            info = info.makeColorType(dstColorType);
-            if (info.alphaType() == kUnpremul_SkAlphaType) {
-                // FIXME: Currently we cannot draw unpremultiplied sources.
-                info = info.makeAlphaType(kPremul_SkAlphaType);
-            }
-            if (!bitmap.tryAllocPixels(info)) {
-                return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(),
-                                      info.width(), info.height());
-            }
-            SkAutoLockPixels alp(bitmap);
-            const SkImageGenerator::Result result = codec->getPixels(info, bitmap.getPixels(),
-                                                                     bitmap.rowBytes());
-            switch (result) {
+            break;
+        default:
+            decodeInfo = decodeInfo.makeColorType(canvasColorType);
+            break;
+    }
+
+    // Construct a color table for the decode if necessary
+    SkAutoTUnref<SkColorTable> colorTable(NULL);
+    SkPMColor* colorPtr = NULL;
+    int* colorCountPtr = NULL;
+    int maxColors = 256;
+    if (kIndex_8_SkColorType == decodeInfo.colorType()) {
+        SkPMColor colors[256];
+        colorTable.reset(SkNEW_ARGS(SkColorTable, (colors, maxColors)));
+        colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
+        colorCountPtr = &maxColors;
+    }
+
+    // FIXME: Currently we cannot draw unpremultiplied sources.
+    if (decodeInfo.alphaType() == kUnpremul_SkAlphaType) {
+        decodeInfo = decodeInfo.makeAlphaType(kPremul_SkAlphaType);
+    }
+
+    SkBitmap bitmap;
+    if (!bitmap.tryAllocPixels(decodeInfo, NULL, colorTable.get())) {
+        return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(),
+                              decodeInfo.width(), decodeInfo.height());
+    }
+
+    switch (fMode) {
+        case kNormal_Mode:
+            switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), NULL,
+                    colorPtr, colorCountPtr)) {
                 case SkImageGenerator::kSuccess:
-                // We consider incomplete to be valid, since we should still decode what is
-                // available.
+                    // We consider incomplete to be valid, since we should still decode what is
+                    // available.
                 case SkImageGenerator::kIncompleteInput:
                     break;
                 case SkImageGenerator::kInvalidConversion:
@@ -82,15 +141,72 @@
                     // Everything else is considered a failure.
                     return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str());
             }
-        } else {
-            if (!SkImageDecoder::DecodeMemory(encoded->data(), encoded->size(), &bitmap,
-                                              dstColorType, SkImageDecoder::kDecodePixels_Mode)) {
-                return SkStringPrintf("Couldn't decode %s.", fPath.c_str());
+            break;
+        case kScanline_Mode: {
+            SkScanlineDecoder* scanlineDecoder = codec->getScanlineDecoder(decodeInfo, NULL,
+                    colorPtr, colorCountPtr);
+            if (NULL == scanlineDecoder) {
+                return Error::Nonfatal("Cannot use scanline decoder for all images");
             }
-            if (kRGB_565_SkColorType == dstColorType && !bitmap.isOpaque()) {
-                // Do not draw a bitmap with alpha to a destination without alpha.
-                return Error::Nonfatal("Uninteresting to decode image with alpha into 565.");
+            for (int y = 0; y < decodeInfo.height(); ++y) {
+                const SkImageGenerator::Result result = scanlineDecoder->getScanlines(
+                        bitmap.getAddr(0, y), 1, 0);
+                switch (result) {
+                    case SkImageGenerator::kSuccess:
+                    case SkImageGenerator::kIncompleteInput:
+                        break;
+                    default:
+                        return SkStringPrintf("%s failed after %d scanlines with error message %d",
+                                              fPath.c_str(), y-1, (int) result);
+                }
             }
+            break;
+        }
+    }
+    canvas->drawBitmap(bitmap, 0, 0);
+    return "";
+}
+
+SkISize CodecSrc::size() const {
+    SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
+    SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
+    if (NULL != codec) {
+        return codec->getInfo().dimensions();
+    } else {
+        return SkISize::Make(0, 0);
+    }
+}
+
+Name CodecSrc::name() const {
+    return SkOSPath::Basename(fPath.c_str());
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ImageSrc::ImageSrc(Path path, int divisor) : fPath(path), fDivisor(divisor) {}
+
+Error ImageSrc::draw(SkCanvas* canvas) const {
+    SkImageInfo canvasInfo;
+    if (NULL == canvas->peekPixels(&canvasInfo, NULL)) {
+        // TODO: Instead, use lazy decoding to allow the GPU to handle cases like YUV.
+        return Error::Nonfatal("No need to test decoding to non-raster backend.");
+    }
+
+    SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str()));
+    if (!encoded) {
+        return SkStringPrintf("Couldn't read %s.", fPath.c_str());
+    }
+    const SkColorType dstColorType = canvasInfo.colorType();
+    if (fDivisor == 0) {
+        // Decode the full image.
+        SkBitmap bitmap;
+        if (!SkImageDecoder::DecodeMemory(encoded->data(), encoded->size(), &bitmap,
+                                          dstColorType, SkImageDecoder::kDecodePixels_Mode)) {
+            return SkStringPrintf("Couldn't decode %s.", fPath.c_str());
+        }
+        if (kRGB_565_SkColorType == dstColorType && !bitmap.isOpaque()) {
+            // Do not draw a bitmap with alpha to a destination without alpha.
+            return Error::Nonfatal("Uninteresting to decode image with alpha into 565.");
         }
         encoded.reset((SkData*)NULL);  // Might as well drop this when we're done with it.
         canvas->drawBitmap(bitmap, 0,0);
@@ -166,19 +282,31 @@
     if (!stream) {
         return SkStringPrintf("Couldn't read %s.", fPath.c_str());
     }
-    SkAutoTUnref<SkPicture> pic(SkPicture::CreateFromStream(stream));
+    SkAutoTUnref<SkPicture> pic(SkPicture::CreateFromStream(stream, &lazy_decode_bitmap));
     if (!pic) {
         return SkStringPrintf("Couldn't decode %s as a picture.", fPath.c_str());
     }
     stream.reset((SkStream*)NULL);  // Might as well drop this when we're done with it.
+
     canvas->clipRect(kSKPViewport);
     canvas->drawPicture(pic);
     return "";
 }
 
 SkISize SKPSrc::size() const {
-    // This may be unnecessarily large.
-    return kSKPViewport.roundOut().size();
+    SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(fPath.c_str()));
+    if (!stream) {
+        return SkISize::Make(0,0);
+    }
+    SkPictInfo info;
+    if (!SkPicture::InternalOnly_StreamIsSKP(stream, &info)) {
+        return SkISize::Make(0,0);
+    }
+    SkRect viewport = kSKPViewport;
+    if (!viewport.intersect(info.fCullRect)) {
+        return SkISize::Make(0,0);
+    }
+    return viewport.roundOut().size();
 }
 
 Name SKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
@@ -251,30 +379,48 @@
     int width  = src.size().width(),
         height = src.size().height();
 
-    const int kLetterWidth  = 612,  // 8.5 * 72
-              kLetterHeight = 792;  // 11 * 72
-    const SkRect letter = SkRect::MakeWH(SkIntToScalar(kLetterWidth),
-                                         SkIntToScalar(kLetterHeight));
+    if (FLAGS_multiPage) {
+        const int kLetterWidth = 612,  // 8.5 * 72
+                kLetterHeight = 792;   // 11 * 72
+        const SkRect letter = SkRect::MakeWH(SkIntToScalar(kLetterWidth),
+                                             SkIntToScalar(kLetterHeight));
 
-    int xPages = ((width - 1) / kLetterWidth) + 1;
-    int yPages = ((height - 1) / kLetterHeight) + 1;
+        int xPages = ((width - 1) / kLetterWidth) + 1;
+        int yPages = ((height - 1) / kLetterHeight) + 1;
 
-    for (int y = 0; y < yPages; ++y) {
-        for (int x = 0; x < xPages; ++x) {
-            int w = SkTMin(kLetterWidth, width - (x * kLetterWidth));
-            int h = SkTMin(kLetterHeight, height - (y * kLetterHeight));
-            SkCanvas* canvas =
-                    doc->beginPage(SkIntToScalar(w), SkIntToScalar(h));
-            canvas->clipRect(letter);
-            canvas->translate(-letter.width() * x, -letter.height() * y);
-            Error err = src.draw(canvas);
-            if (!err.isEmpty()) {
-                return err;
+        for (int y = 0; y < yPages; ++y) {
+            for (int x = 0; x < xPages; ++x) {
+                int w = SkTMin(kLetterWidth, width - (x * kLetterWidth));
+                int h = SkTMin(kLetterHeight, height - (y * kLetterHeight));
+                SkCanvas* canvas =
+                        doc->beginPage(SkIntToScalar(w), SkIntToScalar(h));
+                if (!canvas) {
+                    return "SkDocument::beginPage(w,h) returned NULL";
+                }
+                canvas->clipRect(letter);
+                canvas->translate(-letter.width() * x, -letter.height() * y);
+                Error err = src.draw(canvas);
+                if (!err.isEmpty()) {
+                    return err;
+                }
+                doc->endPage();
             }
-            doc->endPage();
         }
+    } else {
+        SkCanvas* canvas =
+                doc->beginPage(SkIntToScalar(width), SkIntToScalar(height));
+        if (!canvas) {
+            return "SkDocument::beginPage(w,h) returned NULL";
+        }
+        Error err = src.draw(canvas);
+        if (!err.isEmpty()) {
+            return err;
+        }
+        doc->endPage();
     }
-    doc->close();
+    if (!doc->close()) {
+        return "SkDocument::close() returned false";
+    }
     dst->flush();
     return "";
 }
@@ -347,6 +493,27 @@
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
+// Handy for front-patching a Src.  Do whatever up-front work you need, then call draw_to_canvas(),
+// passing the Sink draw() arguments, a size, and a function draws into an SkCanvas.
+// Several examples below.
+
+static Error draw_to_canvas(Sink* sink, SkBitmap* bitmap, SkWStream* stream, SkString* log,
+                            SkISize size, SkFunction<Error(SkCanvas*)> draw) {
+    class ProxySrc : public Src {
+    public:
+        ProxySrc(SkISize size, SkFunction<Error(SkCanvas*)> draw) : fSize(size), fDraw(draw) {}
+        Error   draw(SkCanvas* canvas) const override { return fDraw(canvas); }
+        Name                    name() const override { sk_throw(); return ""; } // Won't be called.
+        SkISize                 size() const override { return fSize; }
+    private:
+        SkISize                      fSize;
+        SkFunction<Error(SkCanvas*)> fDraw;
+    };
+    return sink->draw(ProxySrc(size, draw), bitmap, stream, log);
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
 static SkISize auto_compute_translate(SkMatrix* matrix, int srcW, int srcH) {
     SkRect bounds = SkRect::MakeIWH(srcW, srcH);
     matrix->mapRect(&bounds);
@@ -354,32 +521,20 @@
     return SkISize::Make(SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height()));
 }
 
-ViaMatrix::ViaMatrix(SkMatrix matrix, Sink* sink) : fMatrix(matrix), fSink(sink) {}
+ViaMatrix::ViaMatrix(SkMatrix matrix, Sink* sink) : Via(sink), fMatrix(matrix) {}
 
 Error ViaMatrix::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
-    // We turn our arguments into a Src, then draw that Src into our Sink to fill bitmap or stream.
-    struct ProxySrc : public Src {
-        const Src&  fSrc;
-        SkMatrix    fMatrix;
-        SkISize     fSize;
-
-        ProxySrc(const Src& src, SkMatrix matrix) : fSrc(src), fMatrix(matrix) {
-            fSize = auto_compute_translate(&fMatrix, src.size().width(), src.size().height());
-        }
-
-        Error draw(SkCanvas* canvas) const SK_OVERRIDE {
-            canvas->concat(fMatrix);
-            return fSrc.draw(canvas);
-        }
-        SkISize size() const SK_OVERRIDE { return fSize; }
-        Name name() const SK_OVERRIDE { sk_throw(); return ""; }  // No one should be calling this.
-    } proxy(src, fMatrix);
-    return fSink->draw(proxy, bitmap, stream, log);
+    SkMatrix matrix = fMatrix;
+    SkISize size = auto_compute_translate(&matrix, src.size().width(), src.size().height());
+    return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) {
+        canvas->concat(matrix);
+        return src.draw(canvas);
+    });
 }
 
 // Undoes any flip or 90 degree rotate without changing the scale of the bitmap.
 // This should be pixel-preserving.
-ViaUpright::ViaUpright(SkMatrix matrix, Sink* sink) : fMatrix(matrix), fSink(sink) {}
+ViaUpright::ViaUpright(SkMatrix matrix, Sink* sink) : Via(sink), fMatrix(matrix) {}
 
 Error ViaUpright::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
     Error err = fSink->draw(src, bitmap, stream, log);
@@ -414,38 +569,49 @@
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
-ViaPipe::ViaPipe(Sink* sink) : fSink(sink) {}
-
 Error ViaPipe::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
-    // We turn ourselves into another Src that draws our argument into bitmap/stream via pipe.
-    struct ProxySrc : public Src {
-        const Src& fSrc;
-        ProxySrc(const Src& src) : fSrc(src) {}
-
-        Error draw(SkCanvas* canvas) const SK_OVERRIDE {
-            SkISize size = this->size();
-            PipeController controller(canvas, &SkImageDecoder::DecodeMemory);
-            SkGPipeWriter pipe;
-            const uint32_t kFlags = 0; // We mirror SkDeferredCanvas, which doesn't use any flags.
-            return fSrc.draw(pipe.startRecording(&controller, kFlags, size.width(), size.height()));
-        }
-        SkISize size() const SK_OVERRIDE { return fSrc.size(); }
-        Name name() const SK_OVERRIDE { sk_throw(); return ""; }  // No one should be calling this.
-    } proxy(src);
-    return fSink->draw(proxy, bitmap, stream, log);
+    auto size = src.size();
+    return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) {
+        PipeController controller(canvas, &SkImageDecoder::DecodeMemory);
+        SkGPipeWriter pipe;
+        const uint32_t kFlags = 0; // We mirror SkDeferredCanvas, which doesn't use any flags.
+        return src.draw(pipe.startRecording(&controller, kFlags, size.width(), size.height()));
+    });
 }
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
-ViaSerialization::ViaSerialization(Sink* sink) : fSink(sink) {}
+Error ViaDeferred::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
+    // We draw via a deferred canvas into a surface that's compatible with the original canvas,
+    // then snap that surface as an image and draw it into the original canvas.
+    return draw_to_canvas(fSink, bitmap, stream, log, src.size(), [&](SkCanvas* canvas) -> Error {
+        SkAutoTUnref<SkSurface> surface(canvas->newSurface(canvas->imageInfo()));
+        if (!surface.get()) {
+            return "can't make surface for deferred canvas";
+        }
+        SkAutoTDelete<SkDeferredCanvas> defcan(SkDeferredCanvas::Create(surface));
+        Error err = src.draw(defcan);
+        if (!err.isEmpty()) {
+            return err;
+        }
+        SkAutoTUnref<SkImage> image(defcan->newImageSnapshot());
+        if (!image) {
+            return "failed to create deferred image snapshot";
+        }
+        canvas->drawImage(image, 0, 0, NULL);
+        return "";
+    });
+}
 
-Error ViaSerialization::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log)
-    const {
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+Error ViaSerialization::draw(
+        const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
     // Record our Src into a picture.
-    SkSize size;
-    size = src.size();
+    auto size = src.size();
     SkPictureRecorder recorder;
-    Error err = src.draw(recorder.beginRecording(size.width(), size.height()));
+    Error err = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
+                                                 SkIntToScalar(size.height())));
     if (!err.isEmpty()) {
         return err;
     }
@@ -455,88 +621,169 @@
     SkDynamicMemoryWStream wStream;
     pic->serialize(&wStream);
     SkAutoTDelete<SkStream> rStream(wStream.detachAsStream());
-    SkAutoTUnref<SkPicture> deserialized(SkPicture::CreateFromStream(rStream));
+    SkAutoTUnref<SkPicture> deserialized(SkPicture::CreateFromStream(rStream, &lazy_decode_bitmap));
 
-    // Turn that deserialized picture into a Src, draw it into our Sink to fill bitmap or stream.
-    struct ProxySrc : public Src {
-        const SkPicture* fPic;
-        const SkISize fSize;
-        ProxySrc(const SkPicture* pic, SkISize size) : fPic(pic), fSize(size) {}
-
-        Error draw(SkCanvas* canvas) const SK_OVERRIDE {
-            canvas->drawPicture(fPic);
-            return "";
-        }
-        SkISize size() const SK_OVERRIDE { return fSize; }
-        Name name() const SK_OVERRIDE { sk_throw(); return ""; }  // No one should be calling this.
-    } proxy(deserialized, src.size());
-    return fSink->draw(proxy, bitmap, stream, log);
+    return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) {
+        canvas->drawPicture(deserialized);
+        return "";
+    });
 }
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
 ViaTiles::ViaTiles(int w, int h, SkBBHFactory* factory, Sink* sink)
-    : fW(w)
+    : Via(sink)
+    , fW(w)
     , fH(h)
-    , fFactory(factory)
-    , fSink(sink) {}
+    , fFactory(factory) {}
 
 Error ViaTiles::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
-    // Record our Src into a picture.
-    SkSize size;
-    size = src.size();
+    auto size = src.size();
     SkPictureRecorder recorder;
-    Error err = src.draw(recorder.beginRecording(size.width(), size.height(), fFactory.get()));
+    Error err = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
+                                                 SkIntToScalar(size.height()),
+                                                 fFactory.get()));
     if (!err.isEmpty()) {
         return err;
     }
-    SkAutoTUnref<SkPicture> pic(recorder.endRecording());
+    SkAutoTUnref<SkPicture> pic(recorder.endRecordingAsPicture());
 
-    // Turn that picture into a Src that draws into our Sink via tiles + MPD.
-    struct ProxySrc : public Src {
-        const int fW, fH;
-        const SkPicture* fPic;
-        const SkISize fSize;
-        ProxySrc(int w, int h, const SkPicture* pic, SkISize size)
-            : fW(w), fH(h), fPic(pic), fSize(size) {}
+    return draw_to_canvas(fSink, bitmap, stream, log, src.size(), [&](SkCanvas* canvas) {
+        const int xTiles = (size.width()  + fW - 1) / fW,
+                  yTiles = (size.height() + fH - 1) / fH;
+        SkMultiPictureDraw mpd(xTiles*yTiles);
+        SkTDArray<SkSurface*> surfaces;
+        surfaces.setReserve(xTiles*yTiles);
 
-        Error draw(SkCanvas* canvas) const SK_OVERRIDE {
-            const int xTiles = (fSize.width()  + fW - 1) / fW,
-                      yTiles = (fSize.height() + fH - 1) / fH;
-            SkMultiPictureDraw mpd(xTiles*yTiles);
-            SkTDArray<SkSurface*> surfaces;
-            surfaces.setReserve(xTiles*yTiles);
-
-            SkImageInfo info = canvas->imageInfo().makeWH(fW, fH);
-            for (int j = 0; j < yTiles; j++) {
-                for (int i = 0; i < xTiles; i++) {
-                    // This lets our ultimate Sink determine the best kind of surface.
-                    // E.g., if it's a GpuSink, the surfaces and images are textures.
-                    SkSurface* s = canvas->newSurface(info);
-                    if (!s) {
-                        s = SkSurface::NewRaster(info);  // Some canvases can't create surfaces.
-                    }
-                    surfaces.push(s);
-                    SkCanvas* c = s->getCanvas();
-                    c->translate(SkIntToScalar(-i * fW),
-                                 SkIntToScalar(-j * fH));  // Line up the canvas with this tile.
-                    mpd.add(c, fPic);
+        SkImageInfo info = canvas->imageInfo().makeWH(fW, fH);
+        for (int j = 0; j < yTiles; j++) {
+            for (int i = 0; i < xTiles; i++) {
+                // This lets our ultimate Sink determine the best kind of surface.
+                // E.g., if it's a GpuSink, the surfaces and images are textures.
+                SkSurface* s = canvas->newSurface(info);
+                if (!s) {
+                    s = SkSurface::NewRaster(info);  // Some canvases can't create surfaces.
                 }
+                surfaces.push(s);
+                SkCanvas* c = s->getCanvas();
+                c->translate(SkIntToScalar(-i * fW),
+                             SkIntToScalar(-j * fH));  // Line up the canvas with this tile.
+                mpd.add(c, pic);
             }
-            mpd.draw();
-            for (int j = 0; j < yTiles; j++) {
-                for (int i = 0; i < xTiles; i++) {
-                    SkAutoTUnref<SkImage> image(surfaces[i+xTiles*j]->newImageSnapshot());
-                    canvas->drawImage(image, SkIntToScalar(i*fW), SkIntToScalar(j*fH));
-                }
-            }
-            surfaces.unrefAll();
-            return "";
         }
-        SkISize size() const SK_OVERRIDE { return fSize; }
-        Name name() const SK_OVERRIDE { sk_throw(); return ""; }  // No one should be calling this.
-    } proxy(fW, fH, pic, src.size());
-    return fSink->draw(proxy, bitmap, stream, log);
+        mpd.draw();
+        for (int j = 0; j < yTiles; j++) {
+            for (int i = 0; i < xTiles; i++) {
+                SkAutoTUnref<SkImage> image(surfaces[i+xTiles*j]->newImageSnapshot());
+                canvas->drawImage(image, SkIntToScalar(i*fW), SkIntToScalar(j*fH));
+            }
+        }
+        surfaces.unrefAll();
+        return "";
+    });
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+// Draw the Src into two pictures, then draw the second picture into the wrapped Sink.
+// This tests that any shortcuts we may take while recording that second picture are legal.
+Error ViaSecondPicture::draw(
+        const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
+    auto size = src.size();
+    return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) -> Error {
+        SkPictureRecorder recorder;
+        SkAutoTUnref<SkPicture> pic;
+        for (int i = 0; i < 2; i++) {
+            Error err = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
+                                                         SkIntToScalar(size.height())));
+            if (!err.isEmpty()) {
+                return err;
+            }
+            pic.reset(recorder.endRecordingAsPicture());
+        }
+        canvas->drawPicture(pic);
+        return "";
+    });
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+// Draw the Src twice.  This can help exercise caching.
+Error ViaTwice::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
+    return draw_to_canvas(fSink, bitmap, stream, log, src.size(), [&](SkCanvas* canvas) -> Error {
+        for (int i = 0; i < 2; i++) {
+            SkAutoCanvasRestore acr(canvas, true/*save now*/);
+            canvas->clear(SK_ColorTRANSPARENT);
+            Error err = src.draw(canvas);
+            if (err.isEmpty()) {
+                return err;
+            }
+        }
+        return "";
+    });
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+// This is like SkRecords::Draw, in that it plays back SkRecords ops into a Canvas.
+// Unlike SkRecords::Draw, it builds a single-op sub-picture out of each Draw-type op.
+// This is an only-slightly-exaggerated simluation of Blink's Slimming Paint pictures.
+struct DrawsAsSingletonPictures {
+    SkCanvas* fCanvas;
+
+    SK_CREATE_MEMBER_DETECTOR(paint);
+
+    template <typename T>
+    void draw(const T& op, SkCanvas* canvas) {
+        // We must pass SkMatrix::I() as our initial matrix.
+        // By default SkRecords::Draw() uses the canvas' matrix as its initial matrix,
+        // which would have the funky effect of applying transforms over and over.
+        SkRecords::Draw(canvas, nullptr, nullptr, 0, &SkMatrix::I())(op);
+    }
+
+    // Most things that have paints are Draw-type ops.  Create sub-pictures for each.
+    template <typename T>
+    SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
+        SkPictureRecorder rec;
+        this->draw(op, rec.beginRecording(SkRect::MakeLargest()));
+        SkAutoTUnref<SkPicture> pic(rec.endRecordingAsPicture());
+        fCanvas->drawPicture(pic);
+    }
+
+    // If you don't have a paint or are a SaveLayer, you're not a Draw-type op.
+    // We cannot make subpictures out of these because they affect state.  Draw them directly.
+    template <typename T>
+    SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { this->draw(op, fCanvas); }
+    void operator()(const SkRecords::SaveLayer& op)            { this->draw(op, fCanvas); }
+};
+
+// Record Src into a picture, then record it into a macro picture with a sub-picture for each draw.
+// Then play back that macro picture into our wrapped sink.
+Error ViaSingletonPictures::draw(
+        const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
+    auto size = src.size();
+    return draw_to_canvas(fSink, bitmap, stream, log, size, [&](SkCanvas* canvas) -> Error {
+        // Use low-level (Skia-private) recording APIs so we can read the SkRecord.
+        SkRecord skr;
+        SkRecorder recorder(&skr, size.width(), size.height());
+        Error err = src.draw(&recorder);
+        if (!err.isEmpty()) {
+            return err;
+        }
+
+        // Record our macro-picture, with each draw op as its own sub-picture.
+        SkPictureRecorder macroRec;
+        SkCanvas* macroCanvas = macroRec.beginRecording(SkIntToScalar(size.width()),
+                                                        SkIntToScalar(size.height()));
+        DrawsAsSingletonPictures drawsAsSingletonPictures = { macroCanvas };
+        for (unsigned i = 0; i < skr.count(); i++) {
+            skr.visit<void>(i, drawsAsSingletonPictures);
+        }
+        SkAutoTUnref<SkPicture> macroPic(macroRec.endRecordingAsPicture());
+
+        canvas->drawPicture(macroPic);
+        return "";
+    });
 }
 
 }  // namespace DM
diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h
index dcd169e..0537e26 100644
--- a/dm/DMSrcSink.h
+++ b/dm/DMSrcSink.h
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #ifndef DMSrcSink_DEFINED
 #define DMSrcSink_DEFINED
 
@@ -6,6 +13,7 @@
 #include "SkBBoxHierarchy.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "SkCodec.h"
 #include "SkData.h"
 #include "SkGPipe.h"
 #include "SkPicture.h"
@@ -74,22 +82,45 @@
 public:
     explicit GMSrc(skiagm::GMRegistry::Factory);
 
-    Error draw(SkCanvas*) const SK_OVERRIDE;
-    SkISize size() const SK_OVERRIDE;
-    Name name() const SK_OVERRIDE;
+    Error draw(SkCanvas*) const override;
+    SkISize size() const override;
+    Name name() const override;
 private:
     skiagm::GMRegistry::Factory fFactory;
 };
 
+class CodecSrc : public Src {
+public:
+    enum Mode {
+        kNormal_Mode,
+        kScanline_Mode,
+    };
+    enum DstColorType {
+        kGetFromCanvas_DstColorType,
+        kIndex8_Always_DstColorType,
+        kGrayscale_Always_DstColorType,
+    };
+    CodecSrc(Path, Mode, DstColorType);
+
+    Error draw(SkCanvas*) const override;
+    SkISize size() const override;
+    Name name() const override;
+private:
+    Path                   fPath;
+    Mode                   fMode;
+    DstColorType           fDstColorType;
+};
+
+
 class ImageSrc : public Src {
 public:
     // divisor == 0 means decode the whole image
     // divisor > 0 means decode in subsets, dividing into a divisor x divisor grid.
     explicit ImageSrc(Path path, int divisor = 0);
 
-    Error draw(SkCanvas*) const SK_OVERRIDE;
-    SkISize size() const SK_OVERRIDE;
-    Name name() const SK_OVERRIDE;
+    Error draw(SkCanvas*) const override;
+    SkISize size() const override;
+    Name name() const override;
 private:
     Path fPath;
     const int  fDivisor;
@@ -99,9 +130,9 @@
 public:
     explicit SKPSrc(Path path);
 
-    Error draw(SkCanvas*) const SK_OVERRIDE;
-    SkISize size() const SK_OVERRIDE;
-    Name name() const SK_OVERRIDE;
+    Error draw(SkCanvas*) const override;
+    SkISize size() const override;
+    Name name() const override;
 private:
     Path fPath;
 };
@@ -112,9 +143,9 @@
 public:
     NullSink() {}
 
-    Error draw(const Src& src, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return kAnyThread_Enclave; }
-    const char* fileExtension() const SK_OVERRIDE { return ""; }
+    Error draw(const Src& src, SkBitmap*, SkWStream*, SkString*) const override;
+    int enclave() const override { return kAnyThread_Enclave; }
+    const char* fileExtension() const override { return ""; }
 };
 
 
@@ -122,9 +153,9 @@
 public:
     GPUSink(GrContextFactory::GLContextType, GrGLStandard, int samples, bool dfText, bool threaded);
 
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE;
-    const char* fileExtension() const SK_OVERRIDE { return "png"; }
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+    int enclave() const override;
+    const char* fileExtension() const override { return "png"; }
 private:
     GrContextFactory::GLContextType fContextType;
     GrGLStandard                    fGpuAPI;
@@ -137,27 +168,27 @@
 public:
     PDFSink();
 
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return kAnyThread_Enclave; }
-    const char* fileExtension() const SK_OVERRIDE { return "pdf"; }
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+    int enclave() const override { return kAnyThread_Enclave; }
+    const char* fileExtension() const override { return "pdf"; }
 };
 
 class XPSSink : public Sink {
 public:
     XPSSink();
 
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return kAnyThread_Enclave; }
-    const char* fileExtension() const SK_OVERRIDE { return "xps"; }
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+    int enclave() const override { return kAnyThread_Enclave; }
+    const char* fileExtension() const override { return "xps"; }
 };
 
 class RasterSink : public Sink {
 public:
     explicit RasterSink(SkColorType);
 
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return kAnyThread_Enclave; }
-    const char* fileExtension() const SK_OVERRIDE { return "png"; }
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+    int enclave() const override { return kAnyThread_Enclave; }
+    const char* fileExtension() const override { return "png"; }
 private:
     SkColorType    fColorType;
 };
@@ -166,80 +197,91 @@
 public:
     SKPSink();
 
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return kAnyThread_Enclave; }
-    const char* fileExtension() const SK_OVERRIDE { return "skp"; }
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+    int enclave() const override { return kAnyThread_Enclave; }
+    const char* fileExtension() const override { return "skp"; }
 };
 
 class SVGSink : public Sink {
 public:
     SVGSink();
 
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return kAnyThread_Enclave; }
-    const char* fileExtension() const SK_OVERRIDE { return "svg"; }
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+    int enclave() const override { return kAnyThread_Enclave; }
+    const char* fileExtension() const override { return "svg"; }
 };
 
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
-class ViaMatrix : public Sink {
+class Via : public Sink {
+public:
+    explicit Via(Sink* sink) : fSink(sink) {}
+    const char* fileExtension() const override { return fSink->fileExtension(); }
+    int               enclave() const override { return fSink->enclave(); }
+protected:
+    SkAutoTDelete<Sink> fSink;
+};
+
+class ViaMatrix : public Via {
 public:
     ViaMatrix(SkMatrix, Sink*);
-
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return fSink->enclave(); }
-    const char* fileExtension() const SK_OVERRIDE { return fSink->fileExtension(); }
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
 private:
-    SkMatrix            fMatrix;
-    SkAutoTDelete<Sink> fSink;
+    const SkMatrix fMatrix;
 };
 
-class ViaUpright : public Sink {
+class ViaUpright : public Via {
 public:
     ViaUpright(SkMatrix, Sink*);
-
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return fSink->enclave(); }
-    const char* fileExtension() const SK_OVERRIDE { return fSink->fileExtension(); }
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
 private:
-    SkMatrix            fMatrix;
-    SkAutoTDelete<Sink> fSink;
+    const SkMatrix fMatrix;
 };
 
-class ViaPipe : public Sink {
+class ViaPipe : public Via {
 public:
-    explicit ViaPipe(Sink*);
-
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return fSink->enclave(); }
-    const char* fileExtension() const SK_OVERRIDE { return fSink->fileExtension(); }
-private:
-    SkAutoTDelete<Sink>  fSink;
+    explicit ViaPipe(Sink* sink) : Via(sink) {}
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
 };
 
-class ViaSerialization : public Sink {
+class ViaDeferred : public Via {
 public:
-    explicit ViaSerialization(Sink*);
-
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return fSink->enclave(); }
-    const char* fileExtension() const SK_OVERRIDE { return fSink->fileExtension(); }
-private:
-    SkAutoTDelete<Sink> fSink;
+    explicit ViaDeferred(Sink* sink) : Via(sink) {}
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
 };
 
-class ViaTiles : public Sink {
+class ViaSerialization : public Via {
+public:
+    explicit ViaSerialization(Sink* sink) : Via(sink) {}
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+};
+
+class ViaTiles : public Via {
 public:
     ViaTiles(int w, int h, SkBBHFactory*, Sink*);
-
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return fSink->enclave(); }
-    const char* fileExtension() const SK_OVERRIDE { return fSink->fileExtension(); }
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
 private:
     const int                   fW, fH;
     SkAutoTDelete<SkBBHFactory> fFactory;
-    SkAutoTDelete<Sink>         fSink;
+};
+
+class ViaSecondPicture : public Via {
+public:
+    explicit ViaSecondPicture(Sink* sink) : Via(sink) {}
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+};
+
+class ViaSingletonPictures : public Via {
+public:
+    explicit ViaSingletonPictures(Sink* sink) : Via(sink) {}
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+};
+
+class ViaTwice : public Via {
+public:
+    explicit ViaTwice(Sink* sink) : Via(sink) {}
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
 };
 
 }  // namespace DM
diff --git a/dm/DMSrcSinkAndroid.cpp b/dm/DMSrcSinkAndroid.cpp
index 114259e..63c9d82 100644
--- a/dm/DMSrcSinkAndroid.cpp
+++ b/dm/DMSrcSinkAndroid.cpp
@@ -8,361 +8,27 @@
 #include "DMSrcSink.h"
 #include "DMSrcSinkAndroid.h"
 
-#include "AnimationContext.h"
-#include "DisplayListCanvas.h"
-#include "IContextFactory.h"
-#include "RenderNode.h"
+#include "SkAndroidSDKCanvas.h"
 #include "SkCanvas.h"
+#include "SkHwuiRenderer.h"
 #include "SkiaCanvasProxy.h"
-#include "SkTLazy.h"
-#include "SkMaskFilter.h"
-#include "SkPictureRecorder.h"
 #include "SkStream.h"
-#include "android/rect.h"
-#include "android/native_window.h"
-#include "gui/BufferQueue.h"
-#include "gui/CpuConsumer.h"
-#include "gui/IGraphicBufferConsumer.h"
-#include "gui/IGraphicBufferProducer.h"
-#include "gui/Surface.h"
-#include "renderthread/RenderProxy.h"
-#include "renderthread/TimeLord.h"
 
 /* These functions are only compiled in the Android Framework. */
 
-namespace {
-
-/** Discard SkShaders not exposed by the Android Java API. */
-
-void CheckShader(SkPaint* paint) {
-    SkShader* shader = paint->getShader();
-    if (!shader) {
-        return;
-    }
-
-    if (shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
-        return;
-    }
-    if (shader->asACompose(NULL)) {
-        return;
-    }
-    SkShader::GradientType gtype = shader->asAGradient(NULL);
-    if (gtype == SkShader::kLinear_GradientType ||
-        gtype == SkShader::kRadial_GradientType ||
-        gtype == SkShader::kSweep_GradientType) {
-        return;
-    }
-    paint->setShader(NULL);
-}
-
-/** Simplify a paint.  */
-
-void Filter(SkPaint* paint) {
-
-    uint32_t flags = paint->getFlags();
-    flags &= ~SkPaint::kLCDRenderText_Flag;
-    paint->setFlags(flags);
-
-    // Android doesn't support Xfermodes above kLighten_Mode
-    SkXfermode::Mode mode;
-    SkXfermode::AsMode(paint->getXfermode(), &mode);
-    if (mode > SkXfermode::kLighten_Mode) {
-        paint->setXfermode(NULL);
-    }
-
-    // Force bilinear scaling or none
-    if (paint->getFilterQuality() != kNone_SkFilterQuality) {
-        paint->setFilterQuality(kLow_SkFilterQuality);
-    }
-
-    CheckShader(paint);
-
-    // Android SDK only supports mode & matrix color filters
-    // (and, again, no modes above kLighten_Mode).
-    SkColorFilter* cf = paint->getColorFilter();
-    if (cf) {
-        SkColor color;
-        SkXfermode::Mode mode;
-        SkScalar srcColorMatrix[20];
-        bool isMode = cf->asColorMode(&color, &mode);
-        if (isMode && mode > SkXfermode::kLighten_Mode) {
-            paint->setColorFilter(
-                SkColorFilter::CreateModeFilter(color, SkXfermode::kSrcOver_Mode));
-        } else if (!isMode && !cf->asColorMatrix(srcColorMatrix)) {
-            paint->setColorFilter(NULL);
-        }
-    }
-
-    SkPathEffect* pe = paint->getPathEffect();
-    if (pe && !pe->exposedInAndroidJavaAPI()) {
-        paint->setPathEffect(NULL);
-    }
-
-    // TODO: Android doesn't support all the flags that can be passed to
-    // blur filters; we need plumbing to get them out.
-
-    paint->setImageFilter(NULL);
-    paint->setLooper(NULL);
-};
-
-/** SkDrawFilter is likely to be deprecated; this is a proxy
-    canvas that does the same thing: alter SkPaint fields.
-
-    onDraw*() functions may have their SkPaint modified, and are then
-    passed on to the same function on proxyTarget.
-
-    This still suffers one of the same architectural flaws as SkDrawFilter:
-    TextBlob paints are incomplete when filter is called.
-*/
-
-#define FILTER(p)             \
-    SkPaint filteredPaint(p); \
-    Filter(&filteredPaint);
-
-#define FILTER_PTR(p)                          \
-    SkTLazy<SkPaint> lazyPaint;                \
-    SkPaint* filteredPaint = (SkPaint*) p;     \
-    if (p) {                                   \
-        filteredPaint = lazyPaint.set(*p);     \
-        Filter(filteredPaint);                 \
-    }
-
-
-class FilteringCanvas : public SkCanvas {
-public:
-    FilteringCanvas(SkCanvas* proxyTarget) : fProxyTarget(proxyTarget) { }
-
-protected:
-    void onDrawPaint(const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawPaint(filteredPaint);
-    }
-    void onDrawPoints(PointMode pMode, size_t count, const SkPoint pts[],
-                      const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawPoints(pMode, count, pts, filteredPaint);
-    }
-    void onDrawOval(const SkRect& r, const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawOval(r, filteredPaint);
-    }
-    void onDrawRect(const SkRect& r, const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawRect(r, filteredPaint);
-    }
-    void onDrawRRect(const SkRRect& r, const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawRRect(r, filteredPaint);
-    }
-    void onDrawPath(const SkPath& path, const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawPath(path, filteredPaint);
-    }
-    void onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
-                      const SkPaint* paint) SK_OVERRIDE {
-        FILTER_PTR(paint);
-        fProxyTarget->drawBitmap(bitmap, left, top, filteredPaint);
-    }
-    void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
-                          const SkPaint* paint, DrawBitmapRectFlags flags) SK_OVERRIDE {
-        FILTER_PTR(paint);
-        fProxyTarget->drawBitmapRectToRect(bitmap, src, dst, filteredPaint, flags);
-    }
-    void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
-                          const SkRect& dst, const SkPaint* paint) SK_OVERRIDE {
-        FILTER_PTR(paint);
-        fProxyTarget->drawBitmapNine(bitmap, center, dst, filteredPaint);
-    }
-    void onDrawSprite(const SkBitmap& bitmap, int left, int top,
-                      const SkPaint* paint) SK_OVERRIDE {
-        FILTER_PTR(paint);
-        fProxyTarget->drawSprite(bitmap, left, top, filteredPaint);
-    }
-    void onDrawVertices(VertexMode vMode, int vertexCount, const SkPoint vertices[],
-                        const SkPoint texs[], const SkColor colors[], SkXfermode* xMode,
-                        const uint16_t indices[], int indexCount,
-                        const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawVertices(vMode, vertexCount, vertices, texs, colors,
-                                   xMode, indices, indexCount, filteredPaint);
-    }
-
-    void onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
-                      const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawDRRect(outer, inner, filteredPaint);
-    }
-
-    void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                    const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawText(text, byteLength, x, y, filteredPaint);
-    }
-    void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                       const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawPosText(text, byteLength, pos, filteredPaint);
-    }
-    void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                        SkScalar constY, const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawPosTextH(text, byteLength, xpos, constY, filteredPaint);
-    }
-    void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                          const SkMatrix* matrix, const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawTextOnPath(text, byteLength, path, matrix, filteredPaint);
-    }
-    void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                        const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawTextBlob(blob, x, y, filteredPaint);
-    }
-
-    void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
-                     const SkPoint texCoords[4], SkXfermode* xmode,
-                     const SkPaint& paint) SK_OVERRIDE {
-        FILTER(paint);
-        fProxyTarget->drawPatch(cubics, colors, texCoords, xmode, filteredPaint);
-    }
-
-protected:
-    SkCanvas* fProxyTarget;
-};
-
-/**
- * Helper class for setting up android::uirenderer::renderthread::RenderProxy.
- */
-class ContextFactory : public android::uirenderer::IContextFactory {
-public:
-    android::uirenderer::AnimationContext* createAnimationContext
-        (android::uirenderer::renderthread::TimeLord& clock) SK_OVERRIDE {
-        return new android::uirenderer::AnimationContext(clock);
-    }
-};
-
-}  // namespace
-
 namespace DM {
 
 Error HWUISink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString*) const {
-    // Do all setup in this function because we don't know the size
-    // for the RenderNode and RenderProxy during the constructor.
-    // In practice this doesn't seem too expensive.
-    const SkISize size = src.size();
-
-    // Based on android::SurfaceTexture_init()
-    android::sp<android::IGraphicBufferProducer> producer;
-    android::sp<android::IGraphicBufferConsumer> consumer;
-    android::BufferQueue::createBufferQueue(&producer, &consumer);
-
-    // Consumer setup
-
-    android::sp<android::CpuConsumer> cpuConsumer =
-        new android::CpuConsumer(consumer, 1);
-    cpuConsumer->setName(android::String8("SkiaTestClient"));
-    cpuConsumer->setDefaultBufferSize(size.width(), size.height());
-
-    // Producer setup
-
-    android::sp<android::Surface> surface = new android::Surface(producer);
-    native_window_set_buffers_dimensions(surface.get(), size.width(), size.height());
-    native_window_set_buffers_format(surface.get(), android::PIXEL_FORMAT_RGBA_8888);
-    native_window_set_usage(surface.get(), GRALLOC_USAGE_SW_READ_OFTEN |
-                                           GRALLOC_USAGE_SW_WRITE_NEVER |
-                                           GRALLOC_USAGE_HW_RENDER);
-
-    // RenderNode setup based on hwui/tests/main.cpp:TreeContentAnimation
-    SkAutoTDelete<android::uirenderer::RenderNode> rootNode
-        (new android::uirenderer::RenderNode());
-    rootNode->incStrong(nullptr);
-
-    // Values set here won't be applied until the framework has called
-    // RenderNode::pushStagingPropertiesChanges() during RenderProxy::syncAndDrawFrame().
-    rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, size.width(), size.height());
-    rootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::X |
-                                     android::uirenderer::RenderNode::Y);
-    rootNode->mutateStagingProperties().setClipToBounds(false);
-    rootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::GENERIC);
-
-    // RenderProxy setup based on hwui/tests/main.cpp:TreeContentAnimation
-    ContextFactory factory;
-    SkAutoTDelete<android::uirenderer::renderthread::RenderProxy> proxy
-        (new android::uirenderer::renderthread::RenderProxy(false, rootNode, &factory));
-    proxy->loadSystemProperties();
-
-    proxy->initialize(surface.get());
-
-    float lightX = size.width() / 2.0f;
-    android::uirenderer::Vector3 lightVector { lightX, -200.0f, 800.0f };
-    proxy->setup(size.width(), size.height(), 800.0f, 255 * 0.075f, 255 * 0.15f);
-    proxy->setLightCenter(lightVector);
-
-    // Do the draw
-
-    SkAutoTDelete<android::uirenderer::DisplayListCanvas> canvas
-        (new android::uirenderer::DisplayListCanvas());
-    canvas->setViewport(size.width(), size.height());
-    canvas->prepare();
-    canvas->clipRect(0, 0, size.width(), size.height(), SkRegion::Op::kReplace_Op);
-
-    Error err = src.draw(canvas->asSkCanvas());
+    SkHwuiRenderer renderer;
+    renderer.initialize(src.size());
+    SkCanvas* canvas = renderer.prepareToDraw();
+    Error err = src.draw(canvas);
     if (!err.isEmpty()) {
         return err;
     }
-
-    canvas->finish();
-    rootNode->setStagingDisplayList(canvas->finishRecording());
-
-    proxy->syncAndDrawFrame();
-    proxy->fence();
-
-    // Capture pixels
-
-    SkImageInfo destinationConfig =
-        SkImageInfo::Make(size.width(), size.height(),
-                          kRGBA_8888_SkColorType, kPremul_SkAlphaType);
-    dst->allocPixels(destinationConfig);
-    sk_memset32((uint32_t*) dst->getPixels(), SK_ColorRED, size.width() * size.height());
-
-    android::CpuConsumer::LockedBuffer nativeBuffer;
-    android::status_t retval = cpuConsumer->lockNextBuffer(&nativeBuffer);
-    if (retval == android::BAD_VALUE) {
-        SkDebugf("HWUISink::draw() got no buffer; returning transparent");
-        // No buffer ready to read - commonly triggered by dm sending us
-        // a no-op source, or calling code that doesn't do anything on this
-        // backend.
-        dst->eraseColor(SK_ColorTRANSPARENT);
-        return "";
-    } else if (retval) {
-        return SkStringPrintf("Failed to lock buffer to read pixels: %d.", retval);
-    }
-
-    // Move the pixels into the destination SkBitmap
-
-    SK_ALWAYSBREAK(nativeBuffer.format == android::PIXEL_FORMAT_RGBA_8888 &&
-                   "Native buffer not RGBA!");
-    SkImageInfo nativeConfig =
-        SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height,
-                          kRGBA_8888_SkColorType, kPremul_SkAlphaType);
-
-    // Android stride is in pixels, Skia stride is in bytes
-    SkBitmap nativeWrapper;
-    bool success =
-        nativeWrapper.installPixels(nativeConfig, nativeBuffer.data, nativeBuffer.stride * 4);
-    if (!success) {
-        return "Failed to wrap HWUI buffer in a SkBitmap";
-    }
-
-    SK_ALWAYSBREAK(dst->colorType() == kRGBA_8888_SkColorType &&
-                   "Destination buffer not RGBA!");
-    success =
-        nativeWrapper.readPixels(destinationConfig, dst->getPixels(), dst->rowBytes(), 0, 0);
-    if (!success) {
-        return "Failed to extract pixels from HWUI buffer";
-    }
-
-    cpuConsumer->unlockBuffer(nativeBuffer);
+    renderer.finishDrawing();
+    renderer.proxy->fence();
+    renderer.capturePixels(dst);
     return "";
 }
 
@@ -379,21 +45,22 @@
         ProxySrc(const Src& src)
             : fSrc(src) {}
 
-        Error draw(SkCanvas* canvas) const SK_OVERRIDE {
+        Error draw(SkCanvas* canvas) const override {
             // Pass through HWUI's upper layers to get operational transforms
             SkAutoTDelete<android::Canvas> ac (android::Canvas::create_canvas(canvas));
             SkAutoTUnref<android::uirenderer::SkiaCanvasProxy> scProxy
                 (new android::uirenderer::SkiaCanvasProxy(ac));
 
             // Pass through another proxy to get paint transforms
-            FilteringCanvas fc(scProxy);
+            SkAndroidSDKCanvas fc;
+            fc.reset(scProxy);
 
             fSrc.draw(&fc);
 
             return "";
         }
-        SkISize size() const SK_OVERRIDE { return fSrc.size(); }
-        Name name() const SK_OVERRIDE { sk_throw(); return ""; }
+        SkISize size() const override { return fSrc.size(); }
+        Name name() const override { sk_throw(); return ""; }
     } proxy(src);
 
     return fSink->draw(proxy, bitmap, stream, log);
diff --git a/dm/DMSrcSinkAndroid.h b/dm/DMSrcSinkAndroid.h
index 1a7459f..bd4adcb 100644
--- a/dm/DMSrcSinkAndroid.h
+++ b/dm/DMSrcSinkAndroid.h
@@ -22,9 +22,9 @@
 public:
     HWUISink() { }
 
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return kGPU_Enclave; }
-    const char* fileExtension() const SK_OVERRIDE { return "png"; }
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+    int enclave() const override { return kGPU_Enclave; }
+    const char* fileExtension() const override { return "png"; }
 };
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@@ -35,9 +35,9 @@
 public:
     explicit ViaAndroidSDK(Sink*);
 
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const SK_OVERRIDE;
-    int enclave() const SK_OVERRIDE { return fSink->enclave(); }
-    const char* fileExtension() const SK_OVERRIDE { return fSink->fileExtension(); }
+    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+    int enclave() const override { return fSink->enclave(); }
+    const char* fileExtension() const override { return fSink->fileExtension(); }
 
 private:
     SkAutoTDelete<Sink> fSink;
diff --git a/docs/quickstart.md b/docs/quickstart.md
deleted file mode 100644
index 12002ca..0000000
--- a/docs/quickstart.md
+++ /dev/null
@@ -1,43 +0,0 @@
-Skia Quickstart Guide
-=====================
-
-
-WARNING: Several steps in this guide are out of sync with our automatically-
-tested, officially-supported processes for checking out and building Skia.
-The officially supported processes are the ones documented in https://skia.org;
-see https://skia.org/user/quick.
-
-The steps documented within this file are more experimental in nature.
-
-
-This guide assumes you've got `git`, `ninja`, and `python` on your path.
-
-1. First, checkout Skia:
-    * `git clone https://skia.googlesource.com/skia.git`
-    * `cd skia`
-2. Then download the dependencies.  You only need to rerun this when
-   the dependencies change.
-    * `python tools/git-sync-deps`
-3. Create our Ninja build files from our Gyp meta-build files.  You only need
-   to rerun this when you sync or change a `.gyp` file.
-    * `GYP_GENERATORS=ninja ./gyp_skia`
-4. Now, let's build Skia.  There are a few options:
-    * `ninja -C out/Debug`: no optimization, asserts enabled
-    * `ninja -C out/Release`: optimization, asserts disabled
-    * `ninja -C out/Coverage`: no optimization, asserts enabled, code coverage generated
-5. Run some tests:
-    * `out/Debug/dm`: runs golden master tests from gm/, unit tests from tests/
-6. Make some changes:
-    * `git checkout -b my-new-feature origin/master`
-    * `vim src/...`
-    * `git commit -am "Changes for my new feature."`
-    * `vim tests/...`
-    * `git commit --amend -a`
-    * `ninja -C out/Debug && out/Debug/dm && echo ok`
-7. Rebase your change onto the latest Skia code:
-    * `git pull --rebase`
-    * `ninja -C out/Debug && out/Debug/dm && echo ok`
-8. Upload your change and send it out for review:
-    * `git cl upload -r my-skia-reviewer@google.com -s`
-    * `git cl web`
-9. Go through code review, get an LGTM, submit using the checkbox on the code review page.
diff --git a/example/HelloWorld.h b/example/HelloWorld.h
index e5cde27..a1508c5 100644
--- a/example/HelloWorld.h
+++ b/example/HelloWorld.h
@@ -25,7 +25,7 @@
         kGPU_DeviceType,
     };
     HelloWorldWindow(void* hwnd);
-    virtual ~HelloWorldWindow() SK_OVERRIDE;
+    virtual ~HelloWorldWindow() override;
 
     // Changes the device type of the object.
     bool setUpBackend();
@@ -33,7 +33,7 @@
     DeviceType getDeviceType() const { return fType; }
 
 protected:
-    SkSurface* createSurface() SK_OVERRIDE {
+    SkSurface* createSurface() override {
         if (kGPU_DeviceType == fType) {
             SkSurfaceProps props(INHERITED::getSurfaceProps());
             return SkSurface::NewRenderTargetDirect(fRenderTarget, &props);
@@ -43,16 +43,16 @@
         return fSurface = SkSurface::NewRaster(info);
    }
 
-    void draw(SkCanvas* canvas) SK_OVERRIDE;
+    void draw(SkCanvas* canvas) override;
     void drawContents(SkCanvas* canvas);
 
-    void onSizeChange() SK_OVERRIDE;
+    void onSizeChange() override;
 
 private:
     bool findNextMatch();  // Set example to the first one that matches FLAGS_match.
     void setTitle();
     void setUpRenderTarget();
-    bool onHandleChar(SkUnichar unichar) SK_OVERRIDE;
+    bool onHandleChar(SkUnichar unichar) override;
     void tearDownBackend();
 
     // draw contents
diff --git a/experimental/AndroidPathRenderer/GrAndroidPathRenderer.h b/experimental/AndroidPathRenderer/GrAndroidPathRenderer.h
index 38d2030..a7225fa 100644
--- a/experimental/AndroidPathRenderer/GrAndroidPathRenderer.h
+++ b/experimental/AndroidPathRenderer/GrAndroidPathRenderer.h
@@ -16,13 +16,13 @@
     virtual bool canDrawPath(const SkPath& path,
                              const SkStrokeRec& stroke,
                              const GrDrawTarget* target,
-                             bool antiAlias) const SK_OVERRIDE;
+                             bool antiAlias) const override;
 
 protected:
     virtual bool onDrawPath(const SkPath& path,
                             const SkStrokeRec& stroke,
                             GrDrawTarget* target,
-                            bool antiAlias) SK_OVERRIDE;
+                            bool antiAlias) override;
 
 private:
     typedef GrPathRenderer INHERITED;
diff --git a/experimental/PdfViewer/SkNulCanvas.h b/experimental/PdfViewer/SkNulCanvas.h
index 58c239a..c8a95e9 100644
--- a/experimental/PdfViewer/SkNulCanvas.h
+++ b/experimental/PdfViewer/SkNulCanvas.h
@@ -26,13 +26,13 @@
     explicit SkNulCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap) {}
     virtual ~SkNulCanvas() {}
 
-    void beginCommentGroup(const char* description) SK_OVERRIDE {}
-    void addComment(const char* kywd, const char* value) SK_OVERRIDE {}
-    void endCommentGroup() SK_OVERRIDE {}
-    SkDrawFilter* setDrawFilter(SkDrawFilter* filter) SK_OVERRIDE {return NULL;}
+    void beginCommentGroup(const char* description) override {}
+    void addComment(const char* kywd, const char* value) override {}
+    void endCommentGroup() override {}
+    SkDrawFilter* setDrawFilter(SkDrawFilter* filter) override {return NULL;}
 
-    bool isClipEmpty() const SK_OVERRIDE { return false; }
-    bool getClipBounds(SkRect* bounds) const SK_OVERRIDE {
+    bool isClipEmpty() const override { return false; }
+    bool getClipBounds(SkRect* bounds) const override {
         if (NULL != bounds) {
             bounds->setXYWH(0, 0,
                             SkIntToScalar(this->imageInfo().width()),
@@ -40,7 +40,7 @@
         }
         return true;
     }
-    bool getClipDeviceBounds(SkIRect* bounds) const SK_OVERRIDE {
+    bool getClipDeviceBounds(SkIRect* bounds) const override {
         if (NULL != bounds) {
             bounds->setLargest();
         }
@@ -52,54 +52,54 @@
     virtual SkBaseDevice* setDevice(SkBaseDevice* device) {return NULL;}
 
     virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
-                                            SaveFlags flags) SK_OVERRIDE {
+                                            SaveFlags flags) override {
         this->INHERITED::willSaveLayer(bounds, paint, flags);
         return kNoLayer_SaveLayerStrategy;
     }
 
     virtual void onDrawText(const void* text, size_t byteLength, SkScalar x,
-                          SkScalar y, const SkPaint& paint) SK_OVERRIDE {}
+                          SkScalar y, const SkPaint& paint) override {}
     virtual void onDrawPosText(const void* text, size_t byteLength,
-                             const SkPoint pos[], const SkPaint& paint) SK_OVERRIDE {}
+                             const SkPoint pos[], const SkPaint& paint) override {}
     virtual void onDrawPosTextH(const void* text, size_t byteLength,
                               const SkScalar xpos[], SkScalar constY,
-                              const SkPaint& paint) SK_OVERRIDE {}
+                              const SkPaint& paint) override {}
     virtual void onDrawTextOnPath(const void* text, size_t byteLength,
                                 const SkPath& path, const SkMatrix* matrix,
-                                const SkPaint& paint) SK_OVERRIDE {}
+                                const SkPaint& paint) override {}
 
-    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE {}
-    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE {}
-    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE {}
-    void onClipRegion(const SkRegion&, SkRegion::Op)  SK_OVERRIDE {}
+    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override {}
+    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override {}
+    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override {}
+    void onClipRegion(const SkRegion&, SkRegion::Op)  override {}
 
-    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) SK_OVERRIDE {}
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override {}
 
-    void onDrawPaint(const SkPaint& paint) SK_OVERRIDE {}
+    void onDrawPaint(const SkPaint& paint) override {}
     void onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
-                      const SkPaint& paint) SK_OVERRIDE {}
-    void onDrawRect(const SkRect& rect, const SkPaint& paint) SK_OVERRIDE {}
-    void onDrawOval(const SkRect& oval, const SkPaint&) SK_OVERRIDE {}
-    void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) SK_OVERRIDE {}
-    void onDrawPath(const SkPath& path, const SkPaint& paint) SK_OVERRIDE {}
+                      const SkPaint& paint) override {}
+    void onDrawRect(const SkRect& rect, const SkPaint& paint) override {}
+    void onDrawOval(const SkRect& oval, const SkPaint&) override {}
+    void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {}
+    void onDrawPath(const SkPath& path, const SkPaint& paint) override {}
     void onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
-                      const SkPaint* paint = NULL) SK_OVERRIDE {}
+                      const SkPaint* paint = NULL) override {}
     void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
                           const SkRect& dst,
                           const SkPaint* paint,
-                          DrawBitmapRectFlags flags) SK_OVERRIDE {}
-    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE {}
+                          DrawBitmapRectFlags flags) override {}
+    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override {}
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
-                         const SkPaint*) SK_OVERRIDE{}
+                         const SkPaint*) override{}
     void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
-                          const SkRect& dst, const SkPaint* paint = NULL) SK_OVERRIDE {}
+                          const SkRect& dst, const SkPaint* paint = NULL) override {}
     void onDrawSprite(const SkBitmap& bitmap, int left, int top,
-                      const SkPaint* paint = NULL) SK_OVERRIDE {}
+                      const SkPaint* paint = NULL) override {}
     void onDrawVertices(VertexMode vmode, int vertexCount,
                         const SkPoint vertices[], const SkPoint texs[],
                         const SkColor colors[], SkXfermode* xmode,
                         const uint16_t indices[], int indexCount,
-                        const SkPaint& paint) SK_OVERRIDE {}
+                        const SkPaint& paint) override {}
 
     
 private:
diff --git a/experimental/PdfViewer/src/SkPdfContext.cpp b/experimental/PdfViewer/src/SkPdfContext.cpp
index 038d38e..835a89c 100644
--- a/experimental/PdfViewer/src/SkPdfContext.cpp
+++ b/experimental/PdfViewer/src/SkPdfContext.cpp
@@ -19,8 +19,8 @@
                   SkCanvas* canvas)
         : INHERITED(tokenizer, pdfContext, canvas) {}
 
-    SkPdfResult consumeToken(PdfToken& token) SK_OVERRIDE;
-    void loop() SK_OVERRIDE;
+    SkPdfResult consumeToken(PdfToken& token) override;
+    void loop() override;
 
 private:
     typedef SkPdfTokenLooper INHERITED;
diff --git a/experimental/PdfViewer/src/SkPdfRenderer.cpp b/experimental/PdfViewer/src/SkPdfRenderer.cpp
index 0376066..6a9fd74 100644
--- a/experimental/PdfViewer/src/SkPdfRenderer.cpp
+++ b/experimental/PdfViewer/src/SkPdfRenderer.cpp
@@ -212,8 +212,8 @@
     explicit PdfInlineImageLooper(SkPdfTokenLooper* parent)
         : INHERITED(parent) {}
 
-    SkPdfResult consumeToken(PdfToken& token) SK_OVERRIDE;
-    void loop() SK_OVERRIDE;
+    SkPdfResult consumeToken(PdfToken& token) override;
+    void loop() override;
 
 private:
     typedef SkPdfTokenLooper INHERITED;
@@ -224,8 +224,8 @@
     explicit PdfCompatibilitySectionLooper(SkPdfTokenLooper* parent)
         : INHERITED (parent) {}
 
-    SkPdfResult consumeToken(PdfToken& token) SK_OVERRIDE;
-    void loop() SK_OVERRIDE;
+    SkPdfResult consumeToken(PdfToken& token) override;
+    void loop() override;
 
 private:
     typedef SkPdfTokenLooper INHERITED;
@@ -1069,7 +1069,7 @@
                 "Text positioning not implemented for 2+ chars", NULL, pdfContext);
 
     pdfContext->fGraphicsState.fMatrixTm = matrix;
-    pdfContext->fGraphicsState.fMatrixTlm = matrix;;
+    pdfContext->fGraphicsState.fMatrixTlm = matrix;
 
     return kPartial_SkPdfResult;
 }
diff --git a/experimental/SkV8Example/SkV8Example.h b/experimental/SkV8Example/SkV8Example.h
index 38a9c85..5185722 100644
--- a/experimental/SkV8Example/SkV8Example.h
+++ b/experimental/SkV8Example/SkV8Example.h
@@ -25,15 +25,15 @@
     virtual ~SkV8ExampleWindow();
 
 protected:
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE;
-    void onSizeChange() SK_OVERRIDE;
+    void onDraw(SkCanvas* canvas) override;
+    void onSizeChange() override;
 
 #if SK_SUPPORT_GPU
-    SkSurface* createSurface() SK_OVERRIDE;
+    SkSurface* createSurface() override;
 #endif
 
 #ifdef SK_BUILD_FOR_WIN
-    void onHandleInval(const SkIRect&) SK_OVERRIDE;
+    void onHandleInval(const SkIRect&) override;
 #endif
 
     void windowSizeChanged();
diff --git a/experimental/StrokePathRenderer/GrStrokePathRenderer.cpp b/experimental/StrokePathRenderer/GrStrokePathRenderer.cpp
index 4b41dba..e693c41 100644
--- a/experimental/StrokePathRenderer/GrStrokePathRenderer.cpp
+++ b/experimental/StrokePathRenderer/GrStrokePathRenderer.cpp
@@ -39,8 +39,8 @@
     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));
+    res.set((SkScalarMul(pre, x3 - x4) - SkScalarMul(x1 - x2, post) / d,
+            (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) ||
@@ -184,9 +184,7 @@
                                                 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 rSq = radius*radius / sinHalfAngleSq;
                                                 const SkScalar sqRLimit =
                                                         SkScalarMul(sqMiterLimit, rSq);
                                                 if (sqDist0 > sqRLimit || sqDist1 > sqRLimit) {
diff --git a/experimental/StrokePathRenderer/GrStrokePathRenderer.h b/experimental/StrokePathRenderer/GrStrokePathRenderer.h
index 9cf34d8..85752c5 100644
--- a/experimental/StrokePathRenderer/GrStrokePathRenderer.h
+++ b/experimental/StrokePathRenderer/GrStrokePathRenderer.h
@@ -20,11 +20,11 @@
     virtual bool canDrawPath(const SkPath& path,
                              const SkStrokeRec& stroke,
                              const GrDrawTarget* target,
-                             bool antiAlias) const SK_OVERRIDE;
+                             bool antiAlias) const override;
 
 protected:
     virtual bool onDrawPath(const SkPath& path,
                             const SkStrokeRec& stroke,
                             GrDrawTarget* target,
-                            bool antiAlias) SK_OVERRIDE;
+                            bool antiAlias) override;
 };
diff --git a/experimental/iOSSampleApp/Shared/skia_ios.mm b/experimental/iOSSampleApp/Shared/skia_ios.mm
index 45675fa..b8680c4 100644
--- a/experimental/iOSSampleApp/Shared/skia_ios.mm
+++ b/experimental/iOSSampleApp/Shared/skia_ios.mm
@@ -1,3 +1,9 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 #import <UIKit/UIKit.h>
 #include "SkApplication.h"
 
@@ -5,14 +11,24 @@
     signal(SIGPIPE, SIG_IGN);
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
     application_init();
+
     // Identify the documents directory
     NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
     NSString *docsDir = [dirPaths objectAtIndex:0];
-    const char *d = [docsDir UTF8String];
-    IOS_launch_type launchType = set_cmd_line_args(argc, argv, d);
-    int retVal = launchType == kApplication__iOSLaunchType
-            ? UIApplicationMain(argc, argv, nil, nil) : (int) launchType;
+    NSString *resourceDir = [docsDir stringByAppendingString:@"/resources"];
+    const char *d = [resourceDir UTF8String];
+
+    // change to the dcouments directory. To allow the 'writePath' flag to use relative paths. 
+    NSFileManager *filemgr = [NSFileManager defaultManager];
+    int retVal = 99; 
+    if ([filemgr changeCurrentDirectoryPath: docsDir] == YES)
+    {
+        IOS_launch_type launchType = set_cmd_line_args(argc, argv, d);
+        retVal = launchType == kApplication__iOSLaunchType
+                ? UIApplicationMain(argc, argv, nil, nil) : (int) launchType;
+    }
     application_term();
+    [filemgr release];
     [pool release];
     return retVal;
 }
diff --git a/experimental/iOSSampleApp/SkSampleUIView.mm b/experimental/iOSSampleApp/SkSampleUIView.mm
index 1d27e11..c0e27d6 100644
--- a/experimental/iOSSampleApp/SkSampleUIView.mm
+++ b/experimental/iOSSampleApp/SkSampleUIView.mm
@@ -1,7 +1,14 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #import "SkSampleUIView.h"
 
-#define SKGL_CONFIG         kEAGLColorFormatRGB565
-//#define SKGL_CONFIG         kEAGLColorFormatRGBA8
+//#define SKGL_CONFIG         kEAGLColorFormatRGB565
+#define SKGL_CONFIG         kEAGLColorFormatRGBA8
 
 #define FORCE_REDRAW
 
@@ -40,7 +47,7 @@
 #endif
     }
     
-    void setUpBackend(SampleWindow* win, int msaaSampleCount) SK_OVERRIDE {
+    void setUpBackend(SampleWindow* win, int msaaSampleCount) override {
         SkASSERT(SkOSWindow::kNone_BackEndType == fBackend);
         
         fBackend = SkOSWindow::kNone_BackEndType;
@@ -101,7 +108,7 @@
         this->windowSizeChanged(win);
     }
     
-    void tearDownBackend(SampleWindow *win) SK_OVERRIDE {
+    void tearDownBackend(SampleWindow *win) override {
 #if SK_SUPPORT_GPU
         SkSafeUnref(fCurContext);
         fCurContext = NULL;
@@ -116,7 +123,7 @@
         fBackend = SampleWindow::kNone_BackEndType;
     }
 
-    SkSurface* createSurface(SampleWindow::DeviceType dType, SampleWindow* win) SK_OVERRIDE{
+    SkSurface* createSurface(SampleWindow::DeviceType dType, SampleWindow* win) override{
 #if SK_SUPPORT_GPU
         if (SampleWindow::IsGpuDeviceType(dType) && fCurContext) {
             SkSurfaceProps props(win->getSurfaceProps());
@@ -128,7 +135,7 @@
 
     virtual void publishCanvas(SampleWindow::DeviceType dType,
                                SkCanvas* canvas,
-                               SampleWindow* win) SK_OVERRIDE {
+                               SampleWindow* win) override {
 #if SK_SUPPORT_GPU
         if (NULL != fCurContext) {
             fCurContext->flush();
@@ -137,7 +144,7 @@
         win->present();
     }
     
-    void windowSizeChanged(SampleWindow* win) SK_OVERRIDE {
+    void windowSizeChanged(SampleWindow* win) override {
 #if SK_SUPPORT_GPU
         if (NULL != fCurContext) {
             SkOSWindow::AttachmentInfo info;
@@ -159,7 +166,7 @@
 #endif
     }
     
-    GrContext* getGrContext() SK_OVERRIDE {
+    GrContext* getGrContext() override {
 #if SK_SUPPORT_GPU
         return fCurContext;
 #else
@@ -167,7 +174,7 @@
 #endif
     }
     
-    GrRenderTarget* getGrRenderTarget() SK_OVERRIDE {
+    GrRenderTarget* getGrRenderTarget() override {
 #if SK_SUPPORT_GPU
         return fCurRenderTarget;
 #else
diff --git a/experimental/tools/PageCachingDocument.cpp b/experimental/tools/PageCachingDocument.cpp
index 7822d55..9baf45c 100644
--- a/experimental/tools/PageCachingDocument.cpp
+++ b/experimental/tools/PageCachingDocument.cpp
@@ -25,10 +25,10 @@
     virtual ~PageCachingDocument();
     virtual SkCanvas* onBeginPage(SkScalar width,
                                   SkScalar height,
-                                  const SkRect& content) SK_OVERRIDE;
-    void onEndPage() SK_OVERRIDE;
-    bool onClose(SkWStream*) SK_OVERRIDE;
-    void onAbort() SK_OVERRIDE;
+                                  const SkRect& content) override;
+    void onEndPage() override;
+    bool onClose(SkWStream*) override;
+    void onAbort() override;
 
 private:
     struct Page {
diff --git a/experimental/tools/SkDmuxWStream.h b/experimental/tools/SkDmuxWStream.h
index 52376c5..aac8b18 100644
--- a/experimental/tools/SkDmuxWStream.h
+++ b/experimental/tools/SkDmuxWStream.h
@@ -19,10 +19,10 @@
 public:
     SkDmuxWStream(SkWStream* const streamArray[], size_t count);
     ~SkDmuxWStream();
-    virtual bool write(const void* buffer, size_t size) SK_OVERRIDE;
-    virtual void newline() SK_OVERRIDE;
-    virtual void flush() SK_OVERRIDE;
-    virtual size_t bytesWritten() const SK_OVERRIDE;
+    virtual bool write(const void* buffer, size_t size) override;
+    virtual void newline() override;
+    virtual void flush() override;
+    virtual size_t bytesWritten() const override;
 
 private:
     SkTDArray<SkWStream*> fWStreams;
diff --git a/experimental/tools/multipage_pdf_profiler.cpp b/experimental/tools/multipage_pdf_profiler.cpp
index 337db1f..08a32a2 100644
--- a/experimental/tools/multipage_pdf_profiler.cpp
+++ b/experimental/tools/multipage_pdf_profiler.cpp
@@ -31,11 +31,11 @@
 public:
     NullWStream() : fBytesWritten(0) {
     }
-    bool write(const void*, size_t size) SK_OVERRIDE {
+    bool write(const void*, size_t size) override {
         fBytesWritten += size;
         return true;
     }
-    size_t bytesWritten() const SK_OVERRIDE {
+    size_t bytesWritten() const override {
         return fBytesWritten;
     }
     size_t fBytesWritten;
diff --git a/gm/Android.mk b/gm/Android.mk
deleted file mode 100644
index 44740a9..0000000
--- a/gm/Android.mk
+++ /dev/null
@@ -1,273 +0,0 @@
-
-###############################################################################
-#
-# THIS FILE IS AUTOGENERATED BY GYP_TO_ANDROID.PY. DO NOT EDIT.
-#
-###############################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_CFLAGS += \
-	-fPIC \
-	-Wno-c++11-extensions \
-	-Wno-unused-parameter \
-	-U_FORTIFY_SOURCE \
-	-D_FORTIFY_SOURCE=1
-
-LOCAL_CPPFLAGS := \
-	-Wno-invalid-offsetof
-
-LOCAL_SRC_FILES := \
-	gm.cpp \
-	gmmain.cpp \
-	system_preferences_default.cpp \
-	../src/pipe/utils/SamplePipeControllers.cpp \
-	aaclip.cpp \
-	aarectmodes.cpp \
-	alphagradients.cpp \
-	arcofzorro.cpp \
-	arithmode.cpp \
-	astcbitmap.cpp \
-	beziereffects.cpp \
-	beziers.cpp \
-	bigblurs.cpp \
-	bigmatrix.cpp \
-	bigtext.cpp \
-	bitmapfilters.cpp \
-	bitmappremul.cpp \
-	bitmaprect.cpp \
-	bitmaprecttest.cpp \
-	bitmapscroll.cpp \
-	bitmapshader.cpp \
-	bitmapsource.cpp \
-	bleed.cpp \
-	blurcircles.cpp \
-	blurs.cpp \
-	blurquickreject.cpp \
-	blurrect.cpp \
-	blurroundrect.cpp \
-	circles.cpp \
-	circularclips.cpp \
-	clipdrawdraw.cpp \
-	clip_strokerect.cpp \
-	clippedbitmapshaders.cpp \
-	cgms.cpp \
-	cgm.c \
-	colorcube.cpp \
-	coloremoji.cpp \
-	colorfilterimagefilter.cpp \
-	colorfilters.cpp \
-	colormatrix.cpp \
-	colortype.cpp \
-	colortypexfermode.cpp \
-	colorwheel.cpp \
-	complexclip.cpp \
-	complexclip2.cpp \
-	complexclip3.cpp \
-	composeshader.cpp \
-	conicpaths.cpp \
-	convexpaths.cpp \
-	convexpolyclip.cpp \
-	convexpolyeffect.cpp \
-	copyTo4444.cpp \
-	cubicpaths.cpp \
-	cmykjpeg.cpp \
-	degeneratesegments.cpp \
-	dcshader.cpp \
-	discard.cpp \
-	dashcubics.cpp \
-	dashing.cpp \
-	distantclip.cpp \
-	dftext.cpp \
-	displacement.cpp \
-	downsamplebitmap.cpp \
-	drawlooper.cpp \
-	dropshadowimagefilter.cpp \
-	drrect.cpp \
-	etc1bitmap.cpp \
-	extractbitmap.cpp \
-	emboss.cpp \
-	emptypath.cpp \
-	fatpathfill.cpp \
-	factory.cpp \
-	filltypes.cpp \
-	filltypespersp.cpp \
-	filterbitmap.cpp \
-	filterfastbounds.cpp \
-	filterindiabox.cpp \
-	fontcache.cpp \
-	fontmgr.cpp \
-	fontscaler.cpp \
-	gammatext.cpp \
-	getpostextpath.cpp \
-	giantbitmap.cpp \
-	glyph_pos.cpp \
-	glyph_pos_align.cpp \
-	gradients.cpp \
-	gradients_2pt_conical.cpp \
-	gradients_no_texture.cpp \
-	gradientDirtyLaundry.cpp \
-	gradient_matrix.cpp \
-	gradtext.cpp \
-	grayscalejpg.cpp \
-	hairlines.cpp \
-	hairmodes.cpp \
-	hittestpath.cpp \
-	imagealphathreshold.cpp \
-	imageblur.cpp \
-	imageblur2.cpp \
-	imageblurtiled.cpp \
-	imagemagnifier.cpp \
-	imageresizetiled.cpp \
-	inversepaths.cpp \
-	lerpmode.cpp \
-	lighting.cpp \
-	lumafilter.cpp \
-	image.cpp \
-	imagefiltersbase.cpp \
-	imagefiltersclipped.cpp \
-	imagefilterscropped.cpp \
-	imagefiltersgraph.cpp \
-	imagefiltersscaled.cpp \
-	internal_links.cpp \
-	lcdtext.cpp \
-	linepaths.cpp \
-	matrixconvolution.cpp \
-	matriximagefilter.cpp \
-	megalooper.cpp \
-	mixedxfermodes.cpp \
-	modecolorfilters.cpp \
-	morphology.cpp \
-	multipicturedraw.cpp \
-	nested.cpp \
-	ninepatchstretch.cpp \
-	nonclosedpaths.cpp \
-	offsetimagefilter.cpp \
-	ovals.cpp \
-	patch.cpp \
-	patchgrid.cpp \
-	patheffects.cpp \
-	pathfill.cpp \
-	pathinterior.cpp \
-	pathopsinverse.cpp \
-	pathopsskpclip.cpp \
-	pathreverse.cpp \
-	peekpixels.cpp \
-	perlinnoise.cpp \
-	picture.cpp \
-	pictureimagefilter.cpp \
-	pictureshader.cpp \
-	pictureshadertile.cpp \
-	points.cpp \
-	poly2poly.cpp \
-	polygons.cpp \
-	quadpaths.cpp \
-	rects.cpp \
-	resizeimagefilter.cpp \
-	rrect.cpp \
-	rrects.cpp \
-	roundrects.cpp \
-	samplerstress.cpp \
-	shaderbounds.cpp \
-	selftest.cpp \
-	shadows.cpp \
-	shallowgradient.cpp \
-	simpleaaclip.cpp \
-	skbug1719.cpp \
-	smallarc.cpp \
-	smallimage.cpp \
-	stringart.cpp \
-	spritebitmap.cpp \
-	srcmode.cpp \
-	strokefill.cpp \
-	strokerect.cpp \
-	strokerects.cpp \
-	strokes.cpp \
-	stroketext.cpp \
-	surface.cpp \
-	tablecolorfilter.cpp \
-	texteffects.cpp \
-	testimagefilters.cpp \
-	texdata.cpp \
-	variedtext.cpp \
-	tallstretchedbitmaps.cpp \
-	textblob.cpp \
-	textblobshader.cpp \
-	texturedomaineffect.cpp \
-	thinrects.cpp \
-	thinstrokedrects.cpp \
-	tiledscaledbitmap.cpp \
-	tileimagefilter.cpp \
-	tilemodes.cpp \
-	tilemodes_scaled.cpp \
-	tinybitmap.cpp \
-	transparency.cpp \
-	twopointradial.cpp \
-	typeface.cpp \
-	vertices.cpp \
-	verttext.cpp \
-	verttext2.cpp \
-	xfermodeimagefilter.cpp \
-	xfermodes.cpp \
-	xfermodes2.cpp \
-	xfermodes3.cpp \
-	yuvtorgbeffect.cpp \
-	../src/utils/debugger/SkDrawCommand.cpp \
-	../src/utils/debugger/SkDebugCanvas.cpp \
-	../src/utils/debugger/SkObjectParser.cpp \
-	../tools/AndroidSkDebugToStdOut.cpp \
-	../tools/flags/SkCommandLineFlags.cpp \
-	../tools/CrashHandler.cpp \
-	gm_expectations.cpp \
-	../tools/ProcStats.cpp \
-	../tools/Resources.cpp \
-	../tools/sk_tool_utils.cpp \
-	../tools/sk_tool_utils_font.cpp \
-	../src/gpu/GrContextFactory.cpp \
-	../src/gpu/GrTest.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	liblog \
-	libskia \
-	libGLESv2 \
-	libEGL \
-	libz
-
-LOCAL_STATIC_LIBRARIES := \
-	libjsoncpp
-
-LOCAL_C_INCLUDES := \
-	$(LOCAL_PATH)/../include/c \
-	$(LOCAL_PATH)/../include/config \
-	$(LOCAL_PATH)/../include/core \
-	$(LOCAL_PATH)/../include/pathops \
-	$(LOCAL_PATH)/../include/pipe \
-	$(LOCAL_PATH)/../include/effects \
-	$(LOCAL_PATH)/../include/images \
-	$(LOCAL_PATH)/../include/ports \
-	$(LOCAL_PATH)/../src/sfnt \
-	$(LOCAL_PATH)/../include/utils \
-	$(LOCAL_PATH)/../src/utils \
-	$(LOCAL_PATH)/../include/gpu \
-	$(LOCAL_PATH)/../src/core \
-	$(LOCAL_PATH)/../tools \
-	$(LOCAL_PATH)/../tools/flags \
-	$(LOCAL_PATH)/../src/fonts \
-	$(LOCAL_PATH)/../src/gpu \
-	$(LOCAL_PATH)/../src/effects \
-	$(LOCAL_PATH)/../src/images \
-	$(LOCAL_PATH)/../src/pipe/utils \
-	$(LOCAL_PATH)/../src/utils/debugger \
-	$(LOCAL_PATH)/../src/lazy \
-	$(LOCAL_PATH)/../third_party/etc1 \
-	$(LOCAL_PATH)/../include/pdf
-
-LOCAL_MODULE_TAGS := \
-	tests
-
-LOCAL_MODULE := \
-	skia_gm
-
-LOCAL_PICKUP_FILES := $(LOCAL_PATH)/DATA
-
-include $(BUILD_NATIVE_TEST)
diff --git a/gm/aaclip.cpp b/gm/aaclip.cpp
index e977f5f..8a0084c 100644
--- a/gm/aaclip.cpp
+++ b/gm/aaclip.cpp
@@ -71,15 +71,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("aaclip");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(240, 120);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         // Initial pixel-boundary-aligned draw
         draw_rect_tests(canvas);
 
@@ -154,15 +154,15 @@
     CGImageGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("cgimage");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(800, 250);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         const struct {
             SkColorType fCT;
             SkAlphaType fAT;
@@ -193,3 +193,72 @@
 DEF_GM( return SkNEW(CGImageGM); )
 #endif
 #endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+// skbug.com/3716
+class ClipCubicGM : public skiagm::GM {
+    const SkScalar W = 100;
+    const SkScalar H = 240;
+
+    SkPath fVPath, fHPath;
+public:
+    ClipCubicGM() {
+        fVPath.moveTo(W, 0);
+        fVPath.cubicTo(W, H-10, 0, 10, 0, H);
+    
+        SkMatrix pivot;
+        pivot.setRotate(90, W/2, H/2);
+        fVPath.transform(pivot, &fHPath);
+    }
+
+protected:
+    SkString onShortName() override {
+        return SkString("clipcubic");
+    }
+    
+    SkISize onISize() override {
+        return SkISize::Make(400, 410);
+    }
+
+    void doDraw(SkCanvas* canvas, const SkPath& path) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        
+        paint.setColor(0xFFCCCCCC);
+        canvas->drawPath(path, paint);
+        
+        paint.setColor(SK_ColorRED);
+        paint.setStyle(SkPaint::kStroke_Style);
+        canvas->drawPath(path, paint);
+    }
+
+    void drawAndClip(SkCanvas* canvas, const SkPath& path, SkScalar dx, SkScalar dy) {
+        SkAutoCanvasRestore acr(canvas, true);
+
+        SkRect r = SkRect::MakeXYWH(0, H/4, W, H/2);
+        SkPaint paint;
+        paint.setColor(0xFF8888FF);
+
+        canvas->drawRect(r, paint);
+        this->doDraw(canvas, path);
+        
+        canvas->translate(dx, dy);
+
+        canvas->drawRect(r, paint);
+        canvas->clipRect(r);
+        this->doDraw(canvas, path);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        canvas->translate(80, 10);
+        this->drawAndClip(canvas, fVPath, 200, 0);
+        canvas->translate(0, 200);
+        this->drawAndClip(canvas, fHPath, 200, 0);
+    }
+    
+private:
+    typedef skiagm::GM INHERITED;
+};
+DEF_GM( return SkNEW(ClipCubicGM); )
+
diff --git a/gm/aarectmodes.cpp b/gm/aarectmodes.cpp
index 83456a0..205876b 100644
--- a/gm/aarectmodes.cpp
+++ b/gm/aarectmodes.cpp
@@ -113,8 +113,7 @@
     *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = SkPackARGB32(0xFF, 0xCC,
                                                              0xCC, 0xCC);
 
-    SkMatrix m;
-    m.setScale(SkIntToScalar(6), SkIntToScalar(6));
+    const SkMatrix m = SkMatrix::MakeScale(SkIntToScalar(6), SkIntToScalar(6));
     SkShader* s = SkShader::CreateBitmapShader(bm,
                                                SkShader::kRepeat_TileMode,
                                                SkShader::kRepeat_TileMode,
@@ -134,13 +133,13 @@
 
     protected:
 
-        SkString onShortName() SK_OVERRIDE {
+        SkString onShortName() override {
             return SkString("aarectmodes");
         }
 
-        SkISize onISize() SK_OVERRIDE { return SkISize::Make(640, 480); }
+        SkISize onISize() override { return SkISize::Make(640, 480); }
 
-        void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        void onDraw(SkCanvas* canvas) override {
             if (false) { // avoid bit rot, suppress warning
                 test4(canvas);
             }
diff --git a/gm/addarc.cpp b/gm/addarc.cpp
index 8bdbca3..742f16f 100644
--- a/gm/addarc.cpp
+++ b/gm/addarc.cpp
@@ -16,11 +16,11 @@
     AddArcGM() : fRotate(0) {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE { return SkString("addarc"); }
+    SkString onShortName() override { return SkString("addarc"); }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(1040, 1040); }
+    SkISize onISize() override { return SkISize::Make(1040, 1040); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(20, 20);
 
         SkRect r = SkRect::MakeWH(1000, 1000);
@@ -51,7 +51,7 @@
         }
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         fRotate = timer.scaled(1, 360);
         return true;
     }
@@ -71,11 +71,11 @@
     AddArcMeasGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE { return SkString("addarc_meas"); }
+    SkString onShortName() override { return SkString("addarc_meas"); }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(2*R + 40, 2*R + 40); }
+    SkISize onISize() override { return SkISize::Make(2*R + 40, 2*R + 40); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(R + 20, R + 20);
 
         SkPaint paint;
@@ -122,11 +122,11 @@
     StrokeCircleGM() : fRotate(0) {}
     
 protected:
-    SkString onShortName() SK_OVERRIDE { return SkString("strokecircle"); }
+    SkString onShortName() override { return SkString("strokecircle"); }
     
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(520, 520); }
+    SkISize onISize() override { return SkISize::Make(520, 520); }
     
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->scale(20, 20);
         canvas->translate(13, 13);
 
@@ -151,7 +151,7 @@
         }
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         fRotate = timer.scaled(60, 360);
         return true;
     }
@@ -178,11 +178,11 @@
     ManyArcsGM() {}
     
 protected:
-    SkString onShortName() SK_OVERRIDE { return SkString("manyarcs"); }
+    SkString onShortName() override { return SkString("manyarcs"); }
     
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(620, 330); }
+    SkISize onISize() override { return SkISize::Make(620, 330); }
     
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setStyle(SkPaint::kStroke_Style);
diff --git a/gm/all_bitmap_configs.cpp b/gm/all_bitmap_configs.cpp
new file mode 100644
index 0000000..7ff8675
--- /dev/null
+++ b/gm/all_bitmap_configs.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "sk_tool_utils.h"
+#include "SkSurface.h"
+#include "Resources.h"
+#include "gm.h"
+
+#include "SkMath.h"
+#include "SkColorPriv.h"
+
+static SkBitmap copy_bitmap(const SkBitmap& src, SkColorType colorType) {
+    SkBitmap copy;
+    src.copyTo(&copy, colorType);
+    copy.setImmutable();
+    return copy;
+}
+
+#define SCALE 128
+
+// Make either A8 or gray8 bitmap.
+static SkBitmap make_bitmap(SkColorType ct) {
+    SkBitmap bm;
+    switch (ct) {
+        case kAlpha_8_SkColorType:
+            bm.allocPixels(SkImageInfo::MakeA8(SCALE, SCALE));
+            break;
+        case kGray_8_SkColorType:
+            bm.allocPixels(
+                    SkImageInfo::Make(SCALE, SCALE, ct, kOpaque_SkAlphaType));
+            break;
+        default:
+            SkASSERT(false);
+            return bm;
+    }
+    SkAutoLockPixels autoLockPixels(bm);
+    uint8_t spectrum[256];
+    for (int y = 0; y < 256; ++y) {
+        spectrum[y] = y;
+    }
+    for (int y = 0; y < 128; ++y) {
+        // Shift over one byte each scanline.
+        memcpy(bm.getAddr8(0, y), &spectrum[y], 128);
+    }
+    bm.setImmutable();
+    return bm;
+}
+
+static void draw_center_letter(char c,
+                               SkPaint* p,
+                               SkColor color,
+                               SkScalar x,
+                               SkScalar y,
+                               SkCanvas* canvas) {
+    SkRect bounds;
+    p->setColor(color);
+    p->measureText(&c, 1, &bounds);
+    canvas->drawText(&c, 1, x - bounds.centerX(), y - bounds.centerY(), *p);
+}
+
+static void color_wheel_native(SkCanvas* canvas) {
+    SkAutoCanvasRestore autoCanvasRestore(canvas, true);
+    canvas->translate(0.5f * SCALE, 0.5f * SCALE);
+    SkPaint p;
+    p.setAntiAlias(false);
+    p.setColor(SK_ColorWHITE);
+    canvas->drawCircle(0.0f, 0.0f, SCALE * 0.5f, p);
+
+    const double sqrt_3_over_2 = 0.8660254037844387;
+    const SkScalar Z = 0.0f;
+    const SkScalar D = 0.3f * SkIntToScalar(SCALE);
+    const SkScalar X = SkDoubleToScalar(D * sqrt_3_over_2);
+    const SkScalar Y = D * SK_ScalarHalf;
+    sk_tool_utils::set_portable_typeface(&p, NULL, SkTypeface::kBold);
+    p.setTextSize(0.28125f * SCALE);
+    draw_center_letter('K', &p, SK_ColorBLACK, Z, Z, canvas);
+    draw_center_letter('R', &p, SK_ColorRED, Z, D, canvas);
+    draw_center_letter('G', &p, SK_ColorGREEN, -X, -Y, canvas);
+    draw_center_letter('B', &p, SK_ColorBLUE, X, -Y, canvas);
+    draw_center_letter('C', &p, SK_ColorCYAN, Z, -D, canvas);
+    draw_center_letter('M', &p, SK_ColorMAGENTA, X, Y, canvas);
+    draw_center_letter('Y', &p, SK_ColorYELLOW, -X, Y, canvas);
+}
+
+template <typename T>
+int find(T* array, int N, T item) {
+    for (int i = 0; i < N; ++i) {
+        if (array[i] == item) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static SkPMColor premultiply_color(SkColor c) {
+    return SkPremultiplyARGBInline(SkColorGetA(c), SkColorGetR(c),
+                                   SkColorGetG(c), SkColorGetB(c));
+}
+
+static SkBitmap indexed_bitmap() {
+    SkBitmap n32bitmap;
+    n32bitmap.allocN32Pixels(SCALE, SCALE);
+    n32bitmap.eraseColor(SK_ColorTRANSPARENT);
+
+    SkCanvas canvas(n32bitmap);
+    color_wheel_native(&canvas);
+    const SkColor colors[] = {
+            SK_ColorTRANSPARENT,
+            SK_ColorWHITE,
+            SK_ColorBLACK,
+            SK_ColorRED,
+            SK_ColorGREEN,
+            SK_ColorBLUE,
+            SK_ColorCYAN,
+            SK_ColorMAGENTA,
+            SK_ColorYELLOW,
+    };
+    SkPMColor pmColors[SK_ARRAY_COUNT(colors)];
+    for (size_t i = 0; i < SK_ARRAY_COUNT(colors); ++i) {
+        pmColors[i] = premultiply_color(colors[i]);
+    }
+    SkBitmap bm;
+    SkAutoTUnref<SkColorTable> ctable(
+            SkNEW_ARGS(SkColorTable, (pmColors, SK_ARRAY_COUNT(pmColors))));
+    SkImageInfo info = SkImageInfo::Make(SCALE, SCALE, kIndex_8_SkColorType,
+                                         kPremul_SkAlphaType);
+    bm.allocPixels(info, NULL, ctable);
+    SkAutoLockPixels autoLockPixels1(n32bitmap);
+    SkAutoLockPixels autoLockPixels2(bm);
+    for (int y = 0; y < SCALE; ++y) {
+        for (int x = 0; x < SCALE; ++x) {
+            SkPMColor c = *n32bitmap.getAddr32(x, y);
+            int idx = find(pmColors, SK_ARRAY_COUNT(pmColors), c);
+            *bm.getAddr8(x, y) = SkClampMax(idx, SK_ARRAY_COUNT(pmColors) - 1);
+        }
+    }
+    return bm;
+}
+
+static void draw(SkCanvas* canvas,
+                 const SkPaint& p,
+                 const SkBitmap& src,
+                 SkColorType colorType,
+                 const char text[]) {
+    SkASSERT(src.colorType() == colorType);
+    canvas->drawBitmap(src, 0.0f, 0.0f);
+    canvas->drawText(text, strlen(text), 0.0f, 12.0f, p);
+}
+
+DEF_SIMPLE_GM(all_bitmap_configs, canvas, SCALE, 6 * SCALE) {
+    SkAutoCanvasRestore autoCanvasRestore(canvas, true);
+    SkPaint p;
+    p.setColor(SK_ColorBLACK);
+    p.setAntiAlias(true);
+    sk_tool_utils::set_portable_typeface(&p, NULL);
+
+    sk_tool_utils::draw_checkerboard(canvas, SK_ColorLTGRAY, SK_ColorWHITE, 8);
+
+    SkBitmap bitmap;
+    if (GetResourceAsBitmap("color_wheel.png", &bitmap)) {
+        bitmap.setImmutable();
+        draw(canvas, p, bitmap, kN32_SkColorType, "Native 32");
+
+        canvas->translate(0.0f, SkIntToScalar(SCALE));
+        SkBitmap copy565 = copy_bitmap(bitmap, kRGB_565_SkColorType);
+        p.setColor(SK_ColorRED);
+        draw(canvas, p, copy565, kRGB_565_SkColorType, "RGB 565");
+        p.setColor(SK_ColorBLACK);
+
+        canvas->translate(0.0f, SkIntToScalar(SCALE));
+        SkBitmap copy4444 = copy_bitmap(bitmap, kARGB_4444_SkColorType);
+        draw(canvas, p, copy4444, kARGB_4444_SkColorType, "ARGB 4444");
+    } else {
+        canvas->translate(0.0f, SkIntToScalar(2 * SCALE));
+    }
+
+    canvas->translate(0.0f, SkIntToScalar(SCALE));
+    SkBitmap bitmapIndexed = indexed_bitmap();
+    draw(canvas, p, bitmapIndexed, kIndex_8_SkColorType, "Index 8");
+
+    canvas->translate(0.0f, SkIntToScalar(SCALE));
+    SkBitmap bitmapA8 = make_bitmap(kAlpha_8_SkColorType);
+    draw(canvas, p, bitmapA8, kAlpha_8_SkColorType, "Alpha 8");
+
+    p.setColor(SK_ColorRED);
+    canvas->translate(0.0f, SkIntToScalar(SCALE));
+    SkBitmap bitmapG8 = make_bitmap(kGray_8_SkColorType);
+    draw(canvas, p, bitmapG8, kGray_8_SkColorType, "Gray 8");
+}
diff --git a/gm/alphagradients.cpp b/gm/alphagradients.cpp
index 304c523..426acaa 100644
--- a/gm/alphagradients.cpp
+++ b/gm/alphagradients.cpp
@@ -14,11 +14,11 @@
     AlphaGradientsGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("alphagradients");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
@@ -38,7 +38,7 @@
         canvas->drawRect(r, paint);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         static const struct {
             SkColor fColor0;
             SkColor fColor1;
diff --git a/gm/anisotropic.cpp b/gm/anisotropic.cpp
new file mode 100644
index 0000000..a004d75
--- /dev/null
+++ b/gm/anisotropic.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2015 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"
+
+namespace skiagm {
+
+// This GM exercises HighQuality anisotropic filtering.
+class AnisotropicGM : public GM {
+public:
+    AnisotropicGM() : fFilterQuality(kHigh_SkFilterQuality) {
+        this->setBGColor(0xFFCCCCCC);
+    }
+
+protected:
+
+    SkString onShortName() override { return SkString("anisotropic_hq"); }
+
+    SkISize onISize() override {
+        return SkISize::Make(2*kImageSize + 3*kSpacer,
+                             kNumVertImages*kImageSize + (kNumVertImages+1)*kSpacer);
+    }
+
+    // Create an image consisting of lines radiating from its center
+    void onOnceBeforeDraw() override {
+        static const int kNumLines = 100;
+        static const SkScalar kAngleStep = 360.0f / kNumLines;
+        static const int kInnerOffset = 10;
+
+        fBM.allocN32Pixels(kImageSize, kImageSize, true);
+
+        SkCanvas canvas(fBM);
+
+        canvas.clear(SK_ColorWHITE);
+
+        SkPaint p;
+        p.setAntiAlias(true);
+
+        SkScalar angle = 0.0f, sin, cos;
+
+        canvas.translate(kImageSize/2.0f, kImageSize/2.0f);
+        for (int i = 0; i < kNumLines; ++i, angle += kAngleStep) {
+            sin = SkScalarSinCos(angle, &cos);
+            canvas.drawLine(cos * kInnerOffset, sin * kInnerOffset,
+                            cos * kImageSize/2, sin * kImageSize/2, p);
+        }
+    }
+
+    void draw(SkCanvas* canvas, int x, int y, int xSize, int ySize) {
+        SkRect r = SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
+                                    SkIntToScalar(xSize), SkIntToScalar(ySize));
+        SkPaint p;
+        p.setFilterQuality(fFilterQuality);
+        canvas->drawBitmapRect(fBM, r, &p);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        SkScalar gScales[] = { 0.9f, 0.8f, 0.75f, 0.6f, 0.5f, 0.4f, 0.25f, 0.2f, 0.1f };
+        
+        SkASSERT(kNumVertImages-1 == (int)SK_ARRAY_COUNT(gScales)/2);
+
+        // Minimize vertically
+        for (int i = 0; i < (int)SK_ARRAY_COUNT(gScales); ++i) {
+            int height = SkScalarFloorToInt(fBM.height() * gScales[i]);
+
+            int yOff;
+            if (i <= (int)SK_ARRAY_COUNT(gScales)/2) {
+                yOff = kSpacer + i * (fBM.height() + kSpacer);
+            } else {
+                // Position the more highly squashed images with their less squashed counterparts
+                yOff = (SK_ARRAY_COUNT(gScales) - i) * (fBM.height() + kSpacer) - height;
+            }
+
+            this->draw(canvas, kSpacer, yOff, fBM.width(), height);
+        }
+
+        // Minimize horizontally
+        for (int i = 0; i < (int)SK_ARRAY_COUNT(gScales); ++i) {
+            int width = SkScalarFloorToInt(fBM.width() * gScales[i]);
+
+            int xOff, yOff;
+            if (i <= (int)SK_ARRAY_COUNT(gScales)/2) {
+                xOff = fBM.width() + 2*kSpacer;
+                yOff = kSpacer + i * (fBM.height() + kSpacer);
+            } else {
+                // Position the more highly squashed images with their less squashed counterparts
+                xOff = fBM.width() + 2*kSpacer + fBM.width() - width;
+                yOff = kSpacer + (SK_ARRAY_COUNT(gScales) - i - 1) * (fBM.height() + kSpacer);
+            }
+
+            this->draw(canvas, xOff, yOff, width, fBM.height());
+        }
+    }
+
+private:
+    static const int kImageSize     = 256;
+    static const int kSpacer        = 10;
+    static const int kNumVertImages = 5;
+
+    SkBitmap         fBM;
+    SkFilterQuality  fFilterQuality;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(AnisotropicGM); )
+
+}
diff --git a/gm/arcofzorro.cpp b/gm/arcofzorro.cpp
index 9aa4bb4..ef2a36b 100644
--- a/gm/arcofzorro.cpp
+++ b/gm/arcofzorro.cpp
@@ -21,15 +21,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("arcofzorro");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1000, 1000);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkRandom rand;
 
         SkRect rect = SkRect::MakeXYWH(10, 10, 200, 200);
diff --git a/gm/astcbitmap.cpp b/gm/astcbitmap.cpp
index 808b23e..316bf88 100644
--- a/gm/astcbitmap.cpp
+++ b/gm/astcbitmap.cpp
@@ -53,15 +53,15 @@
     virtual ~ASTCBitmapGM() { }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("astcbitmap");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(kGMDimension, kGMDimension);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         for (int j = 0; j < 4; ++j) {
             for (int i = 0; i < 4; ++i) {
                 SkString filename = GetResourcePath(get_astc_filename(j*4+i));
diff --git a/gm/badpaint.cpp b/gm/badpaint.cpp
new file mode 100644
index 0000000..cce8146
--- /dev/null
+++ b/gm/badpaint.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 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 "SkShader.h"
+
+
+/** This GM draws with invalid paints. It should draw nothing other than the background. */
+class BadPaintGM : public skiagm::GM {
+ public:
+    BadPaintGM() {}
+
+protected:
+    SkString onShortName() override { return SkString("badpaint"); }
+
+    SkISize onISize() override { return SkISize::Make(100, 100); }
+
+    void onOnceBeforeDraw() override {
+        SkBitmap emptyBmp;
+
+        SkBitmap blueBmp;
+        blueBmp.allocN32Pixels(10, 10);
+        blueBmp.eraseColor(SK_ColorBLUE);
+
+        SkMatrix badMatrix;
+        badMatrix.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+        // Empty bitmap.
+        fPaints.push_back().setColor(SK_ColorGREEN);
+        fPaints.back().setShader(SkShader::CreateBitmapShader(emptyBmp, SkShader::kClamp_TileMode,
+                                                              SkShader::kClamp_TileMode))->unref();
+
+        // Non-invertible local matrix.
+        fPaints.push_back().setColor(SK_ColorGREEN);
+        fPaints.back().setShader(SkShader::CreateBitmapShader(blueBmp, SkShader::kClamp_TileMode,
+                                                              SkShader::kClamp_TileMode,
+                                                              &badMatrix))->unref();
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        SkRect rect = SkRect::MakeXYWH(10, 10, 80, 80);
+        for (int i = 0; i < fPaints.count(); ++i) {
+            canvas->drawRect(rect, fPaints[i]);
+        }
+    }
+
+private:
+    SkTArray<SkPaint> fPaints;
+
+    typedef skiagm::GM INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(BadPaintGM); )
diff --git a/gm/beziereffects.cpp b/gm/beziereffects.cpp
index d86a8b8..5070eea 100644
--- a/gm/beziereffects.cpp
+++ b/gm/beziereffects.cpp
@@ -13,7 +13,6 @@
 #if SK_SUPPORT_GPU
 
 #include "GrBatchTarget.h"
-#include "GrBufferAllocPool.h"
 #include "GrContext.h"
 #include "GrPathUtils.h"
 #include "GrTest.h"
@@ -36,7 +35,7 @@
         SkRect fBounds;
     };
 
-    const char* name() const SK_OVERRIDE { return "BezierCubicOrConicTestBatch"; }
+    const char* name() const override { return "BezierCubicOrConicTestBatch"; }
 
     static GrBatch* Create(const GrGeometryProcessor* gp, const Geometry& geo,
                            const SkScalar klmEqs[9], SkScalar sign) {
@@ -46,7 +45,7 @@
 private:
     BezierCubicOrConicTestBatch(const GrGeometryProcessor* gp, const Geometry& geo,
                                 const SkScalar klmEqs[9], SkScalar sign)
-        : INHERITED(gp) {
+        : INHERITED(gp, geo.fBounds) {
         for (int i = 0; i < 9; i++) {
             fKlmEqs[i] = klmEqs[i];
         }
@@ -60,30 +59,25 @@
         float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
     };
 
-    Geometry* geoData(int index) SK_OVERRIDE {
+    Geometry* geoData(int index) override {
         SkASSERT(0 == index);
         return &fGeometry;
     }
 
-    void onGenerateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    const Geometry* geoData(int index) const override {
+        SkASSERT(0 == index);
+        return &fGeometry;
+    }
+
+    void onGenerateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
+        QuadHelper helper;
         size_t vertexStride = this->geometryProcessor()->getVertexStride();
-
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
-
-        void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              kVertsPerCubic,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices || !batchTarget->quadIndexBuffer()) {
-            SkDebugf("Could not allocate buffers\n");
+        SkASSERT(vertexStride == sizeof(Vertex));
+        Vertex* verts = reinterpret_cast<Vertex*>(helper.init(batchTarget, vertexStride, 1));
+        if (!verts) {
             return;
         }
 
-        SkASSERT(vertexStride == sizeof(Vertex));
-        Vertex* verts = reinterpret_cast<Vertex*>(vertices);
-
         verts[0].fPosition.setRectFan(fGeometry.fBounds.fLeft, fGeometry.fBounds.fTop,
                                       fGeometry.fBounds.fRight, fGeometry.fBounds.fBottom,
                                       sizeof(Vertex));
@@ -92,16 +86,7 @@
             verts[v].fKLM[1] = eval_line(verts[v].fPosition, fKlmEqs + 3, fSign);
             verts[v].fKLM[2] = eval_line(verts[v].fPosition, fKlmEqs + 6, 1.f);
         }
-
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangleFan_GrPrimitiveType);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setStartVertex(firstVertex);
-        drawInfo.setVertexCount(kVertsPerCubic);
-        drawInfo.setStartIndex(0);
-        drawInfo.setIndexCount(kIndicesPerCubic);
-        drawInfo.setIndexBuffer(batchTarget->quadIndexBuffer());
-        batchTarget->draw(drawInfo);
+        helper.issueDraw(batchTarget);
     }
 
     Geometry fGeometry;
@@ -124,15 +109,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("bezier_cubic_effects");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(800, 800);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         if (NULL == rt) {
             this->drawGpuOnlyMessage(canvas);
@@ -158,6 +143,7 @@
         SkScalar h = SkIntToScalar(rt->height()) / numRows;
         int row = 0;
         int col = 0;
+        static const GrColor color = 0xff000000;
 
         for (int i = 0; i < kNumCubics; ++i) {
             SkPoint baseControlPts[] = {
@@ -175,7 +161,7 @@
                         continue;
                     }
                     GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
-                    gp.reset(GrCubicEffect::Create(0xff000000, SkMatrix::I(), et,
+                    gp.reset(GrCubicEffect::Create(color, SkMatrix::I(), et,
                                                    *tt.target()->caps()));
                     if (!gp) {
                         continue;
@@ -237,13 +223,13 @@
                     pipelineBuilder.setRenderTarget(rt);
 
                     BezierCubicOrConicTestBatch::Geometry geometry;
-                    geometry.fColor = gp->color();
+                    geometry.fColor = color;
                     geometry.fBounds = bounds;
 
                     SkAutoTUnref<GrBatch> batch(
                             BezierCubicOrConicTestBatch::Create(gp, geometry, klmEqs, klmSigns[c]));
 
-                    tt.target()->drawBatch(&pipelineBuilder, batch, NULL);
+                    tt.target()->drawBatch(&pipelineBuilder, batch);
                 }
                 ++col;
                 if (numCols == col) {
@@ -270,16 +256,16 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("bezier_conic_effects");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(800, 800);
     }
 
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         if (NULL == rt) {
             this->drawGpuOnlyMessage(canvas);
@@ -305,6 +291,7 @@
         SkScalar h = SkIntToScalar(rt->height()) / numRows;
         int row = 0;
         int col = 0;
+        static const GrColor color = 0xff000000;
 
         for (int i = 0; i < kNumConics; ++i) {
             SkPoint baseControlPts[] = {
@@ -322,7 +309,7 @@
                         continue;
                     }
                     GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
-                    gp.reset(GrConicEffect::Create(0xff000000, SkMatrix::I(), et,
+                    gp.reset(GrConicEffect::Create(color, SkMatrix::I(), et,
                                                    *tt.target()->caps(), SkMatrix::I()));
                     if (!gp) {
                         continue;
@@ -381,13 +368,13 @@
                     pipelineBuilder.setRenderTarget(rt);
 
                     BezierCubicOrConicTestBatch::Geometry geometry;
-                    geometry.fColor = gp->color();
+                    geometry.fColor = color;
                     geometry.fBounds = bounds;
 
                     SkAutoTUnref<GrBatch> batch(
                             BezierCubicOrConicTestBatch::Create(gp, geometry, klmEqs, 1.f));
 
-                    tt.target()->drawBatch(&pipelineBuilder, batch, NULL);
+                    tt.target()->drawBatch(&pipelineBuilder, batch);
                 }
                 ++col;
                 if (numCols == col) {
@@ -447,7 +434,7 @@
         SkRect fBounds;
     };
 
-    const char* name() const SK_OVERRIDE { return "BezierQuadTestBatch"; }
+    const char* name() const override { return "BezierQuadTestBatch"; }
 
     static GrBatch* Create(const GrGeometryProcessor* gp, const Geometry& geo,
                            const GrPathUtils::QuadUVMatrix& devToUV) {
@@ -457,7 +444,7 @@
 private:
     BezierQuadTestBatch(const GrGeometryProcessor* gp, const Geometry& geo,
                         const GrPathUtils::QuadUVMatrix& devToUV)
-        : INHERITED(gp)
+        : INHERITED(gp, geo.fBounds)
         , fGeometry(geo)
         , fDevToUV(devToUV) {
     }
@@ -467,46 +454,29 @@
         float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
     };
 
-    Geometry* geoData(int index) SK_OVERRIDE {
+    Geometry* geoData(int index) override {
         SkASSERT(0 == index);
         return &fGeometry;
     }
 
-    void onGenerateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    const Geometry* geoData(int index) const override {
+        SkASSERT(0 == index);
+        return &fGeometry;
+    }
+
+    void onGenerateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
+        QuadHelper helper;
         size_t vertexStride = this->geometryProcessor()->getVertexStride();
-
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
-
-        void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              kVertsPerCubic,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices || !batchTarget->quadIndexBuffer()) {
-            SkDebugf("Could not allocate buffers\n");
+        SkASSERT(vertexStride == sizeof(Vertex));
+        Vertex* verts = reinterpret_cast<Vertex*>(helper.init(batchTarget, vertexStride, 1));
+        if (!verts) {
             return;
         }
-
-        SkASSERT(vertexStride == sizeof(Vertex));
-        Vertex* verts = reinterpret_cast<Vertex*>(vertices);
-
         verts[0].fPosition.setRectFan(fGeometry.fBounds.fLeft, fGeometry.fBounds.fTop,
                                       fGeometry.fBounds.fRight, fGeometry.fBounds.fBottom,
                                       sizeof(Vertex));
-
         fDevToUV.apply<4, sizeof(Vertex), sizeof(SkPoint)>(verts);
-
-
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setStartVertex(firstVertex);
-        drawInfo.setVertexCount(kVertsPerCubic);
-        drawInfo.setStartIndex(0);
-        drawInfo.setIndexCount(kIndicesPerCubic);
-        drawInfo.setIndexBuffer(batchTarget->quadIndexBuffer());
-        batchTarget->draw(drawInfo);
+        helper.issueDraw(batchTarget);
     }
 
     Geometry fGeometry;
@@ -528,16 +498,16 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("bezier_quad_effects");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(800, 800);
     }
 
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         if (NULL == rt) {
             this->drawGpuOnlyMessage(canvas);
@@ -562,6 +532,7 @@
         SkScalar h = SkIntToScalar(rt->height()) / numRows;
         int row = 0;
         int col = 0;
+        static const GrColor color = 0xff000000;
 
         for (int i = 0; i < kNumQuads; ++i) {
             SkPoint baseControlPts[] = {
@@ -578,7 +549,7 @@
                         continue;
                     }
                     GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
-                    gp.reset(GrQuadEffect::Create(0xff000000, SkMatrix::I(), et,
+                    gp.reset(GrQuadEffect::Create(color, SkMatrix::I(), et,
                                                   *tt.target()->caps(), SkMatrix::I()));
                     if (!gp) {
                         continue;
@@ -636,12 +607,12 @@
                     GrPathUtils::QuadUVMatrix DevToUV(pts);
 
                     BezierQuadTestBatch::Geometry geometry;
-                    geometry.fColor = gp->color();
+                    geometry.fColor = color;
                     geometry.fBounds = bounds;
 
                     SkAutoTUnref<GrBatch> batch(BezierQuadTestBatch::Create(gp, geometry, DevToUV));
 
-                    tt.target()->drawBatch(&pipelineBuilder, batch, NULL);
+                    tt.target()->drawBatch(&pipelineBuilder, batch);
                 }
                 ++col;
                 if (numCols == col) {
diff --git a/gm/beziers.cpp b/gm/beziers.cpp
index 5ad34fb..3dd8fa7 100755
--- a/gm/beziers.cpp
+++ b/gm/beziers.cpp
@@ -47,15 +47,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("beziers");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(W, H*2);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setStyle(SkPaint::kStroke_Style);
         paint.setStrokeWidth(SkIntToScalar(9)/2);
diff --git a/gm/bigblurs.cpp b/gm/bigblurs.cpp
index 39713c5..2f1011b 100644
--- a/gm/bigblurs.cpp
+++ b/gm/bigblurs.cpp
@@ -23,15 +23,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("bigblurs");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(kWidth, kHeight);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         static const int kBig = 65536;
         static const SkScalar kSigma = SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(4));
 
diff --git a/gm/bigmatrix.cpp b/gm/bigmatrix.cpp
index df542fd..32e637b 100644
--- a/gm/bigmatrix.cpp
+++ b/gm/bigmatrix.cpp
@@ -77,7 +77,7 @@
                                             &s);
         paint.setShader(shader)->unref();
         paint.setAntiAlias(false);
-        paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        paint.setFilterQuality(kLow_SkFilterQuality);
         rect.setLTRB(pt.fX - small, pt.fY - small,
                      pt.fX + small, pt.fY + small);
         canvas->drawRect(rect, paint);
diff --git a/gm/bigtext.cpp b/gm/bigtext.cpp
index 4fe77cc..c69d9b9 100644
--- a/gm/bigtext.cpp
+++ b/gm/bigtext.cpp
@@ -20,15 +20,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("bigtext");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         sk_tool_utils::set_portable_typeface(&paint);
diff --git a/gm/bitmapfilters.cpp b/gm/bitmapfilters.cpp
index f1348b1..8b68d3b 100644
--- a/gm/bitmapfilters.cpp
+++ b/gm/bitmapfilters.cpp
@@ -41,7 +41,7 @@
 static SkScalar draw_set(SkCanvas* c, const SkBitmap& bm, SkScalar x,
                          SkPaint* p) {
     x += draw_bm(c, bm, x, 0, p);
-    p->setFilterLevel(SkPaint::kLow_FilterLevel);
+    p->setFilterQuality(kLow_SkFilterQuality);
     x += draw_bm(c, bm, x, 0, p);
     p->setDither(true);
     return x + draw_bm(c, bm, x, 0, p);
diff --git a/gm/bitmappremul.cpp b/gm/bitmappremul.cpp
index 975dd67..172612d 100644
--- a/gm/bitmappremul.cpp
+++ b/gm/bitmappremul.cpp
@@ -84,7 +84,7 @@
 static SkBitmap make_argb4444_stripes() {
     SkBitmap bitmap;
     init_bitmap(kARGB_4444_SkColorType, &bitmap);
-    uint8_t rowColor = 0;;
+    uint8_t rowColor = 0;
     for (int y = 0; y < SLIDE_SIZE; y++) {
         uint16_t* dst = bitmap.getAddr16(0, y);
         for (int x = 0; x < SLIDE_SIZE; x++) {
@@ -109,15 +109,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("bitmap_premul");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(SLIDE_SIZE * 2, SLIDE_SIZE * 2);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkScalar slideSize = SkIntToScalar(SLIDE_SIZE);
         canvas->drawBitmap(make_argb8888_gradient(), 0, 0);
         canvas->drawBitmap(make_argb4444_gradient(), slideSize, 0);
diff --git a/gm/bitmaprect.cpp b/gm/bitmaprect.cpp
index 32eb7b1..aa71f48 100644
--- a/gm/bitmaprect.cpp
+++ b/gm/bitmaprect.cpp
@@ -35,17 +35,17 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString str;
         str.printf("bitmaprect_%s", fUseIRect ? "i" : "s");
         return str;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->drawColor(0xFFCCCCCC);
 
         const SkIRect src[] = {
@@ -124,17 +124,17 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString str;
         str.printf("3x3bitmaprect");
         return str;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         SkBitmap bitmap;
         make_3x3_bitmap(&bitmap);
@@ -161,9 +161,9 @@
         for (int x = 0; x < gXSize; ++x) {
             if (x <= gBorderWidth || x >= gXSize-gBorderWidth ||
                 y <= gBorderWidth || y >= gYSize-gBorderWidth) {
-                *bitmap->getAddr32(x, y) = 0x88FFFFFF;
+                *bitmap->getAddr32(x, y) = SkPreMultiplyColor(0x88FFFFFF);
             } else {
-                *bitmap->getAddr32(x, y) = 0x88FF0000;
+                *bitmap->getAddr32(x, y) = SkPreMultiplyColor(0x88FF0000);
             }
         }
     }
@@ -183,21 +183,21 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString str;
         str.printf("bigbitmaprect_%s", fUseIRect ? "i" : "s");
         return str;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         make_big_bitmap(&fBigBitmap);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         SkXfermode* mode = SkXfermode::Create(SkXfermode::kXor_Mode);
 
@@ -236,17 +236,17 @@
     BitmapRectRounding() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString str;
         str.printf("bitmaprect_rounding");
         return str;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         fBM.allocN32Pixels(10, 10);
         fBM.eraseColor(SK_ColorBLUE);
     }
@@ -256,7 +256,7 @@
     // If it does, we may see a red-line at the bottom, instead of the bitmap exactly matching
     // the clip (in which case we should see all blue).
     // The correct image should be all blue.
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setColor(SK_ColorRED);
 
diff --git a/gm/bitmaprecttest.cpp b/gm/bitmaprecttest.cpp
index dba6374..eaa7a3a 100644
--- a/gm/bitmaprecttest.cpp
+++ b/gm/bitmaprecttest.cpp
@@ -56,15 +56,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("bitmaprecttest");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(320, 240);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         test_bitmaprect(canvas);
     }
 
diff --git a/gm/bitmapscroll.cpp b/gm/bitmapscroll.cpp
index 9f8aaca..0603c3c 100644
--- a/gm/bitmapscroll.cpp
+++ b/gm/bitmapscroll.cpp
@@ -59,15 +59,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("bitmapscroll");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
       return SkISize::Make(800, 600);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         this->init();
         SkIRect scrollCenterRegion = SkIRect::MakeXYWH(
             quarterWidth, quarterHeight, quarterWidth*2+1, quarterHeight*2+1);
diff --git a/gm/bitmapshader.cpp b/gm/bitmapshader.cpp
index ac8bf66..1d866c6 100644
--- a/gm/bitmapshader.cpp
+++ b/gm/bitmapshader.cpp
@@ -43,7 +43,7 @@
 class BitmapShaderGM : public GM {
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         this->setBGColor(SK_ColorGRAY);
         draw_bm(&fBitmap);
         draw_mask(&fMask);
diff --git a/gm/bitmapsource.cpp b/gm/bitmapsource.cpp
index 1c35a0b..a0a3eca 100644
--- a/gm/bitmapsource.cpp
+++ b/gm/bitmapsource.cpp
@@ -17,7 +17,7 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("bitmapsource");
     }
 
@@ -34,9 +34,9 @@
         canvas.drawText(str, strlen(str), SkIntToScalar(20), SkIntToScalar(70), paint);
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(500, 150); }
+    SkISize onISize() override { return SkISize::Make(500, 150); }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         this->makeBitmap();
     }
 
@@ -49,8 +49,8 @@
         canvas->restore();
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
-        canvas->clear(0x00000000);
+    void onDraw(SkCanvas* canvas) override {
+        canvas->clear(SK_ColorBLACK);
         {
             SkRect srcRect = SkRect::MakeXYWH(20, 20, 30, 30);
             SkRect dstRect = SkRect::MakeXYWH(0, 10, 60, 60);
diff --git a/gm/bitmapsource2.cpp b/gm/bitmapsource2.cpp
new file mode 100644
index 0000000..bc4acf7
--- /dev/null
+++ b/gm/bitmapsource2.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2015 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 "SkBitmapSource.h"
+
+namespace skiagm {
+
+// This GM reproduces the issue in crbug.com/472795. The SkBitmapSource image
+// is shifted for high quality mode between cpu and gpu.
+class BitmapSourceGM : public GM {
+public:
+    BitmapSourceGM(const char* suffix, SkFilterQuality filter) : fSuffix(suffix), fFilter(filter) {
+        this->setBGColor(0xFFFFFFFF);
+    }
+
+protected:
+    SkString onShortName() override {
+        SkString name("bitmapsrc2_");
+        name.append(fSuffix);
+        return name;
+    }
+
+    SkISize onISize() override { return SkISize::Make(256, 256); }
+
+    // Create a bitmap with high frequency vertical stripes
+    void onOnceBeforeDraw() override {
+        static const SkPMColor gColors[] = {
+            SK_ColorRED,     SK_ColorGRAY,
+            SK_ColorGREEN,   SK_ColorGRAY,
+            SK_ColorBLUE,    SK_ColorGRAY,
+            SK_ColorCYAN,    SK_ColorGRAY,
+            SK_ColorMAGENTA, SK_ColorGRAY,
+            SK_ColorYELLOW,  SK_ColorGRAY,
+            SK_ColorWHITE,   SK_ColorGRAY,
+        };
+
+        fBM.allocN32Pixels(kImageSize, kImageSize, true);
+
+        SkCanvas canvas(fBM);
+
+        int curColor = 0;
+
+        for (int x = 0; x < kImageSize; x += 3) {
+            SkRect r = SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(0), 
+                                        SkIntToScalar(3), SkIntToScalar(kImageSize));
+            SkPaint p;
+            p.setColor(gColors[curColor]);
+            canvas.drawRect(r, p);
+
+            curColor = (curColor+1) % SK_ARRAY_COUNT(gColors);
+        }
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        SkRect srcRect = SkRect::MakeLTRB(0, 0,
+                                          SkIntToScalar(kImageSize), SkIntToScalar(kImageSize));
+        SkRect dstRect = SkRect::MakeLTRB(0.75f, 0.75f, 225.75f, 225.75f);
+
+        SkAutoTUnref<SkImageFilter> filter(SkBitmapSource::Create(fBM, srcRect, dstRect, fFilter));
+
+        SkPaint p;
+        p.setImageFilter(filter);
+
+        canvas->saveLayer(NULL, &p);
+        canvas->restore();
+    }
+
+private:
+    static const int kImageSize = 503;
+
+    SkString fSuffix;
+    SkFilterQuality fFilter;
+    SkBitmap fBM;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW_ARGS(BitmapSourceGM, ("none", kNone_SkFilterQuality) );   )
+DEF_GM( return SkNEW_ARGS(BitmapSourceGM, ("low",  kLow_SkFilterQuality) );    )
+DEF_GM( return SkNEW_ARGS(BitmapSourceGM, ("med",  kMedium_SkFilterQuality) ); )
+DEF_GM( return SkNEW_ARGS(BitmapSourceGM, ("high", kHigh_SkFilterQuality) );   )
+
+}
diff --git a/gm/bleed.cpp b/gm/bleed.cpp
index f39f3a1..75cf8ca 100644
--- a/gm/bleed.cpp
+++ b/gm/bleed.cpp
@@ -86,15 +86,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("bleed");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(kWidth, 780);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         make_ringed_bitmap(&fBitmapSmall, kSmallTextureSize, kSmallTextureSize);
 
         // To exercise the GPU's tiling path we need a texture
@@ -104,14 +104,14 @@
 
     // Draw only the center of the small bitmap
     void drawCase1(SkCanvas* canvas, int transX, int transY,
-                   SkCanvas::DrawBitmapRectFlags flags, SkPaint::FilterLevel filter) {
+                   SkCanvas::DrawBitmapRectFlags flags, SkFilterQuality filter) {
         SkRect src = SkRect::MakeXYWH(2, 2,
                                       SkIntToScalar(kSmallTextureSize-4),
                                       SkIntToScalar(kSmallTextureSize-4));
         SkRect dst = SkRect::MakeXYWH(0, 0, SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
 
         SkPaint paint;
-        paint.setFilterLevel(filter);
+        paint.setFilterQuality(filter);
 
         canvas->save();
         canvas->translate(SkIntToScalar(transX), SkIntToScalar(transY));
@@ -121,14 +121,14 @@
 
     // Draw almost all of the large bitmap
     void drawCase2(SkCanvas* canvas, int transX, int transY,
-                   SkCanvas::DrawBitmapRectFlags flags, SkPaint::FilterLevel filter) {
+                   SkCanvas::DrawBitmapRectFlags flags, SkFilterQuality filter) {
         SkRect src = SkRect::MakeXYWH(2, 2,
                                       SkIntToScalar(fBitmapBig.width()-4),
                                       SkIntToScalar(fBitmapBig.height()-4));
         SkRect dst = SkRect::MakeXYWH(0, 0, SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
 
         SkPaint paint;
-        paint.setFilterLevel(filter);
+        paint.setFilterQuality(filter);
 
         canvas->save();
         canvas->translate(SkIntToScalar(transX), SkIntToScalar(transY));
@@ -138,14 +138,14 @@
 
     // Draw ~1/4 of the large bitmap
     void drawCase3(SkCanvas* canvas, int transX, int transY,
-                   SkCanvas::DrawBitmapRectFlags flags, SkPaint::FilterLevel filter) {
+                   SkCanvas::DrawBitmapRectFlags flags, SkFilterQuality filter) {
         SkRect src = SkRect::MakeXYWH(2, 2,
                                       SkIntToScalar(fBitmapBig.width()/2-2),
                                       SkIntToScalar(fBitmapBig.height()/2-2));
         SkRect dst = SkRect::MakeXYWH(0, 0, SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
 
         SkPaint paint;
-        paint.setFilterLevel(filter);
+        paint.setFilterQuality(filter);
 
         canvas->save();
         canvas->translate(SkIntToScalar(transX), SkIntToScalar(transY));
@@ -155,14 +155,14 @@
 
     // Draw the center of the small bitmap with a mask filter
     void drawCase4(SkCanvas* canvas, int transX, int transY,
-                   SkCanvas::DrawBitmapRectFlags flags, SkPaint::FilterLevel filter) {
+                   SkCanvas::DrawBitmapRectFlags flags, SkFilterQuality filter) {
         SkRect src = SkRect::MakeXYWH(2, 2,
                                       SkIntToScalar(kSmallTextureSize-4),
                                       SkIntToScalar(kSmallTextureSize-4));
         SkRect dst = SkRect::MakeXYWH(0, 0, SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize));
 
         SkPaint paint;
-        paint.setFilterLevel(filter);
+        paint.setFilterQuality(filter);
         SkMaskFilter* mf = SkBlurMaskFilter::Create(kNormal_SkBlurStyle,
                                          SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(3)));
         paint.setMaskFilter(mf)->unref();
@@ -173,7 +173,7 @@
         canvas->restore();
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         canvas->clear(SK_ColorGRAY);
 
@@ -189,22 +189,22 @@
             }
 
             // First draw a column with no bleeding, tiling, or filtering
-            this->drawCase1(canvas, kCol0X, kRow0Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kNone_FilterLevel);
-            this->drawCase2(canvas, kCol0X, kRow1Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kNone_FilterLevel);
-            this->drawCase3(canvas, kCol0X, kRow2Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kNone_FilterLevel);
-            this->drawCase4(canvas, kCol0X, kRow3Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kNone_FilterLevel);
+            this->drawCase1(canvas, kCol0X, kRow0Y, SkCanvas::kNone_DrawBitmapRectFlag, kNone_SkFilterQuality);
+            this->drawCase2(canvas, kCol0X, kRow1Y, SkCanvas::kNone_DrawBitmapRectFlag, kNone_SkFilterQuality);
+            this->drawCase3(canvas, kCol0X, kRow2Y, SkCanvas::kNone_DrawBitmapRectFlag, kNone_SkFilterQuality);
+            this->drawCase4(canvas, kCol0X, kRow3Y, SkCanvas::kNone_DrawBitmapRectFlag, kNone_SkFilterQuality);
 
             // Then draw a column with no bleeding or tiling but with low filtering
-            this->drawCase1(canvas, kCol1X, kRow0Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
-            this->drawCase2(canvas, kCol1X, kRow1Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
-            this->drawCase3(canvas, kCol1X, kRow2Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
-            this->drawCase4(canvas, kCol1X, kRow3Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
+            this->drawCase1(canvas, kCol1X, kRow0Y, SkCanvas::kNone_DrawBitmapRectFlag, kLow_SkFilterQuality);
+            this->drawCase2(canvas, kCol1X, kRow1Y, SkCanvas::kNone_DrawBitmapRectFlag, kLow_SkFilterQuality);
+            this->drawCase3(canvas, kCol1X, kRow2Y, SkCanvas::kNone_DrawBitmapRectFlag, kLow_SkFilterQuality);
+            this->drawCase4(canvas, kCol1X, kRow3Y, SkCanvas::kNone_DrawBitmapRectFlag, kLow_SkFilterQuality);
 
             // Then draw a column with no bleeding or tiling but with high filtering
-            this->drawCase1(canvas, kCol2X, kRow0Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
-            this->drawCase2(canvas, kCol2X, kRow1Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
-            this->drawCase3(canvas, kCol2X, kRow2Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
-            this->drawCase4(canvas, kCol2X, kRow3Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
+            this->drawCase1(canvas, kCol2X, kRow0Y, SkCanvas::kNone_DrawBitmapRectFlag, kHigh_SkFilterQuality);
+            this->drawCase2(canvas, kCol2X, kRow1Y, SkCanvas::kNone_DrawBitmapRectFlag, kHigh_SkFilterQuality);
+            this->drawCase3(canvas, kCol2X, kRow2Y, SkCanvas::kNone_DrawBitmapRectFlag, kHigh_SkFilterQuality);
+            this->drawCase4(canvas, kCol2X, kRow3Y, SkCanvas::kNone_DrawBitmapRectFlag, kHigh_SkFilterQuality);
 
 #if SK_SUPPORT_GPU
             GrContext* ctx = canvas->getGrContext();
@@ -217,28 +217,28 @@
 #endif
 
             // Then draw a column with no bleeding but with tiling and low filtering
-            this->drawCase1(canvas, kCol3X, kRow0Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
-            this->drawCase2(canvas, kCol3X, kRow1Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
-            this->drawCase3(canvas, kCol3X, kRow2Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
-            this->drawCase4(canvas, kCol3X, kRow3Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
+            this->drawCase1(canvas, kCol3X, kRow0Y, SkCanvas::kNone_DrawBitmapRectFlag, kLow_SkFilterQuality);
+            this->drawCase2(canvas, kCol3X, kRow1Y, SkCanvas::kNone_DrawBitmapRectFlag, kLow_SkFilterQuality);
+            this->drawCase3(canvas, kCol3X, kRow2Y, SkCanvas::kNone_DrawBitmapRectFlag, kLow_SkFilterQuality);
+            this->drawCase4(canvas, kCol3X, kRow3Y, SkCanvas::kNone_DrawBitmapRectFlag, kLow_SkFilterQuality);
 
             // Then draw a column with no bleeding but with tiling and high filtering
-            this->drawCase1(canvas, kCol4X, kRow0Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
-            this->drawCase2(canvas, kCol4X, kRow1Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
-            this->drawCase3(canvas, kCol4X, kRow2Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
-            this->drawCase4(canvas, kCol4X, kRow3Y, SkCanvas::kNone_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
+            this->drawCase1(canvas, kCol4X, kRow0Y, SkCanvas::kNone_DrawBitmapRectFlag, kHigh_SkFilterQuality);
+            this->drawCase2(canvas, kCol4X, kRow1Y, SkCanvas::kNone_DrawBitmapRectFlag, kHigh_SkFilterQuality);
+            this->drawCase3(canvas, kCol4X, kRow2Y, SkCanvas::kNone_DrawBitmapRectFlag, kHigh_SkFilterQuality);
+            this->drawCase4(canvas, kCol4X, kRow3Y, SkCanvas::kNone_DrawBitmapRectFlag, kHigh_SkFilterQuality);
 
             // Then draw a column with bleeding, tiling, and low filtering
-            this->drawCase1(canvas, kCol5X, kRow0Y, SkCanvas::kBleed_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
-            this->drawCase2(canvas, kCol5X, kRow1Y, SkCanvas::kBleed_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
-            this->drawCase3(canvas, kCol5X, kRow2Y, SkCanvas::kBleed_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
-            this->drawCase4(canvas, kCol5X, kRow3Y, SkCanvas::kBleed_DrawBitmapRectFlag, SkPaint::kLow_FilterLevel);
+            this->drawCase1(canvas, kCol5X, kRow0Y, SkCanvas::kBleed_DrawBitmapRectFlag, kLow_SkFilterQuality);
+            this->drawCase2(canvas, kCol5X, kRow1Y, SkCanvas::kBleed_DrawBitmapRectFlag, kLow_SkFilterQuality);
+            this->drawCase3(canvas, kCol5X, kRow2Y, SkCanvas::kBleed_DrawBitmapRectFlag, kLow_SkFilterQuality);
+            this->drawCase4(canvas, kCol5X, kRow3Y, SkCanvas::kBleed_DrawBitmapRectFlag, kLow_SkFilterQuality);
 
             // Finally draw a column with bleeding, tiling, and high filtering
-            this->drawCase1(canvas, kCol6X, kRow0Y, SkCanvas::kBleed_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
-            this->drawCase2(canvas, kCol6X, kRow1Y, SkCanvas::kBleed_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
-            this->drawCase3(canvas, kCol6X, kRow2Y, SkCanvas::kBleed_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
-            this->drawCase4(canvas, kCol6X, kRow3Y, SkCanvas::kBleed_DrawBitmapRectFlag, SkPaint::kHigh_FilterLevel);
+            this->drawCase1(canvas, kCol6X, kRow0Y, SkCanvas::kBleed_DrawBitmapRectFlag, kHigh_SkFilterQuality);
+            this->drawCase2(canvas, kCol6X, kRow1Y, SkCanvas::kBleed_DrawBitmapRectFlag, kHigh_SkFilterQuality);
+            this->drawCase3(canvas, kCol6X, kRow2Y, SkCanvas::kBleed_DrawBitmapRectFlag, kHigh_SkFilterQuality);
+            this->drawCase4(canvas, kCol6X, kRow3Y, SkCanvas::kBleed_DrawBitmapRectFlag, kHigh_SkFilterQuality);
 
 #if SK_SUPPORT_GPU
             if (ctx) {
diff --git a/gm/blend.cpp b/gm/blend.cpp
new file mode 100644
index 0000000..90453a5
--- /dev/null
+++ b/gm/blend.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 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"
+
+DEF_SIMPLE_GM(blend, canvas, 300, 100) {
+    SkPaint p;
+
+    // All three of these blocks should be the same color.
+    canvas->save();
+        canvas->scale(100,100);
+
+        p.setColor(SK_ColorRED);
+        canvas->drawRect(SkRect::MakeXYWH(0,0,1,1), p);
+        p.setColor(0xFC008000);
+        canvas->drawRect(SkRect::MakeXYWH(0,0,1,1), p);
+
+        p.setColor(SK_ColorRED);
+        canvas->drawRect(SkRect::MakeXYWH(1,0,1,1), p);
+        canvas->saveLayer(NULL, NULL);
+            p.setColor(0xFC008000);
+            canvas->drawRect(SkRect::MakeXYWH(1,0,1,1), p);
+        canvas->restore();
+
+        p.setColor(SK_ColorRED);
+        canvas->drawRect(SkRect::MakeXYWH(2,0,1,1), p);
+        canvas->saveLayerAlpha(NULL, 0xFC);
+            p.setColor(0xFF008000);
+            canvas->drawRect(SkRect::MakeXYWH(2,0,1,1), p);
+        canvas->restore();
+    canvas->restore();
+
+    // Print out the colors in each block (if we're looking at 8888 raster).
+    if (canvas->imageInfo().colorType() == kN32_SkColorType) {
+        if (const SkPMColor* px = (const SkPMColor*)canvas->peekPixels(NULL, NULL)) {
+            p.setColor(SK_ColorWHITE);
+            for (int i = 0; i < 3; i++) {
+                SkPMColor c = px[i * 100];
+                SkString text = SkStringPrintf("0x%08x", c);
+                canvas->drawText(text.c_str(), text.size(), i * 100.0f + 20.0f, 50.0f, p);
+            }
+        }
+    }
+}
diff --git a/gm/blurcircles.cpp b/gm/blurcircles.cpp
index ee30200..e4f956f 100644
--- a/gm/blurcircles.cpp
+++ b/gm/blurcircles.cpp
@@ -20,15 +20,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(950, 950);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->scale(1.5f, 1.5f);
         canvas->translate(50,50);
 
diff --git a/gm/blurquickreject.cpp b/gm/blurquickreject.cpp
index 7c74fdf..11697fc 100644
--- a/gm/blurquickreject.cpp
+++ b/gm/blurquickreject.cpp
@@ -22,15 +22,15 @@
     BlurQuickRejectGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("blurquickreject");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(kWidth, kHeight);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         static const SkScalar kBlurRadius = SkIntToScalar(20);
         static const SkScalar kBoxSize = SkIntToScalar(100);
 
@@ -55,7 +55,7 @@
         hairlinePaint.setStrokeWidth(0);
 
         SkPaint blurPaint;
-        blurPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        blurPaint.setFilterQuality(kLow_SkFilterQuality);
         SkMaskFilter* mf = SkBlurMaskFilter::Create(kNormal_SkBlurStyle,
                                                     SkBlurMask::ConvertRadiusToSigma(kBlurRadius));
         blurPaint.setMaskFilter(mf)->unref();
diff --git a/gm/blurrect.cpp b/gm/blurrect.cpp
index fb3ee37..0726f2c 100644
--- a/gm/blurrect.cpp
+++ b/gm/blurrect.cpp
@@ -74,7 +74,7 @@
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
-    return SkGradientShader::CreateTwoPointRadial(center1, (pts[1].fX - pts[0].fX) / 7,
+    return SkGradientShader::CreateTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
                                                   center0, (pts[1].fX - pts[0].fX) / 2,
                                                   colors, pos, SK_ARRAY_COUNT(colors), tm,
                                                   0, &scale);
@@ -93,7 +93,7 @@
     }
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         for (int i = 0; i <= kLastEnum_SkBlurStyle; ++i) {
             fMaskFilters[i].reset(SkBlurMaskFilter::Create((SkBlurStyle)i,
                                   SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(STROKE_WIDTH/2)),
@@ -101,15 +101,15 @@
         }
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(860, 820);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2);
 
         SkRect  r = { 0, 0, 100, 50 };
@@ -329,7 +329,7 @@
         }
 
 protected:
-    bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
+    bool makeMask(SkMask *m, const SkRect& r) override {
         return SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(this->radius()),
                                     m, r, this->style());
     }
@@ -345,7 +345,7 @@
         }
 
 protected:
-    bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
+    bool makeMask(SkMask *m, const SkRect& r) override {
         SkMask src;
         r.roundOut(&src.fBounds);
         src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop);  // move to origin
@@ -376,7 +376,7 @@
         }
 
 protected:
-    SkBlurQuality getQuality() SK_OVERRIDE {
+    SkBlurQuality getQuality() override {
         return kLow_SkBlurQuality;
     }
 private:
@@ -391,7 +391,7 @@
         }
 
 protected:
-    bool makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
+    bool makeMask(SkMask *m, const SkRect& r) override {
         SkMask src;
         r.roundOut(&src.fBounds);
         src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop);  // move to origin
diff --git a/gm/blurroundrect.cpp b/gm/blurroundrect.cpp
index 45a1103..6b11f84 100644
--- a/gm/blurroundrect.cpp
+++ b/gm/blurroundrect.cpp
@@ -42,16 +42,16 @@
         fRRect.setRectRadii(r, radii);
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(SkScalarCeilToInt(fRRect.rect().width()),
                              SkScalarCeilToInt(fRRect.rect().height()));
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkLayerDrawLooper::Builder looperBuilder;
         {
             SkLayerDrawLooper::LayerInfo info;
@@ -112,10 +112,10 @@
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
-    return SkGradientShader::CreateTwoPointRadial(center1, (pts[1].fX - pts[0].fX) / 7,
-                                                  center0, (pts[1].fX - pts[0].fX) / 2,
-                                                  colors, pos, SK_ARRAY_COUNT(colors), tm,
-                                                  0, &scale);
+    return SkGradientShader::CreateTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
+                                                   center0, (pts[1].fX - pts[0].fX) / 2,
+                                                   colors, pos, SK_ARRAY_COUNT(colors), tm,
+                                                   0, &scale);
 }
 
 // Simpler blurred RR test cases where all the radii are the same.
@@ -127,15 +127,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1000, 500);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->scale(1.5f, 1.5f);
         canvas->translate(50,50);
 
diff --git a/gm/blurs.cpp b/gm/blurs.cpp
index 96a9e30..fdf10be 100644
--- a/gm/blurs.cpp
+++ b/gm/blurs.cpp
@@ -17,15 +17,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("blurs");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(700, 500);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkBlurStyle NONE = SkBlurStyle(-999);
         static const struct {
             SkBlurStyle fStyle;
@@ -101,15 +101,15 @@
 //
 class Blur2RectsGM : public skiagm::GM {
 public:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("blur2rects");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(700, 500);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
 
         paint.setMaskFilter(SkBlurMaskFilter::Create(kNormal_SkBlurStyle,
@@ -133,15 +133,15 @@
 
 class Blur2RectsNonNinePatchGM : public skiagm::GM {
 public:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("blur2rectsnonninepatch");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(700, 500);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setMaskFilter(SkBlurMaskFilter::Create(kNormal_SkBlurStyle,
                                                      4.3f))->unref();
diff --git a/gm/bmpfilterqualityrepeat.cpp b/gm/bmpfilterqualityrepeat.cpp
new file mode 100644
index 0000000..1097141
--- /dev/null
+++ b/gm/bmpfilterqualityrepeat.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2015 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 "SkShader.h"
+
+// Inspired by svg/as-border-image/svg-as-border-image.html. Draws a four-color checker board bitmap
+// such that it is stretched and repeat tiled with different filter qualities. It is testing whether
+// the bmp filter respects the repeat mode at the tile seams.
+class BmpFilterQualityRepeat : public skiagm::GM {
+public:
+    BmpFilterQualityRepeat() { this->setBGColor(0xFFCCBBAA); }
+
+protected:
+
+    void onOnceBeforeDraw() override {
+        fBmp.allocN32Pixels(40, 40, true);
+        SkCanvas canvas(fBmp);
+        SkBitmap colorBmp;
+        colorBmp.allocN32Pixels(20, 20, true);
+        colorBmp.eraseColor(0xFFFF0000);
+        canvas.drawBitmap(colorBmp, 0, 0);
+        colorBmp.eraseColor(0xFF008200);
+        canvas.drawBitmap(colorBmp, 20, 0);
+        colorBmp.eraseColor(0xFFFF9000);
+        canvas.drawBitmap(colorBmp, 0, 20);
+        colorBmp.eraseColor(0xFF2000FF);
+        canvas.drawBitmap(colorBmp, 20, 20);
+    }
+
+    SkString onShortName() override { return SkString("bmp_filter_quality_repeat"); }
+
+    SkISize onISize() override { return SkISize::Make(1000, 235); }
+
+    void onDraw(SkCanvas* canvas) override {
+
+        static const struct { 
+            SkFilterQuality fQuality;
+            const char* fName;
+        } kQualities[] = {
+            {kNone_SkFilterQuality, "none"},
+            {kLow_SkFilterQuality, "low"},
+            {kMedium_SkFilterQuality, "medium"},
+            {kHigh_SkFilterQuality, "high"},
+        };
+
+        for (size_t q = 0; q < SK_ARRAY_COUNT(kQualities); ++q) {
+            SkPaint paint;
+            paint.setFilterQuality(kQualities[q].fQuality);
+            SkPaint bmpPaint(paint);
+            SkMatrix lm = SkMatrix::I();
+            lm.setScaleX(2.5);
+            lm.setTranslateX(423);
+            lm.setTranslateY(330);
+
+            static const SkShader::TileMode kTM = SkShader::kRepeat_TileMode;
+            bmpPaint.setShader(SkShader::CreateBitmapShader(fBmp, kTM, kTM, &lm))->unref();
+            SkRect rect = SkRect::MakeLTRB(20, 60, 220, 210);
+            canvas->drawRect(rect, bmpPaint);
+            paint.setAntiAlias(true);
+            canvas->drawText(kQualities[q].fName, strlen(kQualities[q].fName), 20, 40, paint);
+            canvas->translate(250, 0);
+        }
+    }
+
+private:
+    SkBitmap    fBmp;
+
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(BmpFilterQualityRepeat); )
diff --git a/gm/cgms.cpp b/gm/cgms.cpp
index 4f2c1b1..e3e84b0 100644
--- a/gm/cgms.cpp
+++ b/gm/cgms.cpp
@@ -15,15 +15,15 @@
     C_GM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("c_gms");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         sk_test_c_api((sk_canvas_t*)canvas);
     }
 
diff --git a/gm/circles.cpp b/gm/circles.cpp
index c0503fd..a178ba9 100644
--- a/gm/circles.cpp
+++ b/gm/circles.cpp
@@ -26,11 +26,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("circles");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1200, 900);
     }
 
@@ -152,7 +152,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         // Draw a giant AA circle as the background.
         SkISize size = this->getISize();
         SkScalar giantRadius = SkTMin(SkIntToScalar(size.fWidth),
diff --git a/gm/circularclips.cpp b/gm/circularclips.cpp
index 614ebb6..15f583f 100644
--- a/gm/circularclips.cpp
+++ b/gm/circularclips.cpp
@@ -14,7 +14,7 @@
     SkPath   fCircle1, fCircle2;
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         fX1 = 80;
         fX2 = 120;
         fY = 50;
@@ -25,17 +25,17 @@
     }
 
 
-    bool runAsBench() const SK_OVERRIDE { return true; }
+    bool runAsBench() const override { return true; }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("circular-clips");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(800, 600);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkRegion::Op ops[] = {
             SkRegion::kDifference_Op,
             SkRegion::kIntersect_Op,
@@ -49,6 +49,20 @@
 
         SkPaint fillPaint;
 
+        // Giant background circular clips (AA, non-inverted, replace/isect)
+        fillPaint.setColor(0x80808080);
+        canvas->save();
+        canvas->scale(10, 10);
+        canvas->translate(-((fX1 + fX2)/2 - fR), -(fY - 2*fR/3));
+        canvas->clipPath(fCircle1, SkRegion::kReplace_Op, true);
+        canvas->clipPath(fCircle2, SkRegion::kIntersect_Op, true);
+
+        canvas->drawRect(rect, fillPaint);
+
+        canvas->restore();
+      
+        fillPaint.setColor(0xFF000000);
+
         for (size_t i = 0; i < 4; i++) {
             fCircle1.toggleInverseFillType();
             if (i % 2 == 0) {
diff --git a/gm/clip_strokerect.cpp b/gm/clip_strokerect.cpp
index a0dc7fd..0175eda 100644
--- a/gm/clip_strokerect.cpp
+++ b/gm/clip_strokerect.cpp
@@ -16,15 +16,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("clip_strokerect");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(200, 400);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint p;
         p.setColor(SK_ColorRED);
         p.setAntiAlias(true);
diff --git a/gm/clipdrawdraw.cpp b/gm/clipdrawdraw.cpp
index 3846dc0..11561b2 100644
--- a/gm/clipdrawdraw.cpp
+++ b/gm/clipdrawdraw.cpp
@@ -23,9 +23,9 @@
     ClipDrawDrawGM() { this->setBGColor(0xFFCCCCCC); }
 
 protected:
-    SkString onShortName() SK_OVERRIDE { return SkString("clipdrawdraw"); }
+    SkString onShortName() override { return SkString("clipdrawdraw"); }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(512, 512); }
+    SkISize onISize() override { return SkISize::Make(512, 512); }
 
     static void Draw(SkCanvas* canvas, const SkRect& rect) {
         SkPaint p;
@@ -46,7 +46,7 @@
         canvas->restore();
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         // Vertical remnant
         const SkRect rect1 = SkRect::MakeLTRB(136.5f, 137.5f, 338.5f, 293.5f);
 
diff --git a/gm/clippedbitmapshaders.cpp b/gm/clippedbitmapshaders.cpp
index ee20847..6225abe 100644
--- a/gm/clippedbitmapshaders.cpp
+++ b/gm/clippedbitmapshaders.cpp
@@ -89,7 +89,7 @@
         paint.setShader(shader)->unref();
 
         if (fHQ) {
-            paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
+            paint.setFilterQuality(kHigh_SkFilterQuality);
         }
 
         SkScalar margin = (SLIDE_SIZE / 3 - RECT_SIZE) / 2;
diff --git a/gm/cmykjpeg.cpp b/gm/cmykjpeg.cpp
index 75be712..851e9c7 100644
--- a/gm/cmykjpeg.cpp
+++ b/gm/cmykjpeg.cpp
@@ -22,7 +22,7 @@
     CMYKJpegGM() {}
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         // parameters to the "decode" call
         bool dither = false;
 
@@ -42,15 +42,15 @@
         }
     }
 
-    virtual SkString onShortName() SK_OVERRIDE {
+    virtual SkString onShortName() override {
         return SkString("cmykjpeg");
     }
 
-    virtual SkISize onISize() SK_OVERRIDE {
+    virtual SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    virtual void onDraw(SkCanvas* canvas) override {
 
         canvas->translate(20*SK_Scalar1, 20*SK_Scalar1);
         canvas->drawBitmap(fBitmap, 0, 0);
diff --git a/gm/coloremoji.cpp b/gm/coloremoji.cpp
index 8276ea8..36897c5 100644
--- a/gm/coloremoji.cpp
+++ b/gm/coloremoji.cpp
@@ -53,16 +53,9 @@
         SkTypeface* typeface;
         const char* text;
     } emojiFonts[2];
-    virtual void onOnceBeforeDraw() SK_OVERRIDE {
-        SkString filename = GetResourcePath("/Funkster.ttf");
-        SkAutoTDelete<SkFILEStream> stream(new SkFILEStream(filename.c_str()));
-        if (stream->isValid()) {
-            fCBDT_CBLC_Typeface.reset(SkTypeface::CreateFromStream(stream.detach()));
-            emojiFonts[0].typeface = fCBDT_CBLC_Typeface;
-        } else {
-            SkDebugf("Could not find Funkster.ttf, please set --resourcePath correctly.\n");
-            emojiFonts[0].typeface = NULL;
-        }
+    virtual void onOnceBeforeDraw() override {
+        fCBDT_CBLC_Typeface.reset(GetResourceAsTypeface("/fonts/Funkster.ttf"));
+        emojiFonts[0].typeface = fCBDT_CBLC_Typeface;
         emojiFonts[0].text = "hamburgerfons";
 
         fSBIX_Typeface.reset(SkTypeface::CreateFromName("Apple Color Emoji", SkTypeface::kNormal));
@@ -74,15 +67,15 @@
                              "\xF0\x9F\x87\xBA" "\xF0\x9F\x87\xB8" "\xF0\x9F\x87\xA6"; // 🇺🇸🇦
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("coloremoji");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(650, 900);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         canvas->drawColor(SK_ColorGRAY);
 
diff --git a/gm/colorfilters.cpp b/gm/colorfilters.cpp
index 2020806..bedb7f6 100644
--- a/gm/colorfilters.cpp
+++ b/gm/colorfilters.cpp
@@ -41,15 +41,15 @@
     }
 
 protected:
-    virtual SkString onShortName() SK_OVERRIDE {
+    virtual SkString onShortName() override {
         return fName;
     }
 
-    virtual SkISize onISize() SK_OVERRIDE {
+    virtual SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         SkRect r;
         r.setWH(600, 50);
diff --git a/gm/colormatrix.cpp b/gm/colormatrix.cpp
index 841f36a..36cf8c5 100644
--- a/gm/colormatrix.cpp
+++ b/gm/colormatrix.cpp
@@ -103,7 +103,6 @@
         const SkBitmap bmps[] = { fSolidBitmap, fTransparentBitmap };
 
         for (size_t i = 0; i < SK_ARRAY_COUNT(bmps); ++i) {
-
             matrix.setIdentity();
             setColorMatrix(&paint, matrix);
             canvas->drawBitmap(bmps[i], 0, 0, &paint);
diff --git a/gm/colortype.cpp b/gm/colortype.cpp
index c75a166..48e39d0 100644
--- a/gm/colortype.cpp
+++ b/gm/colortype.cpp
@@ -40,15 +40,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("colortype");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setTypeface(fColorType);
diff --git a/gm/colortypexfermode.cpp b/gm/colortypexfermode.cpp
index 1b6e289..bf86565 100644
--- a/gm/colortypexfermode.cpp
+++ b/gm/colortypexfermode.cpp
@@ -18,7 +18,7 @@
 class ColorTypeXfermodeGM : public GM {
     SkBitmap    fBG;
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         fBG.installPixels(SkImageInfo::Make(2, 2, kARGB_4444_SkColorType,
                                             kOpaque_SkAlphaType), gData, 4);
     }
@@ -54,15 +54,15 @@
     }
 
 protected:
-    virtual SkString onShortName() SK_OVERRIDE {
+    virtual SkString onShortName() override {
         return SkString("colortype_xfermodes");
     }
 
-    virtual SkISize onISize() SK_OVERRIDE {
+    virtual SkISize onISize() override {
         return SkISize::Make(400, 640);
     }
 
-    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    virtual void onDraw(SkCanvas* canvas) override {
         canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
 
         const struct {
diff --git a/gm/colorwheel.cpp b/gm/colorwheel.cpp
index db2924d..6b9c149 100644
--- a/gm/colorwheel.cpp
+++ b/gm/colorwheel.cpp
@@ -5,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-#include "sk_tool_utils.h"
 #include "Resources.h"
 #include "SkData.h"
 #include "gm.h"
diff --git a/gm/complexclip2.cpp b/gm/complexclip2.cpp
index 361b1c0..dfec4e7 100644
--- a/gm/complexclip2.cpp
+++ b/gm/complexclip2.cpp
@@ -38,7 +38,7 @@
     }
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         this->setBGColor(SkColorSetRGB(0xDD,0xA0,0xDD));
 
         // offset the rects a bit so we get antialiasing even in the rect case
diff --git a/gm/composeshader.cpp b/gm/composeshader.cpp
index 76ff8ed..8933cf3 100644
--- a/gm/composeshader.cpp
+++ b/gm/composeshader.cpp
@@ -49,15 +49,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("composeshader");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(120, 120);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         SkPaint paint;
 
@@ -79,15 +79,15 @@
     ComposeShaderAlphaGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("composeshader_alpha");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(220, 750);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkAutoTUnref<SkShader> shader0(make_shader(SkXfermode::kDstIn_Mode));
         SkAutoTUnref<SkShader> shader1(make_shader(SkXfermode::kSrcOver_Mode));
         SkShader* shaders[] = { shader0.get(), shader1.get() };
diff --git a/gm/concavepaths.cpp b/gm/concavepaths.cpp
index bc1b533..46b2ef4 100644
--- a/gm/concavepaths.cpp
+++ b/gm/concavepaths.cpp
@@ -344,15 +344,15 @@
     ConcavePathsGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("concavepaths");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(WIDTH, HEIGHT);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
 
         paint.setAntiAlias(true);
diff --git a/gm/conicpaths.cpp b/gm/conicpaths.cpp
index 661f19f..717dd8e 100644
--- a/gm/conicpaths.cpp
+++ b/gm/conicpaths.cpp
@@ -12,15 +12,15 @@
 class ConicPathsGM : public skiagm::GM {
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("conicpaths");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(920, 960);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         {
             const SkScalar w = SkScalarSqrt(2)/2;
             SkPath* conicCirlce = &fPaths.push_back();
@@ -88,7 +88,7 @@
         canvas->drawPath(fGiantCircle, paint);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         const SkAlpha kAlphaValue[] = { 0xFF, 0x40 };
 
         const SkScalar margin = 15;
diff --git a/gm/constcolorprocessor.cpp b/gm/constcolorprocessor.cpp
new file mode 100644
index 0000000..159bf06
--- /dev/null
+++ b/gm/constcolorprocessor.cpp
@@ -0,0 +1,198 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This test only works with the GPU backend.
+
+#include "gm.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrTest.h"
+#include "effects/GrConstColorProcessor.h"
+#include "SkGr.h"
+#include "SkGradientShader.h"
+
+namespace skiagm {
+/**
+ * This GM directly exercises GrConstColorProcessor.
+ */
+class ConstColorProcessor : public GM {
+public:
+    ConstColorProcessor() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    SkString onShortName() override {
+        return SkString("const_color_processor");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(kWidth, kHeight);
+    }
+
+    void onOnceBeforeDraw() override {
+        SkColor colors[] = { 0xFFFF0000, 0x2000FF00, 0xFF0000FF};
+        SkPoint pts[] = { SkPoint::Make(0, 0), SkPoint::Make(kRectSize, kRectSize) };
+        fShader.reset(SkGradientShader::CreateLinear(pts, colors, NULL, SK_ARRAY_COUNT(colors),
+                       SkShader::kClamp_TileMode));
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
+        if (NULL == rt) {
+            return;
+        }
+        GrContext* context = rt->getContext();
+        if (NULL == context) {
+            this->drawGpuOnlyMessage(canvas);
+            return;
+        }
+
+        static const GrColor kColors[] = {
+            0xFFFFFFFF,
+            0xFFFF00FF,
+            0x80000000,
+            0x00000000,
+        };
+
+        static const SkColor kPaintColors[] = {
+            0xFFFFFFFF,
+            0xFFFF0000,
+            0x80FF0000,
+            0x00000000,
+        };
+
+        static const char* kModeStrs[] {
+            "kIgnore",
+            "kModulateRGBA",
+            "kModulateA",
+        };
+        GR_STATIC_ASSERT(SK_ARRAY_COUNT(kModeStrs) == GrConstColorProcessor::kInputModeCnt);
+
+        SkScalar y = kPad;
+        SkScalar x = kPad;
+        SkScalar maxW = 0;
+        for (size_t paintType = 0; paintType < SK_ARRAY_COUNT(kPaintColors) + 1; ++paintType) {
+            for (size_t procColor = 0; procColor < SK_ARRAY_COUNT(kColors); ++procColor) {
+                for (int m = 0; m < GrConstColorProcessor::kInputModeCnt; ++m) {
+                    // translate by x,y for the canvas draws and the test target draws.
+                    canvas->save();
+                    canvas->translate(x, y);
+                    const SkMatrix viewMatrix = SkMatrix::MakeTrans(x, y);
+
+                    // rect to draw
+                    SkRect renderRect = SkRect::MakeXYWH(0, 0, kRectSize, kRectSize);
+
+                    GrTestTarget tt;
+                    context->getTestTarget(&tt);
+                    if (NULL == tt.target()) {
+                        SkDEBUGFAIL("Couldn't get Gr test target.");
+                        return;
+                    }
+
+                    GrPaint grPaint;
+                    SkPaint skPaint;
+                    if (paintType >= SK_ARRAY_COUNT(kPaintColors)) {
+                        skPaint.setShader(fShader);
+                    } else {
+                        skPaint.setColor(kPaintColors[paintType]);
+                    }
+                    SkAssertResult(SkPaint2GrPaint(context, rt, skPaint, viewMatrix, false,
+                                                   &grPaint));
+
+                    GrConstColorProcessor::InputMode mode = (GrConstColorProcessor::InputMode) m;
+                    GrColor color = kColors[procColor];
+                    SkAutoTUnref<GrFragmentProcessor> fp(GrConstColorProcessor::Create(color, mode));
+
+                    GrPipelineBuilder pipelineBuilder;
+                    GrClip clip;
+                    pipelineBuilder.setFromPaint(grPaint, rt, clip);
+                    pipelineBuilder.addColorProcessor(fp);
+
+                    tt.target()->drawSimpleRect(&pipelineBuilder,
+                                                grPaint.getColor(),
+                                                viewMatrix,
+                                                renderRect);
+
+                    // Draw labels for the input to the processor and the processor to the right of
+                    // the test rect. The input label appears above the processor label.
+                    SkPaint labelPaint;
+                    labelPaint.setAntiAlias(true);
+                    labelPaint.setTextSize(10.f);
+                    SkString inputLabel;
+                    inputLabel.set("Input: ");
+                    if (paintType >= SK_ARRAY_COUNT(kPaintColors)) {
+                        inputLabel.append("gradient");
+                    } else {
+                        inputLabel.appendf("0x%08x", kPaintColors[paintType]);
+                    }
+                    SkString procLabel;
+                    procLabel.printf("Proc: [0x%08x, %s]", kColors[procColor], kModeStrs[m]);
+
+                    SkRect inputLabelBounds;
+                    // get the bounds of the text in order to position it
+                    labelPaint.measureText(inputLabel.c_str(), inputLabel.size(),
+                                           &inputLabelBounds);
+                    canvas->drawText(inputLabel.c_str(), inputLabel.size(),
+                                     renderRect.fRight + kPad,
+                                     -inputLabelBounds.fTop, labelPaint);
+                    // update the bounds to reflect the offset we used to draw it.
+                    inputLabelBounds.offset(renderRect.fRight + kPad, -inputLabelBounds.fTop);
+
+                    SkRect procLabelBounds;
+                    labelPaint.measureText(procLabel.c_str(), procLabel.size(),
+                                           &procLabelBounds);
+                    canvas->drawText(procLabel.c_str(), procLabel.size(),
+                                     renderRect.fRight + kPad,
+                                     inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop,
+                                     labelPaint);
+                    procLabelBounds.offset(renderRect.fRight + kPad,
+                                           inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop);
+
+                    labelPaint.setStrokeWidth(0);
+                    labelPaint.setStyle(SkPaint::kStroke_Style);
+                    canvas->drawRect(renderRect, labelPaint);
+
+                    canvas->restore();
+
+                    // update x and y for the next test case.
+                    SkScalar height = renderRect.height();
+                    SkScalar width = SkTMax(inputLabelBounds.fRight, procLabelBounds.fRight);
+                    maxW = SkTMax(maxW, width);
+                    y += height + kPad;
+                    if (y + height > kHeight) {
+                        y = kPad;
+                        x += maxW + kPad;
+                        maxW = 0;
+                    }
+                }
+            }
+        }
+    }
+
+private:
+    // Use this as a way of generating and input FP
+    SkAutoTUnref<SkShader>      fShader;
+
+    static const SkScalar       kPad;
+    static const SkScalar       kRectSize;
+    static const int            kWidth  = 820;
+    static const int            kHeight = 500;
+
+    typedef GM INHERITED;
+};
+
+const SkScalar ConstColorProcessor::kPad = 10.f;
+const SkScalar ConstColorProcessor::kRectSize = 20.f;
+
+DEF_GM( return SkNEW(ConstColorProcessor); )
+}
+
+#endif
diff --git a/gm/convex_all_line_paths.cpp b/gm/convex_all_line_paths.cpp
new file mode 100644
index 0000000..81b98b8
--- /dev/null
+++ b/gm/convex_all_line_paths.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2015 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"
+
+static void create_ngon(int n, SkPoint* pts, SkScalar width, SkScalar height) {
+    float angleStep = 360.0f / n, angle = 0.0f, sin, cos;
+    if ((n % 2) == 1) {
+        angle = angleStep/2.0f;
+    }
+
+    for (int i = 0; i < n; ++i) {
+        sin = SkScalarSinCos(SkDegreesToRadians(angle), &cos);
+        pts[i].fX = -sin * width;
+        pts[i].fY = cos * height;
+        angle += angleStep;
+    }
+}
+
+namespace skiagm {
+
+// This GM is intended to exercise Ganesh's handling of convex line-only
+// paths
+class ConvexLineOnlyPathsGM : public GM {
+public:
+    ConvexLineOnlyPathsGM() { 
+        this->setBGColor(0xFFFFFFFF);
+    }
+
+protected:
+    SkString onShortName() override { return SkString("convex-lineonly-paths"); }
+    SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
+    bool runAsBench() const override { return true; }
+
+    static SkPath GetPath(int index, int offset, SkPath::Direction dir) {
+        // narrow rect
+        const SkPoint gPoints0[] = {
+            { -1.5f, -50.0f },
+            {  1.5f, -50.0f },
+            {  1.5f,  50.0f },
+            { -1.5f,  50.0f }
+        };
+        // narrow rect on an angle
+        const SkPoint gPoints1[] = {
+            { -50.0f, -49.0f },
+            { -49.0f, -50.0f },
+            {  50.0f,  49.0f },
+            {  49.0f,  50.0f }
+        };
+        // trap - narrow on top - wide on bottom
+        const SkPoint gPoints2[] = {
+            { -10.0f, -50.0f },
+            {  10.0f, -50.0f },
+            {  50.0f,  50.0f },
+            { -50.0f,  50.0f }
+        };
+        // wide skewed rect
+        const SkPoint gPoints3[] = {
+            { -50.0f, -50.0f },
+            {   0.0f, -50.0f },
+            {  50.0f,  50.0f },
+            {   0.0f,  50.0f }
+        };
+        // thin rect with colinear-ish lines
+        const SkPoint gPoints4[] = {
+            { -6.0f, -50.0f },
+            {  4.0f, -50.0f },
+            {  5.0f, -25.0f },
+            {  6.0f,   0.0f },
+            {  5.0f,  25.0f },
+            {  4.0f,  50.0f },
+            { -4.0f,  50.0f }
+        };
+        // degenerate
+        const SkPoint gPoints5[] = {
+            { -0.025f, -0.025f  },
+            {  0.025f, -0.025f  },
+            {  0.025f,  0.025f },
+            { -0.025f,  0.025f }
+        };
+        // Triangle in which the first point should fuse with last
+        const SkPoint gPoints6[] = {
+            { -20.0f, -13.0f },
+            { -20.0f, -13.05f },
+            {  20.0f, -13.0f },
+            {  20.0f,  27.0f }
+        };
+        // thin rect with colinear lines
+        const SkPoint gPoints7[] = {
+            { -10.0f, -50.0f },
+            {  10.0f, -50.0f },
+            {  10.0f, -25.0f },
+            {  10.0f,   0.0f },
+            {  10.0f,  25.0f },
+            {  10.0f,  50.0f },
+            { -10.0f,  50.0f }
+        };
+        // capped teardrop
+        const SkPoint gPoints8[] = {
+            {  50.00f,  50.00f },
+            {   0.00f,  50.00f },
+            { -15.45f,  47.55f },
+            { -29.39f,  40.45f },
+            { -40.45f,  29.39f },
+            { -47.55f,  15.45f },
+            { -50.00f,   0.00f },
+            { -47.55f, -15.45f },
+            { -40.45f, -29.39f },
+            { -29.39f, -40.45f },
+            { -15.45f, -47.55f },
+            {   0.00f, -50.00f },
+            {  50.00f, -50.00f }
+        };
+        // teardrop
+        const SkPoint gPoints9[] = {
+            {   4.39f,  40.45f },
+            {  -9.55f,  47.55f },
+            { -25.00f,  50.00f },
+            { -40.45f,  47.55f },
+            { -54.39f,  40.45f },
+            { -65.45f,  29.39f },
+            { -72.55f,  15.45f },
+            { -75.00f,   0.00f },
+            { -72.55f, -15.45f },
+            { -65.45f, -29.39f },
+            { -54.39f, -40.45f },
+            { -40.45f, -47.55f },
+            { -25.0f,  -50.0f },
+            {  -9.55f, -47.55f },
+            {   4.39f, -40.45f },
+            {  75.00f,   0.00f }
+        };
+        // clipped triangle
+        const SkPoint gPoints10[] = {
+            { -10.0f, -50.0f },
+            {  10.0f, -50.0f },
+            {  50.0f,  31.0f },
+            {  40.0f,  50.0f },
+            { -40.0f,  50.0f },
+            { -50.0f,  31.0f },
+        };
+
+        const SkPoint* gPoints[] = {
+            gPoints0, gPoints1, gPoints2, gPoints3, gPoints4, gPoints5, gPoints6,
+            gPoints7, gPoints8, gPoints9, gPoints10,
+        };
+
+        const size_t gSizes[] = {
+            SK_ARRAY_COUNT(gPoints0),
+            SK_ARRAY_COUNT(gPoints1),
+            SK_ARRAY_COUNT(gPoints2),
+            SK_ARRAY_COUNT(gPoints3),
+            SK_ARRAY_COUNT(gPoints4),
+            SK_ARRAY_COUNT(gPoints5),
+            SK_ARRAY_COUNT(gPoints6),
+            SK_ARRAY_COUNT(gPoints7),
+            SK_ARRAY_COUNT(gPoints8),
+            SK_ARRAY_COUNT(gPoints9),
+            SK_ARRAY_COUNT(gPoints10),
+        };
+        SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gSizes) == SK_ARRAY_COUNT(gPoints), array_mismatch);
+
+        SkAutoTDeleteArray<SkPoint> data(NULL);
+        const SkPoint* points;
+        int numPts;
+        if (index < (int) SK_ARRAY_COUNT(gPoints)) {
+            // manually specified
+            points = gPoints[index];
+            numPts = (int) gSizes[index];
+        } else {
+            // procedurally generated
+            SkScalar width = kMaxPathHeight/2;
+            SkScalar height = kMaxPathHeight/2;
+            switch (index-SK_ARRAY_COUNT(gPoints)) {
+            case 0:
+                numPts = 3;
+                break;
+            case 1:
+                numPts = 4;
+                break;
+            case 2:
+                numPts = 5;
+                break;
+            case 3:             // squashed pentagon
+                numPts = 5;
+                width = kMaxPathHeight/5;
+                break;
+            case 4:
+                numPts = 6;
+                break;
+            case 5:
+                numPts = 8;
+                break;
+            case 6:              // squashed octogon
+                numPts = 8;
+                width = kMaxPathHeight/5;
+                break;
+            case 7:
+                numPts = 20;
+                break;
+            case 8:
+                numPts = 100;
+                break;
+            default:
+                numPts = 3;
+                break;
+            }
+
+            data.reset(SkNEW_ARRAY(SkPoint, numPts));
+
+            create_ngon(numPts, data.get(), width, height);
+            points = data.get();
+        }
+
+        SkPath path;
+
+        if (SkPath::kCW_Direction == dir) {
+            path.moveTo(points[0]);
+            for (int i = 1; i < numPts; ++i) {
+                path.lineTo(points[i]);
+            }
+        } else {
+            path.moveTo(points[numPts-1]);
+            for (int i = numPts-2; i >= 0; --i) {
+                path.lineTo(points[i]);
+            }
+        }
+
+        path.close();
+#ifdef SK_DEBUG
+        // Each path this method returns should be convex, only composed of
+        // lines, wound the right direction, and short enough to fit in one
+        // of the GMs rows.
+        SkASSERT(path.isConvex());
+        SkASSERT(SkPath::kLine_SegmentMask == path.getSegmentMasks());
+        SkPath::Direction actualDir;
+        SkASSERT(path.cheapComputeDirection(&actualDir));
+        SkASSERT(dir == actualDir);
+        SkRect bounds = path.getBounds();
+        SkASSERT(SkScalarNearlyEqual(bounds.centerX(), 0.0f));
+        SkASSERT(bounds.height() <= kMaxPathHeight);
+#endif
+        return path;
+    }
+
+    // Draw a single path several times, shrinking it, flipping its direction
+    // and changing its start vertex each time.
+    void drawPath(SkCanvas* canvas, int index, SkPoint* offset) {
+
+        SkPoint center;
+        {
+            SkPath path = GetPath(index, 0, SkPath::kCW_Direction);
+            if (offset->fX+path.getBounds().width() > kGMWidth) {
+                offset->fX = 0;
+                offset->fY += kMaxPathHeight;
+            }
+            center = { offset->fX + SkScalarHalf(path.getBounds().width()), offset->fY};
+            offset->fX += path.getBounds().width();
+        }
+
+        const SkColor colors[2] = { SK_ColorBLACK, SK_ColorWHITE };
+        const SkPath::Direction dirs[2] = { SkPath::kCW_Direction, SkPath::kCCW_Direction };
+        const float scales[] = { 1.0f, 0.75f, 0.5f, 0.25f, 0.1f, 0.01f, 0.001f };
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(scales); ++i) {
+            SkPath path = GetPath(index, (int) i, dirs[i%2]);
+
+            canvas->save();
+                canvas->translate(center.fX, center.fY);
+                canvas->scale(scales[i], scales[i]);
+                paint.setColor(colors[i%2]);
+                canvas->drawPath(path, paint);
+            canvas->restore();
+        }
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        // the right edge of the last drawn path
+        SkPoint offset = { 0, SkScalarHalf(kMaxPathHeight) }; 
+
+        for (int i = 0; i < kNumPaths; ++i) {
+            this->drawPath(canvas, i, &offset);
+        }
+
+        // Repro for crbug.com/472723 (Missing AA on portions of graphic with GPU rasterization)
+        {
+            canvas->translate(356.0f, 50.0f);
+
+            SkPaint p;
+            p.setAntiAlias(true);
+
+            SkPath p1;
+            p1.moveTo(60.8522949f, 364.671021f);
+            p1.lineTo(59.4380493f, 364.671021f);
+            p1.lineTo(385.414276f, 690.647217f);
+            p1.lineTo(386.121399f, 689.940125f);
+            canvas->drawPath(p1, p);
+        }
+    }
+
+private:
+    static const int kNumPaths      = 20;
+    static const int kMaxPathHeight = 100;
+    static const int kGMWidth       = 512;
+    static const int kGMHeight      = 512;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(ConvexLineOnlyPathsGM); )
+
+}
diff --git a/gm/convexpolyclip.cpp b/gm/convexpolyclip.cpp
index 8d54b00..7e61673 100644
--- a/gm/convexpolyclip.cpp
+++ b/gm/convexpolyclip.cpp
@@ -79,11 +79,11 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("convex_poly_clip");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         // When benchmarking the saveLayer set of draws is skipped.
         int w = 435;
         if (kBench_Mode != this->getMode()) {
@@ -92,7 +92,7 @@
         return SkISize::Make(w, 540);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         SkPath tri;
         tri.moveTo(5.f, 5.f);
         tri.lineTo(100.f, 20.f);
@@ -135,7 +135,7 @@
         fBmp = make_bmp(100, 100);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkScalar y = 0;
         static const SkScalar kMargin = 10.f;
 
@@ -214,7 +214,7 @@
         }
     }
 
-    bool runAsBench() const SK_OVERRIDE { return true; }
+    bool runAsBench() const override { return true; }
 
 private:
     class Clip {
diff --git a/gm/convexpolyeffect.cpp b/gm/convexpolyeffect.cpp
index f3335c9..9c6741a 100644
--- a/gm/convexpolyeffect.cpp
+++ b/gm/convexpolyeffect.cpp
@@ -13,7 +13,6 @@
 #if SK_SUPPORT_GPU
 
 #include "GrBatchTarget.h"
-#include "GrBufferAllocPool.h"
 #include "GrContext.h"
 #include "GrDefaultGeoProcFactory.h"
 #include "GrPathUtils.h"
@@ -34,7 +33,7 @@
         SkRect fBounds;
     };
 
-    const char* name() const SK_OVERRIDE { return "ConvexPolyTestBatch"; }
+    const char* name() const override { return "ConvexPolyTestBatch"; }
 
     static GrBatch* Create(const GrGeometryProcessor* gp, const Geometry& geo) {
         return SkNEW_ARGS(ConvexPolyTestBatch, (gp, geo));
@@ -42,55 +41,39 @@
 
 private:
     ConvexPolyTestBatch(const GrGeometryProcessor* gp, const Geometry& geo)
-        : INHERITED(gp)
+        : INHERITED(gp, geo.fBounds)
         , fGeometry(geo) {
     }
 
-    Geometry* geoData(int index) SK_OVERRIDE {
+    Geometry* geoData(int index) override {
         SkASSERT(0 == index);
         return &fGeometry;
     }
 
-    void onGenerateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    const Geometry* geoData(int index) const override {
+        SkASSERT(0 == index);
+        return &fGeometry;
+    }
+
+    void onGenerateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         size_t vertexStride = this->geometryProcessor()->getVertexStride();
-
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
-
-        void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              kVertsPerCubic,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices || !batchTarget->quadIndexBuffer()) {
-            SkDebugf("Could not allocate buffers\n");
+        SkASSERT(vertexStride == sizeof(SkPoint));
+        QuadHelper helper;
+        SkPoint* verts = reinterpret_cast<SkPoint*>(helper.init(batchTarget, vertexStride, 1));
+        if (!verts) {
             return;
         }
 
-        SkASSERT(vertexStride == sizeof(SkPoint));
-        SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
-
         // Make sure any artifacts around the exterior of path are visible by using overly
         // conservative bounding geometry.
         fGeometry.fBounds.outset(5.f, 5.f);
         fGeometry.fBounds.toQuad(verts);
 
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangleFan_GrPrimitiveType);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setStartVertex(firstVertex);
-        drawInfo.setVertexCount(kVertsPerCubic);
-        drawInfo.setStartIndex(0);
-        drawInfo.setIndexCount(kIndicesPerCubic);
-        drawInfo.setIndexBuffer(batchTarget->quadIndexBuffer());
-        batchTarget->draw(drawInfo);
+        helper.issueDraw(batchTarget);
     }
 
     Geometry fGeometry;
 
-    static const int kVertsPerCubic = 4;
-    static const int kIndicesPerCubic = 6;
-
     typedef GrTestBatch INHERITED;
 };
 
@@ -104,15 +87,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("convex_poly_effect");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(720, 800);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         SkPath tri;
         tri.moveTo(5.f, 5.f);
         tri.lineTo(100.f, 20.f);
@@ -162,7 +145,7 @@
         fRects.addToTail(SkRect::MakeLTRB(100.f, 50.5f, 5.f, 0.5f));
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         if (NULL == rt) {
             this->drawGpuOnlyMessage(canvas);
@@ -173,9 +156,9 @@
             return;
         }
 
+        static const GrColor color = 0xff000000;
         SkAutoTUnref<const GrGeometryProcessor> gp(
-                GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
-                                                0xff000000));
+                GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType, color));
 
         SkScalar y = 0;
         for (SkTLList<SkPath>::Iter iter(fPaths, SkTLList<SkPath>::Iter::kHead_IterStart);
@@ -191,9 +174,8 @@
                     SkDEBUGFAIL("Couldn't get Gr test target.");
                     return;
                 }
-                SkMatrix m;
+                const SkMatrix m = SkMatrix::MakeTrans(x, y);
                 SkPath p;
-                m.setTranslate(x, y);
                 path->transform(m, &p);
 
                 GrPrimitiveEdgeType edgeType = (GrPrimitiveEdgeType) et;
@@ -207,12 +189,12 @@
                 pipelineBuilder.setRenderTarget(rt);
 
                 ConvexPolyTestBatch::Geometry geometry;
-                geometry.fColor = gp->color();
+                geometry.fColor = color;
                 geometry.fBounds = p.getBounds();
 
                 SkAutoTUnref<GrBatch> batch(ConvexPolyTestBatch::Create(gp, geometry));
 
-                tt.target()->drawBatch(&pipelineBuilder, batch, NULL);
+                tt.target()->drawBatch(&pipelineBuilder, batch);
 
                 x += SkScalarCeilToScalar(path->getBounds().width() + 10.f);
             }
@@ -256,12 +238,12 @@
                 pipelineBuilder.setRenderTarget(rt);
 
                 ConvexPolyTestBatch::Geometry geometry;
-                geometry.fColor = gp->color();
+                geometry.fColor = color;
                 geometry.fBounds = rect;
 
                 SkAutoTUnref<GrBatch> batch(ConvexPolyTestBatch::Create(gp, geometry));
 
-                tt.target()->drawBatch(&pipelineBuilder, batch, NULL);
+                tt.target()->drawBatch(&pipelineBuilder, batch);
 
                 x += SkScalarCeilToScalar(rect.width() + 10.f);
             }
diff --git a/gm/dashing.cpp b/gm/dashing.cpp
index ba3ede4..bd906cb 100644
--- a/gm/dashing.cpp
+++ b/gm/dashing.cpp
@@ -403,9 +403,9 @@
 
 protected:
 
-    bool runAsBench() const SK_OVERRIDE { return true; }
+    bool runAsBench() const override { return true; }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         if (fDoAA) {
             return SkString("dashing5_aa");
         } else {
@@ -413,9 +413,9 @@
         }
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(400, 200); }
+    SkISize onISize() override { return SkISize::Make(400, 200); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         static const int kOn = 4;
         static const int kOff = 4;
         static const int kIntervalLength = kOn + kOff;
diff --git a/gm/dcshader.cpp b/gm/dcshader.cpp
index 4b8f5ed..6cfd00e 100644
--- a/gm/dcshader.cpp
+++ b/gm/dcshader.cpp
@@ -29,16 +29,16 @@
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DCShader);
 
-    void flatten(SkWriteBuffer& buf) const SK_OVERRIDE {
+    void flatten(SkWriteBuffer& buf) const override {
         buf.writeMatrix(fDeviceMatrix);
     }
 
     bool asFragmentProcessor(GrContext*, const SkPaint& paint, const SkMatrix& viewM,
                              const SkMatrix* localMatrix, GrColor* color,
-                             GrFragmentProcessor** fp) const SK_OVERRIDE;
+                             GrFragmentProcessor** fp) const override;
 
 #ifndef SK_IGNORE_TO_STRING
-    void toString(SkString* str) const SK_OVERRIDE {
+    void toString(SkString* str) const override {
         str->appendf("DCShader: ()");
     }
 #endif
@@ -60,10 +60,10 @@
         this->initClassID<DCFP>();
     }
 
-    void getGLProcessorKey(const GrGLCaps& caps,
-                            GrProcessorKeyBuilder* b) const SK_OVERRIDE {}
+    void getGLProcessorKey(const GrGLSLCaps& caps,
+                            GrProcessorKeyBuilder* b) const override {}
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
+    GrGLFragmentProcessor* createGLInstance() const override {
         class DCGLFP : public GrGLFragmentProcessor {
             void emitCode(GrGLFPBuilder* builder,
                             const GrFragmentProcessor& fp,
@@ -71,7 +71,7 @@
                             const char* inputColor,
                             const TransformedCoordsArray& coords,
                             const TextureSamplerArray& samplers) {
-                GrGLFPFragmentBuilder* fpb = builder->getFragmentShaderBuilder();
+                GrGLFragmentBuilder* fpb = builder->getFragmentShaderBuilder();
                 fpb->codeAppendf("vec2 c = %s;", fpb->ensureFSCoords2D(coords, 0).c_str());
                 fpb->codeAppend("vec2 r = mod(c, vec2(20.0));");
                 fpb->codeAppend("vec4 color = vec4(0.5*sin(c.x / 15.0) + 0.5,"
@@ -82,19 +82,19 @@
                                     "%s = color * %s;",
                                     outputColor, GrGLSLExpr4(inputColor).c_str());
             }
-            void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE {}
+            void setData(const GrGLProgramDataManager&, const GrProcessor&) override {}
         };
         return SkNEW(DCGLFP);
     }
 
-    const char* name() const SK_OVERRIDE { return "DCFP"; }
+    const char* name() const override { return "DCFP"; }
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE {
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
         inout->mulByUnknownFourComponents();
     }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE { return true; }
+    bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
 
     GrCoordTransform fDeviceTransform;
 };
@@ -113,7 +113,7 @@
         this->setBGColor(0xFFAABBCC);
     }
 
-    ~DCShaderGM() SK_OVERRIDE {
+    ~DCShaderGM() override {
         for (int i = 0; i < fPrims.count(); ++i) {
             SkDELETE(fPrims[i]);
         }
@@ -121,15 +121,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("dcshader");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(1000, 900); }
+    SkISize onISize() override { return SkISize::Make(1000, 900); }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         struct Rect : public Prim {
-            SkRect draw(SkCanvas* canvas, const SkPaint& paint) SK_OVERRIDE {
+            SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
                 SkRect rect = SkRect::MakeXYWH(0, 0, 50, 50);
                 canvas->drawRect(rect, paint);
                 return rect;
@@ -137,7 +137,7 @@
         };
 
         struct Circle : public Prim {
-            SkRect draw(SkCanvas* canvas, const SkPaint& paint) SK_OVERRIDE {
+            SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
                 static const SkScalar radius = 25;
                 canvas->drawCircle(radius, radius, radius, paint);
                 return SkRect::MakeXYWH(0, 0, 2 * radius, 2 * radius);
@@ -145,7 +145,7 @@
         };
 
         struct RRect : public Prim {
-            SkRect draw(SkCanvas* canvas, const SkPaint& paint) SK_OVERRIDE {
+            SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
                 SkRRect rrect;
                 rrect.setRectXY(SkRect::MakeXYWH(0, 0, 50, 50), 10, 10);
                 canvas->drawRRect(rrect, paint);
@@ -154,7 +154,7 @@
         };
 
         struct DRRect : public Prim {
-            SkRect draw(SkCanvas* canvas, const SkPaint& paint) SK_OVERRIDE {
+            SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
                 SkRRect outerRRect;
                 outerRRect.setRectXY(SkRect::MakeXYWH(0, 0, 50, 50), 5, 5);
                 SkRRect innerRRect;
@@ -164,7 +164,7 @@
             }
         };
         struct Path : public Prim {
-            SkRect draw(SkCanvas* canvas, const SkPaint& paint) SK_OVERRIDE {
+            SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
                 SkPath path;
                 path.addCircle(15, 15, 10);
                 path.addOval(SkRect::MakeXYWH(2, 2, 22, 37));
@@ -177,7 +177,7 @@
         struct Points : public Prim {
             Points(SkCanvas::PointMode mode) : fMode(mode) {}
 
-            SkRect draw(SkCanvas* canvas, const SkPaint& paint) SK_OVERRIDE {
+            SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
                 SkRandom random;
                 SkPoint points[500];
                 SkRect bounds = SkRect::MakeWH(50, 50);
@@ -196,7 +196,7 @@
         };
 
         struct Text : public Prim {
-            SkRect draw(SkCanvas* canvas, const SkPaint& origPaint) SK_OVERRIDE {
+            SkRect draw(SkCanvas* canvas, const SkPaint& origPaint) override {
                 SkPaint paint = origPaint;
                 paint.setTextSize(30.f);
                 this->setFont(&paint);
@@ -217,21 +217,14 @@
         };
 
         struct BmpText : public Text {
-           void setFont(SkPaint* paint) SK_OVERRIDE {
+           void setFont(SkPaint* paint) override {
                if (!fTypeface) {
-                    SkString filename = GetResourcePath("/Funkster.ttf");
-                    SkAutoTDelete<SkFILEStream> stream(new SkFILEStream(filename.c_str()));
-                    if (!stream->isValid()) {
-                        SkDebugf("Could not find Funkster.ttf, please set --resourcePath "
-                                 "correctly.\n");
-                        return;
-                    }
-                    fTypeface.reset(SkTypeface::CreateFromStream(stream.detach()));
+                    fTypeface.reset(GetResourceAsTypeface("/fonts/Funkster.ttf"));
                }
                paint->setTypeface(fTypeface);
             }
 
-            const char* text() const SK_OVERRIDE { return "Hi, Skia!"; }
+            const char* text() const override { return "Hi, Skia!"; }
 
             SkAutoTUnref<SkTypeface> fTypeface;
         };
@@ -247,7 +240,7 @@
         fPrims.push_back(SkNEW(BmpText));
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         // This GM exists to test a specific feature of the GPU backend. It does not work with the
         // sw rasterizer, tile modes, etc.
         if (NULL == canvas->getGrContext()) {
diff --git a/gm/dftext.cpp b/gm/dftext.cpp
index 6544e89..63e7cdf 100755
--- a/gm/dftext.cpp
+++ b/gm/dftext.cpp
@@ -23,22 +23,15 @@
     }
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
-        SkString filename = GetResourcePath("/Funkster.ttf");
-        SkAutoTDelete<SkFILEStream> stream(new SkFILEStream(filename.c_str()));
-        if (!stream->isValid()) {
-            SkDebugf("Could not find Funkster.ttf, please set --resourcePath correctly.\n");
-            return;
-        }
-
-        fTypeface = SkTypeface::CreateFromStream(stream.detach());
+    void onOnceBeforeDraw() override {
+        fTypeface = GetResourceAsTypeface("/fonts/Funkster.ttf");
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("dftext");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1024, 768);
     }
 
@@ -50,7 +43,7 @@
         canvas->translate(-px, -py);
     }
 
-    virtual void onDraw(SkCanvas* inputCanvas) SK_OVERRIDE {
+    virtual void onDraw(SkCanvas* inputCanvas) override {
 #ifdef SK_BUILD_FOR_ANDROID
         SkScalar textSizes[] = { 9.0f, 9.0f*2.0f, 9.0f*5.0f, 9.0f*2.0f*5.0f };
 #else
@@ -196,8 +189,29 @@
             y += paint.getFontMetrics(NULL);
         }
 
+        // check skew
+        {
+            paint.setLCDRenderText(false);
+            SkAutoCanvasRestore acr(canvas, true);
+            canvas->skew(0.0f, 0.151515f);
+            paint.setTextSize(SkIntToScalar(32));
+            canvas->drawText(text, textLen, 745, 70, paint);
+        }
+        {
+            paint.setLCDRenderText(true);
+            SkAutoCanvasRestore acr(canvas, true);
+            canvas->skew(0.5f, 0.0f);
+            paint.setTextSize(SkIntToScalar(32));
+            canvas->drawText(text, textLen, 580, 230, paint);
+        }
+
         // check color emoji
         paint.setTypeface(fTypeface);
+#ifdef SK_BUILD_FOR_ANDROID
+        paint.setTextSize(SkIntToScalar(19));
+#else
+        paint.setTextSize(SkIntToScalar(22));
+#endif
         canvas->drawText(text, textLen, 670, 100, paint);
 
 #if SK_SUPPORT_GPU
diff --git a/gm/discard.cpp b/gm/discard.cpp
index fc271d2..a762288 100644
--- a/gm/discard.cpp
+++ b/gm/discard.cpp
@@ -26,15 +26,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("discard");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(100, 100);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         GrContext* context = canvas->getGrContext();
         if (NULL == context) {
             return;
diff --git a/gm/displacement.cpp b/gm/displacement.cpp
index bcf759a..7b3d39c 100644
--- a/gm/displacement.cpp
+++ b/gm/displacement.cpp
@@ -5,10 +5,10 @@
  * found in the LICENSE file.
  */
 
-#include "sk_tool_utils.h"
 #include "SkBitmapSource.h"
 #include "SkDisplacementMapEffect.h"
 #include "gm.h"
+#include "sk_tool_utils.h"
 
 namespace skiagm {
 
@@ -68,7 +68,7 @@
             make_bitmaps();
             fInitialized = true;
         }
-        canvas->clear(0x00000000);
+        canvas->clear(SK_ColorBLACK);
         SkPaint paint;
         SkAutoTUnref<SkImageFilter> displ(SkBitmapSource::Create(fCheckerboard));
         paint.setImageFilter(SkDisplacementMapEffect::Create(
diff --git a/gm/downsamplebitmap.cpp b/gm/downsamplebitmap.cpp
index 15280f2..8decc69 100644
--- a/gm/downsamplebitmap.cpp
+++ b/gm/downsamplebitmap.cpp
@@ -39,29 +39,29 @@
     SkBitmap    fBM;
     SkString    fName;
     bool        fBitmapMade;
-    SkPaint::FilterLevel fFilterLevel;
+    SkFilterQuality fFilterQuality;
 
-    DownsampleBitmapGM(SkPaint::FilterLevel filterLevel)
-        : fFilterLevel(filterLevel)
+    DownsampleBitmapGM(SkFilterQuality filterQuality)
+        : fFilterQuality(filterQuality)
     {
         this->setBGColor(0xFFDDDDDD);
         fBitmapMade = false;
     }
 
-    const char* filterLevelToString() {
-        static const char *filterLevelNames[] = {
+    const char* filterQualityToString() {
+        static const char *filterQualityNames[] = {
             "none", "low", "medium", "high"
         };
-        return filterLevelNames[fFilterLevel];
+        return filterQualityNames[fFilterQuality];
     }
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         make_bitmap_wrapper();
         return SkISize::Make(fBM.width(), 4 * fBM.height());
     }
@@ -75,7 +75,7 @@
 
     virtual void make_bitmap() = 0;
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         make_bitmap_wrapper();
 
         int curY = 0;
@@ -87,7 +87,7 @@
             matrix.setScale( curScale, curScale );
 
             SkPaint paint;
-            paint.setFilterLevel(fFilterLevel);
+            paint.setFilterQuality(fFilterQuality);
 
             canvas->save();
             canvas->translate(0, (SkScalar)curY);
@@ -107,16 +107,16 @@
 
 class DownsampleBitmapTextGM: public DownsampleBitmapGM {
   public:
-      DownsampleBitmapTextGM(float textSize, SkPaint::FilterLevel filterLevel)
-      : INHERITED(filterLevel), fTextSize(textSize)
+      DownsampleBitmapTextGM(float textSize, SkFilterQuality filterQuality)
+      : INHERITED(filterQuality), fTextSize(textSize)
         {
-            fName.printf("downsamplebitmap_text_%s_%.2fpt", this->filterLevelToString(), fTextSize);
+            fName.printf("downsamplebitmap_text_%s_%.2fpt", this->filterQualityToString(), fTextSize);
         }
 
   protected:
       float fTextSize;
 
-      void make_bitmap() SK_OVERRIDE {
+      void make_bitmap() override {
           fBM.allocN32Pixels(int(fTextSize * 8), int(fTextSize * 6));
           SkCanvas canvas(fBM);
           canvas.drawColor(SK_ColorWHITE);
@@ -141,17 +141,17 @@
 
 class DownsampleBitmapCheckerboardGM: public DownsampleBitmapGM {
   public:
-      DownsampleBitmapCheckerboardGM(int size, int numChecks, SkPaint::FilterLevel filterLevel)
-      : INHERITED(filterLevel), fSize(size), fNumChecks(numChecks)
+      DownsampleBitmapCheckerboardGM(int size, int numChecks, SkFilterQuality filterQuality)
+      : INHERITED(filterQuality), fSize(size), fNumChecks(numChecks)
         {
-            fName.printf("downsamplebitmap_checkerboard_%s_%d_%d", this->filterLevelToString(), fSize, fNumChecks);
+            fName.printf("downsamplebitmap_checkerboard_%s_%d_%d", this->filterQualityToString(), fSize, fNumChecks);
         }
 
   protected:
       int fSize;
       int fNumChecks;
 
-      void make_bitmap() SK_OVERRIDE {
+      void make_bitmap() override {
           make_checker(&fBM, fSize, fNumChecks);
       }
   private:
@@ -160,17 +160,17 @@
 
 class DownsampleBitmapImageGM: public DownsampleBitmapGM {
   public:
-      DownsampleBitmapImageGM(const char filename[], SkPaint::FilterLevel filterLevel)
-      : INHERITED(filterLevel), fFilename(filename)
+      DownsampleBitmapImageGM(const char filename[], SkFilterQuality filterQuality)
+      : INHERITED(filterQuality), fFilename(filename)
         {
-            fName.printf("downsamplebitmap_image_%s_%s", this->filterLevelToString(), filename);
+            fName.printf("downsamplebitmap_image_%s_%s", this->filterQualityToString(), filename);
         }
 
   protected:
       SkString fFilename;
       int fSize;
 
-      void make_bitmap() SK_OVERRIDE {
+      void make_bitmap() override {
           SkImageDecoder* codec = NULL;
           SkString resourcePath = GetResourcePath(fFilename.c_str());
           SkFILEStream stream(resourcePath.c_str());
@@ -208,15 +208,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("showmiplevels");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(fBM.width() + 8, 2 * fBM.height() + 80);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkScalar x = 4;
         SkScalar y = 4;
         canvas->drawBitmap(fBM, x, y, NULL);
@@ -248,26 +248,26 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-DEF_GM( return new DownsampleBitmapTextGM(72, SkPaint::kHigh_FilterLevel); )
-DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, SkPaint::kHigh_FilterLevel); )
-DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", SkPaint::kHigh_FilterLevel); )
+DEF_GM( return new DownsampleBitmapTextGM(72, kHigh_SkFilterQuality); )
+DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, kHigh_SkFilterQuality); )
+DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", kHigh_SkFilterQuality); )
 DEF_GM( return new DownsampleBitmapImageGM("mandrill_132x132_12x12.astc",
-                                            SkPaint::kHigh_FilterLevel); )
+                                            kHigh_SkFilterQuality); )
 
-DEF_GM( return new DownsampleBitmapTextGM(72, SkPaint::kMedium_FilterLevel); )
-DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, SkPaint::kMedium_FilterLevel); )
-DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", SkPaint::kMedium_FilterLevel); )
+DEF_GM( return new DownsampleBitmapTextGM(72, kMedium_SkFilterQuality); )
+DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, kMedium_SkFilterQuality); )
+DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", kMedium_SkFilterQuality); )
 DEF_GM( return new DownsampleBitmapImageGM("mandrill_132x132_12x12.astc",
-                                           SkPaint::kMedium_FilterLevel); )
+                                           kMedium_SkFilterQuality); )
 
-DEF_GM( return new DownsampleBitmapTextGM(72, SkPaint::kLow_FilterLevel); )
-DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, SkPaint::kLow_FilterLevel); )
-DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", SkPaint::kLow_FilterLevel); )
+DEF_GM( return new DownsampleBitmapTextGM(72, kLow_SkFilterQuality); )
+DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, kLow_SkFilterQuality); )
+DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", kLow_SkFilterQuality); )
 DEF_GM( return new DownsampleBitmapImageGM("mandrill_132x132_12x12.astc",
-                                           SkPaint::kLow_FilterLevel); )
+                                           kLow_SkFilterQuality); )
 
-DEF_GM( return new DownsampleBitmapTextGM(72, SkPaint::kNone_FilterLevel); )
-DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, SkPaint::kNone_FilterLevel); )
-DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", SkPaint::kNone_FilterLevel); )
+DEF_GM( return new DownsampleBitmapTextGM(72, kNone_SkFilterQuality); )
+DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, kNone_SkFilterQuality); )
+DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", kNone_SkFilterQuality); )
 DEF_GM( return new DownsampleBitmapImageGM("mandrill_132x132_12x12.astc",
-                                           SkPaint::kNone_FilterLevel); )
+                                           kNone_SkFilterQuality); )
diff --git a/gm/drawbitmaprect.cpp b/gm/drawbitmaprect.cpp
index 39d46db..715f45b 100644
--- a/gm/drawbitmaprect.cpp
+++ b/gm/drawbitmaprect.cpp
@@ -141,15 +141,15 @@
     SkString              fName;
 
 protected:
-    SkString onShortName() SK_OVERRIDE { return fName; }
+    SkString onShortName() override { return fName; }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(gSize, gSize); }
+    SkISize onISize() override { return SkISize::Make(gSize, gSize); }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         fImage.reset(makebm(&fLargeBitmap, gBmpSize, gBmpSize));
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkRect dstRect = { 0, 0, SkIntToScalar(64), SkIntToScalar(64)};
         static const int kMaxSrcRectSize = 1 << (SkNextLog2(gBmpSize) + 2);
 
@@ -217,7 +217,7 @@
             SkBitmap bm;
 
             bm = make_chessbm(5, 5);
-            paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+            paint.setFilterQuality(kLow_SkFilterQuality);
 
             srcRect.setXYWH(1, 1, 3, 3);
             SkMaskFilter* mf = SkBlurMaskFilter::Create(
diff --git a/gm/drawfilter.cpp b/gm/drawfilter.cpp
index 85bec15..8c29f07 100644
--- a/gm/drawfilter.cpp
+++ b/gm/drawfilter.cpp
@@ -19,34 +19,36 @@
  * the second red and sharp.
  */
 
+namespace {
 class TestFilter : public SkDrawFilter {
 public:
-    bool filter(SkPaint* p, Type) SK_OVERRIDE {
+    bool filter(SkPaint* p, Type) override {
         p->setColor(SK_ColorRED);
         p->setMaskFilter(NULL);
         return true;
     }
 };
+}
 
 class DrawFilterGM : public skiagm::GM {
     SkAutoTUnref<SkMaskFilter> fBlur;
 
 protected:
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(320, 240);
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("drawfilter");
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         fBlur.reset(SkBlurMaskFilter::Create(kNormal_SkBlurStyle,
                     SkBlurMask::ConvertRadiusToSigma(10.0f),
                     kLow_SkBlurQuality));
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint p;
         p.setColor(SK_ColorBLUE);
         p.setMaskFilter(fBlur.get());
diff --git a/gm/drawlooper.cpp b/gm/drawlooper.cpp
index 56b4125..5fcdc30 100644
--- a/gm/drawlooper.cpp
+++ b/gm/drawlooper.cpp
@@ -27,15 +27,15 @@
     }
 
 protected:
-    virtual SkISize onISize() SK_OVERRIDE {
+    virtual SkISize onISize() override {
         return SkISize::Make(520, 160);
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("drawlooper");
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         this->init();
 
         SkPaint  paint;
diff --git a/gm/dropshadowimagefilter.cpp b/gm/dropshadowimagefilter.cpp
index f60f22c..e06264c 100644
--- a/gm/dropshadowimagefilter.cpp
+++ b/gm/dropshadowimagefilter.cpp
@@ -133,11 +133,11 @@
             SkDropShadowImageFilter::Create(7.0f, 7.0f, 3.0f, 3.0f, SK_ColorBLUE,
                 SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode),
             SkDropShadowImageFilter::Create(7.0f, 7.0f, 3.0f, 3.0f, SK_ColorBLUE,
-                SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, cfif, NULL, 0),
+                SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, cfif, NULL),
             SkDropShadowImageFilter::Create(7.0f, 7.0f, 3.0f, 3.0f, SK_ColorBLUE,
-                SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, NULL, &cropRect, 0),
+                SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, NULL, &cropRect),
             SkDropShadowImageFilter::Create(7.0f, 7.0f, 3.0f, 3.0f, SK_ColorBLUE,
-                SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, NULL, &bogusRect, 0),
+                SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, NULL, &bogusRect),
             SkDropShadowImageFilter::Create(7.0f, 7.0f, 3.0f, 3.0f, SK_ColorBLUE,
                 SkDropShadowImageFilter::kDrawShadowOnly_ShadowMode),
         };
diff --git a/gm/drrect.cpp b/gm/drrect.cpp
index fd5a936..e3d3e6e 100644
--- a/gm/drrect.cpp
+++ b/gm/drrect.cpp
@@ -16,15 +16,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("drrect");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
 
diff --git a/gm/dstreadshuffle.cpp b/gm/dstreadshuffle.cpp
index b98b10e..756e922 100644
--- a/gm/dstreadshuffle.cpp
+++ b/gm/dstreadshuffle.cpp
@@ -33,11 +33,11 @@
         kNumShapeTypes
     };
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("dstreadshuffle");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(kWidth, kHeight);
     }
 
@@ -94,24 +94,32 @@
         }
     }
 
-    static SkColor GetColor(SkRandom* random, int i) {
+    static SkColor GetColor(SkRandom* random, int i, int nextColor) {
+        static SkColor colors[] = { SK_ColorRED,
+                                    0xFFFF7F00, // Orange
+                                    SK_ColorYELLOW,
+                                    SK_ColorGREEN,
+                                    SK_ColorBLUE,
+                                    0xFF4B0082, // indigo
+                                    0xFF7F00FF }; // violet
         SkColor color;
+        int index = nextColor % SK_ARRAY_COUNT(colors);
         switch (i) {
             case 0:
                 color = SK_ColorTRANSPARENT;
                 break;
             case 1:
                 color = SkColorSetARGB(0xff,
-                                       random->nextULessThan(256),
-                                       random->nextULessThan(256),
-                                       random->nextULessThan(256));
+                                       SkColorGetR(colors[index]),
+                                       SkColorGetG(colors[index]),
+                                       SkColorGetB(colors[index]));
                 break;
             default:
-                uint8_t alpha = random->nextULessThan(256);
+                uint8_t alpha = 0x80;
                 color = SkColorSetARGB(alpha,
-                                       random->nextRangeU(0, alpha),
-                                       random->nextRangeU(0, alpha),
-                                       random->nextRangeU(0, alpha));
+                                       SkColorGetR(colors[index]),
+                                       SkColorGetG(colors[index]),
+                                       SkColorGetB(colors[index]));
                 break;
         }
         return color;
@@ -133,7 +141,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkRandom random;
         SkScalar y = 100;
         for (int i = 0; i < kNumShapeTypes; i++) {
@@ -143,12 +151,14 @@
                 for (int width = 0; width <= 1; width++) {
                     for (int alpha = 0; alpha <= 2; alpha++) {
                         for (int r = 0; r <= 5; r++) {
-                            SkColor color = GetColor(&random, alpha);
+                            SkColor color = GetColor(&random, alpha, style + width + alpha + r);
 
                             SkPaint p;
                             p.setAntiAlias(true);
                             p.setColor(color);
-                            p.setXfermodeMode(r % 3 == 0 ? SkXfermode::kHardLight_Mode :
+                            // In order to get some batching on the GPU backend we do 2 src over for
+                            // each xfer mode which requires a dst read
+                            p.setXfermodeMode(r % 3 == 0 ? SkXfermode::kLighten_Mode :
                                                            SkXfermode::kSrcOver_Mode);
                             SetStyle(&p, style, width);
                             canvas->save();
diff --git a/gm/emboss.cpp b/gm/emboss.cpp
index a72e64d..d3a8937 100644
--- a/gm/emboss.cpp
+++ b/gm/emboss.cpp
@@ -29,15 +29,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("emboss");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(600, 120);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         SkBitmap bm = make_bm();
         canvas->drawBitmap(bm, 10, 10, &paint);
diff --git a/gm/etc1bitmap.cpp b/gm/etc1bitmap.cpp
index e93ed1d..c66f7b2 100644
--- a/gm/etc1bitmap.cpp
+++ b/gm/etc1bitmap.cpp
@@ -81,19 +81,19 @@
     virtual ~ETC1BitmapGM() { }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString str = SkString("etc1bitmap_");
         str.append(this->fileExtension());
         return str;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(128, 128);
     }
 
     virtual SkString fileExtension() const = 0;
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkBitmap bm;
         SkString filename = GetResourcePath("mandrill_128.");
         filename.append(this->fileExtension());
@@ -123,7 +123,7 @@
 
 protected:
 
-    SkString fileExtension() const SK_OVERRIDE { return SkString("pkm"); }
+    SkString fileExtension() const override { return SkString("pkm"); }
 
 private:
     typedef ETC1BitmapGM INHERITED;
@@ -137,7 +137,7 @@
 
 protected:
 
-    SkString fileExtension() const SK_OVERRIDE { return SkString("ktx"); }
+    SkString fileExtension() const override { return SkString("ktx"); }
 
 private:
     typedef ETC1BitmapGM INHERITED;
@@ -151,7 +151,7 @@
 
 protected:
 
-    SkString fileExtension() const SK_OVERRIDE { return SkString("r11.ktx"); }
+    SkString fileExtension() const override { return SkString("r11.ktx"); }
 
 private:
     typedef ETC1BitmapGM INHERITED;
@@ -170,15 +170,15 @@
     virtual ~ETC1Bitmap_NPOT_GM() { }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("etc1bitmap_npot");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(124, 124);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkBitmap bm;
         SkString pkmFilename = GetResourcePath("mandrill_128.pkm");
         SkAutoDataUnref fileData(SkData::NewFromFileName(pkmFilename.c_str()));
diff --git a/gm/extractbitmap.cpp b/gm/extractbitmap.cpp
index c324483..1b9f018 100644
--- a/gm/extractbitmap.cpp
+++ b/gm/extractbitmap.cpp
@@ -32,15 +32,15 @@
 
 protected:
     // overrides from SkEventSink
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("extractbitmap");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(600, 600);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkBitmap bitmap;
         create_bitmap(&bitmap);
         int x = bitmap.width() / 2;
diff --git a/gm/factory.cpp b/gm/factory.cpp
index 08701d3..519b8ea 100644
--- a/gm/factory.cpp
+++ b/gm/factory.cpp
@@ -27,7 +27,7 @@
     FactoryGM() {}
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         // Copyright-free file from http://openclipart.org/detail/29213/paper-plane-by-ddoo
         SkString pngFilename = GetResourcePath("plane.png");
         SkAutoDataUnref data(SkData::NewFromFileName(pngFilename.c_str()));
@@ -41,15 +41,15 @@
         }
     }
 
-    virtual SkString onShortName() SK_OVERRIDE {
+    virtual SkString onShortName() override {
         return SkString("factory");
     }
 
-    virtual SkISize onISize() SK_OVERRIDE {
+    virtual SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    virtual void onDraw(SkCanvas* canvas) override {
         canvas->drawBitmap(fBitmap, 0, 0);
     }
 
diff --git a/gm/fadefilter.cpp b/gm/fadefilter.cpp
new file mode 100644
index 0000000..6f64e1e
--- /dev/null
+++ b/gm/fadefilter.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 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 "SkColorMatrixFilter.h"
+#include "SkColorFilterImageFilter.h"
+
+// This GM renders correctly in 8888, but fails in PDF
+DEF_SIMPLE_GM(fadefilter, canvas, 256, 256) {
+    SkScalar matrix[20] = { 1, 0, 0, 0, 128.0f,
+                            0, 1, 0, 0, 128.0f,
+                            0, 0, 1, 0, 128.0f,
+                            0, 0, 0, 1, 0 };
+    SkAutoTUnref<SkColorFilter> colorFilter(
+            SkColorMatrixFilter::Create(matrix));
+    SkAutoTUnref<SkImageFilter> filter(
+            SkColorFilterImageFilter::Create(colorFilter));
+    SkPaint layerPaint;
+    layerPaint.setImageFilter(filter);
+    canvas->drawRect(SkRect::MakeLTRB(64, 64, 192, 192), layerPaint);
+}
diff --git a/gm/filltypes.cpp b/gm/filltypes.cpp
index 19249b2..4ba3aeb 100644
--- a/gm/filltypes.cpp
+++ b/gm/filltypes.cpp
@@ -26,11 +26,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("filltypes");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(835, 840);
     }
 
@@ -61,7 +61,7 @@
                  scale, paint);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         this->makePath();
 
         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
diff --git a/gm/filltypespersp.cpp b/gm/filltypespersp.cpp
index c76a0fa..92fce3a 100644
--- a/gm/filltypespersp.cpp
+++ b/gm/filltypespersp.cpp
@@ -25,11 +25,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("filltypespersp");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(835, 840);
     }
 
@@ -73,7 +73,7 @@
                  scale, paint);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         this->makePath();
 
         // do perspective drawPaint as the background;
diff --git a/gm/filterbitmap.cpp b/gm/filterbitmap.cpp
index 66f9d34..7d7c40f 100644
--- a/gm/filterbitmap.cpp
+++ b/gm/filterbitmap.cpp
@@ -25,9 +25,9 @@
 }
 
 static void draw_cell(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& mat, SkScalar dx,
-                      SkPaint::FilterLevel lvl) {
+                      SkFilterQuality lvl) {
     SkPaint paint;
-    paint.setFilterLevel(lvl);
+    paint.setFilterQuality(lvl);
 
     SkAutoCanvasRestore acr(canvas, true);
     canvas->translate(dx, 0);
@@ -36,14 +36,14 @@
 }
 
 static void draw_row(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& mat, SkScalar dx) {
-    draw_cell(canvas, bm, mat, 0 * dx, SkPaint::kNone_FilterLevel);
-    draw_cell(canvas, bm, mat, 1 * dx, SkPaint::kLow_FilterLevel);
-    draw_cell(canvas, bm, mat, 2 * dx, SkPaint::kMedium_FilterLevel);
-    draw_cell(canvas, bm, mat, 3 * dx, SkPaint::kHigh_FilterLevel);
+    draw_cell(canvas, bm, mat, 0 * dx, kNone_SkFilterQuality);
+    draw_cell(canvas, bm, mat, 1 * dx, kLow_SkFilterQuality);
+    draw_cell(canvas, bm, mat, 2 * dx, kMedium_SkFilterQuality);
+    draw_cell(canvas, bm, mat, 3 * dx, kHigh_SkFilterQuality);
 }
 
 class FilterBitmapGM : public skiagm::GM {
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
 
         this->makeBitmap();
 
@@ -71,18 +71,18 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1024, 768);
     }
 
     virtual void makeBitmap() = 0;
     virtual SkScalar getScale() = 0;
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         canvas->translate(10, 10);
         for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrix); ++i) {
@@ -110,11 +110,11 @@
   protected:
       float fTextSize;
 
-      SkScalar getScale() SK_OVERRIDE {
+      SkScalar getScale() override {
           return 32.f/fTextSize;
       }
 
-      void makeBitmap() SK_OVERRIDE {
+      void makeBitmap() override {
           fBM.allocN32Pixels(int(fTextSize * 8), int(fTextSize * 6));
           SkCanvas canvas(fBM);
           canvas.drawColor(SK_ColorWHITE);
@@ -138,22 +138,23 @@
 };
 
 class FilterBitmapCheckerboardGM: public FilterBitmapGM {
-  public:
-      FilterBitmapCheckerboardGM(int size, int num_checks)
-      : fSize(size), fNumChecks(num_checks)
-        {
-            fName.printf("filterbitmap_checkerboard_%d_%d", fSize, fNumChecks);
-        }
+public:
+    FilterBitmapCheckerboardGM(int size, int num_checks, bool convertToG8 = false)
+        : fSize(size), fNumChecks(num_checks), fConvertToG8(convertToG8)
+    {
+        fName.printf("filterbitmap_checkerboard_%d_%d%s",
+                     fSize, fNumChecks, convertToG8 ? "_g8" : "");
+    }
 
   protected:
       int fSize;
       int fNumChecks;
 
-      SkScalar getScale() SK_OVERRIDE {
+      SkScalar getScale() override {
           return 192.f/fSize;
       }
 
-      void makeBitmap() SK_OVERRIDE {
+      void makeBitmap() override {
           fBM.allocN32Pixels(fSize, fSize);
           for (int y = 0; y < fSize; y ++) {
               for (int x = 0; x < fSize; x ++) {
@@ -167,28 +168,34 @@
                   }
               }
           }
+          if (fConvertToG8) {
+              SkBitmap tmp;
+              fBM.copyTo(&tmp, kGray_8_SkColorType);
+              fBM = tmp;
+          }
       }
-  private:
-      typedef FilterBitmapGM INHERITED;
+private:
+    const bool fConvertToG8;
+    typedef FilterBitmapGM INHERITED;
 };
 
 class FilterBitmapImageGM: public FilterBitmapGM {
-  public:
-      FilterBitmapImageGM(const char filename[])
-      : fFilename(filename)
-        {
-            fName.printf("filterbitmap_image_%s", filename);
-        }
+public:
+    FilterBitmapImageGM(const char filename[], bool convertToG8 = false)
+        : fFilename(filename), fConvertToG8(convertToG8)
+    {
+        fName.printf("filterbitmap_image_%s%s", filename, convertToG8 ? "_g8" : "");
+    }
 
-  protected:
+protected:
       SkString fFilename;
       int fSize;
 
-      SkScalar getScale() SK_OVERRIDE {
+      SkScalar getScale() override {
           return 192.f/fSize;
       }
 
-      void makeBitmap() SK_OVERRIDE {
+      void makeBitmap() override {
           SkImageDecoder* codec = NULL;
           SkString resourcePath = GetResourcePath(fFilename.c_str());
           SkFILEStream stream(resourcePath.c_str());
@@ -204,9 +211,15 @@
               *(fBM.getAddr32(0,0)) = 0xFF0000FF; // red == bad
           }
           fSize = fBM.height();
+          if (fConvertToG8) {
+              SkBitmap tmp;
+              fBM.copyTo(&tmp, kGray_8_SkColorType);
+              fBM = tmp;
+          }
       }
-  private:
-      typedef FilterBitmapGM INHERITED;
+private:
+    const bool fConvertToG8;
+    typedef FilterBitmapGM INHERITED;
 };
 
 //////////////////////////////////////////////////////////////////////////////
@@ -216,12 +229,14 @@
 DEF_GM( return new FilterBitmapTextGM(10); )
 DEF_GM( return new FilterBitmapCheckerboardGM(4,4); )
 DEF_GM( return new FilterBitmapCheckerboardGM(32,32); )
+DEF_GM( return new FilterBitmapCheckerboardGM(32,32, true); )
 DEF_GM( return new FilterBitmapCheckerboardGM(32,8); )
 DEF_GM( return new FilterBitmapCheckerboardGM(32,2); )
 DEF_GM( return new FilterBitmapCheckerboardGM(192,192); )
 DEF_GM( return new FilterBitmapImageGM("mandrill_16.png"); )
 DEF_GM( return new FilterBitmapImageGM("mandrill_32.png"); )
 DEF_GM( return new FilterBitmapImageGM("mandrill_64.png"); )
+DEF_GM( return new FilterBitmapImageGM("mandrill_64.png", true); )
 DEF_GM( return new FilterBitmapImageGM("mandrill_128.png"); )
 DEF_GM( return new FilterBitmapImageGM("mandrill_256.png"); )
 DEF_GM( return new FilterBitmapImageGM("mandrill_512.png"); )
diff --git a/gm/filterfastbounds.cpp b/gm/filterfastbounds.cpp
index 206903f..5ff398e 100644
--- a/gm/filterfastbounds.cpp
+++ b/gm/filterfastbounds.cpp
@@ -9,7 +9,6 @@
 #include "SkBitmapSource.h"
 #include "SkBlurImageFilter.h"
 #include "SkDropShadowImageFilter.h"
-#include "SkMatrixImageFilter.h"
 #include "SkOffsetImageFilter.h"
 #include "SkPictureImageFilter.h"
 #include "SkPictureRecorder.h"
@@ -93,8 +92,8 @@
         SkMatrix scale;
         scale.setScale(2.0f, 2.0f);
 
-        SkAutoTUnref<SkMatrixImageFilter> scaleMIF(
-            SkMatrixImageFilter::Create(scale, SkPaint::kLow_FilterLevel, source));
+        SkAutoTUnref<SkImageFilter> scaleMIF(
+            SkImageFilter::CreateMatrixFilter(scale, kLow_SkFilterQuality, source));
 
         add_paint(scaleMIF, paints);
     }
@@ -103,8 +102,8 @@
         SkMatrix rot;
         rot.setRotate(-33.3f);
 
-        SkAutoTUnref<SkMatrixImageFilter> rotMIF(
-            SkMatrixImageFilter::Create(rot, SkPaint::kLow_FilterLevel, source));
+        SkAutoTUnref<SkImageFilter> rotMIF(
+            SkImageFilter::CreateMatrixFilter(rot, kLow_SkFilterQuality, source));
 
         add_paint(rotMIF, paints);
     }
@@ -117,7 +116,7 @@
             SkDropShadowImageFilter::Create(10.0f, 10.0f,
                                             3.0f, 3.0f,
                                             SK_ColorRED, kBoth,
-                                            source, NULL, 0));
+                                            source, NULL));
 
         add_paint(dsif, paints);
     }
@@ -128,7 +127,7 @@
                                             3.0f, 3.0f,
                                             SK_ColorRED,
                                             SkDropShadowImageFilter::kDrawShadowOnly_ShadowMode,
-                                            source, NULL, 0));
+                                            source, NULL));
 
         add_paint(dsif, paints);
     }
@@ -160,9 +159,9 @@
     static const int kNumVertTiles = 6;
     static const int kNumXtraCols = 2;
 
-    SkString onShortName() SK_OVERRIDE{ return SkString("filterfastbounds"); }
+    SkString onShortName() override{ return SkString("filterfastbounds"); }
 
-    SkISize onISize() SK_OVERRIDE{
+    SkISize onISize() override{
         return SkISize::Make((SK_ARRAY_COUNT(gDrawMthds) + kNumXtraCols) * kTileWidth,
                              kNumVertTiles * kTileHeight);
     }
@@ -225,7 +224,7 @@
         canvas->restore();
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE{
+    void onDraw(SkCanvas* canvas) override{
 
         SkPaint blackFill;
 
diff --git a/gm/filterindiabox.cpp b/gm/filterindiabox.cpp
index 1ed2a69..d2fc30b 100644
--- a/gm/filterindiabox.cpp
+++ b/gm/filterindiabox.cpp
@@ -24,9 +24,9 @@
 }
 
 static void draw_cell(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& mat, SkScalar dx,
-                      SkPaint::FilterLevel lvl) {
+                      SkFilterQuality lvl) {
     SkPaint paint;
-    paint.setFilterLevel(lvl);
+    paint.setFilterQuality(lvl);
 
     SkAutoCanvasRestore acr(canvas, true);
 
@@ -36,14 +36,14 @@
 }
 
 static void draw_row(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& mat, SkScalar dx) {
-    draw_cell(canvas, bm, mat, 0 * dx, SkPaint::kNone_FilterLevel);
-    draw_cell(canvas, bm, mat, 1 * dx, SkPaint::kLow_FilterLevel);
-    draw_cell(canvas, bm, mat, 2 * dx, SkPaint::kMedium_FilterLevel);
-    draw_cell(canvas, bm, mat, 3 * dx, SkPaint::kHigh_FilterLevel);
+    draw_cell(canvas, bm, mat, 0 * dx, kNone_SkFilterQuality);
+    draw_cell(canvas, bm, mat, 1 * dx, kLow_SkFilterQuality);
+    draw_cell(canvas, bm, mat, 2 * dx, kMedium_SkFilterQuality);
+    draw_cell(canvas, bm, mat, 3 * dx, kHigh_SkFilterQuality);
 }
 
 class FilterIndiaBoxGM : public skiagm::GM {
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         this->makeBitmap();
 
         SkScalar cx = SkScalarHalf(fBM.width());
@@ -70,15 +70,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1024, 768);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(10, 10);
         for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrix); ++i) {
             SkSize size = computeSize(fBM, fMatrix[i]);
diff --git a/gm/fontcache.cpp b/gm/fontcache.cpp
index 0f5080e..5f1141e 100644
--- a/gm/fontcache.cpp
+++ b/gm/fontcache.cpp
@@ -31,20 +31,20 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("fontcache");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1280, 640);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         fTypefaces[0] = sk_tool_utils::create_portable_typeface("serif", SkTypeface::kItalic);
         fTypefaces[1] = sk_tool_utils::create_portable_typeface("sans-serif", SkTypeface::kItalic);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setLCDRenderText(true);
diff --git a/gm/fontmgr.cpp b/gm/fontmgr.cpp
index e89e585..14ea7ea 100644
--- a/gm/fontmgr.cpp
+++ b/gm/fontmgr.cpp
@@ -68,15 +68,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1536, 768);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkScalar y = 20;
         SkPaint paint;
         paint.setAntiAlias(true);
@@ -88,10 +88,10 @@
         int count = SkMin32(fm->countFamilies(), MAX_FAMILIES);
 
         for (int i = 0; i < count; ++i) {
-            SkString fname;
-            fm->getFamilyName(i, &fname);
+            SkString familyName;
+            fm->getFamilyName(i, &familyName);
             paint.setTypeface(NULL);
-            (void)drawString(canvas, fname, 20, y, paint);
+            (void)drawString(canvas, familyName, 20, y, paint);
 
             SkScalar x = 220;
 
@@ -106,10 +106,10 @@
                 x = drawString(canvas, sname, x, y, paint) + 20;
 
                 // check to see that we get different glyphs in japanese and chinese
-                x = drawCharacter(canvas, 0x5203, x, y, paint, fm, fName.c_str(), &zh, 1, fs);
-                x = drawCharacter(canvas, 0x5203, x, y, paint, fm, fName.c_str(), &ja, 1, fs);
+                x = drawCharacter(canvas, 0x5203, x, y, paint, fm, familyName.c_str(), &zh, 1, fs);
+                x = drawCharacter(canvas, 0x5203, x, y, paint, fm, familyName.c_str(), &ja, 1, fs);
                 // check that emoji characters are found
-                x = drawCharacter(canvas, 0x1f601, x, y, paint, fm, fName.c_str(), NULL, 0, fs);
+                x = drawCharacter(canvas, 0x1f601, x, y, paint, fm, familyName.c_str(), NULL,0, fs);
             }
             y += 24;
         }
@@ -130,11 +130,11 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("fontmgr_match");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 1024);
     }
 
@@ -176,7 +176,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setLCDRenderText(true);
@@ -237,15 +237,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1024, 850);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setSubpixelText(true);
diff --git a/gm/fontscaler.cpp b/gm/fontscaler.cpp
index 3fefd56..c3b2c87 100644
--- a/gm/fontscaler.cpp
+++ b/gm/fontscaler.cpp
@@ -20,11 +20,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("fontscaler");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1450, 750);
     }
 
@@ -36,7 +36,7 @@
         canvas->translate(-px, -py);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
 
         paint.setAntiAlias(true);
diff --git a/gm/gammatext.cpp b/gm/gammatext.cpp
index 036d043..ed8c829 100644
--- a/gm/gammatext.cpp
+++ b/gm/gammatext.cpp
@@ -39,11 +39,11 @@
 
 class GammaTextGM : public skiagm::GM {
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("gammatext");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1024, HEIGHT);
     }
 
@@ -58,7 +58,7 @@
         canvas->drawRect(r, paint);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         drawGrad(canvas);
 
         const SkColor fg[] = {
@@ -137,28 +137,28 @@
         }
     }
 
-    ~GammaShaderTextGM() SK_OVERRIDE {
+    ~GammaShaderTextGM() override {
         for (size_t i = 0; i < SK_ARRAY_COUNT(fShaders); ++i) {
             SkSafeUnref(fShaders[i]);
         }
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("gammagradienttext");
     }
     
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(300, 300);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         for (size_t i = 0; i < SK_ARRAY_COUNT(fShaders); ++i) {
             fShaders[i] = make_gradient(fColors[i]);
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setLCDRenderText(true);
diff --git a/gm/getpostextpath.cpp b/gm/getpostextpath.cpp
index db50969..2286602 100644
--- a/gm/getpostextpath.cpp
+++ b/gm/getpostextpath.cpp
@@ -17,11 +17,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("getpostextpath");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(480, 780); }
+    SkISize onISize() override { return SkISize::Make(480, 780); }
 
     static void strokePath(SkCanvas* canvas, const SkPath& path) {
         SkPaint paint;
@@ -31,7 +31,7 @@
         canvas->drawPath(path, paint);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         // explicitly add spaces, to test a prev. bug
         const char* text = "Ham bur ge fons";
         int len = SkToInt(strlen(text));
diff --git a/gm/giantbitmap.cpp b/gm/giantbitmap.cpp
index 2ac952d..2ee3797 100644
--- a/gm/giantbitmap.cpp
+++ b/gm/giantbitmap.cpp
@@ -72,7 +72,7 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString str("giantbitmap_");
         switch (fMode) {
             case SkShader::kClamp_TileMode:
@@ -92,9 +92,9 @@
         return str;
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(640, 480); }
+    SkISize onISize() override { return SkISize::Make(640, 480); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
 
         SkMatrix m;
@@ -109,7 +109,7 @@
         SkShader* s = SkShader::CreateBitmapShader(getBitmap(), fMode, fMode, &m);
 
         paint.setShader(s)->unref();
-        paint.setFilterLevel(fDoFilter ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel);
+        paint.setFilterQuality(fDoFilter ? kLow_SkFilterQuality : kNone_SkFilterQuality);
 
         canvas->translate(SkIntToScalar(50), SkIntToScalar(50));
 
diff --git a/gm/glyph_pos.cpp b/gm/glyph_pos.cpp
index 3f1ea35..16cdfca 100644
--- a/gm/glyph_pos.cpp
+++ b/gm/glyph_pos.cpp
@@ -28,7 +28,7 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString str("glyph_pos");
         if (fStrokeWidth == 0.0f) {
             str.append("_h"); // h == Hairline.
@@ -45,9 +45,9 @@
         return str;
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(800, 600); }
+    SkISize onISize() override { return SkISize::Make(800, 600); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         if (!fProp) {
             fProp.reset(sk_tool_utils::create_portable_typeface("Helvetica", SkTypeface::kNormal));
         }
@@ -82,10 +82,8 @@
         canvas->scale(3.0f, 3.0f);
         SkMatrix skew;
         skew.setIdentity();
-        skew.setSkewX(SkScalarDiv(8.0f,
-                                  25.0f));
-        skew.setSkewY(SkScalarDiv(2.0f,
-                                  25.0f));
+        skew.setSkewX(8.0f / 25.0f);
+        skew.setSkewY(2.0f / 25.0f);
         canvas->concat(skew);
         drawTestCase(canvas, 1.0f);
         canvas->restore();
@@ -95,11 +93,9 @@
         canvas->save();
         SkMatrix perspective;
         perspective.setIdentity();
-        perspective.setPerspX(-SkScalarDiv(SK_Scalar1, 340.0f));
-        perspective.setSkewX(SkScalarDiv(8.0f,
-                                         25.0f));
-        perspective.setSkewY(SkScalarDiv(2.0f,
-                                         25.0f));
+        perspective.setPerspX(-SkScalarInvert(340));
+        perspective.setSkewX(8.0f / 25.0f);
+        perspective.setSkewY(2.0f / 25.0f);
 
 
         canvas->concat(perspective);
diff --git a/gm/glyph_pos_align.cpp b/gm/glyph_pos_align.cpp
index 0314760..e852804 100644
--- a/gm/glyph_pos_align.cpp
+++ b/gm/glyph_pos_align.cpp
@@ -22,13 +22,13 @@
 class GlyphPosAlignGM : public GM {
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("glyph_pos_align");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(kWidth, kHeight); }
+    SkISize onISize() override { return SkISize::Make(kWidth, kHeight); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->clear(SK_ColorBLACK);
 
         SkPaint paint;
diff --git a/gm/gm.h b/gm/gm.h
index e263528..87ad927 100644
--- a/gm/gm.h
+++ b/gm/gm.h
@@ -126,9 +126,9 @@
                  const SkISize& size)
             : fName(name), fDrawProc(drawProc), fSize(size) {}
     protected:
-        void onDraw(SkCanvas* canvas) SK_OVERRIDE;
-        SkISize onISize() SK_OVERRIDE;
-        SkString onShortName() SK_OVERRIDE;
+        void onDraw(SkCanvas* canvas) override;
+        SkISize onISize() override;
+        SkString onShortName() override;
     private:
         SkString fName;
         void (*fDrawProc)(SkCanvas*);
diff --git a/gm/gm_expectations.h b/gm/gm_expectations.h
index 1cd6a1f..89ba385 100644
--- a/gm/gm_expectations.h
+++ b/gm/gm_expectations.h
@@ -193,7 +193,7 @@
          */
         explicit IndividualImageExpectationsSource(const char *rootDir) : fRootDir(rootDir) {}
 
-        Expectations get(const char *testName) const SK_OVERRIDE ;
+        Expectations get(const char *testName) const override ;
 
     private:
         const SkString fRootDir;
@@ -212,7 +212,7 @@
          */
         explicit JsonExpectationsSource(const char *jsonPath);
 
-        Expectations get(const char *testName) const SK_OVERRIDE;
+        Expectations get(const char *testName) const override;
 
     private:
 
diff --git a/gm/gradientDirtyLaundry.cpp b/gm/gradientDirtyLaundry.cpp
index 8c27b8f..ed5ad51 100644
--- a/gm/gradientDirtyLaundry.cpp
+++ b/gm/gradientDirtyLaundry.cpp
@@ -70,10 +70,10 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE { return SkString("gradient_dirty_laundry"); }
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(640, 615); }
+    SkString onShortName() override { return SkString("gradient_dirty_laundry"); }
+    SkISize onISize() override { return SkISize::Make(640, 615); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPoint pts[2] = { { 0, 0 },
                            { SkIntToScalar(100), SkIntToScalar(100) }
         };
diff --git a/gm/gradient_matrix.cpp b/gm/gradient_matrix.cpp
index daeea37..77a0247 100644
--- a/gm/gradient_matrix.cpp
+++ b/gm/gradient_matrix.cpp
@@ -121,15 +121,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("gradient_matrix");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(800, 800);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         draw_gradients(canvas, &make_linear_gradient,
                       linearPts, SK_ARRAY_COUNT(linearPts));
 
diff --git a/gm/gradients.cpp b/gm/gradients.cpp
index 43f8c94..4e6aba9 100644
--- a/gm/gradients.cpp
+++ b/gm/gradients.cpp
@@ -1,10 +1,10 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "gm.h"
 #include "SkGradientShader.h"
 
@@ -70,18 +70,18 @@
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
-    return SkGradientShader::CreateTwoPointRadial(
-                                                  center1, (pts[1].fX - pts[0].fX) / 7,
-                                                  center0, (pts[1].fX - pts[0].fX) / 2,
-                                                  data.fColors, data.fPos, data.fCount, tm,
-                                                  0, &localMatrix);
+    return SkGradientShader::CreateTwoPointConical(
+                                                   center1, (pts[1].fX - pts[0].fX) / 7,
+                                                   center0, (pts[1].fX - pts[0].fX) / 2,
+                                                   data.fColors, data.fPos, data.fCount, tm,
+                                                   0, &localMatrix);
 }
 
 static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data,
                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
-    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
     return SkGradientShader::CreateTwoPointConical(center1, radius1,
@@ -149,6 +149,7 @@
 private:
     typedef GM INHERITED;
 };
+DEF_GM( return new GradientsGM; )
 
 // Based on the original gradient slide, but with perspective applied to the
 // gradient shaders' local matrices
@@ -184,10 +185,8 @@
                 // apply an increasing y perspective as we move to the right
                 SkMatrix perspective;
                 perspective.setIdentity();
-                perspective.setPerspY(SkScalarDiv(SkIntToScalar((unsigned) i+1),
-                                      SkIntToScalar(500)));
-                perspective.setSkewX(SkScalarDiv(SkIntToScalar((unsigned) i+1),
-                                     SkIntToScalar(10)));
+                perspective.setPerspY(SkIntToScalar(i+1) / 500);
+                perspective.setSkewX(SkIntToScalar(i+1) / 10);
 
                 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, perspective);
 
@@ -204,6 +203,7 @@
 private:
     typedef GM INHERITED;
 };
+DEF_GM( return new GradientsLocalPerspectiveGM; )
 
 // Based on the original gradient slide, but with perspective applied to
 // the view matrix
@@ -218,8 +218,8 @@
     virtual void onDraw(SkCanvas* canvas) {
         SkMatrix perspective;
         perspective.setIdentity();
-        perspective.setPerspY(SkScalarDiv(SK_Scalar1, SkIntToScalar(1000)));
-        perspective.setSkewX(SkScalarDiv(SkIntToScalar(8), SkIntToScalar(25)));
+        perspective.setPerspY(0.001f);
+        perspective.setSkewX(SkIntToScalar(8) / 25);
         canvas->concat(perspective);
         INHERITED::onDraw(canvas);
     }
@@ -227,6 +227,7 @@
 private:
     typedef GradientsGM INHERITED;
 };
+DEF_GM( return new GradientsViewPerspectiveGM; )
 
 /*
  Inspired by this <canvas> javascript, where we need to detect that we are not
@@ -270,9 +271,9 @@
         SkPoint c1;
         c1.iset(0, 25);
         SkScalar r1 = SkIntToScalar(150);
-        SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
-                                                             pos, SK_ARRAY_COUNT(pos),
-                                                             SkShader::kClamp_TileMode);
+        SkShader* s = SkGradientShader::CreateTwoPointConical(c0, r0, c1, r1, colors,
+                                                              pos, SK_ARRAY_COUNT(pos),
+                                                              SkShader::kClamp_TileMode);
         SkPaint paint;
         paint.setShader(s)->unref();
         canvas->drawPaint(paint);
@@ -281,6 +282,7 @@
 private:
     typedef GM INHERITED;
 };
+DEF_GM( return new GradientsDegenrate2PointGM; )
 
 /// Tests correctness of *optimized* codepaths in gradients.
 
@@ -319,6 +321,7 @@
 private:
     typedef GM INHERITED;
 };
+DEF_GM( return new ClampedGradientsGM; )
 
 /// Checks quality of large radial gradients, which may display
 /// some banding.
@@ -329,12 +332,12 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE { return SkString("radial_gradient"); }
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(1280, 1280); }
+    SkString onShortName() override { return SkString("radial_gradient"); }
+    SkISize onISize() override { return SkISize::Make(1280, 1280); }
     void drawBG(SkCanvas* canvas) {
         canvas->drawColor(0xFF000000);
     }
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         const SkISize dim = this->getISize();
 
         this->drawBG(canvas);
@@ -361,7 +364,7 @@
 private:
     typedef GM INHERITED;
 };
-
+DEF_GM( return new RadialGradientGM; )
 
 class RadialGradient2GM : public GM {
 public:
@@ -369,14 +372,14 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE { return SkString("radial_gradient2"); }
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(800, 400); }
+    SkString onShortName() override { return SkString("radial_gradient2"); }
+    SkISize onISize() override { return SkISize::Make(800, 400); }
     void drawBG(SkCanvas* canvas) {
         canvas->drawColor(0xFF000000);
     }
 
     // Reproduces the example given in bug 7671058.
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint1, paint2, paint3;
         paint1.setStyle(SkPaint::kFill_Style);
         paint2.setStyle(SkPaint::kFill_Style);
@@ -425,27 +428,36 @@
 private:
     typedef GM INHERITED;
 };
+DEF_GM( return new RadialGradient2GM; )
 
-///////////////////////////////////////////////////////////////////////////////
+// Shallow radial (shows banding on raster)
+class RadialGradient3GM : public GM {
+    SkAutoTUnref<SkShader> fShader;
 
-static GM* MyFactory(void*) { return new GradientsGM; }
-static GMRegistry reg(MyFactory);
+protected:
+    SkString onShortName() override { return SkString("radial_gradient3"); }
 
-static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; }
-static GMRegistry reg2(MyFactory2);
+    SkISize onISize() override { return SkISize::Make(500, 500); }
 
-static GM* MyFactory3(void*) { return new ClampedGradientsGM; }
-static GMRegistry reg3(MyFactory3);
+    bool runAsBench() const override { return true; }
 
-static GM* MyFactory4(void*) { return new RadialGradientGM; }
-static GMRegistry reg4(MyFactory4);
+    void onOnceBeforeDraw() override {
+        const SkPoint center = { 0, 0 };
+        const SkScalar kRadius = 3000;
+        const SkColor gColors[] = { 0xFFFFFFFF, 0xFF000000 };
+        fShader.reset(SkGradientShader::CreateRadial(center, kRadius, gColors, NULL, 2,
+                                                     SkShader::kClamp_TileMode));
+    }
 
-static GM* MyFactory5(void*) { return new GradientsLocalPerspectiveGM; }
-static GMRegistry reg5(MyFactory5);
+    void onDraw(SkCanvas* canvas) override {
+        SkPaint paint;
+        paint.setShader(fShader);
+        canvas->drawRect(SkRect::MakeWH(500, 500), paint);
+    }
+    
+private:
+    typedef GM INHERITED;
+};
+DEF_GM( return new RadialGradient3GM; )
 
-static GM* MyFactory6(void*) { return new GradientsViewPerspectiveGM; }
-static GMRegistry reg6(MyFactory6);
-
-static GM* MyFactory7(void*) { return new RadialGradient2GM; }
-static GMRegistry reg7(MyFactory7);
 }
diff --git a/gm/gradients_2pt_conical.cpp b/gm/gradients_2pt_conical.cpp
index f43356e..c865d71 100644
--- a/gm/gradients_2pt_conical.cpp
+++ b/gm/gradients_2pt_conical.cpp
@@ -40,8 +40,8 @@
 static SkShader* Make2ConicalOutside(const SkPoint pts[2], const GradData& data,
                                      SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
-    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
     return SkGradientShader::CreateTwoPointConical(center0, radius0,
@@ -53,8 +53,8 @@
 static SkShader* Make2ConicalOutsideFlip(const SkPoint pts[2], const GradData& data,
                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
-    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
     return SkGradientShader::CreateTwoPointConical(center1, radius1,
@@ -145,7 +145,7 @@
                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
     SkScalar radius0 = 0.f;
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
     return SkGradientShader::CreateTwoPointConical(center0, radius0,
@@ -158,7 +158,7 @@
                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
     SkScalar radius0 = 0.f;
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
     return SkGradientShader::CreateTwoPointConical(center1, radius1,
@@ -170,8 +170,8 @@
 static SkShader* Make2ConicalEdgeX(const SkPoint pts[2], const GradData& data,
                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
-    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 7);
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7;
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center0.set(center1.fX + radius1, center1.fY);
@@ -184,8 +184,8 @@
 static SkShader* Make2ConicalEdgeY(const SkPoint pts[2], const GradData& data,
                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
-    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 7);
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7;
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center0.set(center1.fX, center1.fY + radius1);
@@ -199,7 +199,7 @@
                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
     SkScalar radius0 = 0.f;
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center0.set(center1.fX + radius1, center1.fY);
@@ -213,7 +213,7 @@
                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
     SkScalar radius0 = 0.f;
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center0.set(center1.fX, center1.fY + radius1);
@@ -226,8 +226,8 @@
 static SkShader* Make2ConicalTouchX(const SkPoint pts[2], const GradData& data,
                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
-    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 7);
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7;
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center0.set(center1.fX - radius1 + radius0, center1.fY);
@@ -240,8 +240,8 @@
 static SkShader* Make2ConicalTouchY(const SkPoint pts[2], const GradData& data,
                              SkShader::TileMode tm, const SkMatrix& localMatrix) {
     SkPoint center0, center1;
-    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 7);
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7;
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center1.set(SkScalarAve(pts[0].fX, pts[1].fX),
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center0.set(center1.fX, center1.fY + radius1 - radius0);
diff --git a/gm/gradients_no_texture.cpp b/gm/gradients_no_texture.cpp
index c180209..391472a 100644
--- a/gm/gradients_no_texture.cpp
+++ b/gm/gradients_no_texture.cpp
@@ -51,7 +51,7 @@
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
-    return SkGradientShader::CreateTwoPointRadial(
+    return SkGradientShader::CreateTwoPointConical(
         center1, (pts[1].fX - pts[0].fX) / 7,
         center0, (pts[1].fX - pts[0].fX) / 2,
         data.fColors, data.fPos, data.fCount, tm);
@@ -59,8 +59,8 @@
 
 static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) {
     SkPoint center0, center1;
-    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
-    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
+    SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
+    SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
     center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
     center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
     return SkGradientShader::CreateTwoPointConical(center1, radius1,
@@ -86,10 +86,10 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE { return SkString("gradients_no_texture"); }
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(640, 615); }
+    SkString onShortName() override { return SkString("gradients_no_texture"); }
+    SkISize onISize() override { return SkISize::Make(640, 615); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         static const SkPoint kPts[2] = { { 0, 0 },
                                          { SkIntToScalar(50), SkIntToScalar(50) } };
         static const SkShader::TileMode kTM = SkShader::kClamp_TileMode;
@@ -209,10 +209,10 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE { return SkString("gradients_many"); }
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(850, 100); }
+    SkString onShortName() override { return SkString("gradients_many"); }
+    SkISize onISize() override { return SkISize::Make(850, 100); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         const Proc procs[] = {
             make0, make1, make2,
         };
diff --git a/gm/gradtext.cpp b/gm/gradtext.cpp
index 87c3b38..eeaff56 100644
--- a/gm/gradtext.cpp
+++ b/gm/gradtext.cpp
@@ -102,11 +102,11 @@
     GradTextGM () {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("gradtext");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(500, 480); }
+    SkISize onISize() override { return SkISize::Make(500, 480); }
 
     static void draw_text(SkCanvas* canvas, const SkPaint& paint) {
         const char* text = "When in the course of human events";
@@ -127,7 +127,7 @@
         draw_text(canvas, p);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         sk_tool_utils::set_portable_typeface(&paint);
         paint.setTextSize(SkIntToScalar(26));
diff --git a/gm/hairlines.cpp b/gm/hairlines.cpp
index 8b0510c..6056415 100644
--- a/gm/hairlines.cpp
+++ b/gm/hairlines.cpp
@@ -15,13 +15,13 @@
 protected:
 
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("hairlines");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(1250, 1250); }
+    SkISize onISize() override { return SkISize::Make(1250, 1250); }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         {
             SkPath* lineAnglesPath = &fPaths.push_back();
             enum {
@@ -161,14 +161,14 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         static const SkAlpha kAlphaValue[] = { 0xFF, 0x40 };
         static const SkScalar kWidths[] = { 0, 0.5f, 1.5f };
 
         enum {
             kMargin = 5,
         };
-        int wrapX = canvas->getDeviceSize().fWidth - kMargin;
+        int wrapX = 1250 - kMargin;
 
         SkScalar maxH = 0;
         canvas->translate(SkIntToScalar(kMargin), SkIntToScalar(kMargin));
diff --git a/gm/hairmodes.cpp b/gm/hairmodes.cpp
index aa1af03..06ae7ed 100644
--- a/gm/hairmodes.cpp
+++ b/gm/hairmodes.cpp
@@ -78,17 +78,17 @@
         SkPaint fBGPaint;
 
     protected:
-        SkString onShortName() SK_OVERRIDE {
+        SkString onShortName() override {
             return SkString("hairmodes");
         }
 
-        virtual SkISize onISize() SK_OVERRIDE { return SkISize::Make(640, 480); }
+        virtual SkISize onISize() override { return SkISize::Make(640, 480); }
 
-        void onOnceBeforeDraw() SK_OVERRIDE {
+        void onOnceBeforeDraw() override {
             fBGPaint.setShader(make_bg_shader())->unref();
         }
 
-        void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        void onDraw(SkCanvas* canvas) override {
             const SkRect bounds = SkRect::MakeWH(W, H);
             static const SkAlpha gAlphaValue[] = { 0xFF, 0x88, 0x88 };
 
diff --git a/gm/hittestpath.cpp b/gm/hittestpath.cpp
index 11fe834..42e3671 100644
--- a/gm/hittestpath.cpp
+++ b/gm/hittestpath.cpp
@@ -35,13 +35,13 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("hittestpath");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(700, 460); }
+    SkISize onISize() override { return SkISize::Make(700, 460); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPath path;
         SkRandom rand;
 
diff --git a/gm/image.cpp b/gm/image.cpp
index ca28dd1..c420c95 100644
--- a/gm/image.cpp
+++ b/gm/image.cpp
@@ -122,15 +122,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("image-surface");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(960, 1200);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         drawJpeg(canvas, this->getISize());
 
         canvas->scale(2, 2);
@@ -202,9 +202,9 @@
     ImageResizeGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE { return SkString("image-resize"); }
+    SkString onShortName() override { return SkString("image-resize"); }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(510, 480); }
+    SkISize onISize() override { return SkISize::Make(510, 480); }
 
     void drawIntoImage(SkCanvas* canvas) {
         SkPaint paint;
@@ -260,7 +260,7 @@
         this->drawResized(canvas, image, W, H, &subset, fq);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(10, 10);
 
         SkAutoTUnref<SkImage> image(this->makeImage(canvas));
diff --git a/gm/imagealphathreshold.cpp b/gm/imagealphathreshold.cpp
index a6b00e4..a418f0f 100644
--- a/gm/imagealphathreshold.cpp
+++ b/gm/imagealphathreshold.cpp
@@ -22,15 +22,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("imagealphathreshold");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(WIDTH, HEIGHT);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkIRect rects[2];
         rects[0] = SkIRect::MakeXYWH(0, 150, WIDTH, HEIGHT - 300);
         rects[1] = SkIRect::MakeXYWH(150, 0, WIDTH - 300, HEIGHT);
diff --git a/gm/imageblur.cpp b/gm/imageblur.cpp
index e424fbf..c7139b4 100644
--- a/gm/imageblur.cpp
+++ b/gm/imageblur.cpp
@@ -24,15 +24,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(WIDTH, HEIGHT);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setImageFilter(SkBlurImageFilter::Create(fSigmaX, fSigmaY))->unref();
         canvas->saveLayer(NULL, &paint);
diff --git a/gm/imageblur2.cpp b/gm/imageblur2.cpp
index 978be7a..480f093 100644
--- a/gm/imageblur2.cpp
+++ b/gm/imageblur2.cpp
@@ -37,15 +37,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(WIDTH, HEIGHT);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         const int sigmaCount = SK_ARRAY_COUNT(kBlurSigmas);
         const int testStringCount = SK_ARRAY_COUNT(kTestStrings);
         SkScalar dx = WIDTH / sigmaCount;
diff --git a/gm/imagefilters.cpp b/gm/imagefilters.cpp
new file mode 100644
index 0000000..54e50e4
--- /dev/null
+++ b/gm/imagefilters.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015 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 "SkImageFilter.h"
+#include "SkColorMatrixFilter.h"
+
+/**
+ *  Test drawing a primitive w/ an imagefilter (in this case, just matrix w/ identity) to see
+ *  that we apply the xfermode *after* the image has been created and filtered, and not during
+ *  the creation step (i.e. before it is filtered).
+ *
+ *  see skbug.com/3741
+ */
+class ImageFilterXfermodeTestGM : public skiagm::GM {
+protected:
+    SkString onShortName() override {
+        return SkString("imagefilters_xfermodes");
+    }
+
+    SkISize onISize() override { return SkISize::Make(480, 480); }
+
+    void doDraw(SkCanvas* canvas, SkXfermode::Mode mode, SkImageFilter* imf) {
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->clipRect(SkRect::MakeWH(220, 220));
+        
+        // want to force a layer, so modes like DstIn can combine meaningfully, but the final
+        // image can still be shown against our default (opaque) background. non-opaque GMs
+        // are a lot more trouble to compare/triage.
+        canvas->saveLayer(NULL, NULL);
+        canvas->drawColor(SK_ColorGREEN);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        
+        SkRect r0 = SkRect::MakeXYWH(10, 60, 200, 100);
+        SkRect r1 = SkRect::MakeXYWH(60, 10, 100, 200);
+        
+        paint.setColor(SK_ColorRED);
+        canvas->drawOval(r0, paint);
+
+        paint.setColor(0x660000FF);
+        paint.setImageFilter(imf);
+        paint.setXfermodeMode(mode);
+        canvas->drawOval(r1, paint);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        canvas->translate(10, 10);
+
+        // just need an imagefilter to trigger the code-path (which creates a tmp layer)
+        SkAutoTUnref<SkImageFilter> imf(SkImageFilter::CreateMatrixFilter(SkMatrix::I(),
+                                                                          kNone_SkFilterQuality));
+
+        const SkXfermode::Mode modes[] = {
+            SkXfermode::kSrcATop_Mode, SkXfermode::kDstIn_Mode
+        };
+        
+        for (size_t i = 0; i < SK_ARRAY_COUNT(modes); ++i) {
+            canvas->save();
+            this->doDraw(canvas, modes[i], NULL);
+            canvas->translate(240, 0);
+            this->doDraw(canvas, modes[i], imf);
+            canvas->restore();
+            
+            canvas->translate(0, 240);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+DEF_GM( return new ImageFilterXfermodeTestGM; )
+
diff --git a/gm/imagefiltersbase.cpp b/gm/imagefiltersbase.cpp
index ea32769..6e8ac29 100644
--- a/gm/imagefiltersbase.cpp
+++ b/gm/imagefiltersbase.cpp
@@ -36,8 +36,8 @@
 protected:
     FailImageFilter() : INHERITED(0, NULL) {}
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE {
+    bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
+                       SkBitmap* result, SkIPoint* offset) const override {
         return false;
     }
 
@@ -78,8 +78,8 @@
 protected:
     IdentityImageFilter(SkImageFilter* input) : INHERITED(1, &input) {}
 
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE {
+    bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
+                       SkBitmap* result, SkIPoint* offset) const override {
         *result = src;
         offset->set(0, 0);
         return true;
@@ -194,11 +194,11 @@
     ImageFiltersBaseGM () {}
 
 protected:
-    virtual SkString onShortName() {
+    SkString onShortName() override {
         return SkString("imagefiltersbase");
     }
 
-    virtual SkISize onISize() { return SkISize::Make(700, 500); }
+    SkISize onISize() override { return SkISize::Make(700, 500); }
 
     void draw_frame(SkCanvas* canvas, const SkRect& r) {
         SkPaint paint;
@@ -207,7 +207,7 @@
         canvas->drawRect(r, paint);
     }
 
-    virtual void onDraw(SkCanvas* canvas) {
+    void onDraw(SkCanvas* canvas) override {
         void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = {
             draw_paint,
             draw_line, draw_rect, draw_path, draw_text,
@@ -254,8 +254,96 @@
 private:
     typedef GM INHERITED;
 };
+DEF_GM( return new ImageFiltersBaseGM; )
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static skiagm::GM* MyFactory(void*) { return new ImageFiltersBaseGM; }
-static skiagm::GMRegistry reg(MyFactory);
+/*
+ *  Want to test combos of filter and LCD text, to be sure we disable LCD in the presence of
+ *  a filter.
+ */
+class ImageFiltersTextBaseGM : public skiagm::GM {
+    SkString fSuffix;
+public:
+    ImageFiltersTextBaseGM(const char suffix[]) : fSuffix(suffix) {}
+
+protected:
+    SkString onShortName() override {
+        SkString name;
+        name.printf("%s_%s", "textfilter", fSuffix.c_str());
+        return name;
+    }
+
+    SkISize onISize() override { return SkISize::Make(512, 342); }
+
+    void drawWaterfall(SkCanvas* canvas, const SkPaint& origPaint) {
+        const uint32_t flags[] = {
+            0,
+            SkPaint::kAntiAlias_Flag,
+            SkPaint::kAntiAlias_Flag | SkPaint::kLCDRenderText_Flag,
+        };
+        SkPaint paint(origPaint);
+        paint.setTextSize(30);
+
+        SkAutoCanvasRestore acr(canvas, true);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(flags); ++i) {
+            paint.setFlags(flags[i]);
+            canvas->drawText("Hamburgefons", 11, 0, 0, paint);
+            canvas->translate(0, 40);
+        }
+    }
+
+    virtual void installFilter(SkPaint* paint) = 0;
+
+    void onDraw(SkCanvas* canvas) override {
+        SkPaint paint;
+
+        canvas->translate(20, 40);
+
+        for (int doSaveLayer = 0; doSaveLayer <= 1; ++doSaveLayer) {
+            SkAutoCanvasRestore acr(canvas, true);
+            for (int useFilter = 0; useFilter <= 1; ++useFilter) {
+                SkAutoCanvasRestore acr2(canvas, true);
+
+                SkPaint paint;
+                if (useFilter) {
+                    this->installFilter(&paint);
+                }
+                if (doSaveLayer) {
+                    canvas->saveLayer(NULL, &paint);
+                    paint.setImageFilter(NULL);
+                }
+                this->drawWaterfall(canvas, paint);
+
+                acr2.restore();
+                canvas->translate(250, 0);
+            }
+            acr.restore();
+            canvas->translate(0, 200);
+        }
+    }
+    
+private:
+    typedef GM INHERITED;
+};
+
+class ImageFiltersText_IF : public ImageFiltersTextBaseGM {
+public:
+    ImageFiltersText_IF() : ImageFiltersTextBaseGM("image") {}
+
+    void installFilter(SkPaint* paint) override {
+        paint->setImageFilter(SkBlurImageFilter::Create(1.5f, 1.5f))->unref();
+    }
+};
+DEF_GM( return new ImageFiltersText_IF; )
+
+class ImageFiltersText_CF : public ImageFiltersTextBaseGM {
+public:
+    ImageFiltersText_CF() : ImageFiltersTextBaseGM("color") {}
+
+    void installFilter(SkPaint* paint) override {
+        paint->setColorFilter(SkColorFilter::CreateModeFilter(SK_ColorBLUE, SkXfermode::kSrcIn_Mode))->unref();
+    }
+};
+DEF_GM( return new ImageFiltersText_CF; )
+
diff --git a/gm/imagefiltersclipped.cpp b/gm/imagefiltersclipped.cpp
index 98f5bbb..cc636aa 100644
--- a/gm/imagefiltersclipped.cpp
+++ b/gm/imagefiltersclipped.cpp
@@ -12,7 +12,6 @@
 #include "SkDisplacementMapEffect.h"
 #include "SkDropShadowImageFilter.h"
 #include "SkGradientShader.h"
-#include "SkMatrixImageFilter.h"
 #include "SkMorphologyImageFilter.h"
 #include "SkOffsetImageFilter.h"
 #include "SkPerlinNoiseShader.h"
@@ -33,11 +32,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("imagefiltersclipped");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(860, 500);
     }
 
@@ -60,7 +59,7 @@
         canvas.drawCircle(x, y, radius, paint);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         if (!fInitialized) {
             fCheckerboard.allocN32Pixels(64, 64);
             SkCanvas checkerboardCanvas(fCheckerboard);
@@ -69,7 +68,7 @@
             this->make_gradient_circle(64, 64);
             fInitialized = true;
         }
-        canvas->clear(0x00000000);
+        canvas->clear(SK_ColorBLACK);
 
         SkAutoTUnref<SkImageFilter> gradient(SkBitmapSource::Create(fGradientCircle));
         SkAutoTUnref<SkImageFilter> checkerboard(SkBitmapSource::Create(fCheckerboard));
@@ -91,7 +90,7 @@
             SkDilateImageFilter::Create(2, 2, checkerboard.get()),
             SkErodeImageFilter::Create(2, 2, checkerboard.get()),
             SkOffsetImageFilter::Create(SkIntToScalar(-16), SkIntToScalar(32)),
-            SkMatrixImageFilter::Create(resizeMatrix, SkPaint::kNone_FilterLevel),
+            SkImageFilter::CreateMatrixFilter(resizeMatrix, kNone_SkFilterQuality),
             SkRectShaderImageFilter::Create(noise),
         };
 
@@ -116,8 +115,7 @@
                     canvas->scale(SkScalarInvert(RESIZE_FACTOR_X),
                                   SkScalarInvert(RESIZE_FACTOR_Y));
                 }
-                canvas->drawCircle(r.centerX(), r.centerY(),
-                                   SkScalarDiv(r.width()*2, SkIntToScalar(5)), paint);
+                canvas->drawCircle(r.centerX(), r.centerY(), r.width() * 2 / 5, paint);
                 canvas->restore();
                 canvas->translate(r.width() + margin, 0);
             }
diff --git a/gm/imagefilterscropexpand.cpp b/gm/imagefilterscropexpand.cpp
index 7db41e0..4fe746a 100644
--- a/gm/imagefilterscropexpand.cpp
+++ b/gm/imagefilterscropexpand.cpp
@@ -143,7 +143,7 @@
             draw(canvas, checkerboard, rect, SkDropShadowImageFilter::Create(
                 SkIntToScalar(10), SkIntToScalar(10), SkIntToScalar(3), SkIntToScalar(3),
                 SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
-                noop_cropped.get(), &big_rect, 0));
+                noop_cropped.get(), &big_rect));
 
             draw(canvas, checkerboard, rect, SkDisplacementMapEffect::Create(
                 SkDisplacementMapEffect::kR_ChannelSelectorType,
diff --git a/gm/imagefilterscropped.cpp b/gm/imagefilterscropped.cpp
index 3db0b12..929f2fe 100644
--- a/gm/imagefilterscropped.cpp
+++ b/gm/imagefilterscropped.cpp
@@ -96,11 +96,11 @@
     ImageFiltersCroppedGM () {}
 
 protected:
-    virtual SkString onShortName() SK_OVERRIDE {
+    virtual SkString onShortName() override {
         return SkString("imagefilterscropped");
     }
 
-    virtual SkISize onISize() SK_OVERRIDE { return SkISize::Make(400, 880); }
+    virtual SkISize onISize() override { return SkISize::Make(400, 880); }
 
     void make_checkerboard() {
         fCheckerboard.allocN32Pixels(80, 80);
@@ -130,11 +130,11 @@
         canvas->drawRect(r, paint);
     }
 
-    virtual void onOnceBeforeDraw() SK_OVERRIDE{
+    virtual void onOnceBeforeDraw() override{
         make_checkerboard();
     }
 
-    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    virtual void onDraw(SkCanvas* canvas) override {
         void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = {
             draw_sprite, draw_bitmap, draw_path, draw_paint, draw_text
         };
diff --git a/gm/imagefiltersgraph.cpp b/gm/imagefiltersgraph.cpp
index 4fa59a8..cfedaf0 100644
--- a/gm/imagefiltersgraph.cpp
+++ b/gm/imagefiltersgraph.cpp
@@ -38,7 +38,7 @@
     }
 
     virtual bool onFilterImage(Proxy* proxy, const SkBitmap& src, const Context& ctx,
-                               SkBitmap* dst, SkIPoint* offset) const SK_OVERRIDE {
+                               SkBitmap* dst, SkIPoint* offset) const override {
         SkBitmap source = src;
         SkImageFilter* input = getInput(0);
         SkIPoint srcOffset = SkIPoint::Make(0, 0);
@@ -66,7 +66,7 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SimpleOffsetFilter);
 
 protected:
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
+    void flatten(SkWriteBuffer& buffer) const override {
         this->INHERITED::flatten(buffer);
         buffer.writeScalar(fDX);
         buffer.writeScalar(fDY);
@@ -110,11 +110,11 @@
     void make_bitmap() {
         fBitmap.allocN32Pixels(100, 100);
         SkCanvas canvas(fBitmap);
-        canvas.clear(0x00000000);
+        canvas.clear(SK_ColorTRANSPARENT);
         SkPaint paint;
         paint.setAntiAlias(true);
         sk_tool_utils::set_portable_typeface(&paint);
-        paint.setColor(0xFFFFFFFF);
+        paint.setColor(SK_ColorWHITE);
         paint.setTextSize(SkIntToScalar(96));
         const char* str = "e";
         canvas.drawText(str, strlen(str), SkIntToScalar(20), SkIntToScalar(70), paint);
@@ -135,7 +135,7 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) {
-        canvas->clear(0x00000000);
+        canvas->clear(SK_ColorBLACK);
         {
             SkAutoTUnref<SkImageFilter> bitmapSource(SkBitmapSource::Create(fBitmap));
             SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED,
@@ -216,7 +216,7 @@
 
             SkPaint paint;
             paint.setImageFilter(color2);
-            paint.setColor(0xFFFF0000);
+            paint.setColor(SK_ColorRED);
             canvas->drawRect(SkRect::MakeXYWH(0, 0, 100, 100), paint);
             canvas->translate(SkIntToScalar(100), 0);
         }
diff --git a/gm/imagefiltersscaled.cpp b/gm/imagefiltersscaled.cpp
index 3a928b7..9a80964 100644
--- a/gm/imagefiltersscaled.cpp
+++ b/gm/imagefiltersscaled.cpp
@@ -13,7 +13,6 @@
 #include "SkDropShadowImageFilter.h"
 #include "SkGradientShader.h"
 #include "SkLightingImageFilter.h"
-#include "SkMatrixImageFilter.h"
 #include "SkMorphologyImageFilter.h"
 #include "SkOffsetImageFilter.h"
 #include "SkPerlinNoiseShader.h"
@@ -69,7 +68,7 @@
             this->make_gradient_circle(64, 64);
             fInitialized = true;
         }
-        canvas->clear(0x00000000);
+        canvas->clear(SK_ColorBLACK);
 
         SkAutoTUnref<SkImageFilter> gradient(SkBitmapSource::Create(fGradientCircle));
         SkAutoTUnref<SkImageFilter> checkerboard(SkBitmapSource::Create(fCheckerboard));
@@ -100,7 +99,7 @@
             SkDilateImageFilter::Create(1, 1, checkerboard.get()),
             SkErodeImageFilter::Create(1, 1, checkerboard.get()),
             SkOffsetImageFilter::Create(SkIntToScalar(32), 0),
-            SkMatrixImageFilter::Create(resizeMatrix, SkPaint::kNone_FilterLevel),
+            SkImageFilter::CreateMatrixFilter(resizeMatrix, kNone_SkFilterQuality),
             SkRectShaderImageFilter::Create(noise),
             SkLightingImageFilter::CreatePointLitDiffuse(pointLocation, white, surfaceScale, kd),
             SkLightingImageFilter::CreateSpotLitDiffuse(spotLocation, spotTarget, spotExponent,
@@ -135,8 +134,7 @@
                     canvas->scale(SkScalarInvert(RESIZE_FACTOR),
                                   SkScalarInvert(RESIZE_FACTOR));
                 }
-                canvas->drawCircle(r.centerX(), r.centerY(),
-                                   SkScalarDiv(r.width()*2, SkIntToScalar(5)), paint);
+                canvas->drawCircle(r.centerX(), r.centerY(), r.width()*2/5, paint);
                 canvas->restore();
                 canvas->translate(r.width() * scales[j].fX + margin, 0);
             }
diff --git a/gm/imagefilterstransformed.cpp b/gm/imagefilterstransformed.cpp
new file mode 100644
index 0000000..794a471
--- /dev/null
+++ b/gm/imagefilterstransformed.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "sk_tool_utils.h"
+#include "SkBitmapSource.h"
+#include "SkBlurImageFilter.h"
+#include "SkColor.h"
+#include "SkDisplacementMapEffect.h"
+#include "SkDropShadowImageFilter.h"
+#include "SkGradientShader.h"
+#include "SkMorphologyImageFilter.h"
+#include "SkScalar.h"
+#include "gm.h"
+
+namespace skiagm {
+
+// This GM draws image filters with a CTM containing shearing / rotation.
+// It checks that the scale portion of the CTM is correctly extracted
+// and applied to the image inputs separately from the non-scale portion.
+
+class ImageFiltersTransformedGM : public GM {
+public:
+    ImageFiltersTransformedGM() {
+        this->setBGColor(SK_ColorBLACK);
+    }
+
+protected:
+
+    SkString onShortName() override { return SkString("imagefilterstransformed"); }
+
+    SkISize onISize() override { return SkISize::Make(420, 240); }
+
+    void makeGradientCircle(int width, int height) {
+        SkScalar x = SkIntToScalar(width / 2);
+        SkScalar y = SkIntToScalar(height / 2);
+        SkScalar radius = SkMinScalar(x, y) * 0.8f;
+        fGradientCircle.allocN32Pixels(width, height);
+        SkCanvas canvas(fGradientCircle);
+        canvas.clear(0x00000000);
+        SkColor colors[2];
+        colors[0] = SK_ColorWHITE;
+        colors[1] = SK_ColorBLACK;
+        SkAutoTUnref<SkShader> shader(
+            SkGradientShader::CreateRadial(SkPoint::Make(x, y), radius, colors, NULL, 2,
+                                           SkShader::kClamp_TileMode)
+        );
+        SkPaint paint;
+        paint.setShader(shader);
+        canvas.drawCircle(x, y, radius, paint);
+    }
+
+    void onOnceBeforeDraw() override {
+        fCheckerboard.allocN32Pixels(64, 64);
+        SkCanvas checkerboardCanvas(fCheckerboard);
+        sk_tool_utils::draw_checkerboard(&checkerboardCanvas, 0xFFA0A0A0, 0xFF404040, 8);
+
+        this->makeGradientCircle(64, 64);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        SkAutoTUnref<SkImageFilter> gradient(SkBitmapSource::Create(fGradientCircle));
+        SkAutoTUnref<SkImageFilter> checkerboard(SkBitmapSource::Create(fCheckerboard));
+        SkImageFilter* filters[] = {
+            SkBlurImageFilter::Create(12, 0),
+            SkDropShadowImageFilter::Create(0, 15, 8, 0, SK_ColorGREEN,
+                SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode),
+            SkDisplacementMapEffect::Create(SkDisplacementMapEffect::kR_ChannelSelectorType,
+                                            SkDisplacementMapEffect::kR_ChannelSelectorType,
+                                            12,
+                                            gradient.get(),
+                                            checkerboard.get()),
+            SkDilateImageFilter::Create(2, 2, checkerboard.get()),
+            SkErodeImageFilter::Create(2, 2, checkerboard.get()),
+        };
+
+        const SkScalar margin = SkIntToScalar(20);
+        const SkScalar size = SkIntToScalar(60);
+
+        for (size_t j = 0; j < 3; j++) {
+            canvas->save();
+            canvas->translate(margin, 0);
+            for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
+                SkPaint paint;
+                paint.setColor(SK_ColorWHITE);
+                paint.setImageFilter(filters[i]);
+                paint.setAntiAlias(true);
+                canvas->save();
+                canvas->translate(size * SK_ScalarHalf, size * SK_ScalarHalf);
+                canvas->scale(SkDoubleToScalar(0.8), SkDoubleToScalar(0.8));
+                if (j == 1) {
+                    canvas->rotate(SkIntToScalar(45));
+                } else if (j == 2) {
+                    canvas->skew(SkDoubleToScalar(0.5), SkDoubleToScalar(0.2));
+                }
+                canvas->translate(-size * SK_ScalarHalf, -size * SK_ScalarHalf);
+                canvas->drawOval(SkRect::MakeXYWH(0, size * SkDoubleToScalar(0.1),
+                                                  size, size * SkDoubleToScalar(0.6)), paint);
+                canvas->restore();
+                canvas->translate(size + margin, 0);
+            }
+            canvas->restore();
+            canvas->translate(0, size + margin);
+        }
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
+            SkSafeUnref(filters[i]);
+        }
+    }
+
+private:
+    SkBitmap fCheckerboard;
+    SkBitmap fGradientCircle;
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return new ImageFiltersTransformedGM; )
+
+}
diff --git a/gm/imagemagnifier.cpp b/gm/imagemagnifier.cpp
index 30be060..5e48350 100644
--- a/gm/imagemagnifier.cpp
+++ b/gm/imagemagnifier.cpp
@@ -22,15 +22,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("imagemagnifier");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(WIDTH, HEIGHT);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint filterPaint;
         filterPaint.setImageFilter(
             SkMagnifierImageFilter::Create(
diff --git a/gm/imageresizetiled.cpp b/gm/imageresizetiled.cpp
index 6eb254c..bc640bc 100644
--- a/gm/imageresizetiled.cpp
+++ b/gm/imageresizetiled.cpp
@@ -6,7 +6,7 @@
  */
 
 #include "gm.h"
-#include "SkMatrixImageFilter.h"
+#include "SkImageFilter.h"
 #include "SkRandom.h"
 
 #define WIDTH 640
@@ -23,20 +23,20 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("imageresizetiled");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(WIDTH, HEIGHT);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         SkMatrix matrix;
         matrix.setScale(RESIZE_FACTOR, RESIZE_FACTOR);
         SkAutoTUnref<SkImageFilter> imageFilter(
-            SkMatrixImageFilter::Create(matrix, SkPaint::kNone_FilterLevel));
+            SkImageFilter::CreateMatrixFilter(matrix, kNone_SkFilterQuality));
         paint.setImageFilter(imageFilter.get());
         const SkScalar tile_size = SkIntToScalar(100);
         SkRect bounds;
diff --git a/gm/inversepaths.cpp b/gm/inversepaths.cpp
index 87a977a..ae54bee 100644
--- a/gm/inversepaths.cpp
+++ b/gm/inversepaths.cpp
@@ -71,15 +71,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("inverse_paths");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(800, 900);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkScalar cx = slideWidth / 2 + slideBoundary;
         SkScalar cy = slideHeight / 2 + slideBoundary;
         SkScalar dx = slideWidth + 2 * slideBoundary;
diff --git a/gm/largeglyphblur.cpp b/gm/largeglyphblur.cpp
new file mode 100644
index 0000000..8723824
--- /dev/null
+++ b/gm/largeglyphblur.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+
+#include "SkBlurMask.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkTextBlob.h"
+
+// This test ensures that glyphs whose point size is less than the SkGlyphCache's maxmium, but
+// who have a large blur, are still handled correctly
+namespace skiagm {
+class LargeGlyphBlur : public GM {
+public:
+    LargeGlyphBlur() {}
+
+protected:
+    SkString onShortName() override {
+        return SkString("largeglyphblur");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(kWidth, kHeight);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        const char text[] = "Hamburgefons";
+
+        SkPaint paint;
+        sk_tool_utils::set_portable_typeface(&paint);
+        paint.setTextSize(256);
+        paint.setAntiAlias(true);
+
+        // setup up maskfilter
+        static const SkScalar kSigma = SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(40));
+
+        SkPaint blurPaint(paint);
+        SkAutoTUnref<SkMaskFilter> mf(SkBlurMaskFilter::Create(kNormal_SkBlurStyle, kSigma));
+        blurPaint.setMaskFilter(mf);
+
+        SkTextBlobBuilder builder;
+
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, 0, 0);
+
+        SkAutoTUnref<const SkTextBlob> blob(builder.build());
+        canvas->drawTextBlob(blob.get(), 10, 200, blurPaint);
+        canvas->drawTextBlob(blob.get(), 10, 200, paint);
+
+        size_t len = strlen(text);
+        canvas->drawText(text, len, 10, 500, blurPaint);
+        canvas->drawText(text, len, 10, 500, paint);
+    }
+
+private:
+    static const int kWidth = 1920;
+    static const int kHeight = 600;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(LargeGlyphBlur); )
+}
diff --git a/gm/lerpmode.cpp b/gm/lerpmode.cpp
index 7b567a2..b483c13 100644
--- a/gm/lerpmode.cpp
+++ b/gm/lerpmode.cpp
@@ -39,15 +39,15 @@
     LerpXfermodeGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("lerpmode");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(240, 120);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         show_circlelayers(canvas, NULL);
         canvas->translate(150, 0);
         SkAutoTUnref<SkXfermode> mode(SkLerpXfermode::Create(0.5f));
diff --git a/gm/lighting.cpp b/gm/lighting.cpp
index 7abfbce..0bfbb47 100644
--- a/gm/lighting.cpp
+++ b/gm/lighting.cpp
@@ -7,9 +7,10 @@
 
 #include "gm.h"
 #include "SkLightingImageFilter.h"
+#include "SkOffsetImageFilter.h"
 
 #define WIDTH 330
-#define HEIGHT 440
+#define HEIGHT 660
 
 namespace skiagm {
 
@@ -21,7 +22,7 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("lighting");
     }
 
@@ -38,7 +39,7 @@
         canvas.drawText(str, strlen(str), SkIntToScalar(20), SkIntToScalar(70), paint);
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(WIDTH, HEIGHT);
     }
 
@@ -51,7 +52,7 @@
         canvas->restore();
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         if (!fInitialized) {
             make_bitmap();
             fInitialized = true;
@@ -86,28 +87,70 @@
         SkPaint paint;
 
         SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(20, 10, 60, 65));
+        SkImageFilter::CropRect fullSizeCropRect(SkRect::MakeXYWH(0, 0, 100, 100));
+        SkAutoTUnref<SkImageFilter> noopCropped(SkOffsetImageFilter::Create(0, 0, NULL, &cropRect));
 
         int y = 0;
-        for (int i = 0; i < 2; i++) {
-            const SkImageFilter::CropRect* cr = (i == 0) ? NULL : &cropRect;
-            paint.setImageFilter(SkLightingImageFilter::CreatePointLitDiffuse(pointLocation, white, surfaceScale, kd, NULL, cr))->unref();
+        for (int i = 0; i < 3; i++) {
+            const SkImageFilter::CropRect* cr = (i == 1) ? &cropRect : (i == 2) ? &fullSizeCropRect : NULL;
+            SkImageFilter* input = (i == 2) ? noopCropped.get() : NULL;
+            paint.setImageFilter(SkLightingImageFilter::CreatePointLitDiffuse(pointLocation,
+                                                                              white,
+                                                                              surfaceScale,
+                                                                              kd,
+                                                                              input,
+                                                                              cr))->unref();
             drawClippedBitmap(canvas, paint, 0, y);
 
-            paint.setImageFilter(SkLightingImageFilter::CreateDistantLitDiffuse(distantDirection, white, surfaceScale, kd, NULL, cr))->unref();
+            paint.setImageFilter(SkLightingImageFilter::CreateDistantLitDiffuse(distantDirection,
+                                                                                white,
+                                                                                surfaceScale,
+                                                                                kd,
+                                                                                input,
+                                                                                cr))->unref();
             drawClippedBitmap(canvas, paint, 110, y);
 
-            paint.setImageFilter(SkLightingImageFilter::CreateSpotLitDiffuse(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, kd, NULL, cr))->unref();
+            paint.setImageFilter(SkLightingImageFilter::CreateSpotLitDiffuse(spotLocation,
+                                                                             spotTarget,
+                                                                             spotExponent,
+                                                                             cutoffAngle,
+                                                                             white,
+                                                                             surfaceScale,
+                                                                             kd,
+                                                                             input,
+                                                                             cr))->unref();
             drawClippedBitmap(canvas, paint, 220, y);
 
             y += 110;
 
-            paint.setImageFilter(SkLightingImageFilter::CreatePointLitSpecular(pointLocation, white, surfaceScale, ks, shininess, NULL, cr))->unref();
+            paint.setImageFilter(SkLightingImageFilter::CreatePointLitSpecular(pointLocation,
+                                                                               white,
+                                                                               surfaceScale,
+                                                                               ks,
+                                                                               shininess,
+                                                                               input,
+                                                                               cr))->unref();
             drawClippedBitmap(canvas, paint, 0, y);
 
-            paint.setImageFilter(SkLightingImageFilter::CreateDistantLitSpecular(distantDirection, white, surfaceScale, ks, shininess, NULL, cr))->unref();
+            paint.setImageFilter(SkLightingImageFilter::CreateDistantLitSpecular(distantDirection,
+                                                                                 white,
+                                                                                 surfaceScale,
+                                                                                 ks,
+                                                                                 shininess,
+                                                                                 input,
+                                                                                 cr))->unref();
             drawClippedBitmap(canvas, paint, 110, y);
 
-            paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, ks, shininess, NULL, cr))->unref();
+            paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(spotLocation,
+                                                                              spotTarget,
+                                                                              spotExponent,
+                                                                              cutoffAngle,
+                                                                              white,
+                                                                              surfaceScale,
+                                                                              ks,
+                                                                              shininess,
+                                                                              input,
+                                                                              cr))->unref();
             drawClippedBitmap(canvas, paint, 220, y);
 
             y += 110;
diff --git a/gm/lumafilter.cpp b/gm/lumafilter.cpp
index e83f327..66221f8 100644
--- a/gm/lumafilter.cpp
+++ b/gm/lumafilter.cpp
@@ -99,15 +99,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("lumafilter");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(600, 420);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkXfermode::Mode modes[] = { SkXfermode::kSrcOver_Mode,
                                      SkXfermode::kDstOver_Mode,
                                      SkXfermode::kSrcATop_Mode,
diff --git a/gm/matrixconvolution.cpp b/gm/matrixconvolution.cpp
index 5b1ede3..b912f90 100644
--- a/gm/matrixconvolution.cpp
+++ b/gm/matrixconvolution.cpp
@@ -84,7 +84,7 @@
             make_bitmap();
             fInitialized = true;
         }
-        canvas->clear(0x00000000);
+        canvas->clear(SK_ColorBLACK);
         SkIPoint kernelOffset = SkIPoint::Make(1, 0);
         for (int x = 10; x < 310; x += 100) {
             this->draw(canvas, x, 10, kernelOffset, MCIF::kClamp_TileMode, true);
diff --git a/gm/matriximagefilter.cpp b/gm/matriximagefilter.cpp
index 59c5106..419c419 100644
--- a/gm/matriximagefilter.cpp
+++ b/gm/matriximagefilter.cpp
@@ -7,7 +7,7 @@
 
 #include "gm.h"
 #include "SkColor.h"
-#include "SkMatrixImageFilter.h"
+#include "SkImageFilter.h"
 
 namespace skiagm {
 
@@ -23,9 +23,9 @@
     }
 
     void draw(SkCanvas* canvas, const SkRect& rect, const SkBitmap& bitmap,
-              const SkMatrix& matrix, SkPaint::FilterLevel filterLevel) {
+              const SkMatrix& matrix, SkFilterQuality filter) {
         SkAutoTUnref<SkImageFilter> imageFilter(
-            SkMatrixImageFilter::Create(matrix, filterLevel));
+            SkImageFilter::CreateMatrixFilter(matrix, filter));
         SkPaint paint;
         paint.setImageFilter(imageFilter.get());
         canvas->saveLayer(&rect, &paint);
@@ -58,7 +58,7 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) {
-        canvas->clear(0x00000000);
+        canvas->clear(SK_ColorBLACK);
         SkMatrix matrix;
         SkScalar margin = SkIntToScalar(10);
         matrix.setSkew(SkDoubleToScalar(0.5), SkDoubleToScalar(0.2));
@@ -68,18 +68,18 @@
         SkRect srcRect = SkRect::MakeWH(96, 96);
 
         canvas->translate(margin, margin);
-        draw(canvas, srcRect, checkerboard, matrix, SkPaint::kNone_FilterLevel);
+        draw(canvas, srcRect, checkerboard, matrix, kNone_SkFilterQuality);
 
         canvas->translate(srcRect.width() + margin, 0);
-        draw(canvas, srcRect, checkerboard, matrix, SkPaint::kLow_FilterLevel);
+        draw(canvas, srcRect, checkerboard, matrix, kLow_SkFilterQuality);
 
 #if 0
         // This may be causing Mac 10.6 to barf.
         canvas->translate(srcRect.width() + margin, 0);
-        draw(canvas, srcRect, checkerboard, matrix, SkPaint::kMedium_FilterLevel);
+        draw(canvas, srcRect, checkerboard, matrix, kMedium_SkFilterQuality);
 
         canvas->translate(srcRect.width() + margin, 0);
-        draw(canvas, srcRect, checkerboard, matrix, SkPaint::kHigh_FilterLevel);
+        draw(canvas, srcRect, checkerboard, matrix, kHigh_SkFilterQuality);
 #endif
     }
 
diff --git a/gm/mipmap.cpp b/gm/mipmap.cpp
index 948fd99..2d4ee0d 100644
--- a/gm/mipmap.cpp
+++ b/gm/mipmap.cpp
@@ -41,7 +41,7 @@
 
     canvas->translate(20, 20);
     for (int i = 0; i < 4; ++i) {
-        paint.setFilterLevel(SkPaint::FilterLevel(i));
+        paint.setFilterQuality(SkFilterQuality(i));
         canvas->drawImageRect(img, NULL, dst, &paint);
         canvas->translate(0, 20);
     }
@@ -53,11 +53,11 @@
     MipMapGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE { return SkString("mipmap"); }
+    SkString onShortName() override { return SkString("mipmap"); }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(400, 200); }
+    SkISize onISize() override { return SkISize::Make(400, 200); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         test_mip(canvas);
     }
 
diff --git a/gm/mixedtextblobs.cpp b/gm/mixedtextblobs.cpp
new file mode 100644
index 0000000..fb1b632
--- /dev/null
+++ b/gm/mixedtextblobs.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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 "Resources.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkStream.h"
+#include "SkTextBlob.h"
+#include "SkTypeface.h"
+
+namespace skiagm {
+
+static void draw_blob(SkCanvas* canvas, const SkTextBlob* blob, const SkPaint& skPaint,
+                      const SkRect& clipRect) {
+    SkPaint clipHairline;
+    clipHairline.setColor(SK_ColorWHITE);
+    clipHairline.setStyle(SkPaint::kStroke_Style);
+
+    SkPaint paint(skPaint);
+    canvas->save();
+    canvas->drawRect(clipRect, clipHairline);
+    paint.setAlpha(0x20);
+    canvas->drawTextBlob(blob, 0, 0, paint);
+    canvas->clipRect(clipRect);
+    paint.setAlpha(0xFF);
+    canvas->drawTextBlob(blob, 0, 0, paint);
+    canvas->restore();
+}
+
+class MixedTextBlobsGM : public GM {
+public:
+    MixedTextBlobsGM() { }
+
+protected:
+    void onOnceBeforeDraw() override {
+#ifndef SK_BUILD_FOR_MAC
+        fEmojiTypeface.reset(GetResourceAsTypeface("/fonts/Funkster.ttf"));
+        fEmojiText = "Emoji!!!";
+#else
+        fEmojiTypeface.reset(SkTypeface::CreateFromName("Apple Color Emoji", SkTypeface::kNormal));
+        fEmojiText = "\xF0\x9F\x92\xB0" "\xF0\x9F\x8F\xA1" "\xF0\x9F\x8E\x85" // 💰🏡🎅
+                     "\xF0\x9F\x8D\xAA" "\xF0\x9F\x8D\x95" "\xF0\x9F\x9A\x80"; // 🍪🍕🚀
+#endif
+        fReallyBigATypeface.reset(GetResourceAsTypeface("/fonts/ReallyBigA.ttf"));
+
+        SkTextBlobBuilder builder;
+
+        // make textblob
+        // Text so large we draw as paths
+        SkPaint paint;
+        paint.setTextSize(384);
+        const char* text = "O";
+        sk_tool_utils::set_portable_typeface(&paint);
+
+        SkRect bounds;
+        paint.measureText(text, strlen(text), &bounds);
+
+        SkScalar yOffset = bounds.height();
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, 10, yOffset);
+        SkScalar corruptedAx = bounds.width();
+        SkScalar corruptedAy = yOffset;
+
+        const SkScalar boundsHalfWidth = bounds.width() * SK_ScalarHalf;
+        const SkScalar boundsHalfHeight = bounds.height() * SK_ScalarHalf;
+
+        SkScalar xOffset = boundsHalfWidth;
+        yOffset = boundsHalfHeight;
+
+        // LCD
+        paint.setTextSize(32);
+        text = "LCD!!!!!";
+        paint.setSubpixelText(true);
+        paint.setLCDRenderText(true);
+        paint.measureText(text, strlen(text), &bounds);
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, xOffset - bounds.width() * 0.25f,
+                                        yOffset - bounds.height() * 0.5f);
+        yOffset += bounds.height();
+
+        // color emoji
+        paint.setSubpixelText(false);
+        paint.setLCDRenderText(false);
+        paint.setTypeface(fEmojiTypeface);
+        text = fEmojiText;
+        paint.measureText(text, strlen(text), &bounds);
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, xOffset - bounds.width() * 0.3f,
+                                        yOffset);
+
+        // Corrupted font
+        paint.setTextSize(12);
+        text = "aA";
+        paint.setTypeface(fReallyBigATypeface);
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, corruptedAx, corruptedAy);
+        fBlob.reset(builder.build());
+    }
+
+    SkString onShortName() override {
+        return SkString("mixedtextblobs");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(kWidth, kHeight);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+
+        canvas->drawColor(SK_ColorGRAY);
+
+        SkPaint paint;
+
+        // setup work needed to draw text with different clips
+        paint.setColor(SK_ColorBLACK);
+        canvas->translate(10, 40);
+
+        paint.setTextSize(40);
+
+        // compute the bounds of the text and setup some clips
+        SkRect bounds = fBlob->bounds();
+
+        const SkScalar boundsHalfWidth = bounds.width() * SK_ScalarHalf;
+        const SkScalar boundsHalfHeight = bounds.height() * SK_ScalarHalf;
+        const SkScalar boundsQuarterWidth = boundsHalfWidth * SK_ScalarHalf;
+        const SkScalar boundsQuarterHeight = boundsHalfHeight * SK_ScalarHalf;
+
+        SkRect upperLeftClip = SkRect::MakeXYWH(bounds.left(), bounds.top(),
+                                                boundsHalfWidth, boundsHalfHeight);
+        SkRect lowerRightClip = SkRect::MakeXYWH(bounds.centerX(), bounds.centerY(),
+                                                 boundsHalfWidth, boundsHalfHeight);
+        SkRect interiorClip = bounds;
+        interiorClip.inset(boundsQuarterWidth, boundsQuarterHeight);
+
+        const SkRect clipRects[] = { bounds, upperLeftClip, lowerRightClip, interiorClip};
+
+        size_t count = sizeof(clipRects) / sizeof(SkRect);
+        for (size_t x = 0; x < count; ++x) {
+            draw_blob(canvas, fBlob, paint, clipRects[x]);
+            if (x == (count >> 1) - 1) {
+                canvas->translate(SkScalarFloorToScalar(bounds.width() + SkIntToScalar(25)),
+                                  -(x * SkScalarFloorToScalar(bounds.height() +
+                                    SkIntToScalar(25))));
+            } else {
+                canvas->translate(0, SkScalarFloorToScalar(bounds.height() + SkIntToScalar(25)));
+            }
+        }
+    }
+
+private:
+    SkAutoTUnref<SkTypeface> fEmojiTypeface;
+    SkAutoTUnref<SkTypeface> fReallyBigATypeface;
+    const char* fEmojiText;
+    SkAutoTUnref<const SkTextBlob> fBlob;
+
+    static const int kWidth = 1250;
+    static const int kHeight = 700;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(MixedTextBlobsGM); )
+}
diff --git a/gm/mixedxfermodes.cpp b/gm/mixedxfermodes.cpp
index f818719..dc858cb 100644
--- a/gm/mixedxfermodes.cpp
+++ b/gm/mixedxfermodes.cpp
@@ -31,11 +31,11 @@
         kNumShapeTypes
     };
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("mixed_xfermodes");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(790, 640);
     }
 
@@ -87,7 +87,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         if (NULL == fBG.get()) {
             static uint32_t kCheckerPixelData[] = { 0xFFFFFFFF,
                                                     0xFFCCCCCC,
diff --git a/gm/multipicturedraw.cpp b/gm/multipicturedraw.cpp
index 97a5ffc..0e11b06 100644
--- a/gm/multipicturedraw.cpp
+++ b/gm/multipicturedraw.cpp
@@ -154,7 +154,7 @@
 
     SkPaint fill;
     fill.setStyle(SkPaint::kFill_Style);
-    fill.setColor(SK_ColorLTGRAY);;
+    fill.setColor(SK_ColorLTGRAY);
 
     SkPaint stroke;
     stroke.setStyle(SkPaint::kStroke_Style);
@@ -423,8 +423,8 @@
 
             SkCanvas* subCanvas = step.fSurf->getCanvas();
 
-            SkMatrix trans;
-            trans.setTranslate(-SkIntToScalar(x*kTileWidth), -SkIntToScalar(y*kTileHeight));
+            const SkMatrix trans = SkMatrix::MakeTrans(-SkIntToScalar(x*kTileWidth), 
+                                                       -SkIntToScalar(y*kTileHeight));
 
             create_content(mpd, pfGen, pictures, subCanvas, trans);
         }
@@ -486,14 +486,14 @@
         Layout           fLayout;
         const SkPicture* fPictures[kNumPictures];
 
-        void onOnceBeforeDraw() SK_OVERRIDE {
+        void onOnceBeforeDraw() override {
             fPictures[0] = make_hex_plane_picture(SK_ColorWHITE);
             fPictures[1] = make_hex_plane_picture(SK_ColorGRAY);
             fPictures[2] = make_sierpinski_picture();
             fPictures[3] = make_single_layer_hex_plane_picture();
         }
 
-        void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        void onDraw(SkCanvas* canvas) override {
             SkMultiPictureDraw mpd;
             SkTArray<ComposeStep> composeSteps;
 
@@ -514,9 +514,9 @@
             }
         }
 
-        SkISize onISize() SK_OVERRIDE { return SkISize::Make(kPicWidth, kPicHeight); }
+        SkISize onISize() override { return SkISize::Make(kPicWidth, kPicHeight); }
 
-        SkString onShortName() SK_OVERRIDE {
+        SkString onShortName() override {
             static const char* gContentNames[] = {
                 "noclip", "rectclip", "rrectclip", "pathclip", 
                 "invpathclip", "sierpinski", "biglayer"
@@ -534,7 +534,7 @@
             return name;
         }
 
-        bool runAsBench() const SK_OVERRIDE { return true; }
+        bool runAsBench() const override { return true; }
 
     private:
         typedef GM INHERITED;
diff --git a/gm/nested.cpp b/gm/nested.cpp
index 025bdce..3dd2c2c 100644
--- a/gm/nested.cpp
+++ b/gm/nested.cpp
@@ -20,7 +20,7 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString name("nested");
         if (fDoAA) {
             name.append("_aa");
@@ -30,7 +30,7 @@
         return name;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(kImageWidth, kImageHeight);
     }
 
@@ -60,7 +60,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         SkPaint shapePaint;
         shapePaint.setColor(SK_ColorBLACK);
diff --git a/gm/ninepatchstretch.cpp b/gm/ninepatchstretch.cpp
index 67f6bf4..5031480 100644
--- a/gm/ninepatchstretch.cpp
+++ b/gm/ninepatchstretch.cpp
@@ -73,7 +73,7 @@
         SkScalar y = SkIntToScalar(100);
 
         SkPaint paint;
-        paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        paint.setFilterQuality(kLow_SkFilterQuality);
 
         for (int iy = 0; iy < 2; ++iy) {
             for (int ix = 0; ix < 2; ++ix) {
diff --git a/gm/nonclosedpaths.cpp b/gm/nonclosedpaths.cpp
index bf49ca0..a3d91b5 100644
--- a/gm/nonclosedpaths.cpp
+++ b/gm/nonclosedpaths.cpp
@@ -33,12 +33,12 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("nonclosedpaths");
     }
 
     // 12 * 18 + 3 cases, every case is 100 * 100 pixels.
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1220, 1920);
     }
 
@@ -67,7 +67,7 @@
         canvas->translate(x, y);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         // Stroke widths are:
         // 0(may use hairline rendering), 10(common case for stroke-style)
         // 40 and 50(>= geometry width/height, make the contour filled in fact)
diff --git a/gm/offsetimagefilter.cpp b/gm/offsetimagefilter.cpp
index fb896dd..9808bd8 100644
--- a/gm/offsetimagefilter.cpp
+++ b/gm/offsetimagefilter.cpp
@@ -77,7 +77,7 @@
 
             fInitialized = true;
         }
-        canvas->clear(0x00000000);
+        canvas->clear(SK_ColorBLACK);
         SkPaint paint;
 
         for (int i = 0; i < 4; i++) {
diff --git a/gm/ovals.cpp b/gm/ovals.cpp
index 7af996e..b0c207b 100755
--- a/gm/ovals.cpp
+++ b/gm/ovals.cpp
@@ -27,11 +27,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("ovals");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1200, 900);
     }
 
@@ -135,7 +135,7 @@
         return SkHSVToColor(hsv);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkRandom rand(1);
         canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
         SkRect oval = SkRect::MakeLTRB(-20, -30, 20, 30);
diff --git a/gm/patch.cpp b/gm/patch.cpp
index 61a6cad..ea7954c 100644
--- a/gm/patch.cpp
+++ b/gm/patch.cpp
@@ -76,15 +76,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("patch_primitive");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(800, 800);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         SkPaint paint;
         
diff --git a/gm/patchgrid.cpp b/gm/patchgrid.cpp
index 7c0ee9c..5c719bf 100644
--- a/gm/patchgrid.cpp
+++ b/gm/patchgrid.cpp
@@ -64,15 +64,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("patch_grid");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(800, 800);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         SkPaint paint;
         
diff --git a/gm/patheffects.cpp b/gm/patheffects.cpp
index c51ac12..c71d32b 100644
--- a/gm/patheffects.cpp
+++ b/gm/patheffects.cpp
@@ -108,13 +108,13 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("patheffect");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(800, 600); }
+    SkISize onISize() override { return SkISize::Make(800, 600); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setStyle(SkPaint::kStroke_Style);
diff --git a/gm/pathfill.cpp b/gm/pathfill.cpp
index 461a4f5..1260654 100644
--- a/gm/pathfill.cpp
+++ b/gm/pathfill.cpp
@@ -118,22 +118,22 @@
     SkPath  fPath[N];
     SkScalar fDY[N];
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         for (size_t i = 0; i < N; i++) {
             fDY[i] = gProcs[i](&fPath[i]);
         }
     }
 
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("pathfill");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
 
@@ -152,17 +152,17 @@
     SkPath  fPath[N];
     SkScalar fDY[N];
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         for (size_t i = 0; i < N; i++) {
             fDY[i] = gProcs[i](&fPath[i]);
         }
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("pathinvfill");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(450, 220);
     }
 
@@ -179,7 +179,7 @@
         canvas->restore();
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPath path;
 
         path.addCircle(SkIntToScalar(50), SkIntToScalar(50), SkIntToScalar(40));
diff --git a/gm/pathinterior.cpp b/gm/pathinterior.cpp
index a354513..f56681a 100644
--- a/gm/pathinterior.cpp
+++ b/gm/pathinterior.cpp
@@ -25,11 +25,11 @@
     }
 
 protected:
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(770, 770);
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("pathinterior");
     }
 
@@ -57,7 +57,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(8.5f, 8.5f);
 
         const SkRect rect = { 0, 0, 80, 80 };
diff --git a/gm/pathopsinverse.cpp b/gm/pathopsinverse.cpp
index 9fe7872..476eda1 100644
--- a/gm/pathopsinverse.cpp
+++ b/gm/pathopsinverse.cpp
@@ -19,17 +19,17 @@
     }
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         const unsigned oneColor = 0xFF8080FF;
         const unsigned twoColor = 0x807F1f1f;
         SkColor blendColor = blend(oneColor, twoColor);
         makePaint(&fOnePaint, oneColor);
         makePaint(&fTwoPaint, twoColor);
-        makePaint(&fOpPaint[kDifference_PathOp], oneColor);
-        makePaint(&fOpPaint[kIntersect_PathOp], blendColor);
-        makePaint(&fOpPaint[kUnion_PathOp], 0xFFc0FFc0);
-        makePaint(&fOpPaint[kReverseDifference_PathOp], twoColor);
-        makePaint(&fOpPaint[kXOR_PathOp], 0xFFa0FFe0);
+        makePaint(&fOpPaint[kDifference_SkPathOp], oneColor);
+        makePaint(&fOpPaint[kIntersect_SkPathOp], blendColor);
+        makePaint(&fOpPaint[kUnion_SkPathOp], 0xFFc0FFc0);
+        makePaint(&fOpPaint[kReverseDifference_SkPathOp], twoColor);
+        makePaint(&fOpPaint[kXOR_SkPathOp], 0xFFa0FFe0);
         makePaint(&fOutlinePaint, 0xFF000000);
         fOutlinePaint.setStyle(SkPaint::kStroke_Style);
     }
@@ -50,15 +50,15 @@
         paint->setColor(color);
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("pathopsinverse");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1200, 900);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPath one, two;
         int yPos = 0;
         for (int oneFill = 0; oneFill <= 1; ++oneFill) {
@@ -82,7 +82,7 @@
                 canvas->drawPath(two, fOutlinePaint);
                 canvas->restore();
                 int xPos = 150;
-                for (int op = kDifference_PathOp; op <= kReverseDifference_PathOp; ++op) {
+                for (int op = kDifference_SkPathOp; op <= kReverseDifference_SkPathOp; ++op) {
                     SkPath result;
                     Op(one, two, (SkPathOp) op, &result);
                     canvas->save();
@@ -102,7 +102,7 @@
     SkPaint fOnePaint;
     SkPaint fTwoPaint;
     SkPaint fOutlinePaint;
-    SkPaint fOpPaint[kReverseDifference_PathOp - kDifference_PathOp + 1];
+    SkPaint fOpPaint[kReverseDifference_SkPathOp - kDifference_SkPathOp + 1];
     typedef GM INHERITED;
 };
 
diff --git a/gm/pathopsskpclip.cpp b/gm/pathopsskpclip.cpp
index ee6765a..c9092f4 100644
--- a/gm/pathopsskpclip.cpp
+++ b/gm/pathopsskpclip.cpp
@@ -24,15 +24,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("pathopsskpclip");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1200, 900);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPictureRecorder recorder;
         SkCanvas* rec = recorder.beginRecording(1200, 900, NULL, 0);
         SkPath p;
diff --git a/gm/pathreverse.cpp b/gm/pathreverse.cpp
index f304467..9f4deb4 100644
--- a/gm/pathreverse.cpp
+++ b/gm/pathreverse.cpp
@@ -69,15 +69,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("path-reverse");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         if (false) test_rev(canvas); // avoid bit rot, suppress warning
         SkRect r = { 10, 10, 100, 60 };
 
diff --git a/gm/peekpixels.cpp b/gm/peekpixels.cpp
index 3a881ff..37ec32f 100644
--- a/gm/peekpixels.cpp
+++ b/gm/peekpixels.cpp
@@ -24,15 +24,15 @@
     PeekPixelsGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("peekpixels");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(360, 120);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
         SkAutoTUnref<SkSurface> surface(canvas->newSurface(info));
         if (surface.get()) {
diff --git a/gm/perlinnoise.cpp b/gm/perlinnoise.cpp
index b69b7d0..702e545 100644
--- a/gm/perlinnoise.cpp
+++ b/gm/perlinnoise.cpp
@@ -58,7 +58,7 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) {
-        canvas->clear(0x00000000);
+        canvas->clear(SK_ColorBLACK);
         test(canvas,   0,   0, SkPerlinNoiseShader::kFractalNoise_Type,
              0.1f, 0.1f, 0, 0, false);
         test(canvas, 100,   0, SkPerlinNoiseShader::kTurbulence_Type,
diff --git a/gm/picture.cpp b/gm/picture.cpp
index 5432fc9..f1d2a79 100644
--- a/gm/picture.cpp
+++ b/gm/picture.cpp
@@ -44,19 +44,19 @@
     {}
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
          fPicture.reset(make_picture());
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("pictures");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(450, 120);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(10, 10);
 
         SkMatrix matrix;
diff --git a/gm/pictureimagefilter.cpp b/gm/pictureimagefilter.cpp
index 6f05853..fd8ae62 100644
--- a/gm/pictureimagefilter.cpp
+++ b/gm/pictureimagefilter.cpp
@@ -18,14 +18,14 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("pictureimagefilter");
     }
 
     void makePicture() {
         SkPictureRecorder recorder;
         SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0);
-        canvas->clear(0x00000000);
+        canvas->clear(SK_ColorBLACK);
         SkPaint paint;
         paint.setAntiAlias(true);
         sk_tool_utils::set_portable_typeface(&paint);
@@ -36,9 +36,9 @@
         fPicture.reset(recorder.endRecording());
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(600, 300); }
+    SkISize onISize() override { return SkISize::Make(600, 300); }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         this->makePicture();
     }
 
@@ -51,8 +51,8 @@
         canvas->restore();
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
-        canvas->clear(0x00000000);
+    void onDraw(SkCanvas* canvas) override {
+        canvas->clear(SK_ColorBLACK);
         {
             SkRect srcRect = SkRect::MakeXYWH(20, 20, 30, 30);
             SkRect emptyRect = SkRect::MakeXYWH(20, 20, 0, 0);
@@ -65,10 +65,10 @@
                 SkPictureImageFilter::Create(fPicture, emptyRect));
             SkAutoTUnref<SkPictureImageFilter> pictureSourceResampled(
                 SkPictureImageFilter::CreateForLocalSpace(fPicture, fPicture->cullRect(),
-                    SkPaint::kLow_FilterLevel));
+                    kLow_SkFilterQuality));
             SkAutoTUnref<SkPictureImageFilter> pictureSourcePixelated(
                 SkPictureImageFilter::CreateForLocalSpace(fPicture, fPicture->cullRect(),
-                    SkPaint::kNone_FilterLevel));
+                    kNone_SkFilterQuality));
 
             canvas->save();
             // Draw the picture unscaled.
diff --git a/gm/pictureshader.cpp b/gm/pictureshader.cpp
index 3f184ec..220649b 100644
--- a/gm/pictureshader.cpp
+++ b/gm/pictureshader.cpp
@@ -30,7 +30,7 @@
     }
 
  protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
        // Build the picture.
         SkPictureRecorder recorder;
         SkCanvas* pictureCanvas = recorder.beginRecording(fTileSize, fTileSize, NULL, 0);
@@ -45,15 +45,15 @@
     }
 
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("pictureshader");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1400, 1450);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         this->drawSceneColumn(canvas, SkPoint::Make(0, 0), 1, 1, 0);
         this->drawSceneColumn(canvas, SkPoint::Make(0, fSceneSize * 6.4f), 1, 2, 0);
         this->drawSceneColumn(canvas, SkPoint::Make(fSceneSize * 2.4f, 0), 1, 1, 1);
diff --git a/gm/pictureshadertile.cpp b/gm/pictureshadertile.cpp
index 46a4386..11d6b34 100644
--- a/gm/pictureshadertile.cpp
+++ b/gm/pictureshadertile.cpp
@@ -83,15 +83,15 @@
 class PictureShaderTileGM : public skiagm::GM {
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("pictureshadertile");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(800, 600);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         SkPictureRecorder recorder;
         SkCanvas* pictureCanvas = recorder.beginRecording(kPictureSize, kPictureSize);
         draw_scene(pictureCanvas, kPictureSize);
@@ -132,7 +132,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->clear(SK_ColorBLACK);
 
         SkPaint paint;
diff --git a/gm/pixelsnap.cpp b/gm/pixelsnap.cpp
new file mode 100644
index 0000000..8a513f3
--- /dev/null
+++ b/gm/pixelsnap.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2015 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 "SkShader.h"
+
+// This class of GMs test how edges/verts snap near rounding boundaries in device space without
+// anti-aliaing.
+class PixelSnapGM : public skiagm::GM {
+public:
+    PixelSnapGM() {}
+
+protected:
+    // kTrans should be even or checkboards wont agree in different test cases.
+    static const int kTrans = 14;
+    static const int kLabelPad = 4;
+    // The inverse of this value should be a perfect SkScalar.
+    static const int kSubPixelSteps = 8;
+    static const int kLabelTextSize = 9;
+
+    SK_COMPILE_ASSERT(kSubPixelSteps < 99, label_offset_too_small);
+    static const int kLabelOffsetX = 2 * kLabelTextSize + kLabelPad;
+    static const int kLabelOffsetY = kLabelTextSize + kLabelPad;
+
+    SkISize onISize() override {
+        return SkISize::Make((kSubPixelSteps + 1) * kTrans + kLabelOffsetX + kLabelPad,
+                             (kSubPixelSteps + 1) * kTrans + kLabelOffsetY + kLabelPad);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        SkPaint bgPaint;
+        bgPaint.setShader(
+            sk_tool_utils::create_checkerboard_shader(0xFFAAAAAA, 0xFF777777, 1))->unref();
+        canvas->drawPaint(bgPaint);
+
+        SkString offset;
+        SkPaint labelPaint;
+        labelPaint.setAntiAlias(true);
+        labelPaint.setColor(SK_ColorWHITE);
+        labelPaint.setTextSize(SkIntToScalar(kLabelTextSize));
+        SkPaint linePaint;
+        linePaint.setColor(SK_ColorWHITE);
+
+        // Drawing labels is useful for debugging, but bad for baselining (x-platform txt diffs).
+        if (false) {
+            // Draw row labels
+            canvas->save();
+                canvas->translate(0, SkIntToScalar(kLabelOffsetY));
+                for (int i = 0; i <= kSubPixelSteps; ++i) {
+                    offset.printf("%d", i);
+                    canvas->drawText(offset.c_str(), offset.size(),
+                                     0, i * kTrans + labelPaint.getTextSize(),
+                                     labelPaint);
+                }
+            canvas->restore();
+
+            // Draw col labels
+            canvas->save();
+                canvas->translate(SkIntToScalar(kLabelOffsetX), 0);
+                for (int i = 0; i <= kSubPixelSteps; ++i) {
+                    offset.printf("%d", i);
+                    canvas->drawText(offset.c_str(), offset.size(),
+                                     i * SkIntToScalar(kTrans), labelPaint.getTextSize(),
+                                     labelPaint);
+                }
+            canvas->restore();
+        }
+
+        canvas->translate(SkIntToScalar(kLabelOffsetX), SkIntToScalar(kLabelOffsetY));
+
+        // Draw test case grid lines (Draw them all at pixel centers to hopefully avoid any
+        // snapping issues).
+        for (int i = 0; i <= kSubPixelSteps + 1; ++i) {
+            canvas->drawLine(0.5f,
+                             i * SkIntToScalar(kTrans) + 0.5f,
+                             SkIntToScalar(kTrans) * (kSubPixelSteps + 1) + 0.5f,
+                             i * SkIntToScalar(kTrans) + 0.5f,
+                             linePaint);
+            canvas->drawLine(i * SkIntToScalar(kTrans) + 0.5f,
+                             0.5f,
+                             i * SkIntToScalar(kTrans) + 0.5f,
+                             SkIntToScalar(kTrans) * (kSubPixelSteps + 1) + 0.5f,
+                             linePaint);
+        }
+
+        for (int i = 0; i <= kSubPixelSteps; ++i) {
+            for (int j = 0; j <= kSubPixelSteps; ++j) {
+                canvas->save();
+                // +1's account for the grid lines around each test case.
+                canvas->translate(j * (kTrans + 1.f/kSubPixelSteps) + 1,
+                                  i * (kTrans + 1.f/kSubPixelSteps) + 1);
+                this->drawElement(canvas);
+                canvas->restore();
+            }
+        }
+    }
+
+    virtual void drawElement(SkCanvas*) = 0;
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+class PointSnapGM : public PixelSnapGM {
+protected:
+    SkString onShortName() override { return SkString("pixel_snap_point"); }
+    void drawElement(SkCanvas* canvas) override { canvas->drawPoint(1, 1, SK_ColorBLUE); }
+
+private:
+    typedef PixelSnapGM INHERITED;
+};
+
+class LineSnapGM : public PixelSnapGM {
+protected:
+    SkString onShortName() override { return SkString("pixel_snap_line"); }
+    void drawElement(SkCanvas* canvas) override {
+        SkPaint paint;
+        paint.setColor(SK_ColorGREEN);
+        // Draw a horizontal and vertical line, each length 3.
+        canvas->drawLine(1, 1, 4, 1, paint);
+        canvas->drawLine(6, 1, 6, 4, paint);
+    }
+
+private:
+    typedef PixelSnapGM INHERITED;
+};
+
+class RectSnapGM : public PixelSnapGM {
+protected:
+    SkString onShortName() override { return SkString("pixel_snap_rect"); }
+    void drawElement(SkCanvas* canvas) override {
+        SkPaint paint;
+        paint.setColor(SK_ColorRED);
+        canvas->drawRect(SkRect::MakeXYWH(1, 1, 3, 3), paint);
+    }
+
+private:
+    typedef PixelSnapGM INHERITED;
+};
+
+class ComboSnapGM : public PixelSnapGM {
+protected:
+    SkString onShortName() override { return SkString("pixel_snap_combo"); }
+    void drawElement(SkCanvas* canvas) override {
+        SkPaint paint;
+        paint.setAntiAlias(false);
+        // A rectangle that exactly covers a pixel, a point at each corner, 8 horiz/vert lines
+        // at rect corners (two at each corner, extending away from rect). They are drawn in this
+        // order lines (green), points (blue), rect(red).
+        SkRect rect = SkRect::MakeXYWH(3, 3, 1, 1);
+        paint.setColor(SK_ColorGREEN);
+        canvas->drawLine(3, 3, 0, 3, paint);
+        canvas->drawLine(3, 3, 3, 0, paint);
+        canvas->drawLine(4, 3, 7, 3, paint);
+        canvas->drawLine(4, 3, 4, 0, paint);
+        canvas->drawLine(3, 4, 0, 4, paint);
+        canvas->drawLine(3, 4, 3, 7, paint);
+        canvas->drawLine(4, 4, 7, 4, paint);
+        canvas->drawLine(4, 4, 4, 7, paint);
+        canvas->drawPoint(4, 3, SK_ColorBLUE);
+        canvas->drawPoint(4, 4, SK_ColorBLUE);
+        canvas->drawPoint(3, 3, SK_ColorBLUE);
+        canvas->drawPoint(3, 4, SK_ColorBLUE);
+        paint.setColor(SK_ColorRED);
+        canvas->drawRect(rect, paint);
+    }
+
+private:
+    typedef PixelSnapGM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+DEF_GM( return SkNEW(PointSnapGM); )
+DEF_GM( return SkNEW(LineSnapGM); )
+DEF_GM( return SkNEW(RectSnapGM); )
+DEF_GM( return SkNEW(ComboSnapGM); )
diff --git a/gm/points.cpp b/gm/points.cpp
index a634f3b..eda3121 100644
--- a/gm/points.cpp
+++ b/gm/points.cpp
@@ -16,11 +16,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("points");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 490);
     }
 
@@ -35,7 +35,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(SK_Scalar1, SK_Scalar1);
 
         SkRandom rand;
diff --git a/gm/poly2poly.cpp b/gm/poly2poly.cpp
index 823616e..e2563e2 100644
--- a/gm/poly2poly.cpp
+++ b/gm/poly2poly.cpp
@@ -180,11 +180,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("poly2poly");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(835, 840);
     }
 
@@ -222,7 +222,7 @@
         canvas->restore();
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         if (false) { test_stroke(canvas); return; }
 
         SkPaint paint;
diff --git a/gm/polygons.cpp b/gm/polygons.cpp
index 1751ead..a5756bb 100644
--- a/gm/polygons.cpp
+++ b/gm/polygons.cpp
@@ -23,18 +23,18 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("polygons");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         int width = kNumPolygons * kCellSize + 40;
         int height = (kNumJoins * kNumStrokeWidths + kNumExtraStyles) * kCellSize + 40;
         return SkISize::Make(width, height);
     }
 
     // Construct all polygons
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         SkPoint p0[] = {{0, 0}, {60, 0}, {90, 40}};  // triangle
         SkPoint p1[] = {{0, 0}, {0, 40}, {60, 40}, {40, 0}};  // trapezoid
         SkPoint p2[] = {{0, 0}, {40, 40}, {80, 40}, {40, 0}};  // diamond
@@ -94,7 +94,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         // Stroke widths are:
         // 0(may use hairline rendering), 10(common case for stroke-style)
         // 40(>= geometry width/height, make the contour filled in fact)
diff --git a/gm/quadpaths.cpp b/gm/quadpaths.cpp
index d9448b6..d5eb646 100644
--- a/gm/quadpaths.cpp
+++ b/gm/quadpaths.cpp
@@ -17,11 +17,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("quadpath");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(1240, 390); }
+    SkISize onISize() override { return SkISize::Make(1240, 390); }
 
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
@@ -40,7 +40,7 @@
         canvas->restore();
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         struct FillAndName {
             SkPath::FillType fFill;
             const char*      fName;
@@ -162,11 +162,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("quadclosepath");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(1240, 390); }
+    SkISize onISize() override { return SkISize::Make(1240, 390); }
 
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
@@ -185,7 +185,7 @@
         canvas->restore();
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         struct FillAndName {
             SkPath::FillType fFill;
             const char*      fName;
diff --git a/gm/rects.cpp b/gm/rects.cpp
index 686f03d..8fb113d 100644
--- a/gm/rects.cpp
+++ b/gm/rects.cpp
@@ -26,11 +26,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("rects");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1200, 900);
     }
 
@@ -251,7 +251,7 @@
                           SK_Scalar1 * 100 * (testCount / 10) + 3 * SK_Scalar1 / 4);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkAutoCommentBlock acb(canvas, "onDraw");
 
         canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
diff --git a/gm/repeated_bitmap.cpp b/gm/repeated_bitmap.cpp
index fa5fe0c..e6b0636 100644
--- a/gm/repeated_bitmap.cpp
+++ b/gm/repeated_bitmap.cpp
@@ -29,3 +29,23 @@
         }
     }
 }
+
+DEF_SIMPLE_GM(repeated_bitmap_jpg, canvas, 576, 576) {
+    sk_tool_utils::draw_checkerboard(canvas, 0xFF999999, SK_ColorWHITE, 12);
+    SkRect rect = SkRect::MakeLTRB(-68.0f, -68.0f, 68.0f, 68.0f);
+    SkPaint paint;
+    paint.setColor(0xFF333333);
+    SkBitmap bm;
+    if (GetResourceAsBitmap("color_wheel.jpg", &bm)) {
+        for (int j = 0; j < 4; ++j) {
+            for (int i = 0; i < 4; ++i) {
+                SkAutoCanvasRestore autoCanvasRestore(canvas, true);
+                canvas->translate(96.0f + 192.0f * SkIntToScalar(i),
+                                  96.0f + 192.0f * SkIntToScalar(j));
+                canvas->rotate(18.0f * (i + 4 * j));
+                canvas->drawRect(rect, paint);
+                canvas->drawBitmap(bm, -64.0f, -64.0f);
+            }
+        }
+    }
+}
diff --git a/gm/resizeimagefilter.cpp b/gm/resizeimagefilter.cpp
index e87dff0..2edb802 100644
--- a/gm/resizeimagefilter.cpp
+++ b/gm/resizeimagefilter.cpp
@@ -9,7 +9,6 @@
 #include "SkBitmapDevice.h"
 #include "SkBitmapSource.h"
 #include "SkColor.h"
-#include "SkMatrixImageFilter.h"
 #include "SkRefCnt.h"
 
 namespace skiagm {
@@ -28,13 +27,13 @@
     void draw(SkCanvas* canvas,
               const SkRect& rect,
               const SkSize& deviceSize,
-              SkPaint::FilterLevel filterLevel,
+              SkFilterQuality filterQuality,
               SkImageFilter* input = NULL) {
         SkRect dstRect;
         canvas->getTotalMatrix().mapRect(&dstRect, rect);
         canvas->save();
-        SkScalar deviceScaleX = SkScalarDiv(deviceSize.width(), dstRect.width());
-        SkScalar deviceScaleY = SkScalarDiv(deviceSize.height(), dstRect.height());
+        SkScalar deviceScaleX = deviceSize.width() / dstRect.width();
+        SkScalar deviceScaleY = deviceSize.height() / dstRect.height();
         canvas->translate(rect.x(), rect.y());
         canvas->scale(deviceScaleX, deviceScaleY);
         canvas->translate(-rect.x(), -rect.y());
@@ -42,7 +41,7 @@
         matrix.setScale(SkScalarInvert(deviceScaleX),
                         SkScalarInvert(deviceScaleY));
         SkAutoTUnref<SkImageFilter> imageFilter(
-            SkMatrixImageFilter::Create(matrix, filterLevel, input));
+            SkImageFilter::CreateMatrixFilter(matrix, filterQuality, input));
         SkPaint filteredPaint;
         filteredPaint.setImageFilter(imageFilter.get());
         canvas->saveLayer(&rect, &filteredPaint);
@@ -60,7 +59,7 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) {
-        canvas->clear(0x00000000);
+        canvas->clear(SK_ColorBLACK);
 
         SkRect srcRect = SkRect::MakeWH(96, 96);
 
@@ -68,25 +67,25 @@
         draw(canvas,
              srcRect,
              deviceSize,
-             SkPaint::kNone_FilterLevel);
+             kNone_SkFilterQuality);
 
         canvas->translate(srcRect.width() + SkIntToScalar(10), 0);
         draw(canvas,
              srcRect,
              deviceSize,
-             SkPaint::kLow_FilterLevel);
+             kLow_SkFilterQuality);
 
         canvas->translate(srcRect.width() + SkIntToScalar(10), 0);
         draw(canvas,
              srcRect,
              deviceSize,
-             SkPaint::kMedium_FilterLevel);
+             kMedium_SkFilterQuality);
 
         canvas->translate(srcRect.width() + SkIntToScalar(10), 0);
         draw(canvas,
              srcRect,
              deviceSize,
-             SkPaint::kHigh_FilterLevel);
+             kHigh_SkFilterQuality);
 
         SkBitmap bitmap;
         bitmap.allocN32Pixels(16, 16);
@@ -97,7 +96,7 @@
             SkPaint paint;
             paint.setColor(0xFF00FF00);
             SkRect ovalRect = SkRect::MakeWH(16, 16);
-            ovalRect.inset(SkScalarDiv(2.0f, 3.0f), SkScalarDiv(2.0f, 3.0f));
+            ovalRect.inset(SkIntToScalar(2)/3, SkIntToScalar(2)/3);
             bitmapCanvas.drawOval(ovalRect, paint);
         }
         SkRect inRect = SkRect::MakeXYWH(-4, -4, 20, 20);
@@ -107,7 +106,7 @@
         draw(canvas,
              srcRect,
              deviceSize,
-             SkPaint::kHigh_FilterLevel,
+             kHigh_SkFilterQuality,
              source.get());
     }
 
diff --git a/gm/roundrects.cpp b/gm/roundrects.cpp
index b012e51..5e2813b 100644
--- a/gm/roundrects.cpp
+++ b/gm/roundrects.cpp
@@ -28,11 +28,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("roundrects");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1200, 900);
     }
 
@@ -136,7 +136,7 @@
         return SkHSVToColor(hsv);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkRandom rand(1);
         canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
         SkRect rect = SkRect::MakeLTRB(-20, -30, 20, 30);
diff --git a/gm/rrect.cpp b/gm/rrect.cpp
index f174c96..4c55654 100644
--- a/gm/rrect.cpp
+++ b/gm/rrect.cpp
@@ -127,15 +127,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("rrect");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(820, 710);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         static const InsetProc insetProcs[] = {
             inset0, inset1, inset2, inset3
         };
diff --git a/gm/rrects.cpp b/gm/rrects.cpp
index 642376f..be46929 100644
--- a/gm/rrects.cpp
+++ b/gm/rrects.cpp
@@ -32,7 +32,7 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString name("rrect");
         switch (fType) {
             case kBW_Draw_Type:
@@ -54,9 +54,9 @@
         return name;
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(kImageWidth, kImageHeight); }
+    SkISize onISize() override { return SkISize::Make(kImageWidth, kImageHeight); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         GrContext* context = NULL;
 #if SK_SUPPORT_GPU
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
diff --git a/gm/samplerstress.cpp b/gm/samplerstress.cpp
index 7f77463..ec07270 100644
--- a/gm/samplerstress.cpp
+++ b/gm/samplerstress.cpp
@@ -29,11 +29,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("gpusamplerstress");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
@@ -88,7 +88,7 @@
         fMaskFilter.reset(SkBlurMaskFilter::Create(kNormal_SkBlurStyle, sigma));
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         createShader();
         createMaskFilter();
 
diff --git a/gm/shaderbounds.cpp b/gm/shaderbounds.cpp
index a7de479..4363d4e 100644
--- a/gm/shaderbounds.cpp
+++ b/gm/shaderbounds.cpp
@@ -35,13 +35,13 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(320, 240); }
+    SkISize onISize() override { return SkISize::Make(320, 240); }
 
-    SkMatrix onGetInitialTransform() const SK_OVERRIDE {
+    SkMatrix onGetInitialTransform() const override {
         SkMatrix result;
         SkScalar scale = 0.8f;
         result.setScale(scale, scale);
@@ -49,7 +49,7 @@
         return result;
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         // The PDF device has already clipped to the content area, but we
         // do it again here so that the raster and pdf results are consistent.
         canvas->clipRect(SkRect::MakeWH(SkIntToScalar(320),
@@ -80,10 +80,9 @@
         if (background) {
             scale = 0.6f;
         }
-        SkScalar shaderWidth = SkScalarDiv(SkIntToScalar(width), scale);
-        SkScalar shaderHeight = SkScalarDiv(SkIntToScalar(height), scale);
-        SkMatrix shaderScale;
-        shaderScale.setScale(scale, scale);
+        SkScalar shaderWidth = width / scale;
+        SkScalar shaderHeight = height / scale;
+        SkMatrix shaderScale = SkMatrix::MakeScale(scale);
         SkShader* shader = fShaderMaker(shaderWidth, shaderHeight, background, shaderScale);
         return shader;
     }
diff --git a/gm/shadertext.cpp b/gm/shadertext.cpp
index fa8e2b5..6845fa1 100644
--- a/gm/shadertext.cpp
+++ b/gm/shadertext.cpp
@@ -64,13 +64,13 @@
     return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount);
 }
 
-static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) {
+static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) {
     SkPoint center0, center1;
     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
-    return SkGradientShader::CreateTwoPointRadial(
+    return SkGradientShader::CreateTwoPointConical(
                             center1, (pts[1].fX - pts[0].fX) / 7,
                             center0, (pts[1].fX - pts[0].fX) / 2,
                             data.fColors, data.fPos, data.fCount, tm);
@@ -79,7 +79,7 @@
 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm);
 
 static const GradMaker gGradMakers[] = {
-    MakeLinear, MakeRadial, MakeSweep, Make2Radial
+    MakeLinear, MakeRadial, MakeSweep, Make2Conical
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -92,13 +92,13 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("shadertext");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(1450, 500); }
+    SkISize onISize() override { return SkISize::Make(1450, 500); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         const char text[] = "Shaded Text";
         const int textLen = SK_ARRAY_COUNT(text) - 1;
         const int pointSize = 36;
diff --git a/gm/shadertext2.cpp b/gm/shadertext2.cpp
index 31d1e89..92a0138 100644
--- a/gm/shadertext2.cpp
+++ b/gm/shadertext2.cpp
@@ -48,13 +48,13 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("shadertext2");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(1800, 900); }
+    SkISize onISize() override { return SkISize::Make(1800, 900); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         static const char kText[] = "SKIA";
         static const int kTextLen = SK_ARRAY_COUNT(kText) - 1;
         static const int kPointSize = 55;
@@ -92,7 +92,7 @@
         fillPaint.setAntiAlias(true);
         sk_tool_utils::set_portable_typeface(&fillPaint);
         fillPaint.setTextSize(SkIntToScalar(kPointSize));
-        fillPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        fillPaint.setFilterQuality(kLow_SkFilterQuality);
 
         SkPaint outlinePaint;
         outlinePaint.setAntiAlias(true);
diff --git a/gm/shadertext3.cpp b/gm/shadertext3.cpp
index 7b84f69..49cdced 100644
--- a/gm/shadertext3.cpp
+++ b/gm/shadertext3.cpp
@@ -53,21 +53,21 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("shadertext3");
     }
 
-    SkISize onISize() SK_OVERRIDE{ return SkISize::Make(800, 1000); }
+    SkISize onISize() override{ return SkISize::Make(800, 1000); }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         makebm(&fBmp, kPointSize / 4, kPointSize / 4);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         SkPaint bmpPaint;
         bmpPaint.setAntiAlias(true);
-        bmpPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        bmpPaint.setFilterQuality(kLow_SkFilterQuality);
         bmpPaint.setAlpha(0x80);
         canvas->drawBitmap(fBmp, 5.f, 5.f, &bmpPaint);
 
@@ -109,7 +109,7 @@
                 fillPaint.setAntiAlias(true);
                 sk_tool_utils::set_portable_typeface(&fillPaint);
                 fillPaint.setTextSize(SkIntToScalar(kPointSize));
-                fillPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+                fillPaint.setFilterQuality(kLow_SkFilterQuality);
                 fillPaint.setShader(shader);
 
                 canvas->drawText(kText, kTextLen, 0, 0, fillPaint);
diff --git a/gm/shadows.cpp b/gm/shadows.cpp
index 66ad32e..adb34b6 100644
--- a/gm/shadows.cpp
+++ b/gm/shadows.cpp
@@ -29,7 +29,7 @@
     SkRect fRect;
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         this->setBGColor(0xFFDDDDDD);
         fCirclePath.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(10) );
         fRect.set(SkIntToScalar(10), SkIntToScalar(10),
diff --git a/gm/shallowgradient.cpp b/gm/shallowgradient.cpp
index 2b7dfbd..415ed41 100644
--- a/gm/shallowgradient.cpp
+++ b/gm/shallowgradient.cpp
@@ -43,15 +43,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(800, 800);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         const SkColor colors[] = { 0xFF555555, 0xFF444444 };
         const int colorCount = SK_ARRAY_COUNT(colors);
 
diff --git a/gm/simpleaaclip.cpp b/gm/simpleaaclip.cpp
index 8a18f48..c232a25 100644
--- a/gm/simpleaaclip.cpp
+++ b/gm/simpleaaclip.cpp
@@ -52,7 +52,7 @@
     }
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         // offset the rects a bit so we get anti-aliasing in the rect case
         fBase.set(100.65f,
                   100.65f,
diff --git a/gm/skbug1719.cpp b/gm/skbug1719.cpp
index 1093601..dc5de45 100644
--- a/gm/skbug1719.cpp
+++ b/gm/skbug1719.cpp
@@ -24,21 +24,21 @@
     SkBug1719GM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("skbug1719");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(300, 100);
     }
 
-    void onDrawBackground(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawBackground(SkCanvas* canvas) override {
         SkPaint bgPaint;
         bgPaint.setColor(0xFF303030);
         canvas->drawPaint(bgPaint);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(SkIntToScalar(-800), SkIntToScalar(-650));
 
         // The data is lifted from an SKP that exhibited the bug.
diff --git a/gm/smallarc.cpp b/gm/smallarc.cpp
index ff7f07c..76611b7 100755
--- a/gm/smallarc.cpp
+++ b/gm/smallarc.cpp
@@ -19,15 +19,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("smallarc");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(762, 762);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint p;
         p.setColor(SK_ColorRED);
         p.setAntiAlias(true);
diff --git a/gm/spritebitmap.cpp b/gm/spritebitmap.cpp
index eeb4b73..7c8677d 100644
--- a/gm/spritebitmap.cpp
+++ b/gm/spritebitmap.cpp
@@ -63,15 +63,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("spritebitmap");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkBitmap bm;
         make_bm(&bm);
 
diff --git a/gm/stlouisarch.cpp b/gm/stlouisarch.cpp
index 06f6078..176fb4b 100644
--- a/gm/stlouisarch.cpp
+++ b/gm/stlouisarch.cpp
@@ -14,13 +14,13 @@
 // this GM tests hairlines which fill nearly the entire render target
 class StLouisArchGM : public GM {
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("stlouisarch");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make((int)kWidth, (int)kHeight); }
+    SkISize onISize() override { return SkISize::Make((int)kWidth, (int)kHeight); }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         {
             SkPath* bigQuad = &fPaths.push_back();
             bigQuad->moveTo(0, 0);
@@ -66,7 +66,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->save();
         canvas->scale(1, -1);
         canvas->translate(0, -kHeight);
diff --git a/gm/stringart.cpp b/gm/stringart.cpp
index fe69626..7c9a22e 100644
--- a/gm/stringart.cpp
+++ b/gm/stringart.cpp
@@ -24,15 +24,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("stringart");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(kWidth, kHeight);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkScalar angle = kAngle*SK_ScalarPI + SkScalarHalf(SK_ScalarPI);
         SkScalar size = SkIntToScalar(SkMin32(kWidth, kHeight));
         SkPoint center = SkPoint::Make(SkScalarHalf(kWidth), SkScalarHalf(kHeight));
@@ -47,7 +47,7 @@
             SkPoint rp = SkPoint::Make(length*SkScalarCos(step) + center.fX,
                                        length*SkScalarSin(step) + center.fY);
             path.lineTo(rp);
-            length += SkScalarDiv(angle, SkScalarHalf(SK_ScalarPI));
+            length += angle / SkScalarHalf(SK_ScalarPI);
             step += angle;
         }
         path.close();
diff --git a/gm/strokefill.cpp b/gm/strokefill.cpp
index 256c7a2..b6e888b 100644
--- a/gm/strokefill.cpp
+++ b/gm/strokefill.cpp
@@ -20,11 +20,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("stroke-fill");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
@@ -36,7 +36,7 @@
         canvas->drawText(text, len, x, y + SkIntToScalar(120), p);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkScalar x = SkIntToScalar(100);
         SkScalar y = SkIntToScalar(88);
 
diff --git a/gm/strokerect.cpp b/gm/strokerect.cpp
index da551e7..8854c6b 100644
--- a/gm/strokerect.cpp
+++ b/gm/strokerect.cpp
@@ -47,15 +47,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("strokerect");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1024, 740);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->drawColor(SK_ColorWHITE);
         canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2);
 
diff --git a/gm/strokerects.cpp b/gm/strokerects.cpp
index 5609c5f..f5167bb 100644
--- a/gm/strokerects.cpp
+++ b/gm/strokerects.cpp
@@ -26,11 +26,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("strokerects");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(W*2, H*2);
     }
 
@@ -46,7 +46,7 @@
         r->offset(-w/2 + woffset, -h/2 + hoffset);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setStyle(SkPaint::kStroke_Style);
 
diff --git a/gm/strokes.cpp b/gm/strokes.cpp
index cadd0d6..a258dfe 100644
--- a/gm/strokes.cpp
+++ b/gm/strokes.cpp
@@ -40,15 +40,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("strokes_round");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(W, H*2);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setStyle(SkPaint::kStroke_Style);
         paint.setStrokeWidth(SkIntToScalar(9)/2);
@@ -81,7 +81,7 @@
 class Strokes2GM : public skiagm::GM {
     SkPath fPath;
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         SkRandom rand;
         fPath.moveTo(0, 0);
         for (int i = 0; i < 13; i++) {
@@ -92,11 +92,11 @@
     }
 
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("strokes_poly");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(W, H*2);
     }
 
@@ -106,7 +106,7 @@
         canvas->concat(matrix);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->drawColor(SK_ColorWHITE);
 
         SkPaint paint;
@@ -190,15 +190,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("strokes3");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1500, 1500);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint origPaint;
         origPaint.setAntiAlias(true);
         origPaint.setStyle(SkPaint::kStroke_Style);
diff --git a/gm/stroketext.cpp b/gm/stroketext.cpp
index 914462c..6ec425b 100644
--- a/gm/stroketext.cpp
+++ b/gm/stroketext.cpp
@@ -72,15 +72,15 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("stroketext");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1200, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         if (true) { test_nulldev(canvas); }
         SkPaint paint;
         paint.setAntiAlias(true);
diff --git a/gm/surface.cpp b/gm/surface.cpp
index 5a4c09a..913de99 100644
--- a/gm/surface.cpp
+++ b/gm/surface.cpp
@@ -61,15 +61,15 @@
     SurfacePropsGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("surfaceprops");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(W * 4, H * 5);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         GrContext* ctx = canvas->getGrContext();
 
         // must be opaque to have a hope of testing LCD text
@@ -118,11 +118,11 @@
     NewSurfaceGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("surfacenew");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(300, 140);
     }
 
@@ -130,7 +130,7 @@
         canvas->drawColor(SK_ColorRED);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
 
         SkAutoTUnref<SkSurface> surf(canvas->newSurface(info, NULL));
diff --git a/gm/tablecolorfilter.cpp b/gm/tablecolorfilter.cpp
index 0d6da8f..deba9e5 100644
--- a/gm/tablecolorfilter.cpp
+++ b/gm/tablecolorfilter.cpp
@@ -164,7 +164,7 @@
             for (unsigned i = 0; i < SK_ARRAY_COUNT(gColorFilterMakers); ++i) {
                 SkAutoTUnref<SkColorFilter> colorFilter1(gColorFilterMakers[i]());
                 SkAutoTUnref<SkImageFilter> imageFilter1(SkColorFilterImageFilter::Create(
-                            colorFilter1, NULL, NULL, 0));
+                            colorFilter1, NULL, NULL));
 
                 // Move down to the next line and draw it
                 // each draw being at xOffset of the previous one
@@ -173,7 +173,7 @@
                 for (unsigned j = 1; j < SK_ARRAY_COUNT(gColorFilterMakers); ++j) {
                     SkAutoTUnref<SkColorFilter> colorFilter2(gColorFilterMakers[j]());
                     SkAutoTUnref<SkImageFilter> imageFilter2(SkColorFilterImageFilter::Create(
-                                colorFilter2, imageFilter1, NULL, 0));
+                                colorFilter2, imageFilter1, NULL));
                     paint.setImageFilter(imageFilter2);
                     canvas->drawBitmap(bm, x, y, &paint);
                     x += xOffset;
diff --git a/gm/tallstretchedbitmaps.cpp b/gm/tallstretchedbitmaps.cpp
index 06b2335..424aa3a 100644
--- a/gm/tallstretchedbitmaps.cpp
+++ b/gm/tallstretchedbitmaps.cpp
@@ -59,15 +59,15 @@
     TallStretchedBitmapsGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("tall_stretched_bitmaps");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(750, 750);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         for (size_t i = 0; i < SK_ARRAY_COUNT(fTallBmps); ++i) {
             int h = SkToInt((4 + i) * 1024);
 
@@ -75,7 +75,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->scale(1.3f, 1.3f);
         for (size_t i = 0; i < SK_ARRAY_COUNT(fTallBmps); ++i) {
             SkASSERT(fTallBmps[i].fItemCnt > 10);
@@ -87,7 +87,7 @@
                                                bmp.width(), bmp.height());
             SkRect dstRect = SkRect::MakeWH(SkIntToScalar(bmp.width()), 10.f * itemHeight);
             SkPaint paint;
-            paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+            paint.setFilterQuality(kLow_SkFilterQuality);
             canvas->drawBitmapRect(bmp, &subRect, dstRect, &paint);
             canvas->translate(SkIntToScalar(bmp.width() + 10), 0);
         }
diff --git a/gm/testimagefilters.cpp b/gm/testimagefilters.cpp
index b85c0aa..2c4fadb 100644
--- a/gm/testimagefilters.cpp
+++ b/gm/testimagefilters.cpp
@@ -97,13 +97,13 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("testimagefilters");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(700, 460); }
+    SkISize onISize() override { return SkISize::Make(700, 460); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 //        this->drawSizeBounds(canvas, 0xFFCCCCCC);
 
         static SkImageFilter* (*gFilterProc[])() = {
diff --git a/gm/texdata.cpp b/gm/texdata.cpp
index 927bea5..dff2944 100644
--- a/gm/texdata.cpp
+++ b/gm/texdata.cpp
@@ -27,15 +27,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("texdata");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(2*S, 2*S);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         GrRenderTarget* target = canvas->internal_private_accessTopLayerRenderTarget();
         GrContext* ctx = canvas->getGrContext();
         if (ctx && target) {
@@ -84,7 +84,8 @@
                 desc.fConfig    = kSkia8888_GrPixelConfig;
                 desc.fWidth     = 2 * S;
                 desc.fHeight    = 2 * S;
-                GrTexture* texture = ctx->createTexture(desc, false, gTextureData.get(), 0);
+                GrTexture* texture = ctx->textureProvider()->createTexture(
+                    desc, false, gTextureData.get(), 0);
 
                 if (!texture) {
                     return;
diff --git a/gm/textblob.cpp b/gm/textblob.cpp
index e802d9b..7c0fd87 100644
--- a/gm/textblob.cpp
+++ b/gm/textblob.cpp
@@ -78,15 +78,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("textblob");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         for (unsigned b = 0; b < SK_ARRAY_COUNT(blobConfigs); ++b) {
             SkAutoTUnref<const SkTextBlob> blob(this->makeBlob(b));
 
diff --git a/gm/textblobcolortrans.cpp b/gm/textblobcolortrans.cpp
new file mode 100644
index 0000000..cb4686e
--- /dev/null
+++ b/gm/textblobcolortrans.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2015 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 "Resources.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkStream.h"
+#include "SkTextBlob.h"
+#include "SkTypeface.h"
+
+namespace skiagm {
+class TextBlobColorTrans : public GM {
+public:
+    // This gm tests that textblobs can be translated and have their colors regenerated
+    // correctly.  With smaller atlas sizes, it can also trigger regeneration of texture coords on
+    // the GPU backend
+    TextBlobColorTrans() { }
+
+protected:
+    void onOnceBeforeDraw() override {
+        SkTextBlobBuilder builder;
+
+        // make textblob
+        // Large text is used to trigger atlas eviction
+        SkPaint paint;
+        paint.setTextSize(256);
+        const char* text = "AB";
+        sk_tool_utils::set_portable_typeface(&paint);
+
+        SkRect bounds;
+        paint.measureText(text, strlen(text), &bounds);
+
+        SkScalar yOffset = bounds.height();
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, 0, yOffset - 30);
+
+        // A8
+        paint.setTextSize(28);
+        text = "The quick brown fox jumps over the lazy dog.";
+        paint.setSubpixelText(false);
+        paint.setLCDRenderText(false);
+        paint.measureText(text, strlen(text), &bounds);
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, 0, yOffset - 8);
+
+        // build
+        fBlob.reset(builder.build());
+    }
+
+    SkString onShortName() override {
+        return SkString("textblobcolortrans");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(kWidth, kHeight);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+
+        canvas->drawColor(SK_ColorGRAY);
+
+        SkPaint paint;
+        canvas->translate(10, 40);
+
+        SkRect bounds = fBlob->bounds();
+
+        // Colors were chosen to map to pairs of canonical colors.  The GPU Backend will cache A8
+        // Texture Blobs based on the canonical color they map to.  Canonical colors are used to
+        // create masks.  For A8 there are 8 of them.
+        SkColor colors[] = {SK_ColorCYAN, SK_ColorLTGRAY, SK_ColorYELLOW, SK_ColorWHITE};
+
+        size_t count = SK_ARRAY_COUNT(colors);
+        size_t colorIndex = 0;
+        for (int y = 0; y + SkScalarFloorToInt(bounds.height()) < kHeight;
+             y += SkScalarFloorToInt(bounds.height())) {
+            paint.setColor(colors[colorIndex++ % count]);
+            canvas->save();
+            canvas->translate(0, SkIntToScalar(y));
+            canvas->drawTextBlob(fBlob, 0, 0, paint);
+            canvas->restore();
+        }
+    }
+
+private:
+    SkAutoTUnref<const SkTextBlob> fBlob;
+
+    static const int kWidth = 675;
+    static const int kHeight = 1600;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(TextBlobColorTrans); )
+}
diff --git a/gm/textblobgeometrychange.cpp b/gm/textblobgeometrychange.cpp
new file mode 100644
index 0000000..fec1ac0
--- /dev/null
+++ b/gm/textblobgeometrychange.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+
+#include "SkCanvas.h"
+#include "SkSurface.h"
+#include "SkTextBlob.h"
+
+// This tests that we don't try to reuse textblobs from the GPU textblob cache across pixel geometry
+// changes when we have LCD.  crbug/486744
+namespace skiagm {
+class TextBlobGeometryChange : public GM {
+public:
+    TextBlobGeometryChange() { }
+
+protected:
+    SkString onShortName() override {
+        return SkString("textblobgeometrychange");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(kWidth, kHeight);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        const char text[] = "Hamburgefons";
+
+        SkPaint paint;
+        sk_tool_utils::set_portable_typeface(&paint);
+        paint.setTextSize(20);
+        paint.setAntiAlias(true);
+        paint.setLCDRenderText(true);
+
+        SkTextBlobBuilder builder;
+
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, 10, 10);
+
+        SkAutoTUnref<const SkTextBlob> blob(builder.build());
+
+        SkImageInfo info = SkImageInfo::MakeN32Premul(200, 200);
+        SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+        SkAutoTUnref<SkSurface> surface(canvas->newSurface(info, &props));
+        if (surface) {
+            SkCanvas* c = surface->getCanvas();
+
+            // LCD text on white background
+            SkRect rect = SkRect::MakeLTRB(0.f, 0.f, SkIntToScalar(kWidth), kHeight / 2.f);
+            SkPaint rectPaint;
+            rectPaint.setColor(0xffffffff);
+            canvas->drawRect(rect, rectPaint);
+            canvas->drawTextBlob(blob.get(), 10, 50, paint);
+
+            // This should not look garbled since we should disable LCD text in this case
+            // (i.e., unknown pixel geometry)
+            c->clear(0x00ffffff);
+            c->drawTextBlob(blob.get(), 10, 150, paint);
+            surface->draw(canvas, 0, 0, nullptr);
+        } else {
+            const char* text = "This test requires a surface";
+            size_t len = strlen(text);
+            SkPaint paint;
+            canvas->drawText(text, len, 10, 100, paint);
+        }
+    }
+
+private:
+    static const int kWidth = 200;
+    static const int kHeight = 200;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(TextBlobGeometryChange); )
+}
diff --git a/gm/textbloblooper.cpp b/gm/textbloblooper.cpp
new file mode 100644
index 0000000..2fbd30b
--- /dev/null
+++ b/gm/textbloblooper.cpp
@@ -0,0 +1,258 @@
+/*
+ * 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 "Sk2DPathEffect.h"
+#include "SkBlurMask.h"
+#include "SkBlurMaskFilter.h"
+#include "SkColorFilter.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkLayerDrawLooper.h"
+#include "SkRandom.h"
+#include "SkTextBlob.h"
+
+namespace skiagm {
+
+static const int kWidth = 1250;
+static const int kHeight = 700;
+
+// Unlike the variant in sk_tool_utils, this version positions the glyphs on a diagonal
+static void add_to_text_blob(SkTextBlobBuilder* builder, const char* text, const SkPaint& origPaint,
+                             SkScalar x, SkScalar y) {
+    SkPaint paint(origPaint);
+    SkTDArray<uint16_t> glyphs;
+
+    size_t len = strlen(text);
+    glyphs.append(paint.textToGlyphs(text, len, NULL));
+    paint.textToGlyphs(text, len, glyphs.begin());
+
+    const SkScalar advanceX = paint.getTextSize() * 0.85f;
+    const SkScalar advanceY = paint.getTextSize() * 1.5f;
+
+    SkTDArray<SkScalar> pos;
+    for (unsigned i = 0; i < len; ++i) {
+        *pos.append() = x + i * advanceX;
+        *pos.append() = y + i * (advanceY / len);
+    }
+
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    const SkTextBlobBuilder::RunBuffer& run = builder->allocRunPos(paint, glyphs.count());
+    memcpy(run.glyphs, glyphs.begin(), glyphs.count() * sizeof(uint16_t));
+    memcpy(run.pos, pos.begin(), len * sizeof(SkScalar) * 2);
+}
+
+typedef void (*LooperProc)(SkPaint*);
+
+struct LooperSettings {
+    SkXfermode::Mode fMode;
+    SkColor          fColor;
+    SkPaint::Style   fStyle;
+    SkScalar         fWidth;
+    SkScalar         fOffset;
+    SkScalar         fSkewX;
+    bool             fEffect;
+};
+
+static void mask_filter(SkPaint* paint) {
+    SkMaskFilter* mf = SkBlurMaskFilter::Create(kNormal_SkBlurStyle,
+                        SkBlurMask::ConvertRadiusToSigma(3.f));
+    paint->setMaskFilter(mf)->unref();
+}
+
+static SkPathEffect* make_tile_effect() {
+    SkMatrix m;
+    m.setScale(1.f, 1.f);
+
+    SkPath path;
+    path.addCircle(0, 0, SkIntToScalar(5));
+
+    return SkPath2DPathEffect::Create(m, path);
+}
+
+static void path_effect(SkPaint* paint) {
+    paint->setPathEffect(make_tile_effect())->unref();
+}
+
+static SkShader* make_shader(const SkRect& bounds) {
+    const SkPoint pts[] = {
+        { bounds.left(), bounds.top() },
+        { bounds.right(), bounds.bottom() },
+    };
+    const SkColor colors[] = {
+        SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK,
+        SK_ColorCYAN, SK_ColorMAGENTA, SK_ColorYELLOW,
+    };
+    return SkGradientShader::CreateLinear(pts,
+                                          colors, NULL, SK_ARRAY_COUNT(colors),
+                                          SkShader::kClamp_TileMode);
+}
+
+static void color_filter(SkPaint* paint) {
+    SkRect r;
+    r.setWH(SkIntToScalar(kWidth), 50);
+    paint->setShader(make_shader(r))->unref();
+    paint->setColorFilter(SkColorFilter::CreateLightingFilter(0xF0F0F0, 0))->unref();
+}
+
+static void kitchen_sink(SkPaint* paint) {
+    color_filter(paint);
+    path_effect(paint);
+    mask_filter(paint);
+
+}
+
+static SkLayerDrawLooper* setupLooper(SkLayerDrawLooper::BitFlags bits,
+                                      LooperProc proc,
+                                      const LooperSettings settings[],
+                                      size_t size) {
+    SkLayerDrawLooper::Builder looperBuilder;
+
+    SkLayerDrawLooper::LayerInfo info;
+    info.fPaintBits = bits;
+
+    info.fColorMode = SkXfermode::kSrc_Mode;
+
+    for (size_t i = 0; i < size; i++) {
+        info.fOffset.set(settings[i].fOffset, settings[i].fOffset);
+        SkPaint* paint = looperBuilder.addLayer(info);
+        paint->setXfermodeMode(settings[i].fMode);
+        paint->setColor(settings[i].fColor);
+        paint->setStyle(settings[i].fStyle);
+        paint->setStrokeWidth(settings[i].fWidth);
+        if (settings[i].fEffect) {
+            (*proc)(paint);
+        }
+    }
+    return looperBuilder.detachLooper();
+}
+
+class TextBlobLooperGM : public GM {
+public:
+    TextBlobLooperGM() {}
+
+protected:
+    void onOnceBeforeDraw() override {
+        SkTextBlobBuilder builder;
+
+        // LCD
+        SkPaint paint;
+        paint.setTextSize(32);
+        const char* text = "The quick brown fox jumps over the lazy dog";
+        paint.setSubpixelText(true);
+        paint.setLCDRenderText(true);
+        paint.setAntiAlias(true);
+        add_to_text_blob(&builder, text, paint, 0, 0);
+        fBlob.reset(builder.build());
+
+        // create a looper which sandwhiches an effect in two normal draws
+        LooperSettings looperSandwhich[] = {
+           { SkXfermode::kSrc_Mode, SK_ColorMAGENTA, SkPaint::kFill_Style, 0, 0, 0, false },
+           { SkXfermode::kSrcOver_Mode, 0x88000000, SkPaint::kFill_Style, 0, 10.f, 0, true },
+           { SkXfermode::kSrcOver_Mode, 0x50FF00FF, SkPaint::kFill_Style, 0, 20.f, 0, false },
+        };
+
+        LooperSettings compound[] = {
+            { SkXfermode::kSrc_Mode, SK_ColorWHITE, SkPaint::kStroke_Style, 1.f * 3/4, 0, 0, false },
+            { SkXfermode::kSrc_Mode, SK_ColorRED, SkPaint::kStroke_Style, 4.f, 0, 0, false },
+            { SkXfermode::kSrc_Mode, SK_ColorBLUE, SkPaint::kFill_Style, 0, 0, 0, false },
+            { SkXfermode::kSrcOver_Mode, 0x88000000, SkPaint::kFill_Style, 0, 10.f, 0, true }
+        };
+
+        LooperSettings xfermode[] = {
+            { SkXfermode::kDifference_Mode, SK_ColorWHITE, SkPaint::kFill_Style, 0, 0, 0, false },
+            { SkXfermode::kSrcOver_Mode, 0xFF000000, SkPaint::kFill_Style, 0, 1.f, 0, true },
+            { SkXfermode::kSrcOver_Mode, 0x50FF00FF, SkPaint::kFill_Style, 0, 2.f, 0, false },
+        };
+
+        // NOTE, this should be ignored by textblobs
+        LooperSettings skew[] = {
+            { SkXfermode::kSrc_Mode, SK_ColorRED, SkPaint::kFill_Style, 0, 0, -1.f, false },
+            { SkXfermode::kSrc_Mode, SK_ColorGREEN, SkPaint::kFill_Style, 0, 10.f, -1.f, false },
+            { SkXfermode::kSrc_Mode, SK_ColorBLUE, SkPaint::kFill_Style, 0, 20.f, -1.f, false },
+        };
+
+        LooperSettings kitchenSink[] = {
+            { SkXfermode::kSrc_Mode, SK_ColorWHITE, SkPaint::kStroke_Style, 1.f * 3/4, 0, 0, false },
+            { SkXfermode::kSrc_Mode, SK_ColorBLACK, SkPaint::kFill_Style, 0, 0, 0, false },
+            { SkXfermode::kDifference_Mode, SK_ColorWHITE, SkPaint::kFill_Style, 1.f, 10.f, 0, false },
+            { SkXfermode::kSrc_Mode, SK_ColorWHITE, SkPaint::kFill_Style, 0, 10.f, 0, true },
+            { SkXfermode::kSrcOver_Mode, 0x50FF00FF, SkPaint::kFill_Style, 0, 20.f, 0, false },
+        };
+
+        fLoopers.push_back().reset(setupLooper(SkLayerDrawLooper::kMaskFilter_Bit |
+                                               SkLayerDrawLooper::kXfermode_Bit |
+                                               SkLayerDrawLooper::kStyle_Bit, &mask_filter,
+                                               compound, SK_ARRAY_COUNT(compound)));
+        fLoopers.push_back().reset(setupLooper(SkLayerDrawLooper::kPathEffect_Bit |
+                                               SkLayerDrawLooper::kXfermode_Bit, &path_effect,
+                                               looperSandwhich, SK_ARRAY_COUNT(looperSandwhich)));
+        fLoopers.push_back().reset(setupLooper(SkLayerDrawLooper::kShader_Bit |
+                                               SkLayerDrawLooper::kColorFilter_Bit |
+                                               SkLayerDrawLooper::kXfermode_Bit, &color_filter,
+                                               looperSandwhich, SK_ARRAY_COUNT(looperSandwhich)));
+        fLoopers.push_back().reset(setupLooper(SkLayerDrawLooper::kShader_Bit |
+                                               SkLayerDrawLooper::kColorFilter_Bit |
+                                               SkLayerDrawLooper::kXfermode_Bit, &color_filter,
+                                               xfermode, SK_ARRAY_COUNT(xfermode)));
+        fLoopers.push_back().reset(setupLooper(0, NULL, skew, SK_ARRAY_COUNT(skew)));
+        fLoopers.push_back().reset(setupLooper(SkLayerDrawLooper::kMaskFilter_Bit |
+                                               SkLayerDrawLooper::kShader_Bit |
+                                               SkLayerDrawLooper::kColorFilter_Bit |
+                                               SkLayerDrawLooper::kPathEffect_Bit |
+                                               SkLayerDrawLooper::kStyle_Bit |
+                                               SkLayerDrawLooper::kXfermode_Bit, &kitchen_sink,
+                                               kitchenSink, SK_ARRAY_COUNT(kitchenSink)));
+
+        // Test we respect overrides
+        fLoopers.push_back().reset(setupLooper(0, &kitchen_sink,
+                                               kitchenSink, SK_ARRAY_COUNT(kitchenSink)));
+    }
+
+    SkString onShortName() override {
+        return SkString("textbloblooper");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(kWidth, kHeight);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+
+        canvas->drawColor(SK_ColorGRAY);
+
+        SkPaint paint;
+        canvas->translate(10, 40);
+
+        paint.setTextSize(40);
+
+        SkRect bounds = fBlob->bounds();
+
+        int y = 0;
+        for (int looper = 0; looper < fLoopers.count(); looper++) {
+            paint.setLooper(fLoopers[looper]);
+            canvas->save();
+            canvas->translate(0, SkIntToScalar(y));
+            canvas->drawTextBlob(fBlob, 0, 0, paint);
+            canvas->restore();
+            y += SkScalarFloorToInt(bounds.height());
+        }
+    }
+
+private:
+    SkAutoTUnref<const SkTextBlob> fBlob;
+    SkTArray<SkAutoTUnref<SkLayerDrawLooper>, true> fLoopers;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(TextBlobLooperGM); )
+}
diff --git a/gm/textblobshader.cpp b/gm/textblobshader.cpp
index 0738d82..5b9b2e9 100644
--- a/gm/textblobshader.cpp
+++ b/gm/textblobshader.cpp
@@ -27,7 +27,7 @@
 
 protected:
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         SkPaint p;
         p.setAntiAlias(true);
         p.setSubpixelText(true);
@@ -73,15 +73,15 @@
                                                      SkShader::kRepeat_TileMode));
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("textblobshader");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint p;
         p.setStyle(SkPaint::kFill_Style);
         p.setShader(fShader);
diff --git a/gm/textblobtransforms.cpp b/gm/textblobtransforms.cpp
new file mode 100644
index 0000000..d24e6e8
--- /dev/null
+++ b/gm/textblobtransforms.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2015 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 "Resources.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkStream.h"
+#include "SkTextBlob.h"
+#include "SkTypeface.h"
+
+namespace skiagm {
+class TextBlobTransforms : public GM {
+public:
+    // This gm tests that textblobs can be translated, rotated, and scaled
+    TextBlobTransforms() {}
+
+protected:
+    void onOnceBeforeDraw() override {
+        SkTextBlobBuilder builder;
+
+        // make textblob.  To stress distance fields, we choose sizes appropriately
+        SkPaint paint;
+        paint.setTextSize(162);
+        const char* text = "A";
+        sk_tool_utils::set_portable_typeface(&paint);
+
+        SkRect bounds;
+        paint.measureText(text, strlen(text), &bounds);
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, 0, 0);
+
+        // Medium
+        SkScalar xOffset = bounds.width() + 5;
+        paint.setTextSize(72);
+        text = "B";
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, xOffset, 0);
+
+        paint.measureText(text, strlen(text), &bounds);
+        SkScalar yOffset = bounds.height();
+
+        // Small
+        paint.setTextSize(32);
+        text = "C";
+        sk_tool_utils::add_to_text_blob(&builder, text, paint, xOffset, -yOffset - 10);
+
+        // build
+        fBlob.reset(builder.build());
+    }
+
+    SkString onShortName() override {
+        return SkString("textblobtransforms");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(kWidth, kHeight);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+
+        canvas->drawColor(SK_ColorGRAY);
+
+        SkPaint paint;
+
+        SkRect bounds = fBlob->bounds();
+        canvas->translate(20, 20);
+
+        // Colors were chosen to map to pairs of canonical colors.  The GPU Backend will cache A8
+        // Texture Blobs based on the canonical color they map to.  Canonical colors are used to
+        // create masks.  For A8 there are 8 of them.
+        //SkColor colors[] = {SK_ColorCYAN, SK_ColorLTGRAY, SK_ColorYELLOW, SK_ColorWHITE};
+
+        SkScalar xOffset = SkScalarCeilToScalar(bounds.width());
+        SkScalar yOffset = SkScalarCeilToScalar(bounds.height());
+        // first translate
+        canvas->translate(xOffset, 2 * yOffset);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+        canvas->translate(-xOffset, 0);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+        canvas->translate(2 * xOffset, 0);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+        canvas->translate(-xOffset, -yOffset);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+        canvas->translate(0, 2 * yOffset);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+
+        // now rotate
+        canvas->translate(4 * xOffset, -yOffset);
+        canvas->rotate(180.f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+        canvas->rotate(-180.f);
+        canvas->translate(0, -yOffset);
+        canvas->rotate(-180.f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+        canvas->rotate(270.f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+        canvas->rotate(-90.f);
+        canvas->translate(-xOffset, yOffset);
+        canvas->rotate(-90.f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+        canvas->rotate(90.f);
+
+        // and scales
+        canvas->translate(- 3 * xOffset, 3 * yOffset);
+        canvas->scale(1.5f, 1.5f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+        canvas->translate(xOffset, 0);
+        canvas->scale(.25f, .25f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+        canvas->translate(xOffset, 0);
+        canvas->scale(3.f, 2.f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+
+        // finally rotates, scales, and translates together
+        canvas->translate(xOffset, 0);
+        canvas->rotate(23.f);
+        canvas->scale(.33f, .5f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+
+        canvas->rotate(-46.f);
+        canvas->translate(xOffset, 0);
+        canvas->scale(1.2f, 1.1f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+
+        canvas->rotate(46.f);
+        canvas->translate(xOffset, 0);
+        canvas->scale(1.1f, 1.2f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+
+        canvas->rotate(46.f);
+        canvas->translate(xOffset, 0);
+        canvas->scale(.95f, 1.1f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+
+        canvas->rotate(46.f);
+        canvas->translate(xOffset, 0);
+        canvas->scale(1.3f, .7f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+
+        canvas->rotate(46.f);
+        canvas->translate(xOffset, 0);
+        canvas->scale(.8f, 1.1f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+
+        canvas->rotate(10.f);
+        canvas->translate(xOffset, 0);
+        canvas->scale(1.f, 5.f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+
+        canvas->rotate(5.f);
+        canvas->translate(xOffset, 0);
+        canvas->scale(5.f, 1.f);
+        canvas->drawTextBlob(fBlob, 0, 0, paint);
+    }
+
+private:
+    SkAutoTUnref<const SkTextBlob> fBlob;
+
+    static const int kWidth = 1000;
+    static const int kHeight = 1200;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(TextBlobTransforms); )
+}
diff --git a/gm/texteffects.cpp b/gm/texteffects.cpp
index 3fce5db..03dbd68 100644
--- a/gm/texteffects.cpp
+++ b/gm/texteffects.cpp
@@ -175,15 +175,15 @@
     TextEffectsGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("texteffects");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(460, 680);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->save();
 
         SkPaint     paint;
diff --git a/gm/texturedomaineffect.cpp b/gm/texturedomaineffect.cpp
index 72a5839..6104d46 100644
--- a/gm/texturedomaineffect.cpp
+++ b/gm/texturedomaineffect.cpp
@@ -30,18 +30,18 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("texture_domain_effect");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         const SkScalar canvasWidth = kDrawPad +
                 (kTargetWidth + 2 * kDrawPad) * GrTextureDomain::kModeCount +
                 kTestPad * GrTextureDomain::kModeCount;
         return SkISize::Make(SkScalarCeilToInt(canvasWidth), 800);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         fBmp.allocN32Pixels(kTargetWidth, kTargetHeight);
         SkCanvas canvas(fBmp);
         canvas.clear(0x00000000);
@@ -68,7 +68,7 @@
                                          fBmp.width() + 10.f, fBmp.height() + 10.f), paint);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         if (NULL == rt) {
             return;
@@ -124,8 +124,7 @@
                     if (!fp) {
                         continue;
                     }
-                    SkMatrix viewMatrix;
-                    viewMatrix.setTranslate(x, y);
+                    const SkMatrix viewMatrix = SkMatrix::MakeTrans(x, y);
                     GrPipelineBuilder pipelineBuilder;
                     pipelineBuilder.setRenderTarget(rt);
                     pipelineBuilder.addColorProcessor(fp);
diff --git a/gm/thinrects.cpp b/gm/thinrects.cpp
index 3facaa9..a25176c 100644
--- a/gm/thinrects.cpp
+++ b/gm/thinrects.cpp
@@ -17,15 +17,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("thinrects");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(240, 320);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         SkPaint white;
         white.setColor(SK_ColorWHITE);
diff --git a/gm/thinstrokedrects.cpp b/gm/thinstrokedrects.cpp
index 40c23e1..8bde102 100644
--- a/gm/thinstrokedrects.cpp
+++ b/gm/thinstrokedrects.cpp
@@ -17,15 +17,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("thinstrokedrects");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(240, 320);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         SkPaint paint;
         paint.setColor(SK_ColorWHITE);
diff --git a/gm/tiledscaledbitmap.cpp b/gm/tiledscaledbitmap.cpp
index 6b47315..79e1111 100644
--- a/gm/tiledscaledbitmap.cpp
+++ b/gm/tiledscaledbitmap.cpp
@@ -31,11 +31,11 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("tiledscaledbitmap");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1016, 616);
     }
 
@@ -50,15 +50,15 @@
         return bm;
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         fBitmap = make_bm(360, 288);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
 
         paint.setAntiAlias(true);
-        paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
+        paint.setFilterQuality(kHigh_SkFilterQuality);
 
         SkMatrix mat;
         mat.setScale(121.f/360.f, 93.f/288.f);
diff --git a/gm/tileimagefilter.cpp b/gm/tileimagefilter.cpp
index 89a6c6c..2c4f6b2 100644
--- a/gm/tileimagefilter.cpp
+++ b/gm/tileimagefilter.cpp
@@ -56,7 +56,7 @@
 
             fInitialized = true;
         }
-        canvas->clear(0x00000000);
+        canvas->clear(SK_ColorBLACK);
 
         int x = 0, y = 0;
         for (size_t i = 0; i < 4; i++) {
diff --git a/gm/tilemodes.cpp b/gm/tilemodes.cpp
index 8dc54a9..391d969 100644
--- a/gm/tilemodes.cpp
+++ b/gm/tilemodes.cpp
@@ -38,7 +38,7 @@
                   SkShader::TileMode tmx, SkShader::TileMode tmy) {
     SkShader* shader = SkShader::CreateBitmapShader(bm, tmx, tmy);
     paint->setShader(shader)->unref();
-    paint->setFilterLevel(filter ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel);
+    paint->setFilterQuality(filter ? kLow_SkFilterQuality : kNone_SkFilterQuality);
 }
 
 static const SkColorType gColorTypes[] = {
@@ -61,7 +61,7 @@
         kNPOTSize = 21,
     };
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString name("tilemodes");
         if (!fPowerOfTwoSize) {
             name.append("_npot");
@@ -69,16 +69,16 @@
         return name;
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(880, 560); }
+    SkISize onISize() override { return SkISize::Make(880, 560); }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         int size = fPowerOfTwoSize ? kPOTSize : kNPOTSize;
         for (size_t i = 0; i < SK_ARRAY_COUNT(gColorTypes); i++) {
             makebm(&fTexture[i], gColorTypes[i], size, size);
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
 
         int size = fPowerOfTwoSize ? kPOTSize : kNPOTSize;
 
@@ -195,13 +195,13 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(880, 560); }
+    SkISize onISize() override { return SkISize::Make(880, 560); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->scale(SkIntToScalar(3)/2, SkIntToScalar(3)/2);
 
         const SkScalar w = SkIntToScalar(gWidth);
diff --git a/gm/tilemodes_scaled.cpp b/gm/tilemodes_scaled.cpp
index 508e576..efb6f85 100644
--- a/gm/tilemodes_scaled.cpp
+++ b/gm/tilemodes_scaled.cpp
@@ -35,11 +35,11 @@
     canvas.drawPaint(paint);
 }
 
-static void setup(SkPaint* paint, const SkBitmap& bm, SkPaint::FilterLevel filter_level,
+static void setup(SkPaint* paint, const SkBitmap& bm, SkFilterQuality filter_level,
                   SkShader::TileMode tmx, SkShader::TileMode tmy) {
     SkShader* shader = SkShader::CreateBitmapShader(bm, tmx, tmy);
     paint->setShader(shader)->unref();
-    paint->setFilterLevel(filter_level);
+    paint->setFilterQuality(filter_level);
 }
 
 static const SkColorType gColorTypes[] = {
@@ -65,7 +65,7 @@
         kNPOTSize = 3,
     };
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString name("scaled_tilemodes");
         if (!fPowerOfTwoSize) {
             name.append("_npot");
@@ -73,16 +73,16 @@
         return name;
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(880, 760); }
+    SkISize onISize() override { return SkISize::Make(880, 760); }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         int size = fPowerOfTwoSize ? kPOTSize : kNPOTSize;
         for (size_t i = 0; i < SK_ARRAY_COUNT(gColorTypes); i++) {
             makebm(&fTexture[i], gColorTypes[i], size, size);
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         float scale = 32.f/kPOTSize;
 
         int size = fPowerOfTwoSize ? kPOTSize : kNPOTSize;
@@ -91,12 +91,12 @@
 
         static const char* gColorTypeNames[] = { "8888" , "565", "4444" };
 
-        static const SkPaint::FilterLevel           gFilterLevels[] =
-            { SkPaint::kNone_FilterLevel,
-              SkPaint::kLow_FilterLevel,
-              SkPaint::kMedium_FilterLevel,
-              SkPaint::kHigh_FilterLevel };
-        static const char*          gFilterNames[] = { "None", "Low", "Medium", "High" };
+        static const SkFilterQuality gFilterQualitys[] =
+            { kNone_SkFilterQuality,
+              kLow_SkFilterQuality,
+              kMedium_SkFilterQuality,
+              kHigh_SkFilterQuality };
+        static const char* gFilterNames[] = { "None", "Low", "Medium", "High" };
 
         static const SkShader::TileMode gModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode };
         static const char*          gModeNames[] = {    "C",                    "R",                   "M" };
@@ -124,7 +124,7 @@
         y = SkIntToScalar(40) / scale;
 
         for (size_t i = 0; i < SK_ARRAY_COUNT(gColorTypes); i++) {
-            for (size_t j = 0; j < SK_ARRAY_COUNT(gFilterLevels); j++) {
+            for (size_t j = 0; j < SK_ARRAY_COUNT(gFilterQualitys); j++) {
                 x = SkIntToScalar(10)/scale;
                 for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
                     for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
@@ -135,7 +135,7 @@
                             makebm(&fTexture[i], gColorTypes[i], size, size);
                         }
 #endif
-                        setup(&paint, fTexture[i], gFilterLevels[j], gModes[kx], gModes[ky]);
+                        setup(&paint, fTexture[i], gFilterQualitys[j], gModes[kx], gModes[ky]);
                         paint.setDither(true);
 
                         canvas->save();
@@ -207,13 +207,13 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return fName;
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(880, 560); }
+    SkISize onISize() override { return SkISize::Make(880, 560); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->scale(SkIntToScalar(3)/2, SkIntToScalar(3)/2);
 
         const SkScalar w = SkIntToScalar(gWidth);
diff --git a/gm/twopointradial.cpp b/gm/twopointradial.cpp
deleted file mode 100644
index f1a27d3..0000000
--- a/gm/twopointradial.cpp
+++ /dev/null
@@ -1,112 +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.
- */
-
-#include "gm.h"
-#include "SkCanvas.h"
-#include "SkPaint.h"
-#include "SkGradientShader.h"
-
-static void intToScalars(SkScalar dst[], const int src[], int n) {
-    for (int i = 0; i < n; ++i) {
-        dst[i] = SkIntToScalar(src[i]);
-    }
-}
-
-static void drawGrad(SkCanvas* canvas, const SkScalar d0[], const SkScalar d1[]) {
-    const SkRect bounds = SkRect::MakeXYWH(SkIntToScalar(-50),
-                                           SkIntToScalar(-50),
-                                           SkIntToScalar(200),
-                                           SkIntToScalar(100));
-
-    SkPoint c0 = { d0[0], d0[1] };
-    SkScalar r0 = d0[2];
-    SkPoint c1 = { d1[0], d1[1] };
-    SkScalar r1 = d1[2];
-
-    SkColor colors[] = { SK_ColorGREEN, SK_ColorRED };
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    sk_tool_utils::set_portable_typeface(&paint);
-
-    SkString str;
-    str.printf("%g,%g,%g  %g,%g,%g",
-               SkScalarToFloat(c0.fX), SkScalarToFloat(c0.fY), SkScalarToFloat(r0),
-               SkScalarToFloat(c1.fX), SkScalarToFloat(c1.fY), SkScalarToFloat(r1));
-    canvas->drawText(str.c_str(), str.size(),
-                     bounds.fLeft, bounds.fTop - paint.getTextSize()/2, paint);
-
-    paint.setShader(SkGradientShader::CreateTwoPointConical(c0, r0, c1, r1,
-                                                            colors, NULL, 2,
-                                                            SkShader::kClamp_TileMode))->unref();
-    canvas->drawRect(bounds, paint);
-
-    paint.setShader(NULL);
-    paint.setColor(0x66000000);
-    paint.setStyle(SkPaint::kStroke_Style);
-    canvas->drawCircle(c0.fX, c0.fY, r0, paint);
-    canvas->drawCircle(c1.fX, c1.fY, r1, paint);
-    canvas->drawRect(bounds, paint);
-}
-
-class TwoPointRadialGM : public skiagm::GM {
-public:
-    TwoPointRadialGM() {}
-
-protected:
-    SkString onShortName() {
-        return SkString("twopointconical");
-    }
-
-    SkISize onISize() { return SkISize::Make(480, 780); }
-
-    virtual void onDraw(SkCanvas* canvas) {
-        if (false) {
-            SkPaint paint;
-            paint.setColor(SK_ColorBLUE);
-            canvas->drawRect(
-                    SkRect::MakeWH(SkIntToScalar(this->getISize().fWidth),
-                                   SkIntToScalar(this->getISize().fHeight)),
-                    paint);
-        }
-        SkPaint paint;
-        const int R0 = 20;
-        const int R1 = 40;
-
-        const SkScalar DX = SkIntToScalar(250);
-        const SkScalar DY = SkIntToScalar(130);
-
-        canvas->translate(SkIntToScalar(60), SkIntToScalar(70));
-
-        static const int gData[] = {
-            0, 0, R0,       0, 0, R1,
-            0, 0, R0,       20, 0, R1,
-            0, 0, R0,       25, 0, R1,
-            0, 0, R0,       100, 0, R1,
-            0, 0, R0,       25, 0, R0,
-            0, 0, R0,       100, 0, R0,
-        };
-
-        int count = SK_ARRAY_COUNT(gData) / 6;
-        for (int i = 0; i < count; ++i) {
-            SkScalar data[6];
-            intToScalars(data, &gData[i * 6], 6);
-
-            int n = canvas->save();
-            drawGrad(canvas, &data[0], &data[3]);
-            canvas->translate(DX, 0);
-            drawGrad(canvas, &data[3], &data[0]);
-            canvas->restoreToCount(n);
-            canvas->translate(0, DY);
-        }
-    }
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static skiagm::GM* F(void*) { return new TwoPointRadialGM; }
-
-static skiagm::GMRegistry gR(F);
diff --git a/gm/typeface.cpp b/gm/typeface.cpp
index 1606cef..4114d2c 100644
--- a/gm/typeface.cpp
+++ b/gm/typeface.cpp
@@ -36,15 +36,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("typeface");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkString text("Typefaces are fun!");
         SkScalar y = 0;
 
@@ -171,7 +171,7 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString name("typefacestyles");
         if (fApplyKerning) {
             name.append("_kerning");
@@ -179,11 +179,11 @@
         return name;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setTextSize(SkIntToScalar(30));
diff --git a/gm/variedtext.cpp b/gm/variedtext.cpp
index bbdbc8a..989bebd 100644
--- a/gm/variedtext.cpp
+++ b/gm/variedtext.cpp
@@ -31,7 +31,7 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString name("varied_text");
         if (fEffectiveClip) {
             name.append("_clipped");
@@ -46,11 +46,11 @@
         return name;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(640, 480);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         fPaint.setAntiAlias(true);
         fPaint.setLCDRenderText(fLCD);
 
@@ -111,7 +111,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         for (int i = 0; i < kCnt; ++i) {
             fPaint.setColor(fColors[i]);
             fPaint.setTextSize(fPtSizes[i]);
@@ -136,7 +136,7 @@
         }
     }
 
-    bool runAsBench() const SK_OVERRIDE { return true; }
+    bool runAsBench() const override { return true; }
 
 private:
     static const int kCnt = 30;
diff --git a/gm/vertices.cpp b/gm/vertices.cpp
index 5d15633..2f7a9b9 100644
--- a/gm/vertices.cpp
+++ b/gm/vertices.cpp
@@ -39,7 +39,7 @@
 
 protected:
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         const SkScalar X = 150;
         const SkScalar Y = 150;
 
@@ -62,7 +62,7 @@
         }
     }
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         SkString name("vertices");
         if (0xFF != fAlpha) {
             name.appendf("_%02X", fAlpha);
@@ -70,11 +70,11 @@
         return name;
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(600, 600);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         // start with the center of a 3x3 grid
         static const uint16_t fan[] = {
             4,
diff --git a/gm/verttext.cpp b/gm/verttext.cpp
index ba355a5..9039685 100644
--- a/gm/verttext.cpp
+++ b/gm/verttext.cpp
@@ -50,11 +50,11 @@
 
 protected:
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("verttext");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(640, 480); }
+    SkISize onISize() override { return SkISize::Make(640, 480); }
 
     static void drawBaseline(SkCanvas* canvas, const SkPaint& paint,
                              SkScalar x, SkScalar y) {
@@ -82,7 +82,7 @@
         canvas->drawCircle(x, y, SK_Scalar1 * 3 / 2, p);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkScalar x = SkIntToScalar(100);
         SkScalar y = SkIntToScalar(50);
 
diff --git a/gm/verttext2.cpp b/gm/verttext2.cpp
index 6cb5235..77bdc92 100644
--- a/gm/verttext2.cpp
+++ b/gm/verttext2.cpp
@@ -33,13 +33,13 @@
 protected:
 
 
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("verttext2");
     }
 
-    SkISize onISize() SK_OVERRIDE { return SkISize::Make(640, 480); }
+    SkISize onISize() override { return SkISize::Make(640, 480); }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         for (int i = 0; i < 3; ++i) {
             SkPaint paint;
             paint.setColor(SK_ColorRED);
diff --git a/gm/verylargebitmap.cpp b/gm/verylargebitmap.cpp
index 1cfecd8..753786b 100644
--- a/gm/verylargebitmap.cpp
+++ b/gm/verylargebitmap.cpp
@@ -59,15 +59,15 @@
     VeryLargeBitmapGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("verylargebitmap");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(500, 600);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         int veryBig = 65*1024; // 64K < size
         int big = 33*1024;     // 32K < size < 64K
         // smaller than many max texture sizes, but large enough to gpu-tile for memory reasons.
diff --git a/gm/xfermodeimagefilter.cpp b/gm/xfermodeimagefilter.cpp
index 0134bd3..649e69f 100644
--- a/gm/xfermodeimagefilter.cpp
+++ b/gm/xfermodeimagefilter.cpp
@@ -25,7 +25,7 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("xfermodeimagefilter");
     }
 
@@ -42,7 +42,7 @@
         canvas.drawText(str, strlen(str), SkIntToScalar(15), SkIntToScalar(65), paint);
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(WIDTH, HEIGHT);
     }
 
@@ -65,7 +65,7 @@
         canvas->restore();
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         make_bitmap();
 
         fCheckerboard.allocN32Pixels(80, 80);
@@ -73,8 +73,8 @@
         sk_tool_utils::draw_checkerboard(&checkerboardCanvas, 0xFFA0A0A0, 0xFF404040, 8);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
-        canvas->clear(0x00000000);
+    void onDraw(SkCanvas* canvas) override {
+        canvas->clear(SK_ColorBLACK);
         SkPaint paint;
 
         const struct {
diff --git a/gm/xfermodes.cpp b/gm/xfermodes.cpp
index 82d1cef..066ad5b 100644
--- a/gm/xfermodes.cpp
+++ b/gm/xfermodes.cpp
@@ -154,7 +154,7 @@
         }
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         fBG.installPixels(SkImageInfo::Make(2, 2, kARGB_4444_SkColorType,
                                             kOpaque_SkAlphaType),
                           gData, 4);
@@ -168,15 +168,15 @@
     XfermodesGM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("xfermodes");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(1990, 640);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
 
         const struct {
diff --git a/gm/xfermodes2.cpp b/gm/xfermodes2.cpp
index 98e6f3a..2ceb2fc 100644
--- a/gm/xfermodes2.cpp
+++ b/gm/xfermodes2.cpp
@@ -18,15 +18,15 @@
     Xfermodes2GM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("xfermodes2");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(455, 475);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
 
         const SkScalar w = SkIntToScalar(kSize);
@@ -86,7 +86,7 @@
     }
 
 private:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         static const uint32_t kCheckData[] = {
             SkPackARGB32(0xFF, 0x40, 0x40, 0x40),
             SkPackARGB32(0xFF, 0xD0, 0xD0, 0xD0),
@@ -104,9 +104,9 @@
                                                SkShader::kRepeat_TileMode,
                                                &lm));
 
-        SkBitmap dstBmp;
-        dstBmp.allocN32Pixels(kSize, kSize);
-        SkPMColor* pixels = reinterpret_cast<SkPMColor*>(dstBmp.getPixels());
+        SkBitmap srcBmp;
+        srcBmp.allocN32Pixels(kSize, kSize);
+        SkPMColor* pixels = reinterpret_cast<SkPMColor*>(srcBmp.getPixels());
 
         for (int y = 0; y < kSize; ++y) {
             int c = y * (1 << kShift);
@@ -115,12 +115,12 @@
                 pixels[kSize * y + x] = rowColor;
             }
         }
-        fSrc.reset(SkShader::CreateBitmapShader(dstBmp,
+        fSrc.reset(SkShader::CreateBitmapShader(srcBmp,
                                                 SkShader::kClamp_TileMode,
                                                 SkShader::kClamp_TileMode));
-        SkBitmap srcBmp;
-        srcBmp.allocN32Pixels(kSize, kSize);
-        pixels = reinterpret_cast<SkPMColor*>(srcBmp.getPixels());
+        SkBitmap dstBmp;
+        dstBmp.allocN32Pixels(kSize, kSize);
+        pixels = reinterpret_cast<SkPMColor*>(dstBmp.getPixels());
 
         for (int x = 0; x < kSize; ++x) {
             int c = x * (1 << kShift);
@@ -129,7 +129,7 @@
                 pixels[kSize * y + x] = colColor;
             }
         }
-        fDst.reset(SkShader::CreateBitmapShader(srcBmp,
+        fDst.reset(SkShader::CreateBitmapShader(dstBmp,
                                                 SkShader::kClamp_TileMode,
                                                 SkShader::kClamp_TileMode));
     }
diff --git a/gm/xfermodes3.cpp b/gm/xfermodes3.cpp
index ffbee1c..003b771 100644
--- a/gm/xfermodes3.cpp
+++ b/gm/xfermodes3.cpp
@@ -27,21 +27,21 @@
     Xfermodes3GM() {}
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("xfermodes3");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(630, 1215);
     }
 
-    void onDrawBackground(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawBackground(SkCanvas* canvas) override {
         SkPaint bgPaint;
         bgPaint.setColor(0xFF70D0E0);
         canvas->drawPaint(bgPaint);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
 
         SkPaint labelP;
@@ -179,7 +179,7 @@
         canvas->restore();
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         static const uint32_t kCheckData[] = {
             SkPackARGB32(0xFF, 0x40, 0x40, 0x40),
             SkPackARGB32(0xFF, 0xD0, 0xD0, 0xD0),
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index 4448265..62855cf 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -34,15 +34,15 @@
     }
 
 protected:
-    SkString onShortName() SK_OVERRIDE {
+    SkString onShortName() override {
         return SkString("yuv_to_rgb_effect");
     }
 
-    SkISize onISize() SK_OVERRIDE {
+    SkISize onISize() override {
         return SkISize::Make(238, 84);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
         fBmp[0].allocPixels(yinfo);
         SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
@@ -66,7 +66,7 @@
         }
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
         if (NULL == rt) {
             return;
diff --git a/gyp/FileReaderApp.gyp b/gyp/FileReaderApp.gyp
index 463688b..f0d0bbb 100644
--- a/gyp/FileReaderApp.gyp
+++ b/gyp/FileReaderApp.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'targets': [
     {
diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp
index 9caeeca..b9b8eff 100644
--- a/gyp/SampleApp.gyp
+++ b/gyp/SampleApp.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 #
 {
   'variables': {
@@ -87,7 +91,6 @@
         '../samplecode/SamplePath.cpp',
         '../samplecode/SamplePathClip.cpp',
         '../samplecode/SamplePathFuzz.cpp',
-        '../samplecode/SamplePathUtils.cpp',
         '../samplecode/SamplePathEffects.cpp',
         '../samplecode/SamplePicture.cpp',
         '../samplecode/SamplePictFile.cpp',
@@ -293,11 +296,6 @@
             'gputest.gyp:skgputest',
           ],
         }],
-        [ 'skia_os == "nacl"', {
-          'sources': [
-            '../../nacl/src/nacl_sample.cpp',
-          ],
-        }],
       ],
       'msvs_settings': {
         'VCLinkerTool': {
diff --git a/gyp/SimpleCocoaApp.gyp b/gyp/SimpleCocoaApp.gyp
index 1500db4..393aa50 100644
--- a/gyp/SimpleCocoaApp.gyp
+++ b/gyp/SimpleCocoaApp.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'targets': [
     {
diff --git a/gyp/SimpleiOSApp.gyp b/gyp/SimpleiOSApp.gyp
index bb5fc8f..59d0aa0 100644
--- a/gyp/SimpleiOSApp.gyp
+++ b/gyp/SimpleiOSApp.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'conditions' : [
     [ 'skia_os != "ios"', {
diff --git a/gyp/android_deps.gyp b/gyp/android_deps.gyp
index 1669b2b..ceecb06 100644
--- a/gyp/android_deps.gyp
+++ b/gyp/android_deps.gyp
@@ -1,3 +1,8 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
 # This GYP file stores the dependencies necessary to build Skia on the Android
 # platform. The OS doesn't provide many stable libraries as part of the
 # distribution so we have to build a few of them ourselves.
@@ -35,18 +40,6 @@
             },
           },
           {
-            'target_name': 'gif',
-            'type': 'none',
-            'direct_dependent_settings': {
-              'libraries' : [
-                'libgif.a',
-              ],
-              'include_dirs': [
-                'external/giflib',
-              ],
-            },
-          },
-          {
             'target_name': 'png',
             'type': 'none',
             'direct_dependent_settings': {
diff --git a/gyp/android_framework_lib.gyp b/gyp/android_framework_lib.gyp
index da68530..9e5bd71 100644
--- a/gyp/android_framework_lib.gyp
+++ b/gyp/android_framework_lib.gyp
@@ -8,6 +8,7 @@
       'target_name': 'libskia',
       'type': 'shared_library',
       'dependencies': [
+        'codec.gyp:codec',
         'core.gyp:core',
         'effects.gyp:effects',
         'images.gyp:images',
diff --git a/gyp/android_output.gyp b/gyp/android_output.gyp
index b22ac9d..96599e1 100644
--- a/gyp/android_output.gyp
+++ b/gyp/android_output.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP file to send android SkDebug to stdout in addition to logcat. To enable,
 # include this project as a dependency.
 {
diff --git a/gyp/android_system.gyp b/gyp/android_system.gyp
index af2492c..fda2dbe 100644
--- a/gyp/android_system.gyp
+++ b/gyp/android_system.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # This GYP file stores the dependencies necessary to build Skia on the Android
 # platform. The OS doesn't provide many stable libraries as part of the
 # distribution so we have to build a few of them ourselves.
diff --git a/gyp/angle.gyp b/gyp/angle.gyp
index 467c7fb..d555cb9 100644
--- a/gyp/angle.gyp
+++ b/gyp/angle.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # ANGLE is the Windows-specific translator from OGL ES to D3D
 
 {
diff --git a/gyp/animator.gyp b/gyp/animator.gyp
index be5607d..8c682bd 100644
--- a/gyp/animator.gyp
+++ b/gyp/animator.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 #Animator is basically Skia's (much saner) version of Flash.
 #On top of Views it provides a declarative UI model which can be updated
 #based on events which trigger changes or scripts.
diff --git a/gyp/apptype_console.gypi b/gyp/apptype_console.gypi
index d375941..d3613b3 100644
--- a/gyp/apptype_console.gypi
+++ b/gyp/apptype_console.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # target_defaults used for executable targets that generate a console app
 {
   'target_defaults': {
@@ -25,11 +29,6 @@
           'android_output.gyp:android_output',
         ],
       }],
-      [ 'skia_os == "nacl"', {
-        'dependencies': [
-          'nacl.gyp:nacl_interface',
-        ],
-      }],
       ['skia_os == "ios"', {
         'target_conditions': [
           ['_type == "executable"', {
diff --git a/gyp/bench.gyp b/gyp/bench.gyp
index 480f876..b2459f9 100644
--- a/gyp/bench.gyp
+++ b/gyp/bench.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP file to build performance testbench.
 #
 {
@@ -10,10 +14,12 @@
       'type': 'executable',
       'sources': [
         '../gm/gm.cpp',
+        '../bench/CodecBench.cpp',
         '../bench/DecodingBench.cpp',
         '../bench/DecodingSubsetBench.cpp',
         '../bench/GMBench.cpp',
         '../bench/RecordingBench.cpp',
+        '../bench/SKPAnimationBench.cpp',
         '../bench/SKPBench.cpp',
         '../bench/nanobench.cpp',
       ],
@@ -33,6 +39,20 @@
         ['skia_android_framework', {
           'libraries': [
             '-lskia',
+            '-landroid',
+            '-lgui',
+            '-lhwui',
+            '-lutils',
+          ],
+          'include_dirs': [
+            '../../../frameworks/base/libs/hwui/',
+            '../../../frameworks/native/include/',
+          ],
+          'sources': [
+            '../bench/nanobenchAndroid.cpp',
+          ],
+          'dependencies': [
+            'utils.gyp:android_utils',
           ],
         }],
       ],
diff --git a/gyp/bench.gypi b/gyp/bench.gypi
index 3b77fc5..3e87f6b 100644
--- a/gyp/bench.gypi
+++ b/gyp/bench.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'include_dirs': [
     '../src/core',
@@ -41,6 +45,7 @@
     '../bench/ColorCubeBench.cpp',
     '../bench/ColorFilterBench.cpp',
     '../bench/ColorPrivBench.cpp',
+    '../bench/ControlBench.cpp',
     '../bench/CoverageBench.cpp',
     '../bench/DashBench.cpp',
     '../bench/DeferredSurfaceCopyBench.cpp',
@@ -79,9 +84,9 @@
     '../bench/PatchGridBench.cpp',
     '../bench/PathBench.cpp',
     '../bench/PathIterBench.cpp',
-    '../bench/PathUtilsBench.cpp',
     '../bench/PerlinNoiseBench.cpp',
     '../bench/PictureNestingBench.cpp',
+    '../bench/PictureOverheadBench.cpp',
     '../bench/PicturePlaybackBench.cpp',
     '../bench/PremulAndUnpremulAlphaOpsBench.cpp',
     '../bench/RTreeBench.cpp',
@@ -101,6 +106,7 @@
     '../bench/StrokeBench.cpp',
     '../bench/TableBench.cpp',
     '../bench/TextBench.cpp',
+    '../bench/TextBlobBench.cpp',
     '../bench/TileBench.cpp',
     '../bench/VertBench.cpp',
     '../bench/WritePixelsBench.cpp',
diff --git a/gyp/canvas_state_lib.gyp b/gyp/canvas_state_lib.gyp
index 1bb2fe4..d9b24b2 100644
--- a/gyp/canvas_state_lib.gyp
+++ b/gyp/canvas_state_lib.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Building test for running CanvasState
 
 # HOW TO USE:
diff --git a/gyp/chromeos_deps.gyp b/gyp/chromeos_deps.gyp
deleted file mode 100644
index 8863791..0000000
--- a/gyp/chromeos_deps.gyp
+++ /dev/null
@@ -1,9 +0,0 @@
-# This GYP file stores the dependencies necessary to build Skia on the Chrome OS
-# platform. The OS doesn't provide many stable libraries as part of the
-# distribution so we have to build a few of them ourselves.
-
-{
-  'includes': [
-    '../platform_tools/chromeos/gyp/dependencies.gypi',
-  ],
-}
diff --git a/gyp/codec.gyp b/gyp/codec.gyp
index b962799..f3fed41 100644
--- a/gyp/codec.gyp
+++ b/gyp/codec.gyp
@@ -1,3 +1,12 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
 # GYP file for codec project.
 {
   'targets': [
@@ -8,15 +17,32 @@
       'standalone_static_library': 1,
       'dependencies': [
         'core.gyp:*',
-        'libpng.gyp:libpng',
+        'giflib.gyp:giflib',
+        'libjpeg.gyp:libjpeg',
+      ],
+      'cflags':[
+        # FIXME: This gets around a longjmp warning. See
+        # http://build.chromium.org/p/client.skia.compile/builders/Build-Ubuntu-GCC-x86_64-Release-Trybot/builds/113/steps/build%20most/logs/stdio
+        '-Wno-clobbered',
       ],
       'include_dirs': [
         '../include/codec',
         '../src/codec',
+        '../src/core',
       ],
       'sources': [
         '../src/codec/SkCodec.cpp',
+        '../src/codec/SkCodec_libbmp.cpp',
+        '../src/codec/SkCodec_libgif.cpp',
+        '../src/codec/SkCodec_libico.cpp',
         '../src/codec/SkCodec_libpng.cpp',
+        '../src/codec/SkCodec_wbmp.cpp',
+        '../src/codec/SkGifInterlaceIter.cpp',
+        '../src/codec/SkJpegCodec.cpp',
+        '../src/codec/SkJpegDecoderMgr.cpp',
+        '../src/codec/SkJpegUtility.cpp',
+        '../src/codec/SkMaskSwizzler.cpp',
+        '../src/codec/SkMasks.cpp',
         '../src/codec/SkSwizzler.cpp',
       ],
       'direct_dependent_settings': {
@@ -24,6 +50,21 @@
           '../include/codec',
         ],
       },
+      'conditions': [
+        [ 'skia_android_framework == 1',
+          {
+            # TODO(djsollen): this is a temporary dependency until we can update
+            # the android framework to a more recent version of libpng.
+            'dependencies': [
+              'libpng.gyp:libpng',
+            ],
+          }, {  # !skia_android_framework
+            'dependencies': [
+              'libpng.gyp:libpng_static',
+            ],
+          }
+        ]
+      ]
     },
   ],
 }
diff --git a/gyp/common.gypi b/gyp/common.gypi
index ce3fc26..f0d653f 100644
--- a/gyp/common.gypi
+++ b/gyp/common.gypi
@@ -25,7 +25,6 @@
     'variables': {
       'conditions': [
         [ 'skia_os != OS and not ((skia_os == "ios" and OS == "mac") or \
-                                  (skia_os == "nacl" and OS == "linux") or \
                                   (skia_os == "chromeos" and OS == "linux"))', {
           'error': '<!(Cannot build with skia_os=<(skia_os) on OS=<(OS))',
         }],
@@ -38,9 +37,6 @@
         [ 'skia_arch_width != 32 and skia_arch_width != 64', {
           'error': '<!(skia_arch_width can only be 32 or 64 bits not <(skia_arch_width) bits)',
         }],
-        [ 'skia_os == "nacl" and OS != "linux"', {
-          'error': '<!(Skia NaCl build only currently supported on Linux.)',
-        }],
         [ 'skia_os == "chromeos" and OS != "linux"', {
           'error': '<!(Skia ChromeOS build is only supported on Linux.)',
         }],
diff --git a/gyp/common_conditions.gypi b/gyp/common_conditions.gypi
index b9c4e98..9b9dc22 100644
--- a/gyp/common_conditions.gypi
+++ b/gyp/common_conditions.gypi
@@ -1,3 +1,8 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
 # conditions used in both common.gypi and skia.gyp in chromium
 #
 {
@@ -203,7 +208,7 @@
     ],
 
     # The following section is common to linux + derivatives and android
-    [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "nacl", "chromeos", "android"]',
+    [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "chromeos", "android"]',
       {
         'cflags': [
           '-g',
@@ -225,6 +230,7 @@
           '-Wno-invalid-offsetof',  # GCC <4.6 is old-school strict about what is POD.
         ],
         'conditions': [
+          [ 'skia_fast', { 'cflags': [ '<@(skia_fast_flags)' ] }],
           [ 'skia_os != "chromeos"', {
             'conditions': [
               [ 'skia_arch_type == "x86_64" and not skia_android_framework', {
@@ -259,28 +265,10 @@
               '-fno-omit-frame-pointer',
             ],
           }],
-          [ 'skia_arch_type == "arm" and arm_thumb == 1', {
-            'cflags': [
-              '-mthumb',
-            ],
-            # The --fix-cortex-a8 switch enables a link-time workaround for
-            # an erratum in certain Cortex-A8 processors.  The workaround is
-            # enabled by default if you target the ARM v7-A arch profile.
-            # It can be enabled otherwise by specifying --fix-cortex-a8, or
-            # disabled unconditionally by specifying --no-fix-cortex-a8.
-            #
-            # The erratum only affects Thumb-2 code.
-            'conditions': [
-              [ 'arm_version < 7', {
-                'ldflags': [
-                  '-Wl,--fix-cortex-a8',
-                ],
-              }],
-            ],
-          }],
           [ 'skia_arch_type == "arm" and arm_version >= 7', {
             'cflags': [
               '-march=armv7-a',
+              '-mthumb',
             ],
             'ldflags': [
               '-march=armv7-a',
@@ -341,13 +329,6 @@
       },
     ],
 
-    [ 'skia_os == "nacl"', {
-      # NaCl compiler is GCC 4.4, which is too old to understand 'c++11', so call it '0x'.
-      # NaCl's newlib needs gnu++ mode to see snprintf, vsnprintf, etc in C++11 mode.
-      'cflags_cc!': [ '-std=c++11' ],
-      'cflags_cc' : [ '-std=gnu++0x' ],
-    }],
-
     ['skia_android_framework', {
       'includes' : [
         'skia_for_android_framework_defines.gypi',
@@ -407,20 +388,19 @@
         # Optimizations for chromium (m30)
         'GR_GL_CUSTOM_SETUP_HEADER "gl/GrGLConfig_chrome.h"',
         'IGNORE_ROT_AA_RECT_OPT',
-        'SkLONGLONG int64_t',
         'SK_DEFAULT_FONT_CACHE_LIMIT   (768 * 1024)',
         'SK_DEFAULT_GLOBAL_DISCARDABLE_MEMORY_POOL_SIZE (512 * 1024)',
         'SK_IGNORE_ETC1_SUPPORT',
         # We can't use the skia_shared_library gyp setting because we need expose
         # this define globally and the the implemention define as a cflag.
         'SKIA_DLL',
-        'SK_OVERRIDE override',
+        'SK_PRINT_CODEC_MESSAGES',
         # Defines from skia_for_android_framework_defines.gypi
         '<@(skia_for_android_framework_defines)',
       ],
     }],
 
-    [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "nacl", "chromeos"]',
+    [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "chromeos"]',
       {
         'defines': [
           'SK_SAMPLES_FOR_X',
@@ -447,26 +427,6 @@
               'SKIA_IMPLEMENTATION=1',
             ],
           }],
-          [ 'skia_os == "nacl"', {
-            'defines': [
-              'SK_BUILD_FOR_NACL',
-            ],
-            'variables': {
-              'nacl_sdk_root': '<!(echo ${NACL_SDK_ROOT})',
-            },
-            'link_settings': {
-              'libraries': [
-                '-lppapi',
-                '-lppapi_cpp',
-                '-lnosys',
-                '-pthread',
-              ],
-              'ldflags': [
-                '-L<(nacl_sdk_root)/lib/newlib_x86_<(skia_arch_width)/Release',
-                '-L<(nacl_sdk_root)/ports/lib/newlib_x86_<(skia_arch_width)/Release',
-              ],
-            },
-          }],
           # Enable asan, tsan, etc.
           [ 'skia_sanitizer', {
             'cflags': [
@@ -526,6 +486,7 @@
         },
         'xcode_settings': {
           'conditions': [
+            [ 'skia_fast', { 'WARNING_CFLAGS': [ '<@(skia_fast_flags)' ] } ],
             [ 'skia_warnings_as_errors', { 'GCC_TREAT_WARNINGS_AS_ERRORS': 'YES' }],
             [ 'skia_arch_width == 32', { 'ARCHS': ['i386']   }],
             [ 'skia_arch_width == 64', { 'ARCHS': ['x86_64'] }],
@@ -585,7 +546,7 @@
         'xcode_settings': {
           'ARCHS': ['armv7'],
           'CODE_SIGNING_REQUIRED': 'NO',
-          'CODE_SIGN_IDENTITY[sdk=iphoneos*]': '',
+          'CODE_SIGN_IDENTITY[sdk=iphoneos*]': 'iPhone Developer: Google Development (3F4Y5873JF)',
           'IPHONEOS_DEPLOYMENT_TARGET': '<(ios_sdk_version)',
           'SDKROOT': 'iphoneos',
           'TARGETED_DEVICE_FAMILY': '1,2',
diff --git a/gyp/common_variables.gypi b/gyp/common_variables.gypi
index 60dcccc..3cd3fc4 100644
--- a/gyp/common_variables.gypi
+++ b/gyp/common_variables.gypi
@@ -102,10 +102,7 @@
           'arm_version%': 7,
           'arm_neon%': 0, # neon asm files known not to work with the ios build
         }],
-        [ 'skia_os == "nacl"', {
-          'skia_egl%': 1,
-        }],
-        [ 'skia_os in ["android", "nacl"] and not skia_android_framework',
+        [ 'skia_os == "android" and not skia_android_framework',
           # skia_freetype_static - on OS variants that normally would
           #     dynamically link the system FreeType library, don't do
           #     that; instead statically link to the version in
@@ -118,17 +115,11 @@
         ],
       ],
 
-      # skia_giflib_static - on OS variants that normally would link giflib
-      #     with '-lgif' and include the headers from '/usr/include/gif_lib.h',
-      #     don't do that; instead compile and staticlly link the version of
-      #     giflib in third_party/externals/giflib.
-      'skia_giflib_static%': '0',
-
-
       # skia_no_fontconfig - On POSIX systems that would normally use the
       #     SkFontHost_fontconfig interface; use the SkFontHost_linux
       #     version instead.
       'skia_no_fontconfig%': '0',
+      'skia_embedded_fonts%': '0',
 
       'skia_sanitizer%': '',
       'skia_scalar%': 'float',
@@ -171,15 +162,6 @@
       }, {
         'skia_release_optimization_level%': '<(skia_default_gcc_optimization_level)',
       }],
-      [ 'skia_os == "android"', {
-          # skia_libpng_static - instead of linking libpng with '-lpng' and
-          #     including the headers from '/usr/include/png.h', compile and
-          #     statically link the version of libpng in
-          #     third_party/externals/libpng.
-          'skia_libpng_static%': '0',
-      }, {
-          'skia_libpng_static%': '1',
-      }],
       [ 'skia_sanitizer', {
         'skia_clang_build': 1,
         'skia_keep_frame_pointer': 1,
@@ -206,8 +188,8 @@
     'os_posix%': '<(os_posix)',
 
     'skia_freetype_static%': '<(skia_freetype_static)',
-    'skia_giflib_static%': '<(skia_giflib_static)',
     'skia_no_fontconfig%': '<(skia_no_fontconfig)',
+    'skia_embedded_fonts%': '<(skia_embedded_fonts)',
     'skia_sanitizer%': '<(skia_sanitizer)',
     'skia_scalar%': '<(skia_scalar)',
     'skia_mesa%': '<(skia_mesa)',
@@ -239,6 +221,14 @@
     'skia_moz2d%': 0,
     'skia_is_bot%': '<!(python -c "import os; print os.environ.get(\'CHROME_HEADLESS\', 0)")',
     'skia_egl%': '<(skia_egl)',
+    'skia_fast%': 0,
+    'skia_fast_flags': [
+        '-O3',                   # Even for Debug builds.
+        '-march=native',         # Use all features of and optimize for THIS machine.
+        '-fomit-frame-pointer',  # Sometimes an extra register is nice, and cuts a push/pop.
+        '-ffast-math',           # Optimize float math even when it breaks IEEE compliance.
+        #'-flto'                  # Enable link-time optimization.
+    ],
 
     # These are referenced by our .gypi files that list files (e.g. core.gypi)
     #
diff --git a/gyp/core.gyp b/gyp/core.gyp
index e65baff..ebf8786 100644
--- a/gyp/core.gyp
+++ b/gyp/core.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Core Skia library code.
 {
   'targets': [
diff --git a/gyp/core.gypi b/gyp/core.gypi
index 2e9fb1b..f6a2ce2 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Include this gypi to include all 'core' files
 # The parent gyp/gypi file must define
 #       'skia_src_path'     e.g. skia/trunk/src
@@ -14,6 +18,7 @@
         '<(skia_src_path)/core/SkAAClip.cpp',
         '<(skia_src_path)/core/SkAnnotation.cpp',
         '<(skia_src_path)/core/SkAdvancedTypefaceMetrics.cpp',
+        '<(skia_src_path)/core/SkAdvancedTypefaceMetrics.h',
         '<(skia_src_path)/core/SkAlphaRuns.cpp',
         '<(skia_src_path)/core/SkAntiRun.h',
         '<(skia_src_path)/core/SkBBHFactory.cpp',
@@ -123,6 +128,8 @@
         '<(skia_src_path)/core/SkMaskGamma.h',
         '<(skia_src_path)/core/SkMath.cpp',
         '<(skia_src_path)/core/SkMatrix.cpp',
+        '<(skia_src_path)/core/SkMatrixImageFilter.cpp',
+        '<(skia_src_path)/core/SkMatrixImageFilter.h',
         '<(skia_src_path)/core/SkMessageBus.h',
         '<(skia_src_path)/core/SkMetaData.cpp',
         '<(skia_src_path)/core/SkMipMap.cpp',
@@ -172,6 +179,7 @@
         '<(skia_src_path)/core/SkRRect.cpp',
         '<(skia_src_path)/core/SkRTree.h',
         '<(skia_src_path)/core/SkRTree.cpp',
+        '<(skia_src_path)/core/SkRWBuffer.cpp',
         '<(skia_src_path)/core/SkScalar.cpp',
         '<(skia_src_path)/core/SkScalerContext.cpp',
         '<(skia_src_path)/core/SkScalerContext.h',
@@ -201,6 +209,7 @@
         '<(skia_src_path)/core/SkTextBlob.cpp',
         '<(skia_src_path)/core/SkTextFormatParams.h',
         '<(skia_src_path)/core/SkTextMapStateProc.h',
+        '<(skia_src_path)/core/SkTime.cpp',
         '<(skia_src_path)/core/SkTDPQueue.h',
         '<(skia_src_path)/core/SkTLList.h',
         '<(skia_src_path)/core/SkTLS.cpp',
@@ -234,7 +243,6 @@
         '<(skia_src_path)/pipe/SkGPipeRead.cpp',
         '<(skia_src_path)/pipe/SkGPipeWrite.cpp',
 
-        '<(skia_include_path)/core/SkAdvancedTypefaceMetrics.h',
         '<(skia_include_path)/core/SkBBHFactory.h',
         '<(skia_include_path)/core/SkBitmap.h',
         '<(skia_include_path)/core/SkBitmapDevice.h',
@@ -329,21 +337,24 @@
         '<(skia_include_path)/pathops/SkPathOps.h',
 
         '<(skia_src_path)/pathops/SkAddIntersections.cpp',
-        '<(skia_src_path)/pathops/SkDCubicIntersection.cpp',
+        '<(skia_src_path)/pathops/SkDConicLineIntersection.cpp',
         '<(skia_src_path)/pathops/SkDCubicLineIntersection.cpp',
         '<(skia_src_path)/pathops/SkDCubicToQuads.cpp',
         '<(skia_src_path)/pathops/SkDLineIntersection.cpp',
-        '<(skia_src_path)/pathops/SkDQuadImplicit.cpp',
-        '<(skia_src_path)/pathops/SkDQuadIntersection.cpp',
         '<(skia_src_path)/pathops/SkDQuadLineIntersection.cpp',
         '<(skia_src_path)/pathops/SkIntersections.cpp',
         '<(skia_src_path)/pathops/SkOpAngle.cpp',
+        '<(skia_src_path)/pathops/SkOpBuilder.cpp',
+        '<(skia_src_path)/pathops/SkOpCoincidence.cpp',
         '<(skia_src_path)/pathops/SkOpContour.cpp',
+        '<(skia_src_path)/pathops/SkOpCubicHull.cpp',
         '<(skia_src_path)/pathops/SkOpEdgeBuilder.cpp',
         '<(skia_src_path)/pathops/SkOpSegment.cpp',
-        '<(skia_src_path)/pathops/SkPathOpsBounds.cpp',
+        '<(skia_src_path)/pathops/SkOpSpan.cpp',
         '<(skia_src_path)/pathops/SkPathOpsCommon.cpp',
+        '<(skia_src_path)/pathops/SkPathOpsConic.cpp',
         '<(skia_src_path)/pathops/SkPathOpsCubic.cpp',
+        '<(skia_src_path)/pathops/SkPathOpsCurve.cpp',
         '<(skia_src_path)/pathops/SkPathOpsDebug.cpp',
         '<(skia_src_path)/pathops/SkPathOpsLine.cpp',
         '<(skia_src_path)/pathops/SkPathOpsOp.cpp',
@@ -351,24 +362,27 @@
         '<(skia_src_path)/pathops/SkPathOpsQuad.cpp',
         '<(skia_src_path)/pathops/SkPathOpsRect.cpp',
         '<(skia_src_path)/pathops/SkPathOpsSimplify.cpp',
+        '<(skia_src_path)/pathops/SkPathOpsTSect.cpp',
         '<(skia_src_path)/pathops/SkPathOpsTightBounds.cpp',
-        '<(skia_src_path)/pathops/SkPathOpsTriangle.cpp',
         '<(skia_src_path)/pathops/SkPathOpsTypes.cpp',
+        '<(skia_src_path)/pathops/SkPathOpsWinding.cpp',
         '<(skia_src_path)/pathops/SkPathWriter.cpp',
-        '<(skia_src_path)/pathops/SkQuarticRoot.cpp',
         '<(skia_src_path)/pathops/SkReduceOrder.cpp',
+
         '<(skia_src_path)/pathops/SkAddIntersections.h',
-        '<(skia_src_path)/pathops/SkDQuadImplicit.h',
         '<(skia_src_path)/pathops/SkIntersectionHelper.h',
         '<(skia_src_path)/pathops/SkIntersections.h',
         '<(skia_src_path)/pathops/SkLineParameters.h',
         '<(skia_src_path)/pathops/SkOpAngle.h',
+        '<(skia_src_path)/pathops/SkOpCoincidence.h',
         '<(skia_src_path)/pathops/SkOpContour.h',
         '<(skia_src_path)/pathops/SkOpEdgeBuilder.h',
         '<(skia_src_path)/pathops/SkOpSegment.h',
         '<(skia_src_path)/pathops/SkOpSpan.h',
+        '<(skia_src_path)/pathops/SkOpTAllocator.h',
         '<(skia_src_path)/pathops/SkPathOpsBounds.h',
         '<(skia_src_path)/pathops/SkPathOpsCommon.h',
+        '<(skia_src_path)/pathops/SkPathOpsConic.h',
         '<(skia_src_path)/pathops/SkPathOpsCubic.h',
         '<(skia_src_path)/pathops/SkPathOpsCurve.h',
         '<(skia_src_path)/pathops/SkPathOpsDebug.h',
@@ -376,10 +390,9 @@
         '<(skia_src_path)/pathops/SkPathOpsPoint.h',
         '<(skia_src_path)/pathops/SkPathOpsQuad.h',
         '<(skia_src_path)/pathops/SkPathOpsRect.h',
-        '<(skia_src_path)/pathops/SkPathOpsTriangle.h',
+        '<(skia_src_path)/pathops/SkPathOpsTSect.h',
         '<(skia_src_path)/pathops/SkPathOpsTypes.h',
         '<(skia_src_path)/pathops/SkPathWriter.h',
-        '<(skia_src_path)/pathops/SkQuarticRoot.h',
         '<(skia_src_path)/pathops/SkReduceOrder.h',
     ],
 }
diff --git a/gyp/debugger.gyp b/gyp/debugger.gyp
index 05c0e14..e913390 100644
--- a/gyp/debugger.gyp
+++ b/gyp/debugger.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'variables': {
     'skia_warnings_as_errors': 0,
@@ -85,6 +89,8 @@
         '../src/ports',     # To pull SkFontDescriptor.h
         '../bench',
         '../tools',
+        '../debugger/QT',   # For all the QT UI Goodies
+        '<@(qt_includes)',
       ],
       'sources': [
         '../debugger/SkDebugger.cpp',
@@ -94,100 +100,75 @@
         '../src/utils/debugger/SkDrawCommand.cpp',
         '../src/utils/debugger/SkObjectParser.h',
         '../src/utils/debugger/SkObjectParser.cpp',
+        '../debugger/debuggermain.cpp',
+        '../debugger/QT/SkDebuggerGUI.cpp',
+        '../debugger/QT/SkDebuggerGUI.h',
+        '../debugger/QT/SkDrawCommandGeometryWidget.h',
+        '../debugger/QT/SkDrawCommandGeometryWidget.cpp',
+        '../debugger/QT/SkCanvasWidget.cpp',
+        '../debugger/QT/SkCanvasWidget.h',
+        '../debugger/QT/SkInspectorWidget.h',
+        '../debugger/QT/SkInspectorWidget.cpp',
+        '../debugger/QT/SkListWidget.h',
+        '../debugger/QT/SkListWidget.cpp',
+        '../debugger/QT/SkSettingsWidget.h',
+        '../debugger/QT/SkSettingsWidget.cpp',
+        '../debugger/QT/SkGLWidget.h',
+        '../debugger/QT/SkGLWidget.cpp',
+        '../debugger/QT/SkRasterWidget.h',
+        '../debugger/QT/SkRasterWidget.cpp',
+
+        # To update this file edit SkIcons.qrc and rerun rcc to generate cpp
+        '../debugger/QT/qrc_SkIcons.cpp',
+
+        # Generated MOC files
+        '<(moc_gen_dir)/moc_SkCanvasWidget.cpp',
+        '<(moc_gen_dir)/moc_SkDebuggerGUI.cpp',
+        '<(moc_gen_dir)/moc_SkDrawCommandGeometryWidget.cpp',
+        '<(moc_gen_dir)/moc_SkInspectorWidget.cpp',
+        '<(moc_gen_dir)/moc_SkSettingsWidget.cpp',
+        '<(moc_gen_dir)/moc_SkRasterWidget.cpp',
+        '<(moc_gen_dir)/moc_SkGLWidget.cpp',
       ],
       'dependencies': [
+        'debugger_qt_mocs',
         'skia_lib.gyp:skia_lib',
         'tools.gyp:picture_renderer',
         'tools.gyp:timer',
       ],
-      'conditions': [
-        [ 'skia_os == "nacl"', {
-          'include_dirs': [
-            '../src/utils',
-          ],
-          'sources': [
-            '../platform_tools/nacl/src/nacl_debugger.cpp',
-          ],
-        }, { # skia_os != "nacl"
-          'include_dirs': [
-            '../debugger/QT',   # For all the QT UI Goodies
-            '<@(qt_includes)',
-          ],
-          'sources': [
-            '../debugger/debuggermain.cpp',
-            '../debugger/QT/SkDebuggerGUI.cpp',
-            '../debugger/QT/SkDebuggerGUI.h',
-            '../debugger/QT/SkDrawCommandGeometryWidget.h',
-            '../debugger/QT/SkDrawCommandGeometryWidget.cpp',
-            '../debugger/QT/SkCanvasWidget.cpp',
-            '../debugger/QT/SkCanvasWidget.h',
-            '../debugger/QT/SkInspectorWidget.h',
-            '../debugger/QT/SkInspectorWidget.cpp',
-            '../debugger/QT/SkListWidget.h',
-            '../debugger/QT/SkListWidget.cpp',
-            '../debugger/QT/SkSettingsWidget.h',
-            '../debugger/QT/SkSettingsWidget.cpp',
-            '../debugger/QT/SkGLWidget.h',
-            '../debugger/QT/SkGLWidget.cpp',
-            '../debugger/QT/SkRasterWidget.h',
-            '../debugger/QT/SkRasterWidget.cpp',
-
-            # To update this file edit SkIcons.qrc and rerun rcc to generate cpp
-            '../debugger/QT/qrc_SkIcons.cpp',
-
-            # Generated MOC files
-            '<(moc_gen_dir)/moc_SkCanvasWidget.cpp',
-            '<(moc_gen_dir)/moc_SkDebuggerGUI.cpp',
-            '<(moc_gen_dir)/moc_SkDrawCommandGeometryWidget.cpp',
-            '<(moc_gen_dir)/moc_SkInspectorWidget.cpp',
-            '<(moc_gen_dir)/moc_SkSettingsWidget.cpp',
-            '<(moc_gen_dir)/moc_SkRasterWidget.cpp',
-            '<(moc_gen_dir)/moc_SkGLWidget.cpp',
-          ],
-          'cflags': [
-            # Clang gets confused by QWeakPointer, see http://llvm.org/bugs/show_bug.cgi?id=13127
-            '-Wno-uninitialized',
-          ],
-          'dependencies': [
-            'debugger_qt_mocs',
-          ],
-          'link_settings': {
-            'libraries': [
-              '<@(qt_libs)',
-            ],
-          },
-        }],
+      'cflags': [
+        # Clang gets confused by QWeakPointer, see http://llvm.org/bugs/show_bug.cgi?id=13127
+        '-Wno-uninitialized',
       ],
+      'link_settings': {
+        'libraries': [
+          '<@(qt_libs)',
+        ],
+      },
     },
-  ],
-  'conditions': [
-    [ 'skia_os != "nacl"', {
-      'targets': [
+    {
+      'target_name': 'debugger_qt_mocs',
+      'type': 'none',
+      'sources': [
+        '<(moc_src_dir)/SkCanvasWidget.h',
+        '<(moc_src_dir)/SkDebuggerGUI.h',
+        '<(moc_src_dir)/SkDrawCommandGeometryWidget.h',
+        '<(moc_src_dir)/SkInspectorWidget.h',
+        '<(moc_src_dir)/SkSettingsWidget.h',
+        '<(moc_src_dir)/SkRasterWidget.h',
+        '<(moc_src_dir)/SkGLWidget.h',
+      ],
+      'rules': [
         {
-          'target_name': 'debugger_qt_mocs',
-          'type': 'none',
-          'sources': [
-            '<(moc_src_dir)/SkCanvasWidget.h',
-            '<(moc_src_dir)/SkDebuggerGUI.h',
-            '<(moc_src_dir)/SkDrawCommandGeometryWidget.h',
-            '<(moc_src_dir)/SkInspectorWidget.h',
-            '<(moc_src_dir)/SkSettingsWidget.h',
-            '<(moc_src_dir)/SkRasterWidget.h',
-            '<(moc_src_dir)/SkGLWidget.h',
-          ],
-          'rules': [
-            {
-              'rule_name': 'generate_moc',
-              'extension': 'h',
-              'outputs': [ '<(moc_gen_dir)/moc_<(RULE_INPUT_ROOT).cpp' ],
-              'action': [ '<(qt_moc)', '-DSK_SUPPORT_GPU=<(skia_gpu)',
-                          '<(RULE_INPUT_PATH)',
-                          '-o', '<(moc_gen_dir)/moc_<(RULE_INPUT_ROOT).cpp' ],
-              'message': 'Generating <(RULE_INPUT_ROOT).cpp.',
-            },
-          ],
+          'rule_name': 'generate_moc',
+          'extension': 'h',
+          'outputs': [ '<(moc_gen_dir)/moc_<(RULE_INPUT_ROOT).cpp' ],
+          'action': [ '<(qt_moc)', '-DSK_SUPPORT_GPU=<(skia_gpu)',
+                      '<(RULE_INPUT_PATH)',
+                      '-o', '<(moc_gen_dir)/moc_<(RULE_INPUT_ROOT).cpp' ],
+          'message': 'Generating <(RULE_INPUT_ROOT).cpp.',
         },
       ],
-    }],
+    },
   ],
 }
diff --git a/gyp/dm.gyp b/gyp/dm.gyp
index dc75729..261163f 100644
--- a/gyp/dm.gyp
+++ b/gyp/dm.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP for "dm" (Diamond Master, a.k.a Dungeon master, a.k.a GM 2).
 {
     'includes': [ 'apptype_console.gypi' ],
@@ -24,6 +28,9 @@
               'sources': [
                 '../dm/DMSrcSinkAndroid.cpp',
               ],
+              'dependencies': [
+                'utils.gyp:android_utils',
+              ],
           }],
         ],
     }]
diff --git a/gyp/dm.gypi b/gyp/dm.gypi
index 71d0733..03a5ad3 100644
--- a/gyp/dm.gypi
+++ b/gyp/dm.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP for "dm" (Diamond Master, a.k.a Dungeon master, a.k.a GM 2).
 {
   'include_dirs': [
diff --git a/gyp/effects.gyp b/gyp/effects.gyp
index 63b8636..154e567 100644
--- a/gyp/effects.gyp
+++ b/gyp/effects.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Gyp file for effects
 {
   'targets': [
@@ -9,7 +13,7 @@
       'dependencies': [
         'core.gyp:*',
         'images.gyp:*',
-        'utils.gyp:*',
+        'utils.gyp:utils',
       ],
       'includes': [
         'effects.gypi',
diff --git a/gyp/effects.gypi b/gyp/effects.gypi
index 24a79be..8b90a45 100644
--- a/gyp/effects.gypi
+++ b/gyp/effects.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Include this gypi to include all 'effects' files
 # The parent gyp/gypi file must define:
 #       'skia_src_path'     e.g. skia/trunk/src
@@ -56,7 +60,6 @@
     '<(skia_src_path)/effects/SkTableMaskFilter.cpp',
     '<(skia_src_path)/effects/SkTestImageFilters.cpp',
     '<(skia_src_path)/effects/SkTileImageFilter.cpp',
-    '<(skia_src_path)/effects/SkMatrixImageFilter.cpp',
     '<(skia_src_path)/effects/SkTransparentShader.cpp',
     '<(skia_src_path)/effects/SkXfermodeImageFilter.cpp',
 
@@ -71,8 +74,6 @@
     '<(skia_src_path)/effects/gradients/SkLinearGradient.h',
     '<(skia_src_path)/effects/gradients/SkRadialGradient.cpp',
     '<(skia_src_path)/effects/gradients/SkRadialGradient.h',
-    '<(skia_src_path)/effects/gradients/SkTwoPointRadialGradient.cpp',
-    '<(skia_src_path)/effects/gradients/SkTwoPointRadialGradient.h',
     '<(skia_src_path)/effects/gradients/SkTwoPointConicalGradient.cpp',
     '<(skia_src_path)/effects/gradients/SkTwoPointConicalGradient.h',
     '<(skia_src_path)/effects/gradients/SkTwoPointConicalGradient_gpu.cpp',
diff --git a/gyp/etc1.gyp b/gyp/etc1.gyp
index 7650d86..f2eb69d 100644
--- a/gyp/etc1.gyp
+++ b/gyp/etc1.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'variables': {
     'skia_warnings_as_errors': 0,
diff --git a/gyp/everything.gyp b/gyp/everything.gyp
index f66b67e..76cb919 100644
--- a/gyp/everything.gyp
+++ b/gyp/everything.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Build EVERYTHING provided by Skia.
 # (Start with the "most" target, and then add targets that we intentionally
 # left out of "most".  See most.gyp for an explanation of which targets are
diff --git a/gyp/example.gyp b/gyp/example.gyp
index 34a7982..9582b4d 100644
--- a/gyp/example.gyp
+++ b/gyp/example.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP file to build hello world example.
 {
   'targets': [
diff --git a/gyp/experimental.gyp b/gyp/experimental.gyp
index ac0d85a..8ea6f3d 100644
--- a/gyp/experimental.gyp
+++ b/gyp/experimental.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP file to build experimental directory.
 {
   'targets': [
diff --git a/gyp/flags.gyp b/gyp/flags.gyp
index 50c1a80..6aae30b 100644
--- a/gyp/flags.gyp
+++ b/gyp/flags.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP file to build flag parser
 #
 {
diff --git a/gyp/freetype.gyp b/gyp/freetype.gyp
index ed6e97b..85018a4 100644
--- a/gyp/freetype.gyp
+++ b/gyp/freetype.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Target for building freetype.
 {
   'targets': [
@@ -14,11 +18,11 @@
               'freetype_static'
             ],
             'conditions': [
-              [ 'skia_os in ["android", "nacl"]',
+              [ 'skia_os == "android"',
                 {
                   'direct_dependent_settings': {
                     'defines': [
-                      # Both Android and NaCl provide at least FreeType 2.4.0
+                      # Android provides at least FreeType 2.4.0
                       'SK_FONTHOST_FREETYPE_RUNTIME_VERSION=0x020400',
                       'SK_CAN_USE_DLOPEN=0',
                     ],
diff --git a/gyp/freetype.gypi b/gyp/freetype.gypi
index ace5d04..9dc80b0 100644
--- a/gyp/freetype.gypi
+++ b/gyp/freetype.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'sources': [
     # base components (required)
diff --git a/gyp/giflib.gyp b/gyp/giflib.gyp
index 3a2c215..e9d9905 100644
--- a/gyp/giflib.gyp
+++ b/gyp/giflib.gyp
@@ -11,12 +11,9 @@
     {
       'target_name': 'giflib',
       'conditions': [
-        [ 'skia_giflib_static',
+        [ 'skia_android_framework == 0',
           {
             'type': 'static_library',
-            'defines': [
-              'HAVE_CONFIG_H',
-            ],
             'include_dirs': [
               '../third_party/externals/giflib',
             ],
@@ -30,22 +27,44 @@
             'cflags': [
               '-w',
             ],
+            'xcode_settings': {
+              'WARNING_CFLAGS': [
+                '-w'
+              ],
+            },
+            'msvs_settings': {
+              'VCCLCompilerTool': {
+                'AdditionalOptions': [
+                  '/w',
+                ],
+              },
+            },
             'sources': [
               '../third_party/externals/giflib/dgif_lib.c',
               '../third_party/externals/giflib/gifalloc.c',
               '../third_party/externals/giflib/gif_err.c',
             ],
-          }, {  # not skia_giflib_static
+            'conditions' : [
+              [ 'skia_os == "win"', {
+                  'include_dirs': [
+                    # Used to include a dummy unistd.h file for windows
+                    '../third_party/giflib',
+                  ],
+                },
+              ],
+            ],
+          }, { # skia_android_framework
             'type': 'none',
             'direct_dependent_settings': {
-              'link_settings': {
-                'libraries': [
-                  '-lgif',
-                ],
-              },
-            },
+              'libraries' : [
+                'libgif.a',
+              ],
+              'include_dirs': [
+                'external/giflib',
+              ]
+            }
           }
-        ],
+        ]
       ]
     }
   ]
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 6ebfe30..79333bd 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # include this gypi to include all the golden master slides.
 {
   'include_dirs': [
@@ -16,10 +20,13 @@
         '../gm/aaclip.cpp',
         '../gm/aarectmodes.cpp',
         '../gm/addarc.cpp',
+        '../gm/all_bitmap_configs.cpp',
         '../gm/alphagradients.cpp',
+        '../gm/anisotropic.cpp',
         '../gm/arcofzorro.cpp',
         '../gm/arithmode.cpp',
         '../gm/astcbitmap.cpp',
+        '../gm/badpaint.cpp',
         '../gm/beziereffects.cpp',
         '../gm/beziers.cpp',
         '../gm/bigblurs.cpp',
@@ -33,12 +40,15 @@
         '../gm/bitmapscroll.cpp',
         '../gm/bitmapshader.cpp',
         '../gm/bitmapsource.cpp',
+        '../gm/bitmapsource2.cpp',
         '../gm/bleed.cpp',
+        '../gm/blend.cpp',
         '../gm/blurcircles.cpp',
         '../gm/blurs.cpp',
         '../gm/blurquickreject.cpp',
         '../gm/blurrect.cpp',
         '../gm/blurroundrect.cpp',
+        '../gm/bmpfilterqualityrepeat.cpp',
         '../gm/circles.cpp',
         '../gm/circularclips.cpp',
         '../gm/clipdrawdraw.cpp',
@@ -60,6 +70,8 @@
         '../gm/complexclip3.cpp',
         '../gm/composeshader.cpp',
         '../gm/conicpaths.cpp',
+        '../gm/constcolorprocessor.cpp',
+        '../gm/convex_all_line_paths.cpp',
         '../gm/convexpaths.cpp',
         '../gm/convexpolyclip.cpp',
         '../gm/convexpolyeffect.cpp',
@@ -86,6 +98,7 @@
         '../gm/extractbitmap.cpp',
         '../gm/emboss.cpp',
         '../gm/emptypath.cpp',
+        '../gm/fadefilter.cpp',
         '../gm/fatpathfill.cpp',
         '../gm/factory.cpp',
         '../gm/filltypes.cpp',
@@ -122,19 +135,23 @@
         '../gm/lighting.cpp',
         '../gm/lumafilter.cpp',
         '../gm/image.cpp',
+        '../gm/imagefilters.cpp',
         '../gm/imagefiltersbase.cpp',
         '../gm/imagefiltersclipped.cpp',
         '../gm/imagefilterscropped.cpp',
         '../gm/imagefilterscropexpand.cpp',
         '../gm/imagefiltersgraph.cpp',
         '../gm/imagefiltersscaled.cpp',
+        '../gm/imagefilterstransformed.cpp',
         '../gm/internal_links.cpp',
+        '../gm/largeglyphblur.cpp',
         '../gm/lcdtext.cpp',
         '../gm/linepaths.cpp',
         '../gm/matrixconvolution.cpp',
         '../gm/matriximagefilter.cpp',
         '../gm/megalooper.cpp',
         '../gm/mixedxfermodes.cpp',
+        '../gm/mixedtextblobs.cpp',
         '../gm/mipmap.cpp',
         '../gm/modecolorfilters.cpp',
         '../gm/morphology.cpp',
@@ -158,6 +175,7 @@
         '../gm/pictureimagefilter.cpp',
         '../gm/pictureshader.cpp',
         '../gm/pictureshadertile.cpp',
+        '../gm/pixelsnap.cpp',
         '../gm/points.cpp',
         '../gm/poly2poly.cpp',
         '../gm/polygons.cpp',
@@ -199,7 +217,11 @@
         '../gm/variedtext.cpp',
         '../gm/tallstretchedbitmaps.cpp',
         '../gm/textblob.cpp',
+        '../gm/textbloblooper.cpp',
+        '../gm/textblobcolortrans.cpp',
+        '../gm/textblobgeometrychange.cpp',
         '../gm/textblobshader.cpp',
+        '../gm/textblobtransforms.cpp',
         '../gm/texturedomaineffect.cpp',
         '../gm/thinrects.cpp',
         '../gm/thinstrokedrects.cpp',
@@ -209,7 +231,6 @@
         '../gm/tilemodes_scaled.cpp',
         '../gm/tinybitmap.cpp',
         '../gm/transparency.cpp',
-        '../gm/twopointradial.cpp',
         '../gm/typeface.cpp',
         '../gm/vertices.cpp',
         '../gm/verttext.cpp',
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index c69a486..7d3dad5 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP for building gpu
 {
   'target_defaults': {
@@ -22,11 +26,7 @@
         'sources/': [ ['exclude', '_android.(h|cpp)$'],
         ],
       }],
-      ['skia_os != "nacl"', {
-        'sources/': [ ['exclude', '_nacl.(h|cpp)$'],
-        ],
-      }],
-      ['skia_os == "nacl" or skia_egl == 0', {
+      ['skia_egl == 0', {
         'sources/': [ ['exclude', '_egl.(h|cpp)$'],
         ],
       }],
@@ -85,7 +85,7 @@
       'standalone_static_library': 1,
       'dependencies': [
         'core.gyp:*',
-        'utils.gyp:*',
+        'utils.gyp:utils',
         'etc1.gyp:libetc1',
         'ktx.gyp:libSkKTX',
       ],
@@ -166,13 +166,6 @@
             ],
           },
         }],
-        [ 'skia_os == "nacl"', {
-          'link_settings': {
-            'libraries': [
-              '-lppapi_gles2',
-            ],
-          },
-        }],
         [ 'skia_egl == 1', {
           'defines': [
             'SK_EGL=1',
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 9a16d36..f54c9da 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Include this gypi to include all 'gpu' files
 # The parent gyp/gypi file must define
 #       'skia_src_path'     e.g. skia/trunk/src
@@ -29,11 +33,14 @@
       '<(skia_include_path)/gpu/GrSurface.h',
       '<(skia_include_path)/gpu/GrShaderVar.h',
       '<(skia_include_path)/gpu/GrTexture.h',
+      '<(skia_include_path)/gpu/GrTextureProvider.h',
       '<(skia_include_path)/gpu/GrTextureAccess.h',
+      '<(skia_include_path)/gpu/GrTestUtils.h',
       '<(skia_include_path)/gpu/GrTypes.h',
       '<(skia_include_path)/gpu/GrUserConfig.h',
       '<(skia_include_path)/gpu/GrXferProcessor.h',
 
+      '<(skia_include_path)/gpu/effects/GrConstColorProcessor.h',
       '<(skia_include_path)/gpu/effects/GrCoverageSetOpXP.h',
       '<(skia_include_path)/gpu/effects/GrCustomXfermode.h',
       '<(skia_include_path)/gpu/effects/GrPorterDuffXferProcessor.h',
@@ -48,20 +55,29 @@
       '<(skia_src_path)/gpu/GrAAHairLinePathRenderer.h',
       '<(skia_src_path)/gpu/GrAAConvexPathRenderer.cpp',
       '<(skia_src_path)/gpu/GrAAConvexPathRenderer.h',
+      '<(skia_src_path)/gpu/GrAAConvexTessellator.cpp',
+      '<(skia_src_path)/gpu/GrAAConvexTessellator.h',
       '<(skia_src_path)/gpu/GrAADistanceFieldPathRenderer.cpp',
       '<(skia_src_path)/gpu/GrAADistanceFieldPathRenderer.h',
       '<(skia_src_path)/gpu/GrAARectRenderer.cpp',
       '<(skia_src_path)/gpu/GrAARectRenderer.h',
       '<(skia_src_path)/gpu/GrAddPathRenderers_default.cpp',
+      '<(skia_src_path)/gpu/GrAutoLocaleSetter.h',
       '<(skia_src_path)/gpu/GrAllocator.h',
       '<(skia_src_path)/gpu/GrAtlas.cpp',
       '<(skia_src_path)/gpu/GrAtlas.h',
+      '<(skia_src_path)/gpu/GrAtlasTextContext.cpp',
+      '<(skia_src_path)/gpu/GrAtlasTextContext.h',
       '<(skia_src_path)/gpu/GrBatch.cpp',
       '<(skia_src_path)/gpu/GrBatch.h',
+      '<(skia_src_path)/gpu/GrBatchAtlas.cpp',
+      '<(skia_src_path)/gpu/GrBatchAtlas.h',
+      '<(skia_src_path)/gpu/GrBatchFontCache.cpp',
+      '<(skia_src_path)/gpu/GrBatchFontCache.h',
       '<(skia_src_path)/gpu/GrBatchTarget.cpp',
       '<(skia_src_path)/gpu/GrBatchTarget.h',
-      '<(skia_src_path)/gpu/GrBitmapTextContext.cpp',
-      '<(skia_src_path)/gpu/GrBitmapTextContext.h',
+      '<(skia_src_path)/gpu/GrBatchTest.cpp',
+      '<(skia_src_path)/gpu/GrBatchTest.h',
       '<(skia_src_path)/gpu/GrBlend.cpp',
       '<(skia_src_path)/gpu/GrBlend.h',
       '<(skia_src_path)/gpu/GrBufferAllocPool.cpp',
@@ -72,24 +88,23 @@
       '<(skia_src_path)/gpu/GrClipMaskManager.h',
       '<(skia_src_path)/gpu/GrClipMaskManager.cpp',
       '<(skia_src_path)/gpu/GrContext.cpp',
+      '<(skia_src_path)/gpu/GrCommandBuilder.h',
+      '<(skia_src_path)/gpu/GrCommandBuilder.cpp',
       '<(skia_src_path)/gpu/GrCoordTransform.cpp',
+      '<(skia_src_path)/gpu/GrDashLinePathRenderer.cpp',
+      '<(skia_src_path)/gpu/GrDashLinePathRenderer.h',
       '<(skia_src_path)/gpu/GrDefaultGeoProcFactory.cpp',
       '<(skia_src_path)/gpu/GrDefaultGeoProcFactory.h',
       '<(skia_src_path)/gpu/GrDefaultPathRenderer.cpp',
       '<(skia_src_path)/gpu/GrDefaultPathRenderer.h',
-      '<(skia_src_path)/gpu/GrDistanceFieldTextContext.h',
-      '<(skia_src_path)/gpu/GrDistanceFieldTextContext.cpp',
       '<(skia_src_path)/gpu/GrDrawTarget.cpp',
       '<(skia_src_path)/gpu/GrDrawTarget.h',
       '<(skia_src_path)/gpu/GrDrawTargetCaps.h',
-      '<(skia_src_path)/gpu/GrFlushToGpuDrawTarget.cpp',
-      '<(skia_src_path)/gpu/GrFlushToGpuDrawTarget.h',
       '<(skia_src_path)/gpu/GrFontAtlasSizes.h',
       '<(skia_src_path)/gpu/GrFontScaler.cpp',
       '<(skia_src_path)/gpu/GrFontScaler.h',
       '<(skia_src_path)/gpu/GrGeometryBuffer.h',
       '<(skia_src_path)/gpu/GrGeometryProcessor.h',
-      '<(skia_src_path)/gpu/GrGeometryProcessor.cpp',
       '<(skia_src_path)/gpu/GrGlyph.h',
       '<(skia_src_path)/gpu/GrGpu.cpp',
       '<(skia_src_path)/gpu/GrGpu.h',
@@ -97,8 +112,11 @@
       '<(skia_src_path)/gpu/GrGpuResourcePriv.h',
       '<(skia_src_path)/gpu/GrGpuResource.cpp',
       '<(skia_src_path)/gpu/GrGpuFactory.cpp',
+      '<(skia_src_path)/gpu/GrGpuFactory.h',
       '<(skia_src_path)/gpu/GrIndexBuffer.h',
       '<(skia_src_path)/gpu/GrInvariantOutput.cpp',
+      '<(skia_src_path)/gpu/GrInOrderCommandBuilder.cpp',
+      '<(skia_src_path)/gpu/GrInOrderCommandBuilder.h',
       '<(skia_src_path)/gpu/GrInOrderDrawBuffer.cpp',
       '<(skia_src_path)/gpu/GrInOrderDrawBuffer.h',
       '<(skia_src_path)/gpu/GrLayerCache.cpp',
@@ -147,21 +165,28 @@
       '<(skia_src_path)/gpu/GrRectanizer_pow2.h',
       '<(skia_src_path)/gpu/GrRectanizer_skyline.cpp',
       '<(skia_src_path)/gpu/GrRectanizer_skyline.h',
+      '<(skia_src_path)/gpu/GrRectBatch.h',
+      '<(skia_src_path)/gpu/GrRectBatch.cpp',
       '<(skia_src_path)/gpu/GrRedBlackTree.h',
       '<(skia_src_path)/gpu/GrRenderTarget.cpp',
       '<(skia_src_path)/gpu/GrRenderTargetPriv.h',
       '<(skia_src_path)/gpu/GrReducedClip.cpp',
       '<(skia_src_path)/gpu/GrReducedClip.h',
+      '<(skia_src_path)/gpu/GrReorderCommandBuilder.h',
+      '<(skia_src_path)/gpu/GrReorderCommandBuilder.cpp',
       '<(skia_src_path)/gpu/GrResourceCache.cpp',
       '<(skia_src_path)/gpu/GrResourceCache.h',
+      '<(skia_src_path)/gpu/GrResourceProvider.cpp',
+      '<(skia_src_path)/gpu/GrResourceProvider.h',
       '<(skia_src_path)/gpu/GrStencil.cpp',
       '<(skia_src_path)/gpu/GrStencil.h',
       '<(skia_src_path)/gpu/GrStencilAndCoverPathRenderer.cpp',
       '<(skia_src_path)/gpu/GrStencilAndCoverPathRenderer.h',
       '<(skia_src_path)/gpu/GrStencilAndCoverTextContext.cpp',
       '<(skia_src_path)/gpu/GrStencilAndCoverTextContext.h',
-      '<(skia_src_path)/gpu/GrStencilBuffer.cpp',
-      '<(skia_src_path)/gpu/GrStencilBuffer.h',
+      '<(skia_src_path)/gpu/GrStencilAttachment.cpp',
+      '<(skia_src_path)/gpu/GrStencilAttachment.h',
+      '<(skia_src_path)/gpu/GrStrokeInfo.cpp',
       '<(skia_src_path)/gpu/GrStrokeInfo.h',
       '<(skia_src_path)/gpu/GrTargetCommands.cpp',
       '<(skia_src_path)/gpu/GrTargetCommands.h',
@@ -171,6 +196,7 @@
       '<(skia_src_path)/gpu/GrTracing.h',
       '<(skia_src_path)/gpu/GrTessellatingPathRenderer.cpp',
       '<(skia_src_path)/gpu/GrTessellatingPathRenderer.h',
+      '<(skia_src_path)/gpu/GrTestUtils.cpp',
       '<(skia_src_path)/gpu/GrSWMaskHelper.cpp',
       '<(skia_src_path)/gpu/GrSWMaskHelper.h',
       '<(skia_src_path)/gpu/GrSoftwarePathRenderer.cpp',
@@ -178,20 +204,23 @@
       '<(skia_src_path)/gpu/GrSurfacePriv.h',
       '<(skia_src_path)/gpu/GrSurface.cpp',
       '<(skia_src_path)/gpu/GrTemplates.h',
+      '<(skia_src_path)/gpu/GrTextBlobCache.cpp',
+      '<(skia_src_path)/gpu/GrTextBlobCache.h',
       '<(skia_src_path)/gpu/GrTextContext.cpp',
       '<(skia_src_path)/gpu/GrTextContext.h',
-      '<(skia_src_path)/gpu/GrFontCache.cpp',
-      '<(skia_src_path)/gpu/GrFontCache.h',
       '<(skia_src_path)/gpu/GrTexture.cpp',
+      '<(skia_src_path)/gpu/GrTextureProvider.cpp',
       '<(skia_src_path)/gpu/GrTexturePriv.h',
       '<(skia_src_path)/gpu/GrTextureAccess.cpp',
       '<(skia_src_path)/gpu/GrTRecorder.h',
       '<(skia_src_path)/gpu/GrVertexBuffer.h',
+      '<(skia_src_path)/gpu/GrVertices.h',
       '<(skia_src_path)/gpu/GrXferProcessor.cpp',
 
       '<(skia_src_path)/gpu/effects/Gr1DKernelEffect.h',
       '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.h',
+      '<(skia_src_path)/gpu/effects/GrConstColorProcessor.cpp',
       '<(skia_src_path)/gpu/effects/GrCoverageSetOpXP.cpp',
       '<(skia_src_path)/gpu/effects/GrCustomXfermode.cpp',
       '<(skia_src_path)/gpu/effects/GrCustomXfermodePriv.h',
@@ -209,8 +238,8 @@
       '<(skia_src_path)/gpu/effects/GrDashingEffect.h',
       '<(skia_src_path)/gpu/effects/GrDisableColorXP.cpp',
       '<(skia_src_path)/gpu/effects/GrDisableColorXP.h',
-      '<(skia_src_path)/gpu/effects/GrDistanceFieldTextureEffect.cpp',
-      '<(skia_src_path)/gpu/effects/GrDistanceFieldTextureEffect.h',
+      '<(skia_src_path)/gpu/effects/GrDistanceFieldGeoProc.cpp',
+      '<(skia_src_path)/gpu/effects/GrDistanceFieldGeoProc.h',
       '<(skia_src_path)/gpu/effects/GrDitherEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrDitherEffect.h',
       '<(skia_src_path)/gpu/effects/GrMatrixConvolutionEffect.cpp',
@@ -279,8 +308,8 @@
       '<(skia_src_path)/gpu/gl/GrGLSL.cpp',
       '<(skia_src_path)/gpu/gl/GrGLSL.h',
       '<(skia_src_path)/gpu/gl/GrGLSL_impl.h',
-      '<(skia_src_path)/gpu/gl/GrGLStencilBuffer.cpp',
-      '<(skia_src_path)/gpu/gl/GrGLStencilBuffer.h',
+      '<(skia_src_path)/gpu/gl/GrGLStencilAttachment.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLStencilAttachment.h',
       '<(skia_src_path)/gpu/gl/GrGLTexture.cpp',
       '<(skia_src_path)/gpu/gl/GrGLTexture.h',
       '<(skia_src_path)/gpu/gl/GrGLTextureRenderTarget.h',
@@ -340,7 +369,6 @@
 
       # Sk files
       '<(skia_src_path)/gpu/gl/mac/SkCreatePlatformGLContext_mac.cpp',
-      '<(skia_src_path)/gpu/gl/nacl/SkCreatePlatformGLContext_nacl.cpp',
       '<(skia_src_path)/gpu/gl/win/SkCreatePlatformGLContext_win.cpp',
       '<(skia_src_path)/gpu/gl/glx/SkCreatePlatformGLContext_glx.cpp',
       '<(skia_src_path)/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp',
diff --git a/gyp/gputest.gyp b/gyp/gputest.gyp
index 5e8cca2..868bc34 100644
--- a/gyp/gputest.gyp
+++ b/gyp/gputest.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'targets': [
     {
diff --git a/gyp/iOSShell.gyp b/gyp/iOSShell.gyp
index 77ff8d1..bc7a2fb 100644
--- a/gyp/iOSShell.gyp
+++ b/gyp/iOSShell.gyp
@@ -1,3 +1,7 @@
+  # Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 #
 {
   'conditions' : [
@@ -18,11 +22,13 @@
             'xml.gyp:xml',
           ],
           'sources': [
+            '../bench/CodecBench.cpp',
             '../bench/DecodingBench.cpp',
             '../bench/DecodingSubsetBench.cpp',
             '../bench/GMBench.cpp',
             '../bench/RecordingBench.cpp',
             '../bench/SKPBench.cpp',
+            '../bench/SKPAnimationBench.cpp',
             '../bench/nanobench.cpp',
             '../tests/skia_test.cpp',
             '../tools/iOSShell.cpp',
diff --git a/gyp/images.gyp b/gyp/images.gyp
index 75eb76b..d40626d 100644
--- a/gyp/images.gyp
+++ b/gyp/images.gyp
@@ -1,3 +1,8 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
 # GYP file for images project.
 {
   'targets': [
@@ -8,6 +13,7 @@
       'standalone_static_library': 1,
       'dependencies': [
         'core.gyp:*',
+        'giflib.gyp:giflib',
         'libjpeg.gyp:*',
         'etc1.gyp:libetc1',
         'ktx.gyp:libSkKTX',
@@ -83,6 +89,9 @@
             '../src/images/SkImageDecoder_libpng.cpp',
             '../src/images/SkMovie_gif.cpp',
           ],
+          'dependencies!': [
+            'giflib.gyp:giflib'
+          ],
           'link_settings': {
             'libraries': [
               '-lwindowscodecs.lib',
@@ -108,29 +117,17 @@
         [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
           'export_dependent_settings': [
             'libpng.gyp:libpng',
-            'giflib.gyp:giflib'
           ],
           'dependencies': [
             'libpng.gyp:libpng',
-            'giflib.gyp:giflib'
           ],
-          # end libpng/libgif stuff
-        }],
-        # FIXME: NaCl should be just like linux, etc, above, but it currently is separated out
-        # to remove gif. Once gif is supported by naclports, this can be merged into the above
-        # condition.
-        [ 'skia_os == "nacl"', {
-          'sources!': [
-            '../src/images/SkImageDecoder_libgif.cpp',
-            '../src/images/SkMovie_gif.cpp',
-          ],
+          # end libpng stuff
         }],
         [ 'skia_os == "android"', {
           'include_dirs': [
              '../src/utils',
           ],
           'dependencies': [
-             'android_deps.gyp:gif',
              'android_deps.gyp:png',
           ],
           'conditions': [
@@ -152,8 +149,7 @@
         }],
         [ 'skia_os == "chromeos"', {
           'dependencies': [
-             'chromeos_deps.gyp:gif',
-             'libpng.gyp:libpng',
+            'libpng.gyp:libpng',
           ],
         }],
         [ 'skia_os == "ios"', {
diff --git a/gyp/ktx.gyp b/gyp/ktx.gyp
index dda353d..1ee0349 100644
--- a/gyp/ktx.gyp
+++ b/gyp/ktx.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'variables': {
     'skia_warnings_as_errors': 0,
diff --git a/gyp/libpng.gyp b/gyp/libpng.gyp
index 1ca6eef..d918f2d 100644
--- a/gyp/libpng.gyp
+++ b/gyp/libpng.gyp
@@ -10,91 +10,109 @@
   'targets': [
     {
       'target_name': 'libpng',
+      'type': 'none',
       'conditions': [
-        [ 'skia_libpng_static',
+        [ 'skia_os == "android"',
           {
-            'type': 'static_library',
-            'include_dirs': [
-              '../third_party/externals/libpng',
-              # Needed for generated pnglibconf.h
-              '../third_party/libpng',
-            ],
             'dependencies': [
-              'zlib.gyp:zlib',
+              'android_deps.gyp:png',
             ],
             'export_dependent_settings': [
-              'zlib.gyp:zlib',
+              'android_deps.gyp:png',
             ],
-            'direct_dependent_settings': {
-              'include_dirs': [
-                '../third_party/externals/libpng',
-                # Needed for generated pnglibconf.h
-                '../third_party/libpng',
-              ],
-            },
-            'cflags': [
-              '-w',
-              '-fvisibility=hidden',
+          }, {  # skia_os != "android"
+            'dependencies': [
+              'libpng.gyp:libpng_static',
             ],
-            'conditions': [
-              ['not arm_neon', {
-                'defines': [
-                    # FIXME: Why is this needed? Without it, pngpriv.h sets it
-                    # to 2 if __ARM_NEON is defined, but shouldn't __ARM_NEON
-                    # not be defined since arm_neon is 0?
-                    'PNG_ARM_NEON_OPT=0',
-                ],
-              }],
+            'export_dependent_settings': [
+              'libpng.gyp:libpng_static',
             ],
-            'sources': [
-              '../third_party/externals/libpng/png.c',
-              '../third_party/externals/libpng/pngerror.c',
-              '../third_party/externals/libpng/pngget.c',
-              '../third_party/externals/libpng/pngmem.c',
-              '../third_party/externals/libpng/pngpread.c',
-              '../third_party/externals/libpng/pngread.c',
-              '../third_party/externals/libpng/pngrio.c',
-              '../third_party/externals/libpng/pngrtran.c',
-              '../third_party/externals/libpng/pngrutil.c',
-              '../third_party/externals/libpng/pngset.c',
-              '../third_party/externals/libpng/pngtrans.c',
-              '../third_party/externals/libpng/pngwio.c',
-              '../third_party/externals/libpng/pngwrite.c',
-              '../third_party/externals/libpng/pngwtran.c',
-              '../third_party/externals/libpng/pngwutil.c',
-            ],
-          }, {  # not skia_libpng_static
-            'type': 'none',
-            'conditions': [
-              [ 'skia_os == "android"',
-                {
-                  # TODO(halcanary): merge all png targets into this file.
-                  'dependencies': [
-                    'android_deps.gyp:png',
-                  ],
-                  'export_dependent_settings': [
-                    'android_deps.gyp:png',
-                  ],
-                }, {  # skia_os != "android"
-                  'dependencies': [
-                    'zlib.gyp:zlib',
-                    ],
-                  'export_dependent_settings': [
-                    'zlib.gyp:zlib',
-                    ],
-                  'direct_dependent_settings': {
-                    'link_settings': {
-                      'libraries': [
-                        '-lpng',
-                      ],
-                    },
-                  },
-                }
-              ]
-            ]
           }
         ]
+      ]
+    },
+    {
+      'target_name': 'libpng_static',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'include_dirs': [
+        # Needed for generated pnglibconf.h and pngprefix.h
+        '../third_party/libpng',
+        '../third_party/externals/libpng',
+      ],
+      'dependencies': [
+        'zlib.gyp:zlib',
+      ],
+      'export_dependent_settings': [
+        'zlib.gyp:zlib',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../third_party/externals/libpng',
+          # Needed for generated pnglibconf.h and pngprefix.h
+          '../third_party/libpng',
+        ],
+        'defines': [
+          'SKIA_PNG_PREFIXED',
+        ],
+      },
+      'cflags': [
+        '-w',
+        '-fvisibility=hidden',
+      ],
+      'sources': [
+        '../third_party/externals/libpng/png.c',
+        '../third_party/externals/libpng/pngerror.c',
+        '../third_party/externals/libpng/pngget.c',
+        '../third_party/externals/libpng/pngmem.c',
+        '../third_party/externals/libpng/pngpread.c',
+        '../third_party/externals/libpng/pngread.c',
+        '../third_party/externals/libpng/pngrio.c',
+        '../third_party/externals/libpng/pngrtran.c',
+        '../third_party/externals/libpng/pngrutil.c',
+        '../third_party/externals/libpng/pngset.c',
+        '../third_party/externals/libpng/pngtrans.c',
+        '../third_party/externals/libpng/pngwio.c',
+        '../third_party/externals/libpng/pngwrite.c',
+        '../third_party/externals/libpng/pngwtran.c',
+        '../third_party/externals/libpng/pngwutil.c',
+      ],
+      'conditions': [
+        [ 'skia_os == "ios"', {
+          # explicitly disable looking for NEON on iOS builds
+          'defines': [
+            'PNG_ARM_NEON_OPT=0',
+          ],
+        }, { # skia_os != "ios" 
+          'dependencies': [
+            'libpng.gyp:libpng_static_neon',
+          ],
+        }],
       ],
     },
+    {
+      'target_name': 'libpng_static_neon',
+      'type': 'static_library',
+      'include_dirs': [
+        # Needed for generated pnglibconf.h and pngprefix.h
+        '../third_party/libpng',
+        '../third_party/externals/libpng',
+      ],
+      'dependencies': [
+        'zlib.gyp:zlib',
+      ],
+     'sources': [
+        '../third_party/externals/libpng/arm/arm_init.c',
+        '../third_party/externals/libpng/arm/filter_neon.S',
+        '../third_party/externals/libpng/arm/filter_neon_intrinsics.c',
+      ],
+      'conditions': [
+        ['arm_neon_optional', {
+          'cflags': [
+            '-mfpu=neon',
+          ],
+        }],
+      ],
+    }
   ]
 }
diff --git a/gyp/most.gyp b/gyp/most.gyp
index c547901..27ce0f1 100644
--- a/gyp/most.gyp
+++ b/gyp/most.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Build ALMOST everything provided by Skia; this should be the default target.
 #
 # This omits the following targets that many developers won't want to build:
diff --git a/gyp/nacl.gyp b/gyp/nacl.gyp
deleted file mode 100644
index 3dd4c8e..0000000
--- a/gyp/nacl.gyp
+++ /dev/null
@@ -1,19 +0,0 @@
-# Common entry point for all Skia executables running in NaCl
-{
-  'targets': [
-    {
-      'target_name': 'nacl_interface',
-      'type': 'static_library',
-      'dependencies': [
-        'skia_lib.gyp:skia_lib',
-      ],
-      'include_dirs': [
-        # For SkThreadUtils.h
-        '../src/utils',
-      ],
-      'sources': [
-        '../platform_tools/nacl/src/nacl_interface.cpp',
-      ],
-    },
-  ],
-}
diff --git a/gyp/nanomsg.gyp b/gyp/nanomsg.gyp
index eba72ae..987702b 100644
--- a/gyp/nanomsg.gyp
+++ b/gyp/nanomsg.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'variables': {
     'skia_warnings_as_errors': 0,
diff --git a/gyp/opts.gyp b/gyp/opts.gyp
index 6ff9008..b4fb609 100644
--- a/gyp/opts.gyp
+++ b/gyp/opts.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Gyp file for building opts target.
 {
   # Source lists live in opts.gypi.  This makes it easier to maintain our Chrome GYP/GN setup.
@@ -62,14 +66,6 @@
             [ 'arm_neon == 1 or arm_neon_optional == 1', {
               'dependencies': [ 'opts_neon' ]
             }],
-            [ 'skia_os == "ios"', {
-              'sources!': [
-                # these fail to compile under xcode for ios
-                '../src/opts/memset.arm.S',
-                '../src/opts/SkBitmapProcState_opts_arm.cpp',
-                '../src/opts/SkBlitRow_opts_arm.cpp',
-              ],
-            }],
           ],
         }],
 
diff --git a/gyp/opts.gypi b/gyp/opts.gypi
index b6ce79c..90fc3ff 100644
--- a/gyp/opts.gypi
+++ b/gyp/opts.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
         'none_sources': [
             '<(skia_src_path)/opts/SkBitmapProcState_opts_none.cpp',
@@ -19,7 +23,6 @@
             '<(skia_src_path)/opts/SkTextureCompression_opts_arm.cpp',
             '<(skia_src_path)/opts/SkUtils_opts_arm.cpp',
             '<(skia_src_path)/opts/SkXfermode_opts_arm.cpp',
-            '<(skia_src_path)/opts/memset.arm.S',
         ],
         'neon_sources': [
             '<(skia_src_path)/opts/SkBitmapProcState_arm_neon.cpp',
@@ -29,9 +32,8 @@
             '<(skia_src_path)/opts/SkBlurImage_opts_neon.cpp',
             '<(skia_src_path)/opts/SkMorphology_opts_neon.cpp',
             '<(skia_src_path)/opts/SkTextureCompression_opts_neon.cpp',
+            '<(skia_src_path)/opts/SkUtils_opts_arm_neon.cpp',
             '<(skia_src_path)/opts/SkXfermode_opts_arm_neon.cpp',
-            '<(skia_src_path)/opts/memset16_neon.S',
-            '<(skia_src_path)/opts/memset32_neon.S',
         ],
         'arm64_sources': [
             '<(skia_src_path)/opts/SkBitmapProcState_arm_neon.cpp',
diff --git a/gyp/pathops.gypi b/gyp/pathops.gypi
deleted file mode 100644
index 3bb163e..0000000
--- a/gyp/pathops.gypi
+++ /dev/null
@@ -1,61 +0,0 @@
-{
-  'include_dirs' : [
-    '../include/pathops',
-    '../src/pathops',
-  ],
-  'sources': [
-    '../include/pathops/SkPathOps.h',
-    '../src/pathops/SkAddIntersections.cpp',
-    '../src/pathops/SkDCubicIntersection.cpp',
-    '../src/pathops/SkDCubicLineIntersection.cpp',
-    '../src/pathops/SkDCubicToQuads.cpp',
-    '../src/pathops/SkDLineIntersection.cpp',
-    '../src/pathops/SkDQuadImplicit.cpp',
-    '../src/pathops/SkDQuadIntersection.cpp',
-    '../src/pathops/SkDQuadLineIntersection.cpp',
-    '../src/pathops/SkIntersections.cpp',
-    '../src/pathops/SkOpAngle.cpp',
-    '../src/pathops/SkOpContour.cpp',
-    '../src/pathops/SkOpEdgeBuilder.cpp',
-    '../src/pathops/SkOpSegment.cpp',
-    '../src/pathops/SkPathOpsBounds.cpp',
-    '../src/pathops/SkPathOpsCommon.cpp',
-    '../src/pathops/SkPathOpsCubic.cpp',
-    '../src/pathops/SkPathOpsDebug.cpp',
-    '../src/pathops/SkPathOpsLine.cpp',
-    '../src/pathops/SkPathOpsOp.cpp',
-    '../src/pathops/SkPathOpsPoint.cpp',
-    '../src/pathops/SkPathOpsQuad.cpp',
-    '../src/pathops/SkPathOpsRect.cpp',
-    '../src/pathops/SkPathOpsSimplify.cpp',
-    '../src/pathops/SkPathOpsTriangle.cpp',
-    '../src/pathops/SkPathOpsTypes.cpp',
-    '../src/pathops/SkPathWriter.cpp',
-    '../src/pathops/SkQuarticRoot.cpp',
-    '../src/pathops/SkReduceOrder.cpp',
-    '../src/pathops/SkAddIntersections.h',
-    '../src/pathops/SkDQuadImplicit.h',
-    '../src/pathops/SkIntersectionHelper.h',
-    '../src/pathops/SkIntersections.h',
-    '../src/pathops/SkLineParameters.h',
-    '../src/pathops/SkOpAngle.h',
-    '../src/pathops/SkOpContour.h',
-    '../src/pathops/SkOpEdgeBuilder.h',
-    '../src/pathops/SkOpSegment.h',
-    '../src/pathops/SkOpSpan.h',
-    '../src/pathops/SkPathOpsBounds.h',
-    '../src/pathops/SkPathOpsCommon.h',
-    '../src/pathops/SkPathOpsCubic.h',
-    '../src/pathops/SkPathOpsCurve.h',
-    '../src/pathops/SkPathOpsDebug.h',
-    '../src/pathops/SkPathOpsLine.h',
-    '../src/pathops/SkPathOpsPoint.h',
-    '../src/pathops/SkPathOpsQuad.h',
-    '../src/pathops/SkPathOpsRect.h',
-    '../src/pathops/SkPathOpsTriangle.h',
-    '../src/pathops/SkPathOpsTypes.h',
-    '../src/pathops/SkPathWriter.h',
-    '../src/pathops/SkQuarticRoot.h',
-    '../src/pathops/SkReduceOrder.h',
-  ],
-}
diff --git a/gyp/pathops_skpclip.gyp b/gyp/pathops_skpclip.gyp
index 4dadd79..6c7e6b4 100755
--- a/gyp/pathops_skpclip.gyp
+++ b/gyp/pathops_skpclip.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP file to build pathops skp clip test.
 {
   'includes': [
diff --git a/gyp/pathops_unittest.gyp b/gyp/pathops_unittest.gyp
index c873254..9e3dbba 100644
--- a/gyp/pathops_unittest.gyp
+++ b/gyp/pathops_unittest.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP file to build pathops unit tests.
 {
   'includes': [
@@ -20,6 +24,7 @@
         '../tests/PathOpsCubicLineIntersectionIdeas.cpp',
         '../tests/PathOpsDebug.cpp',
         '../tests/PathOpsOpLoopThreadedTest.cpp',
+        '../tests/PathOpsTSectDebug.h',
         '../tests/skia_test.cpp',
       ],
       'conditions': [
diff --git a/gyp/pathops_unittest.gypi b/gyp/pathops_unittest.gypi
index 5117b63..75e824b 100644
--- a/gyp/pathops_unittest.gypi
+++ b/gyp/pathops_unittest.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Common gypi for pathops unit tests.
 {
   'include_dirs': [
@@ -19,24 +23,27 @@
 
     '../tests/PathOpsAngleTest.cpp',
     '../tests/PathOpsBoundsTest.cpp',
+    '../tests/PathOpsBuilderTest.cpp',
+    '../tests/PathOpsBuildUseTest.cpp',
+    '../tests/PathOpsConicIntersectionTest.cpp',
+    '../tests/PathOpsConicLineIntersectionTest.cpp',
     '../tests/PathOpsCubicIntersectionTest.cpp',
     '../tests/PathOpsCubicIntersectionTestData.cpp',
     '../tests/PathOpsCubicLineIntersectionTest.cpp',
     '../tests/PathOpsCubicQuadIntersectionTest.cpp',
     '../tests/PathOpsCubicReduceOrderTest.cpp',
-    '../tests/PathOpsCubicToQuadsTest.cpp',
     '../tests/PathOpsDCubicTest.cpp',
     '../tests/PathOpsDLineTest.cpp',
     '../tests/PathOpsDPointTest.cpp',
-    '../tests/PathOpsDQuadTest.cpp',
     '../tests/PathOpsDRectTest.cpp',
-    '../tests/PathOpsDTriangleTest.cpp',
     '../tests/PathOpsDVectorTest.cpp',
     '../tests/PathOpsExtendedTest.cpp',
     '../tests/PathOpsFuzz763Test.cpp',
     '../tests/PathOpsInverseTest.cpp',
+    '../tests/PathOpsIssue3651.cpp',
     '../tests/PathOpsLineIntersectionTest.cpp',
     '../tests/PathOpsLineParametetersTest.cpp',
+    '../tests/PathOpsOpCircleThreadedTest.cpp',
     '../tests/PathOpsOpCubicThreadedTest.cpp',
     '../tests/PathOpsOpRectThreadedTest.cpp',
     '../tests/PathOpsOpTest.cpp',
@@ -44,7 +51,6 @@
     '../tests/PathOpsQuadIntersectionTestData.cpp',
     '../tests/PathOpsQuadLineIntersectionTest.cpp',
     '../tests/PathOpsQuadLineIntersectionThreadedTest.cpp',
-    '../tests/PathOpsQuadParameterizationTest.cpp',
     '../tests/PathOpsQuadReduceOrderTest.cpp',
     '../tests/PathOpsSimplifyDegenerateThreadedTest.cpp',
     '../tests/PathOpsSimplifyFailTest.cpp',
@@ -56,11 +62,15 @@
     '../tests/PathOpsSkpTest.cpp',
     '../tests/PathOpsTestCommon.cpp',
     '../tests/PathOpsThreadedCommon.cpp',
-    '../tests/PathOpsTightBoundsTest.cpp',
+    '../tests/PathOpsThreeWayTest.cpp',
+    '../tests/PathOpsTightBoundsTest.cpp', 
+    '../tests/PathOpsTypesTest.cpp', 
+
     '../tests/PathOpsCubicIntersectionTestData.h',
     '../tests/PathOpsExtendedTest.h',
     '../tests/PathOpsQuadIntersectionTestData.h',
     '../tests/PathOpsTestCommon.h',
     '../tests/PathOpsThreadedCommon.h',
+    '../tests/PathOpsTSectDebug.h',
   ],
 }
diff --git a/gyp/pdf.gyp b/gyp/pdf.gyp
index b253fd8..fe57450 100644
--- a/gyp/pdf.gyp
+++ b/gyp/pdf.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # This file builds the PDF backend.
 {
   'targets': [
diff --git a/gyp/pdf.gypi b/gyp/pdf.gypi
index 6d68439..0c9cb06 100644
--- a/gyp/pdf.gypi
+++ b/gyp/pdf.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Include this gypi to include all 'pdf' files
 # The parent gyp/gypi file must define
 #       'skia_src_path'     e.g. skia/trunk/src
@@ -8,16 +12,14 @@
 {
     'sources': [
         '<(skia_src_path)/doc/SkDocument_PDF.cpp',
+        '<(skia_src_path)/pdf/SkJpegInfo.cpp',
+        '<(skia_src_path)/pdf/SkJpegInfo.h',
         '<(skia_src_path)/pdf/SkPDFBitmap.cpp',
         '<(skia_src_path)/pdf/SkPDFBitmap.h',
         '<(skia_src_path)/pdf/SkPDFCanon.cpp',
         '<(skia_src_path)/pdf/SkPDFCanon.h',
-        '<(skia_src_path)/pdf/SkPDFCatalog.cpp',
-        '<(skia_src_path)/pdf/SkPDFCatalog.h',
         '<(skia_src_path)/pdf/SkPDFDevice.cpp',
         '<(skia_src_path)/pdf/SkPDFDevice.h',
-        '<(skia_src_path)/pdf/SkPDFDocument.cpp',
-        '<(skia_src_path)/pdf/SkPDFDocument.h',
         '<(skia_src_path)/pdf/SkPDFFont.cpp',
         '<(skia_src_path)/pdf/SkPDFFont.h',
         '<(skia_src_path)/pdf/SkPDFFontImpl.h',
@@ -25,10 +27,6 @@
         '<(skia_src_path)/pdf/SkPDFFormXObject.h',
         '<(skia_src_path)/pdf/SkPDFGraphicState.cpp',
         '<(skia_src_path)/pdf/SkPDFGraphicState.h',
-        '<(skia_src_path)/pdf/SkPDFImage.cpp',
-        '<(skia_src_path)/pdf/SkPDFImage.h',
-        '<(skia_src_path)/pdf/SkPDFPage.cpp',
-        '<(skia_src_path)/pdf/SkPDFPage.h',
         '<(skia_src_path)/pdf/SkPDFResourceDict.cpp',
         '<(skia_src_path)/pdf/SkPDFResourceDict.h',
         '<(skia_src_path)/pdf/SkPDFShader.cpp',
@@ -39,6 +37,5 @@
         '<(skia_src_path)/pdf/SkPDFTypes.h',
         '<(skia_src_path)/pdf/SkPDFUtils.cpp',
         '<(skia_src_path)/pdf/SkPDFUtils.h',
-        '<(skia_src_path)/pdf/SkTSet.h',
     ],
 }
diff --git a/gyp/pdfviewer.gyp b/gyp/pdfviewer.gyp
index b21afe5..ff90e2c 100644
--- a/gyp/pdfviewer.gyp
+++ b/gyp/pdfviewer.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP file to build pdfviewer.
 #
 # To build on Linux:
diff --git a/gyp/pdfviewer_lib.gyp b/gyp/pdfviewer_lib.gyp
index feb5f5b..0d4c0bd 100644
--- a/gyp/pdfviewer_lib.gyp
+++ b/gyp/pdfviewer_lib.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP file to build pdfviewer.
 #
 # To build on Linux:
diff --git a/gyp/ports.gyp b/gyp/ports.gyp
index 6fe5263..7968793 100644
--- a/gyp/ports.gyp
+++ b/gyp/ports.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Port-specific Skia library code.
 {
   'targets': [
@@ -22,7 +26,6 @@
         '../src/utils',
       ],
       'sources': [
-        '../src/ports/SkDebug_nacl.cpp',
         '../src/ports/SkDebug_stdio.cpp',
         '../src/ports/SkDebug_win.cpp',
 
@@ -55,7 +58,7 @@
         '../include/ports/SkRemotableFontMgr.h',
       ],
       'conditions': [
-        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "chromeos", "nacl", "android"]', {
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "chromeos", "android"]', {
           'sources': [
             '../src/ports/SkFontHost_FreeType.cpp',
             '../src/ports/SkFontHost_FreeType_common.cpp',
@@ -66,7 +69,43 @@
         }],
         [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "chromeos"]', {
           'conditions': [
-            [ 'skia_no_fontconfig', {
+            [ 'skia_embedded_fonts', {
+              'link_settings': {
+                'libraries': [
+                  '-ldl',
+                ],
+              },
+              'variables': {
+                'embedded_font_data_identifier': 'sk_fonts',
+                'fonts_to_include': [
+                  '../resources/fonts/Funkster.ttf',
+                ],
+              },
+              'sources': [
+                '../src/ports/SkFontHost_linux.cpp',
+              ],
+              'actions': [{
+                'action_name': 'generate_embedded_font_data',
+                'inputs': [
+                  '../tools/embed_resources.py',
+                  '<@(fonts_to_include)',
+                ],
+                'outputs': [
+                  '<(SHARED_INTERMEDIATE_DIR)/ports/fonts/fonts.cpp',
+                ],
+                'action': ['python', '../tools/embed_resources.py',
+                                     '--align', '4',
+                                     '--name', '<(embedded_font_data_identifier)',
+                                     '--input', '<@(fonts_to_include)',
+                                     '--output', '<@(_outputs)',
+                ],
+                'message': 'Generating <@(_outputs)',
+                'process_outputs_as_sources': 1,
+              }],
+              'defines': [
+                'SK_EMBEDDED_FONTS=<(embedded_font_data_identifier)',
+              ],
+            }, 'skia_no_fontconfig', {
               'link_settings': {
                 'libraries': [
                   '-ldl',
@@ -90,18 +129,6 @@
             }]
           ],
         }],
-        [ 'skia_os == "nacl"', {
-          'sources': [
-            '../src/ports/SkFontHost_linux.cpp',
-          ],
-          'sources!': [
-            '../src/ports/SkDebug_stdio.cpp',
-          ],
-        }, {
-          'sources!': [
-            '../src/ports/SkDebug_nacl.cpp',
-          ],
-        }],
         [ 'skia_os == "mac"', {
           'include_dirs': [
             '../include/utils/mac',
diff --git a/gyp/sfnt.gyp b/gyp/sfnt.gyp
index 89a1fbd..944857f 100644
--- a/gyp/sfnt.gyp
+++ b/gyp/sfnt.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'targets': [
     {
diff --git a/gyp/skflate.gyp b/gyp/skflate.gyp
index beed580..d3193a3 100644
--- a/gyp/skflate.gyp
+++ b/gyp/skflate.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Target for including SkFlate.
 {
   'targets': [
diff --git a/gyp/skia_for_android_framework_defines.gypi b/gyp/skia_for_android_framework_defines.gypi
index 47e9016..aba0384 100644
--- a/gyp/skia_for_android_framework_defines.gypi
+++ b/gyp/skia_for_android_framework_defines.gypi
@@ -13,12 +13,12 @@
     # If these become 'permanent', they should be moved into common_variables.gypi
     #
     'skia_for_android_framework_defines': [
-      'SK_SUPPORT_LEGACY_GRADIENT_FACTORIES',
       'SK_SUPPORT_LEGACY_PUBLIC_IMAGEINFO_FIELDS',
       'SK_SUPPORT_LEGACY_GETDEVICE',
       # Needed until we fix skbug.com/2440.
       'SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG',
-      'SK_LEGACY_DRAWPICTURECALLBACK',
+      'SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS',
+      'SK_SUPPORT_LEGACY_SCALAR_DIV',
     ],
   },
 }
diff --git a/gyp/skia_for_chromium_defines.gypi b/gyp/skia_for_chromium_defines.gypi
index bcc6096..14cd393 100644
--- a/gyp/skia_for_chromium_defines.gypi
+++ b/gyp/skia_for_chromium_defines.gypi
@@ -14,6 +14,8 @@
     #
     'skia_for_chromium_defines': [
       'SK_LEGACY_DRAWPICTURECALLBACK',
+      'SK_SUPPORT_LEGACY_OPTIONLESS_GET_PIXELS',
+      'SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS',
     ],
   },
 }
diff --git a/gyp/skia_launcher.gyp b/gyp/skia_launcher.gyp
index d9970b4..e09e171 100644
--- a/gyp/skia_launcher.gyp
+++ b/gyp/skia_launcher.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'includes': [ '../platform_tools/android/gyp/skia_launcher.gypi' ]
 }
diff --git a/gyp/skia_lib.gyp b/gyp/skia_lib.gyp
index 80d4f8f..8cb5fa1 100644
--- a/gyp/skia_lib.gyp
+++ b/gyp/skia_lib.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # The minimal set of static libraries for basic Skia functionality.
 
 {
diff --git a/gyp/svg.gyp b/gyp/svg.gyp
index 82ff53d..df0c45c 100644
--- a/gyp/svg.gyp
+++ b/gyp/svg.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'targets': [
     {
diff --git a/gyp/tests.gypi b/gyp/tests.gypi
index d4ca3af..acbcc35 100644
--- a/gyp/tests.gypi
+++ b/gyp/tests.gypi
@@ -1,6 +1,11 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Common gypi for unit tests.
 {
   'include_dirs': [
+    '../src/codec',
     '../src/core',
     '../src/effects',
     '../src/image',
@@ -71,6 +76,7 @@
     '../tests/ClipCubicTest.cpp',
     '../tests/ClipStackTest.cpp',
     '../tests/ClipperTest.cpp',
+    '../tests/CodexTest.cpp',
     '../tests/ColorFilterTest.cpp',
     '../tests/ColorPrivTest.cpp',
     '../tests/ColorTest.cpp',
@@ -85,6 +91,7 @@
     '../tests/DiscardableMemoryTest.cpp',
     '../tests/DocumentTest.cpp',
     '../tests/DrawBitmapRectTest.cpp',
+    '../tests/DrawFilterTest.cpp',
     '../tests/DrawPathTest.cpp',
     '../tests/DrawTextTest.cpp',
     '../tests/DynamicHashTest.cpp',
@@ -100,6 +107,7 @@
     '../tests/FontNamesTest.cpp',
     '../tests/FontObjTest.cpp',
     '../tests/FrontBufferedStreamTest.cpp',
+    '../tests/FunctionTest.cpp',
     '../tests/GLInterfaceValidationTest.cpp',
     '../tests/GLProgramsTest.cpp',
     '../tests/GeometryTest.cpp',
@@ -126,6 +134,7 @@
     '../tests/ImageGeneratorTest.cpp',
     '../tests/ImageIsOpaqueTest.cpp',
     '../tests/ImageNewShaderTest.cpp',
+    '../tests/IndexedPngOverflowTest.cpp',
     '../tests/InfRectTest.cpp',
     '../tests/InterpolatorTest.cpp',
     '../tests/InvalidIndexedPngTest.cpp',
@@ -160,7 +169,6 @@
     '../tests/PathCoverageTest.cpp',
     '../tests/PathMeasureTest.cpp',
     '../tests/PathTest.cpp',
-    '../tests/PathUtilsTest.cpp',
     '../tests/PictureBBHTest.cpp',
     '../tests/PictureShaderTest.cpp',
     '../tests/PictureTest.cpp',
@@ -194,7 +202,7 @@
     '../tests/ShaderImageFilterTest.cpp',
     '../tests/ShaderOpacityTest.cpp',
     '../tests/SizeTest.cpp',
-    '../tests/Sk4xTest.cpp',
+    '../tests/SkNxTest.cpp',
     '../tests/SkBase64Test.cpp',
     '../tests/SkImageTest.cpp',
     '../tests/SkResourceCacheTest.cpp',
@@ -207,12 +215,13 @@
     '../tests/StrokerTest.cpp',
     '../tests/SurfaceTest.cpp',
     '../tests/SVGDeviceTest.cpp',
+    '../tests/SwizzlerTest.cpp',
     '../tests/TessellatingPathRendererTests.cpp',
     '../tests/TArrayTest.cpp',
+    '../tests/TemplatesTest.cpp',
     '../tests/TDPQueueTest.cpp',
     '../tests/Time.cpp',
     '../tests/TLSTest.cpp',
-    '../tests/TSetTest.cpp',
     '../tests/TextBlobTest.cpp',
     '../tests/TextureCompressionTest.cpp',
     '../tests/ToUnicodeTest.cpp',
diff --git a/gyp/tools.gyp b/gyp/tools.gyp
index 3ac32bd..3ecdf7b 100644
--- a/gyp/tools.gyp
+++ b/gyp/tools.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
     # GYP file to build various tools.
 #
 # To build on Linux:
@@ -14,6 +18,7 @@
       'type': 'none',
       'dependencies': [
         'bench_pictures',
+        'chrome_fuzz',
         'dump_record',
         'filter',
         'gpuveto',
@@ -43,6 +48,16 @@
       ],
     },
     {
+      'target_name': 'chrome_fuzz',
+      'type': 'executable',
+      'sources': [
+        '../tools/chrome_fuzz.cpp',
+      ],
+      'dependencies': [
+        'skia_lib.gyp:skia_lib',
+      ],
+    },
+    {
       'target_name': 'crash_handler',
         'type': 'static_library',
         'sources': [ '../tools/CrashHandler.cpp' ],
@@ -238,22 +253,12 @@
       'target_name': 'skhello',
       'type': 'executable',
       'dependencies': [
+        'flags.gyp:flags',
+        'pdf.gyp:pdf',
         'skia_lib.gyp:skia_lib',
       ],
-      'conditions': [
-        [ 'skia_os == "nacl"', {
-          'sources': [
-            '../platform_tools/nacl/src/nacl_hello.cpp',
-          ],
-        }, {
-          'sources': [
-            '../tools/skhello.cpp',
-          ],
-          'dependencies': [
-            'flags.gyp:flags',
-            'pdf.gyp:pdf',
-          ],
-        }],
+      'sources': [
+        '../tools/skhello.cpp',
       ],
     },
     {
@@ -649,7 +654,6 @@
           '<(skia_include_path)/views/SkOSWindow_Android.h',
           '<(skia_include_path)/views/SkOSWindow_iOS.h',
           '<(skia_include_path)/views/SkOSWindow_Mac.h',
-          '<(skia_include_path)/views/SkOSWindow_NaCl.h',
           '<(skia_include_path)/views/SkOSWindow_SDL.h',
           '<(skia_include_path)/views/SkOSWindow_Unix.h',
           '<(skia_include_path)/views/SkOSWindow_Win.h',
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index 19816f8..d5b2ef7 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Gyp for utils.
 {
   'targets': [
@@ -103,14 +107,6 @@
             '../src/utils/win/SkIStream.cpp',
           ],
         }],
-        [ 'skia_os == "nacl"', {
-          'sources': [
-            '../src/utils/SkThreadUtils_pthread_other.cpp',
-          ],
-          'sources!': [
-            '../src/utils/SkThreadUtils_pthread_linux.cpp',
-          ],
-        }],
         ['skia_run_pdfviewer_in_gm', {
           'defines': [
             'SK_BUILD_NATIVE_PDF_RENDERER',
@@ -124,5 +120,25 @@
         ],
       },
     },
+    {
+      'target_name': 'android_utils',
+      'product_name': 'skia_android_utils',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'dependencies': [
+        'core.gyp:*',
+      ],
+      'sources': [
+        '../src/utils/android/SkAndroidSDKCanvas.h',
+        '../src/utils/android/SkAndroidSDKCanvas.cpp',
+        '../src/utils/android/SkHwuiRenderer.h',
+        '../src/utils/android/SkHwuiRenderer.cpp',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../src/utils/android',
+        ],
+      },
+    },
   ],
 }
diff --git a/gyp/utils.gypi b/gyp/utils.gypi
index ab95043..f382e10 100644
--- a/gyp/utils.gypi
+++ b/gyp/utils.gypi
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Include this gypi to include all 'utils' files
 # The parent gyp/gypi file must define
 #       'skia_src_path'     e.g. skia/trunk/src
@@ -30,6 +34,7 @@
         '<(skia_include_path)/utils/SkNoSaveLayerCanvas.h',
         '<(skia_include_path)/utils/SkNWayCanvas.h',
         '<(skia_include_path)/utils/SkNullCanvas.h',
+        '<(skia_include_path)/utils/SkPaintFilterCanvas.h',
         '<(skia_include_path)/utils/SkParse.h',
         '<(skia_include_path)/utils/SkParsePaint.h',
         '<(skia_include_path)/utils/SkParsePath.h',
@@ -70,6 +75,7 @@
         '<(skia_src_path)/utils/SkNWayCanvas.cpp',
         '<(skia_src_path)/utils/SkNullCanvas.cpp',
         '<(skia_src_path)/utils/SkOSFile.cpp',
+        '<(skia_src_path)/utils/SkPaintFilterCanvas.cpp',
         '<(skia_src_path)/utils/SkParse.cpp',
         '<(skia_src_path)/utils/SkParseColor.cpp',
         '<(skia_src_path)/utils/SkParsePath.cpp',
@@ -78,7 +84,6 @@
         '<(skia_src_path)/utils/SkPatchGrid.h',
         '<(skia_src_path)/utils/SkPatchUtils.cpp',
         '<(skia_src_path)/utils/SkPatchUtils.h',
-        '<(skia_src_path)/utils/SkPathUtils.cpp',
         '<(skia_src_path)/utils/SkSHA1.cpp',
         '<(skia_src_path)/utils/SkSHA1.h',
         '<(skia_src_path)/utils/SkRTConf.cpp',
diff --git a/gyp/v8.gyp b/gyp/v8.gyp
index f73d5f0..81cb89d 100644
--- a/gyp/v8.gyp
+++ b/gyp/v8.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # GYP file to build a V8 sample.
 {
   'targets': [
diff --git a/gyp/views.gyp b/gyp/views.gyp
index 7f53637..06b5fcc 100644
--- a/gyp/views.gyp
+++ b/gyp/views.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 # Views is the Skia windowing toolkit.
 # It provides:
 #  * A portable means of creating native windows.
@@ -28,7 +32,6 @@
         '../include/views/SkKey.h',
         '../include/views/SkOSMenu.h',
         '../include/views/SkOSWindow_Mac.h',
-        '../include/views/SkOSWindow_NaCl.h',
         '../include/views/SkOSWindow_SDL.h',
         '../include/views/SkOSWindow_Unix.h',
         '../include/views/SkOSWindow_Win.h',
@@ -118,17 +121,6 @@
             '../src/views/win/skia_win.cpp',
           ],
         }],
-        [ 'skia_os == "nacl"', {
-          'sources!': [
-            '../src/views/unix/SkOSWindow_Unix.cpp',
-            '../src/views/unix/keysym2ucs.c',
-            '../src/views/unix/skia_unix.cpp',
-          ],
-        }, {
-          'sources!': [
-            '../src/views/nacl/SkOSWindow_NaCl.cpp',
-          ],
-        }],
         [ 'skia_gpu == 1', {
           'include_dirs': [
             '../include/gpu',
diff --git a/gyp/views_animated.gyp b/gyp/views_animated.gyp
index c10e9f7..f31e0c6 100644
--- a/gyp/views_animated.gyp
+++ b/gyp/views_animated.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 #Animated widgets are views which use animator.
 
 {
diff --git a/gyp/xml.gyp b/gyp/xml.gyp
index aaceef2..c5cdd58 100644
--- a/gyp/xml.gyp
+++ b/gyp/xml.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'targets': [
     {
diff --git a/gyp/xps.gyp b/gyp/xps.gyp
index 4a301b6..454e065 100644
--- a/gyp/xps.gyp
+++ b/gyp/xps.gyp
@@ -1,3 +1,7 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'targets': [
     {
@@ -10,7 +14,6 @@
           'defines': [ 'SK_XPS_USE_DETERMINISTIC_IDS', ],
           'dependencies': [
             'skia_lib.gyp:skia_lib',
-            'sfnt.gyp:sfnt',
           ],
           'include_dirs': [
             '../include/device/xps',
diff --git a/gyp_skia b/gyp_skia
index 5828780..e68ac16 100755
--- a/gyp_skia
+++ b/gyp_skia
@@ -68,12 +68,6 @@
   if not output_dir:
     return os.path.join(os.path.abspath(script_dir), 'out')
 
-  if (sys.platform.startswith('darwin') and
-      (not os.getenv(ENVVAR_GYP_GENERATORS) or
-       'xcode' in os.getenv(ENVVAR_GYP_GENERATORS))):
-    print 'ERROR: variable SKIA_OUT is not valid on Mac (using xcodebuild)'
-    sys.exit(-1);
-
   if os.path.isabs(output_dir):
     return output_dir
   else:
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index beb9cb9..2e47a26 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -8,14 +8,16 @@
 #ifndef SkCodec_DEFINED
 #define SkCodec_DEFINED
 
+#include "SkEncodedFormat.h"
 #include "SkImageGenerator.h"
 #include "SkImageInfo.h"
+#include "SkScanlineDecoder.h"
 #include "SkSize.h"
+#include "SkStream.h"
 #include "SkTemplates.h"
 #include "SkTypes.h"
 
 class SkData;
-class SkStream;
 
 /**
  *  Abstraction layer directly on top of an image codec.
@@ -46,47 +48,140 @@
      *
      *  FIXME: Move to SkImageGenerator?
      */
-    SkISize getScaledDimensions(float desiredScale) const;
+    SkISize getScaledDimensions(float desiredScale) const {
+        return this->onGetScaledDimensions(desiredScale);
+    }
+
+    /**
+     *  Format of the encoded data.
+     */
+    SkEncodedFormat getEncodedFormat() const { return this->onGetEncodedFormat(); }
+
+    /**
+     *  Return an object which can be used to decode individual scanlines.
+     *
+     *  This object is owned by the SkCodec, which will handle its lifetime. The
+     *  returned object is only valid until the SkCodec is deleted or the next
+     *  call to getScanlineDecoder, whichever comes first.
+     *
+     *  Calling a second time will rewind and replace the existing one with a
+     *  new one. If the stream cannot be rewound, this will delete the existing
+     *  one and return NULL.
+     *
+     *  @param dstInfo Info of the destination. If the dimensions do not match
+     *      those of getInfo, this implies a scale.
+     *  @param options Contains decoding options, including if memory is zero
+     *      initialized.
+     *  @param ctable A pointer to a color table.  When dstInfo.colorType() is
+     *      kIndex8, this should be non-NULL and have enough storage for 256
+     *      colors.  The color table will be populated after decoding the palette.
+     *  @param ctableCount A pointer to the size of the color table.  When
+     *      dstInfo.colorType() is kIndex8, this should be non-NULL.  It will
+     *      be modified to the true size of the color table (<= 256) after
+     *      decoding the palette.
+     *  @return New SkScanlineDecoder, or NULL on failure.
+     *
+     *  NOTE: If any rows were previously decoded, this requires rewinding the
+     *  SkStream.
+     *
+     *  NOTE: The scanline decoder is owned by the SkCodec and will delete it
+     *  when the SkCodec is deleted.
+     */
+    SkScanlineDecoder* getScanlineDecoder(const SkImageInfo& dstInfo, const Options* options,
+                                          SkPMColor ctable[], int* ctableCount);
+
+    /**
+     *  Simplified version of getScanlineDecoder() that asserts that info is NOT
+     *  kIndex8_SkColorType and uses the default Options.
+     */
+    SkScanlineDecoder* getScanlineDecoder(const SkImageInfo& dstInfo);
+
+    /**
+     *  Some images may initially report that they have alpha due to the format
+     *  of the encoded data, but then never use any colors which have alpha
+     *  less than 100%. This function can be called *after* decoding to
+     *  determine if such an image truly had alpha. Calling it before decoding
+     *  is undefined.
+     *  FIXME: see skbug.com/3582.
+     */
+    bool reallyHasAlpha() const {
+        return this->onReallyHasAlpha();
+    }
 
 protected:
     SkCodec(const SkImageInfo&, SkStream*);
 
-    /**
-     *  The SkAlphaType is a conservative answer. i.e. it is possible that it
-     *  initially returns a non-opaque answer, but completing the decode
-     *  reveals that the image is actually opaque.
-     */
-    bool onGetInfo(SkImageInfo* info) SK_OVERRIDE {
-        *info = fInfo;
-        return true;
-    }
-
-    // Helper for subclasses.
-    const SkImageInfo& getOriginalInfo() { return fInfo; }
-
     virtual SkISize onGetScaledDimensions(float /* desiredScale */) const {
         // By default, scaling is not supported.
-        return fInfo.dimensions();
+        return this->getInfo().dimensions();
     }
 
+    virtual SkEncodedFormat onGetEncodedFormat() const = 0;
+
+    /**
+     *  Override if your codec supports scanline decoding.
+     *
+     *  As in onGetPixels(), the implementation must call rewindIfNeeded() and
+     *  handle as appropriate.
+     *
+     *  @param dstInfo Info of the destination. If the dimensions do not match
+     *      those of getInfo, this implies a scale.
+     *  @param options Contains decoding options, including if memory is zero
+     *      initialized.
+     *  @param ctable A pointer to a color table.  When dstInfo.colorType() is
+     *      kIndex8, this should be non-NULL and have enough storage for 256
+     *      colors.  The color table will be populated after decoding the palette.
+     *  @param ctableCount A pointer to the size of the color table.  When
+     *      dstInfo.colorType() is kIndex8, this should be non-NULL.  It will
+     *      be modified to the true size of the color table (<= 256) after
+     *      decoding the palette.
+     *  @return New SkScanlineDecoder on success, NULL otherwise. The SkCodec
+     *      will take ownership of the returned scanline decoder.
+     */
+    virtual SkScanlineDecoder* onGetScanlineDecoder(const SkImageInfo& dstInfo,
+                                                    const Options& options,
+                                                    SkPMColor ctable[],
+                                                    int* ctableCount) {
+        return NULL;
+    }
+
+    virtual bool onReallyHasAlpha() const { return false; }
+
+    enum RewindState {
+        kRewound_RewindState,
+        kNoRewindNecessary_RewindState,
+        kCouldNotRewind_RewindState
+    };
     /**
      *  If the stream was previously read, attempt to rewind.
      *  @returns:
-     *      true
-     *       - if the stream needed to be rewound, and the rewind
-     *         succeeded.
-     *       - if the stream did not need to be rewound.
-     *      false
-     *       - if the stream needed to be rewound, and rewind failed.
+     *      kRewound if the stream needed to be rewound, and the
+     *               rewind succeeded.
+     *      kNoRewindNecessary if the stream did not need to be
+     *                         rewound.
+     *      kCouldNotRewind if the stream needed to be rewound, and
+     *                      rewind failed.
+     *
      *  Subclasses MUST call this function before reading the stream (e.g. in
      *  onGetPixels). If it returns false, onGetPixels should return
      *  kCouldNotRewind.
      */
-    bool SK_WARN_UNUSED_RESULT rewindIfNeeded();
+    RewindState SK_WARN_UNUSED_RESULT rewindIfNeeded();
+
+    /*
+     *
+     * Get method for the input stream
+     *
+     */
+    SkStream* stream() {
+        return fStream.get();
+    }
 
 private:
-    const SkImageInfo fInfo;
-    SkAutoTDelete<SkStream> fStream;
-    bool fNeedsRewind;
+    SkAutoTDelete<SkStream>             fStream;
+    bool                                fNeedsRewind;
+    SkAutoTDelete<SkScanlineDecoder>    fScanlineDecoder;
+
+    typedef SkImageGenerator INHERITED;
 };
 #endif // SkCodec_DEFINED
diff --git a/include/codec/SkEncodedFormat.h b/include/codec/SkEncodedFormat.h
new file mode 100644
index 0000000..003159a
--- /dev/null
+++ b/include/codec/SkEncodedFormat.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkEncodedFormat_DEFINED
+#define SkEncodedFormat_DEFINED
+
+/**
+ *  Enum describing format of encoded data.
+ */
+enum SkEncodedFormat {
+    kUnknown_SkEncodedFormat,
+    kBMP_SkEncodedFormat,
+    kGIF_SkEncodedFormat,
+    kICO_SkEncodedFormat,
+    kJPEG_SkEncodedFormat,
+    kPNG_SkEncodedFormat,
+    kWBMP_SkEncodedFormat,
+    kWEBP_SkEncodedFormat,
+    kPKM_SkEncodedFormat,
+    kKTX_SkEncodedFormat,
+    kASTC_SkEncodedFormat,
+};
+#endif  // SkEncodedFormat_DEFINED
diff --git a/include/codec/SkScanlineDecoder.h b/include/codec/SkScanlineDecoder.h
new file mode 100644
index 0000000..d7f73dd
--- /dev/null
+++ b/include/codec/SkScanlineDecoder.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkScanlineDecoder_DEFINED
+#define SkScanlineDecoder_DEFINED
+
+#include "SkTypes.h"
+#include "SkTemplates.h"
+#include "SkImageGenerator.h"
+#include "SkImageInfo.h"
+
+class SkScanlineDecoder : public SkNoncopyable {
+public:
+    // Note for implementations: An SkScanlineDecoder will be deleted by (and
+    // therefore *before*) its associated SkCodec, in case the order matters.
+    virtual ~SkScanlineDecoder() {}
+
+    /**
+     *  Write the next countLines scanlines into dst.
+     *
+     *  @param dst Must be non-null, and large enough to hold countLines
+     *      scanlines of size rowBytes.
+     *  @param countLines Number of lines to write.
+     *  @param rowBytes Number of bytes per row. Must be large enough to hold
+     *      a scanline based on the SkImageInfo used to create this object.
+     */
+    SkImageGenerator::Result getScanlines(void* dst, int countLines, size_t rowBytes) {
+        if ((rowBytes < fDstInfo.minRowBytes() && countLines > 1 ) || countLines <= 0
+                || fCurrScanline + countLines > fDstInfo.height()) {
+            return SkImageGenerator::kInvalidParameters;
+        }
+        const SkImageGenerator::Result result = this->onGetScanlines(dst, countLines, rowBytes);
+        this->checkForFinish(countLines);
+        return result;
+    }
+
+    /**
+     *  Skip count scanlines.
+     *
+     *  The default version just calls onGetScanlines and discards the dst.
+     *  NOTE: If skipped lines are the only lines with alpha, this default
+     *  will make reallyHasAlpha return true, when it could have returned
+     *  false.
+     */
+    SkImageGenerator::Result skipScanlines(int countLines) {
+        if (fCurrScanline + countLines > fDstInfo.height()) {
+            // Arguably, we could just skip the scanlines which are remaining,
+            // and return kSuccess. We choose to return invalid so the client
+            // can catch their bug.
+            return SkImageGenerator::kInvalidParameters;
+        }
+        const SkImageGenerator::Result result = this->onSkipScanlines(countLines);
+        this->checkForFinish(countLines);
+        return result;
+    }
+
+    /**
+     *  Some images may initially report that they have alpha due to the format
+     *  of the encoded data, but then never use any colors which have alpha
+     *  less than 100%. This function can be called *after* decoding to
+     *  determine if such an image truly had alpha. Calling it before decoding
+     *  is undefined.
+     *  FIXME: see skbug.com/3582.
+     */
+    bool reallyHasAlpha() const {
+        return this->onReallyHasAlpha();
+    }
+
+protected:
+    SkScanlineDecoder(const SkImageInfo& requested)
+        : fDstInfo(requested)
+        , fCurrScanline(0) {}
+
+    virtual bool onReallyHasAlpha() const { return false; }
+
+    const SkImageInfo& dstInfo() const { return fDstInfo; }
+
+private:
+    const SkImageInfo   fDstInfo;
+    int                 fCurrScanline;
+
+    // Naive default version just calls onGetScanlines on temp memory.
+    virtual SkImageGenerator::Result onSkipScanlines(int countLines) {
+        SkAutoMalloc storage(fDstInfo.minRowBytes());
+        // Note that we pass 0 to rowBytes so we continue to use the same memory.
+        // Also note that while getScanlines checks that rowBytes is big enough,
+        // onGetScanlines bypasses that check.
+        // Calling the virtual method also means we do not double count
+        // countLines.
+        return this->onGetScanlines(storage.get(), countLines, 0);
+    }
+
+    virtual SkImageGenerator::Result onGetScanlines(void* dst, int countLines,
+                                                    size_t rowBytes) = 0;
+
+    /**
+     *  Called after any set of scanlines read/skipped. Updates fCurrScanline,
+     *  and, if we are at the end, calls onFinish().
+     */
+    void checkForFinish(int countLines) {
+        fCurrScanline += countLines;
+        if (fCurrScanline >= fDstInfo.height()) {
+            this->onFinish();
+        }
+    }
+
+    /**
+     *  This function will be called after reading/skipping all scanlines to do
+     *  any necessary cleanups.
+     */
+    virtual void onFinish() {} // Default does nothing.
+};
+#endif // SkScanlineDecoder_DEFINED
diff --git a/include/core/SkAtomics.h b/include/core/SkAtomics.h
index cdd39ef..9866db4 100644
--- a/include/core/SkAtomics.h
+++ b/include/core/SkAtomics.h
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #ifndef SkAtomics_DEFINED
 #define SkAtomics_DEFINED
 
@@ -27,6 +34,9 @@
                                 sk_memory_order success = sk_memory_order_seq_cst,
                                 sk_memory_order failure = sk_memory_order_seq_cst);
 
+template <typename T>
+T sk_atomic_exchange(T*, T, sk_memory_order = sk_memory_order_seq_cst);
+
 // A little wrapper class for small T (think, builtins: int, float, void*) to
 // ensure they're always used atomically.  This is our stand-in for std::atomic<T>.
 template <typename T>
diff --git a/include/core/SkBBHFactory.h b/include/core/SkBBHFactory.h
index 57baf6b..f0c49f1 100644
--- a/include/core/SkBBHFactory.h
+++ b/include/core/SkBBHFactory.h
@@ -23,7 +23,7 @@
 
 class SK_API SkRTreeFactory : public SkBBHFactory {
 public:
-    SkBBoxHierarchy* operator()(const SkRect& bounds) const SK_OVERRIDE;
+    SkBBoxHierarchy* operator()(const SkRect& bounds) const override;
 private:
     typedef SkBBHFactory INHERITED;
 };
diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h
index c28e3f5..dbaca62 100644
--- a/include/core/SkBitmap.h
+++ b/include/core/SkBitmap.h
@@ -694,7 +694,7 @@
     */
     class HeapAllocator : public Allocator {
     public:
-        bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
+        bool allocPixelRef(SkBitmap*, SkColorTable*) override;
     };
 
     class RLEPixels {
@@ -753,8 +753,6 @@
     void freePixels();
     void updatePixelsFromRef() const;
 
-    void legacyUnflatten(SkReadBuffer&);
-
     static void WriteRawPixels(SkWriteBuffer*, const SkBitmap&);
     static bool ReadRawPixels(SkReadBuffer*, SkBitmap*);
 
diff --git a/include/core/SkBitmapDevice.h b/include/core/SkBitmapDevice.h
index 8ca6a52..69adc9a 100644
--- a/include/core/SkBitmapDevice.h
+++ b/include/core/SkBitmapDevice.h
@@ -35,25 +35,25 @@
         return Create(info, NULL);
     }
 
-    SkImageInfo imageInfo() const SK_OVERRIDE;
+    SkImageInfo imageInfo() const override;
 
 protected:
-    bool onShouldDisableLCD(const SkPaint&) const SK_OVERRIDE;
+    bool onShouldDisableLCD(const SkPaint&) const override;
 
     /** These are called inside the per-device-layer loop for each draw call.
      When these are called, we have already applied any saveLayer operations,
      and are handling any looping from the paint, and any effects from the
      DrawFilter.
      */
-    void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE;
+    void drawPaint(const SkDraw&, const SkPaint& paint) override;
     virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
-                            const SkPoint[], const SkPaint& paint) SK_OVERRIDE;
+                            const SkPoint[], const SkPaint& paint) override;
     virtual void drawRect(const SkDraw&, const SkRect& r,
-                          const SkPaint& paint) SK_OVERRIDE;
+                          const SkPaint& paint) override;
     virtual void drawOval(const SkDraw&, const SkRect& oval,
-                          const SkPaint& paint) SK_OVERRIDE;
+                          const SkPaint& paint) override;
     virtual void drawRRect(const SkDraw&, const SkRRect& rr,
-                           const SkPaint& paint) SK_OVERRIDE;
+                           const SkPaint& paint) override;
 
     /**
      *  If pathIsMutable, then the implementation is allowed to cast path to a
@@ -69,11 +69,11 @@
     virtual void drawPath(const SkDraw&, const SkPath& path,
                           const SkPaint& paint,
                           const SkMatrix* prePathMatrix = NULL,
-                          bool pathIsMutable = false) SK_OVERRIDE;
+                          bool pathIsMutable = false) override;
     virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
-                            const SkMatrix& matrix, const SkPaint& paint) SK_OVERRIDE;
+                            const SkMatrix& matrix, const SkPaint& paint) override;
     virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
-                            int x, int y, const SkPaint& paint) SK_OVERRIDE;
+                            int x, int y, const SkPaint& paint) override;
 
     /**
      *  The default impl. will create a bitmap-shader from the bitmap,
@@ -82,27 +82,23 @@
     virtual void drawBitmapRect(const SkDraw&, const SkBitmap&,
                                 const SkRect* srcOrNull, const SkRect& dst,
                                 const SkPaint& paint,
-                                SkCanvas::DrawBitmapRectFlags flags) SK_OVERRIDE;
+                                SkCanvas::DrawBitmapRectFlags flags) override;
 
     /**
      *  Does not handle text decoration.
      *  Decorations (underline and stike-thru) will be handled by SkCanvas.
      */
     virtual void drawText(const SkDraw&, const void* text, size_t len,
-                          SkScalar x, SkScalar y, const SkPaint& paint) SK_OVERRIDE;
+                          SkScalar x, SkScalar y, const SkPaint& paint) override;
     virtual void drawPosText(const SkDraw&, const void* text, size_t len,
                              const SkScalar pos[], int scalarsPerPos,
-                             const SkPoint& offset, const SkPaint& paint) SK_OVERRIDE;
+                             const SkPoint& offset, const SkPaint& paint) override;
     virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount,
                               const SkPoint verts[], const SkPoint texs[],
                               const SkColor colors[], SkXfermode* xmode,
                               const uint16_t indices[], int indexCount,
-                              const SkPaint& paint) SK_OVERRIDE;
-    /** The SkBaseDevice passed will be an SkBaseDevice which was returned by a call to
-        onCreateCompatibleDevice on this device with kSaveLayer_Usage.
-     */
-    virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
-                            const SkPaint&) SK_OVERRIDE;
+                              const SkPaint& paint) override;
+    virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, const SkPaint&) override;
 
     ///////////////////////////////////////////////////////////////////////////
 
@@ -111,7 +107,7 @@
         altered. The config/width/height/rowbytes must remain unchanged.
         @return the device contents as a bitmap
     */
-    const SkBitmap& onAccessBitmap() SK_OVERRIDE;
+    const SkBitmap& onAccessBitmap() override;
 
     SkPixelRef* getPixelRef() const { return fBitmap.pixelRef(); }
     // just for subclasses, to assign a custom pixelref
@@ -120,15 +116,15 @@
         return pr;
     }
 
-    bool onReadPixels(const SkImageInfo&, void*, size_t, int x, int y) SK_OVERRIDE;
-    bool onWritePixels(const SkImageInfo&, const void*, size_t, int, int) SK_OVERRIDE;
-    void* onAccessPixels(SkImageInfo* info, size_t* rowBytes) SK_OVERRIDE;
+    bool onReadPixels(const SkImageInfo&, void*, size_t, int x, int y) override;
+    bool onWritePixels(const SkImageInfo&, const void*, size_t, int, int) override;
+    void* onAccessPixels(SkImageInfo* info, size_t* rowBytes) override;
 
     /** Called when this device is installed into a Canvas. Balanced by a call
         to unlockPixels() when the device is removed from a Canvas.
     */
-    void lockPixels() SK_OVERRIDE;
-    void unlockPixels() SK_OVERRIDE;
+    void lockPixels() override;
+    void unlockPixels() override;
 
 private:
     friend class SkCanvas;
@@ -143,17 +139,19 @@
     // used to change the backend's pixels (and possibly config/rowbytes)
     // but cannot change the width/height, so there should be no change to
     // any clip information.
-    void replaceBitmapBackendForRasterSurface(const SkBitmap&) SK_OVERRIDE;
+    void replaceBitmapBackendForRasterSurface(const SkBitmap&) override;
 
-    SkBaseDevice* onCreateCompatibleDevice(const CreateInfo&) SK_OVERRIDE;
+    SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
 
-    SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) SK_OVERRIDE;
-    const void* peekPixels(SkImageInfo*, size_t* rowBytes) SK_OVERRIDE;
+    SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) override;
+    const void* peekPixels(SkImageInfo*, size_t* rowBytes) override;
 
-    SkImageFilter::Cache* getImageFilterCache() SK_OVERRIDE;
+    SkImageFilter::Cache* getImageFilterCache() override;
 
     SkBitmap    fBitmap;
 
+    void setNewSize(const SkISize&);  // Used by SkCanvas for resetForNextPicture().
+
     typedef SkBaseDevice INHERITED;
 };
 
diff --git a/include/core/SkBlitRow.h b/include/core/SkBlitRow.h
index 89aa214..56121eb 100644
--- a/include/core/SkBlitRow.h
+++ b/include/core/SkBlitRow.h
@@ -64,20 +64,12 @@
 
     static Proc32 Factory32(unsigned flags32);
 
-   /** Function pointer that blends a single color with a row of 32-bit colors
-       onto a 32-bit destination
-   */
-   typedef void (*ColorProc)(SkPMColor dst[], const SkPMColor src[], int count, SkPMColor color);
-
     /** Blend a single color onto a row of S32 pixels, writing the result
         into a row of D32 pixels. src and dst may be the same memory, but
         if they are not, they may not overlap.
      */
     static void Color32(SkPMColor dst[], const SkPMColor src[], int count, SkPMColor color);
 
-    //! Public entry-point to return a blit function ptr
-    static ColorProc ColorProcFactory();
-
     /** These static functions are called by the Factory and Factory32
         functions, and should return either NULL, or a
         platform-specific function-ptr to be used in place of the
@@ -85,7 +77,6 @@
      */
 
     static Proc32 PlatformProcs32(unsigned flags);
-    static ColorProc PlatformColorProc();
 
     static Proc16 PlatformFactory565(unsigned flags);
     static ColorProc16 PlatformColorFactory565(unsigned flags);
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 74bf909c..e7dac5e 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -130,7 +130,7 @@
      * origin of the base layer is always (0,0). The current drawable area may be
      * smaller (due to clipping or saveLayer).
      */
-    SkISize getBaseLayerSize() const;
+    virtual SkISize getBaseLayerSize() const;
 
     /**
      *  DEPRECATED: call getBaseLayerSize
@@ -951,7 +951,9 @@
         @param picture The recorded drawing commands to playback into this
                        canvas.
     */
-    void drawPicture(const SkPicture* picture);
+    void drawPicture(const SkPicture* picture) {
+        this->drawPicture(picture, NULL, NULL);
+    }
 
     /**
      *  Draw the picture into this canvas.
@@ -1256,7 +1258,13 @@
     // points to top of stack
     MCRec*      fMCRec;
     // the first N recs that can fit here mean we won't call malloc
-    uint32_t    fMCRecStorage[32];
+    enum {
+        kMCRecSize      = 128,  // most recent measurement
+        kMCRecCount     = 8,    // common depth for save/restores
+        kDeviceCMSize   = 136,  // most recent measurement
+    };
+    intptr_t fMCRecStorage[kMCRecSize * kMCRecCount / sizeof(intptr_t)];
+    intptr_t fDeviceCMStorage[kDeviceCMSize / sizeof(intptr_t)];
 
     const SkSurfaceProps fProps;
 
@@ -1295,6 +1303,8 @@
     SkCanvas(const SkIRect& bounds, InitFlags);
     SkCanvas(SkBaseDevice*, const SkSurfaceProps*, InitFlags);
 
+    void resetForNextPicture(const SkIRect& bounds);
+
     // needs gettotalclip()
     friend class SkCanvasStateUtils;
 
@@ -1321,9 +1331,8 @@
     void internalDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
                                 const SkRect& dst, const SkPaint* paint);
     void internalDrawPaint(const SkPaint& paint);
-    void internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
-                           SaveFlags, bool justForImageFilter, SaveLayerStrategy strategy);
-    void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*);
+    void internalSaveLayer(const SkRect* bounds, const SkPaint*, SaveFlags, SaveLayerStrategy);
+    void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*, bool isBitmapDevice);
 
     // shared by save() and saveLayer()
     void internalSave();
diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
index fe151a2..ea31481 100644
--- a/include/core/SkColorFilter.h
+++ b/include/core/SkColorFilter.h
@@ -68,31 +68,15 @@
         @param count    the number of entries in the src[] and result[] arrays
         @param result   written by the filter
     */
-    virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) const = 0;
-    /** Called with a scanline of colors, as if there was a shader installed.
-        The implementation writes out its filtered version into result[].
-        Note: shader and result may be the same buffer.
-        @param src      array of colors, possibly generated by a shader
-        @param count    the number of entries in the src[] and result[] arrays
-        @param result   written by the filter
-    */
-    virtual void filterSpan16(const uint16_t shader[], int count,
-                              uint16_t result[]) const;
+    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) const = 0;
 
     enum Flags {
-        /** If set the filter methods will not change the alpha channel of the
-            colors.
+        /** If set the filter methods will not change the alpha channel of the colors.
         */
         kAlphaUnchanged_Flag = 0x01,
-        /** If set, this subclass implements filterSpan16(). If this flag is
-            set, then kAlphaUnchanged_Flag must also be set.
-        */
-        kHasFilter16_Flag    = 0x02
     };
 
-    /** Returns the flags for this filter. Override in subclasses to return
-        custom flags.
+    /** Returns the flags for this filter. Override in subclasses to return custom flags.
     */
     virtual uint32_t getFlags() const { return 0; }
 
diff --git a/include/core/SkColorPriv.h b/include/core/SkColorPriv.h
index 9db7687..15c94ac 100644
--- a/include/core/SkColorPriv.h
+++ b/include/core/SkColorPriv.h
@@ -281,6 +281,16 @@
 // this helper explicitly returns a clean 16bit value (but slower)
 #define SkAlphaMulRGB16_ToU16(c, s)  (uint16_t)SkAlphaMulRGB16(c, s)
 
+/** Blend pre-expanded RGB32 with 16bit color value by the 0..32 scale parameter.
+    The computation yields only 16bits of valid data, but we claim to return
+    32bits, so that the compiler won't generate extra instructions to "clean"
+    the top 16bits.
+*/
+static inline U16CPU SkBlend32_RGB16(uint32_t src_expand, uint16_t dst, unsigned scale) {
+    uint32_t dst_expand = SkExpand_rgb_16(dst) * scale;
+    return SkCompact_rgb_16((src_expand + dst_expand) >> 5);
+}
+
 /** Blend src and dst 16bit colors by the 0..256 scale parameter.
     The computation yields only 16bits of valid data, but we claim
     to return 32bits, so that the compiler won't generate extra instructions to
diff --git a/include/core/SkColorTable.h b/include/core/SkColorTable.h
index d6b3c21..d31ddbd 100644
--- a/include/core/SkColorTable.h
+++ b/include/core/SkColorTable.h
@@ -26,6 +26,8 @@
 public:
     SK_DECLARE_INST_COUNT(SkColorTable)
 
+    /** Copy up to 256 colors into a new SkColorTable.
+     */
     SkColorTable(const SkPMColor colors[], int count);
     virtual ~SkColorTable();
 
diff --git a/include/core/SkComposeShader.h b/include/core/SkComposeShader.h
index 8e0d93f..dd10447 100644
--- a/include/core/SkComposeShader.h
+++ b/include/core/SkComposeShader.h
@@ -34,7 +34,7 @@
     SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode = NULL);
     virtual ~SkComposeShader();
 
-    size_t contextSize() const SK_OVERRIDE;
+    size_t contextSize() const override;
 
     class ComposeShaderContext : public SkShader::Context {
     public:
@@ -48,7 +48,7 @@
 
         virtual ~ComposeShaderContext();
 
-        void shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE;
+        void shadeSpan(int x, int y, SkPMColor[], int count) override;
 
     private:
         SkShader::Context* fShaderContextA;
@@ -62,15 +62,15 @@
     SkShader* getShaderB() { return fShaderB; }
 #endif
 
-    bool asACompose(ComposeRec* rec) const SK_OVERRIDE;
+    bool asACompose(ComposeRec* rec) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeShader)
 
 protected:
     SkComposeShader(SkReadBuffer& );
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void*) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
+    Context* onCreateContext(const ContextRec&, void*) const override;
 
 private:
     SkShader*   fShaderA;
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index c7c5c30..b474687 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -125,10 +125,9 @@
     };
 
 protected:
-    enum Usage {
-       kGeneral_Usage,
-       kSaveLayer_Usage,  // <! internal use only
-       kImageFilter_Usage // <! internal use only
+    enum TileUsage {
+        kPossible_TileUsage,    //!< the created device may be drawn tiled
+        kNever_TileUsage,       //!< the created device will never be drawn tiled
     };
 
     struct TextFlags {
@@ -210,6 +209,10 @@
                                 const SkPaint& paint,
                                 SkCanvas::DrawBitmapRectFlags flags) = 0;
 
+    virtual void drawImage(const SkDraw&, const SkImage*, SkScalar x, SkScalar y, const SkPaint&);
+    virtual void drawImageRect(const SkDraw&, const SkImage*, const SkRect* src, const SkRect& dst,
+                               const SkPaint&);
+
     /**
      *  Does not handle text decoration.
      *  Decorations (underline and stike-thru) will be handled by SkCanvas.
@@ -231,7 +234,7 @@
     virtual void drawPatch(const SkDraw&, const SkPoint cubics[12], const SkColor colors[4],
                            const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint);
     /** The SkDevice passed will be an SkDevice which was returned by a call to
-        onCreateCompatibleDevice on this device with kSaveLayer_Usage.
+        onCreateDevice on this device with kNeverTile_TileExpectation.
      */
     virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
                             const SkPaint&) = 0;
@@ -255,14 +258,6 @@
     virtual void unlockPixels() {}
 
     /**
-     *  Returns true if the device allows processing of this imagefilter. If
-     *  false is returned, then the filter is ignored. This may happen for
-     *  some subclasses that do not support pixel manipulations after drawing
-     *  has occurred (e.g. printing). The default implementation returns true.
-     */
-    virtual bool allowImageFilter(const SkImageFilter*) { return true; }
-
-    /**
      *  Override and return true for filters that the device can handle
      *  intrinsically. Doing so means that SkCanvas will pass-through this
      *  filter to drawSprite and drawDevice (and potentially filterImage).
@@ -336,21 +331,36 @@
                                           const SkPaint*);
 
     struct CreateInfo {
-        static SkPixelGeometry AdjustGeometry(const SkImageInfo&, Usage, SkPixelGeometry geo);
+        static SkPixelGeometry AdjustGeometry(const SkImageInfo&, TileUsage, SkPixelGeometry);
 
-        // The construct may change the pixel geometry based on usage as needed.
-        CreateInfo(const SkImageInfo& info, Usage usage, SkPixelGeometry geo)
+        // The constructor may change the pixel geometry based on other parameters.
+        CreateInfo(const SkImageInfo& info,
+                   TileUsage tileUsage,
+                   SkPixelGeometry geo,
+                   bool forImageFilter = false)
             : fInfo(info)
-            , fUsage(usage)
-            , fPixelGeometry(AdjustGeometry(info, usage, geo))
-        {}
+            , fTileUsage(tileUsage)
+            , fPixelGeometry(AdjustGeometry(info, tileUsage, geo))
+            , fForImageFilter(forImageFilter) {}
 
-        const SkImageInfo     fInfo;
-        const Usage           fUsage;
-        const SkPixelGeometry fPixelGeometry;
+        const SkImageInfo       fInfo;
+        const TileUsage         fTileUsage;
+        const SkPixelGeometry   fPixelGeometry;
+        const bool              fForImageFilter;
     };
 
-    virtual SkBaseDevice* onCreateCompatibleDevice(const CreateInfo&) {
+    /**
+     *  Create a new device based on CreateInfo. If the paint is not null, then it represents a
+     *  preview of how the new device will be composed with its creator device (this).
+     *
+     *  The subclass may be handed this device in drawDevice(), so it must always return
+     *  a device that it knows how to draw, and that it knows how to identify if it is not of the
+     *  same subclass (since drawDevice is passed a SkBaseDevice*). If the subclass cannot fulfill
+     *  that contract (e.g. PDF cannot support some settings on the paint) it should return NULL,
+     *  and the caller may then decide to explicitly create a bitmapdevice, knowing that later
+     *  it could not call drawDevice with it (but it could call drawSprite or drawBitmap).
+     */
+    virtual SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) {
         return NULL;
     }
 
diff --git a/include/core/SkDrawPictureCallback.h b/include/core/SkDrawPictureCallback.h
index ea30f59..ab6ca0a 100644
--- a/include/core/SkDrawPictureCallback.h
+++ b/include/core/SkDrawPictureCallback.h
@@ -27,7 +27,7 @@
 public:
     virtual bool abortDrawing() = 0;
 
-    bool abort() SK_OVERRIDE { return this->abortDrawing(); }
+    bool abort() override { return this->abortDrawing(); }
 };
 #endif
 
diff --git a/include/core/SkFixed.h b/include/core/SkFixed.h
index cde244e..c2a0a7a 100644
--- a/include/core/SkFixed.h
+++ b/include/core/SkFixed.h
@@ -28,7 +28,7 @@
 #define SK_FixedTanPIOver8  (0x6A0A)
 #define SK_FixedRoot2Over2  (0xB505)
 
-#define SkFixedToFloat(x)   ((x) * 1.5258789e-5f)
+#define SkFixedToFloat(x)   ((x) * 1.52587890625e-5f)
 #if 1
     #define SkFloatToFixed(x)   ((SkFixed)((x) * SK_Fixed1))
 #else
@@ -50,7 +50,7 @@
     #define SkFloatToFixed_Check(x) SkFloatToFixed(x)
 #endif
 
-#define SkFixedToDouble(x)  ((x) * 1.5258789e-5)
+#define SkFixedToDouble(x)  ((x) * 1.52587890625e-5)
 #define SkDoubleToFixed(x)  ((SkFixed)((x) * SK_Fixed1))
 
 /** Converts an integer to a SkFixed, asserting that the result does not overflow
@@ -78,20 +78,16 @@
 #define SkFixedAbs(x)       SkAbs32(x)
 #define SkFixedAve(a, b)    (((a) + (b)) >> 1)
 
-SkFixed SkFixedMul_portable(SkFixed, SkFixed);
-
 #define SkFixedDiv(numer, denom)    SkDivBits(numer, denom, 16)
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////
 // Now look for ASM overrides for our portable versions (should consider putting this in its own file)
 
-#ifdef SkLONGLONG
-    inline SkFixed SkFixedMul_longlong(SkFixed a, SkFixed b)
-    {
-        return (SkFixed)((int64_t)a * b >> 16);
-    }
-    #define SkFixedMul(a,b)     SkFixedMul_longlong(a,b)
-#endif
+inline SkFixed SkFixedMul_longlong(SkFixed a, SkFixed b) {
+    return (SkFixed)((int64_t)a * b >> 16);
+}
+#define SkFixedMul(a,b)     SkFixedMul_longlong(a,b)
+
 
 #if defined(SK_CPU_ARM32)
     /* This guy does not handle NaN or other obscurities, but is faster than
@@ -134,10 +130,6 @@
     #define SkFloatToFixed(x)  SkFloatToFixed_arm(x)
 #endif
 
-#ifndef SkFixedMul
-    #define SkFixedMul(x, y)    SkFixedMul_portable(x, y)
-#endif
-
 ///////////////////////////////////////////////////////////////////////////////
 
 typedef int64_t SkFixed3232;   // 32.32
diff --git a/include/core/SkFlattenable.h b/include/core/SkFlattenable.h
index 1868176..74f8717 100644
--- a/include/core/SkFlattenable.h
+++ b/include/core/SkFlattenable.h
@@ -49,7 +49,7 @@
     static SkFlattenable* CreateProc(SkReadBuffer&);                        \
     friend class SkPrivateEffectInitializer;                                \
     public:                                                                 \
-    Factory getFactory() const SK_OVERRIDE { return CreateProc; }
+    Factory getFactory() const override { return CreateProc; }
 
 /** For SkFlattenable derived objects with a valid type
     This macro should only be used in base class objects in core
diff --git a/include/core/SkGraphics.h b/include/core/SkGraphics.h
index b2fc365..ea74c58 100644
--- a/include/core/SkGraphics.h
+++ b/include/core/SkGraphics.h
@@ -78,7 +78,7 @@
     static void PurgeFontCache();
 
     /**
-     *  Scaling bitmaps with the SkPaint::kHigh_FilterLevel setting is
+     *  Scaling bitmaps with the kHigh_SkFilterQuality setting is
      *  expensive, so the result is saved in the global Scaled Image
      *  Cache.
      *
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index ec85516..7128f6e 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -64,6 +64,24 @@
      */
     static SkImage* NewFromData(SkData* data);
 
+    /**
+     *  Create a new image from the specified descriptor. Note - the caller is responsible for
+     *  managing the lifetime of the underlying platform texture.
+     *
+     *  Will return NULL if the specified descriptor is unsupported.
+     */
+    static SkImage* NewFromTexture(GrContext*, const GrBackendTextureDesc&,
+                                   SkAlphaType = kPremul_SkAlphaType);
+
+    /**
+     *  Create a new image by copying the pixels from the specified descriptor. No reference is
+     *  kept to the original platform texture.
+     *
+     *  Will return NULL if the specified descriptor is unsupported.
+     */
+    static SkImage* NewFromTextureCopy(GrContext*, const GrBackendTextureDesc&,
+                                       SkAlphaType = kPremul_SkAlphaType);
+
     int width() const { return fWidth; }
     int height() const { return fHeight; }
     uint32_t uniqueID() const { return fUniqueID; }
@@ -172,19 +190,6 @@
     static uint32_t NextUniqueID();
 
     typedef SkRefCnt INHERITED;
-
-    friend class SkCanvas;
-
-    void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const;
-
-    /**
-     *  Draw the image, cropped to the src rect, to the dst rect of a canvas.
-     *  If src is larger than the bounds of the image, the rest of the image is
-     *  filled with transparent black pixels.
-     *
-     *  See SkCanvas::drawBitmapRectToRect for similar behavior.
-     */
-    void drawRect(SkCanvas*, const SkRect* src, const SkRect& dst, const SkPaint*) const;
 };
 
 #endif
diff --git a/include/core/SkImageDecoder.h b/include/core/SkImageDecoder.h
index 4dfd3c2..f4ae78a 100644
--- a/include/core/SkImageDecoder.h
+++ b/include/core/SkImageDecoder.h
@@ -26,6 +26,7 @@
 public:
     virtual ~SkImageDecoder();
 
+    // TODO (scroggo): Merge with SkEncodedFormat
     enum Format {
         kUnknown_Format,
         kBMP_Format,
diff --git a/include/core/SkImageEncoder.h b/include/core/SkImageEncoder.h
index 6ee173c..a9eecc4 100644
--- a/include/core/SkImageEncoder.h
+++ b/include/core/SkImageEncoder.h
@@ -17,6 +17,7 @@
 
 class SkImageEncoder {
 public:
+    // TODO (scroggo): Merge with SkEncodedFormat.
     enum Type {
         kUnknown_Type,
         kBMP_Type,
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index cb18f7c..dab46ae 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -8,6 +8,7 @@
 #ifndef SkImageFilter_DEFINED
 #define SkImageFilter_DEFINED
 
+#include "SkFilterQuality.h"
 #include "SkFlattenable.h"
 #include "SkMatrix.h"
 #include "SkRect.h"
@@ -192,6 +193,13 @@
     // Default impl returns union of all input bounds.
     virtual void computeFastBounds(const SkRect&, SkRect*) const;
 
+    /**
+     * Create an SkMatrixImageFilter, which transforms its input by the given matrix.
+     */
+    static SkImageFilter* CreateMatrixFilter(const SkMatrix& matrix,
+                                             SkFilterQuality,
+                                             SkImageFilter* input = NULL);
+
 #if SK_SUPPORT_GPU
     /**
      * Wrap the given texture in a texture-backed SkBitmap.
@@ -228,7 +236,6 @@
         const CropRect& cropRect() const { return fCropRect; }
         int             inputCount() const { return fInputs.count(); }
         SkImageFilter** inputs() const { return fInputs.get(); }
-        uint32_t        uniqueID() const { return fUniqueID; }
 
         SkImageFilter*  getInput(int index) const { return fInputs[index]; }
 
@@ -242,12 +249,11 @@
         CropRect fCropRect;
         // most filters accept at most 2 input-filters
         SkAutoSTArray<2, SkImageFilter*> fInputs;
-        uint32_t fUniqueID;
 
         void allocInputs(int count);
     };
 
-    SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect = NULL, uint32_t uniqueID = 0);
+    SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect = NULL);
 
     virtual ~SkImageFilter();
 
@@ -260,7 +266,7 @@
      */
     explicit SkImageFilter(int inputCount, SkReadBuffer& rb);
 
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     /**
      *  This is the virtual which should be overridden by the derived class
diff --git a/include/core/SkImageGenerator.h b/include/core/SkImageGenerator.h
index 5de9d3d..9760205c 100644
--- a/include/core/SkImageGenerator.h
+++ b/include/core/SkImageGenerator.h
@@ -15,6 +15,8 @@
 class SkData;
 class SkImageGenerator;
 
+//#define SK_SUPPORT_LEGACY_OPTIONLESS_GET_PIXELS
+
 /**
  *  Takes ownership of SkImageGenerator.  If this method fails for
  *  whatever reason, it will return false and immediatetely delete
@@ -63,15 +65,9 @@
     SkData* refEncodedData() { return this->onRefEncodedData(); }
 
     /**
-     *  Return some information about the image, allowing the owner of
-     *  this object to allocate pixels.
-     *
-     *  Repeated calls to this function should give the same results,
-     *  allowing the PixelRef to be immutable.
-     *
-     *  @return false if anything goes wrong.
+     *  Return the ImageInfo associated with this generator.
      */
-    bool getInfo(SkImageInfo* info);
+    const SkImageInfo& getInfo() const { return fInfo; }
 
     /**
      *  Used to describe the result of a call to getPixels().
@@ -117,6 +113,34 @@
     };
 
     /**
+     *  Whether or not the memory passed to getPixels is zero initialized.
+     */
+    enum ZeroInitialized {
+        /**
+         *  The memory passed to getPixels is zero initialized. The SkCodec
+         *  may take advantage of this by skipping writing zeroes.
+         */
+        kYes_ZeroInitialized,
+        /**
+         *  The memory passed to getPixels has not been initialized to zero,
+         *  so the SkCodec must write all zeroes to memory.
+         *
+         *  This is the default. It will be used if no Options struct is used.
+         */
+        kNo_ZeroInitialized,
+    };
+
+    /**
+     *  Additional options to pass to getPixels.
+     */
+    struct Options {
+        Options()
+            : fZeroInitialized(kNo_ZeroInitialized) {}
+
+        ZeroInitialized fZeroInitialized;
+    };
+
+    /**
      *  Decode into the given pixels, a block of memory of size at
      *  least (info.fHeight - 1) * rowBytes + (info.fWidth *
      *  bytesPerPixel)
@@ -145,11 +169,12 @@
      *
      *  @return Result kSuccess, or another value explaining the type of failure.
      */
-    Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
+    Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options*,
                      SkPMColor ctable[], int* ctableCount);
 
     /**
-     *  Simplified version of getPixels() that asserts that info is NOT kIndex8_SkColorType.
+     *  Simplified version of getPixels() that asserts that info is NOT kIndex8_SkColorType and
+     *  uses the default Options.
      */
     Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes);
 
@@ -175,14 +200,24 @@
     static SkImageGenerator* NewFromData(SkData*);
 
 protected:
+    SkImageGenerator(const SkImageInfo& info) : fInfo(info) {}
+
     virtual SkData* onRefEncodedData();
-    virtual bool onGetInfo(SkImageInfo* info);
+
+#ifdef SK_SUPPORT_LEGACY_OPTIONLESS_GET_PIXELS
     virtual Result onGetPixels(const SkImageInfo& info,
                                void* pixels, size_t rowBytes,
                                SkPMColor ctable[], int* ctableCount);
+#endif
+    virtual Result onGetPixels(const SkImageInfo& info,
+                               void* pixels, size_t rowBytes, const Options&,
+                               SkPMColor ctable[], int* ctableCount);
     virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]);
     virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
                                  SkYUVColorSpace* colorSpace);
+
+private:
+    const SkImageInfo fInfo;
 };
 
 #endif  // SkImageGenerator_DEFINED
diff --git a/include/core/SkImageInfo.h b/include/core/SkImageInfo.h
index d915c09..01318fd 100644
--- a/include/core/SkImageInfo.h
+++ b/include/core/SkImageInfo.h
@@ -72,8 +72,9 @@
     kRGBA_8888_SkColorType,
     kBGRA_8888_SkColorType,
     kIndex_8_SkColorType,
+    kGray_8_SkColorType,
 
-    kLastEnum_SkColorType = kIndex_8_SkColorType,
+    kLastEnum_SkColorType = kGray_8_SkColorType,
 
 #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
     kN32_SkColorType = kBGRA_8888_SkColorType,
@@ -93,6 +94,7 @@
         4,  // RGBA_8888
         4,  // BGRA_8888
         1,  // kIndex_8
+        1,  // kGray_8
     };
     SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gSize) == (size_t)(kLastEnum_SkColorType + 1),
                       size_mismatch_with_SkColorType_enum);
diff --git a/include/core/SkMallocPixelRef.h b/include/core/SkMallocPixelRef.h
index 4cb1875..1cc9c4e 100644
--- a/include/core/SkMallocPixelRef.h
+++ b/include/core/SkMallocPixelRef.h
@@ -84,7 +84,7 @@
     public:
         virtual SkPixelRef* create(const SkImageInfo&,
                                    size_t rowBytes,
-                                   SkColorTable*) SK_OVERRIDE;
+                                   SkColorTable*) override;
     };
 
 protected:
@@ -93,9 +93,9 @@
                      bool ownPixels);
     virtual ~SkMallocPixelRef();
 
-    bool onNewLockPixels(LockRec*) SK_OVERRIDE;
-    void onUnlockPixels() SK_OVERRIDE;
-    size_t getAllocatedSizeInBytes() const SK_OVERRIDE;
+    bool onNewLockPixels(LockRec*) override;
+    void onUnlockPixels() override;
+    size_t getAllocatedSizeInBytes() const override;
 
 private:
     void*           fStorage;
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index da7838b..0252bc71 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -23,6 +23,24 @@
 */
 class SK_API SkMatrix {
 public:
+    static SkMatrix SK_WARN_UNUSED_RESULT MakeScale(SkScalar sx, SkScalar sy) {
+        SkMatrix m;
+        m.setScale(sx, sy);
+        return m;
+    }
+    
+    static SkMatrix SK_WARN_UNUSED_RESULT MakeScale(SkScalar scale) {
+        SkMatrix m;
+        m.setScale(scale, scale);
+        return m;
+    }
+    
+    static SkMatrix SK_WARN_UNUSED_RESULT MakeTrans(SkScalar dx, SkScalar dy) {
+        SkMatrix m;
+        m.setTranslate(dx, dy);
+        return m;
+    }
+
     /** Enum of bit fields for the mask return by getType().
         Use this to identify the complexity of the matrix.
     */
@@ -401,7 +419,12 @@
         @param count The number of points in src to read, and then transform
                      into dst.
     */
-    void mapPoints(SkPoint dst[], const SkPoint src[], int count) const;
+    void mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
+        SkASSERT((dst && src && count > 0) || 0 == count);
+        // no partial overlap
+        SkASSERT(src == dst || &dst[count] <= &src[0] || &src[count] <= &dst[0]);
+        this->getMapPtsProc()(*this, dst, src, count);
+    }
 
     /** Apply this matrix to the array of points, overwriting it with the
         transformed values.
@@ -457,6 +480,12 @@
         this->getMapXYProc()(*this, x, y, result);
     }
 
+    SkPoint mapXY(SkScalar x, SkScalar y) const {
+        SkPoint result;
+        this->getMapXYProc()(*this, x, y, &result);
+        return result;
+    }
+
     /** Apply this matrix to the array of vectors specified by src, and write
         the transformed vectors into the array of vectors specified by dst.
         This is similar to mapPoints, but ignores any translation in the matrix.
@@ -480,6 +509,17 @@
         this->mapVectors(vecs, vecs, count);
     }
 
+    void mapVector(SkScalar dx, SkScalar dy, SkVector* result) const {
+        SkVector vec = { dx, dy };
+        this->mapVectors(result, &vec, 1);
+    }
+
+    SkVector mapVector(SkScalar dx, SkScalar dy) const {
+        SkVector vec = { dx, dy };
+        this->mapVectors(&vec, &vec, 1);
+        return vec;
+    }
+
     /** Apply this matrix to the src rectangle, and write the transformed
         rectangle into dst. This is accomplished by transforming the 4 corners
         of src, and then setting dst to the bounds of those points.
@@ -607,6 +647,19 @@
     bool getMinMaxScales(SkScalar scaleFactors[2]) const;
 
     /**
+     *  Attempt to decompose this matrix into a scale-only component and whatever remains, where
+     *  the scale component is to be applied first.
+     *
+     *  M -> Remaining * Scale
+     *
+     *  On success, return true and assign the scale and remaining components (assuming their
+     *  respective parameters are not null). On failure return false and ignore the parameters.
+     *
+     *  Possible reasons to fail: perspective, one or more scale factors are zero.
+     */
+    bool decomposeScale(SkSize* scale, SkMatrix* remaining = NULL) const;
+
+    /**
      *  Return a reference to a const identity matrix
      */
     static const SkMatrix& I();
@@ -750,11 +803,10 @@
     static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
     static void ScaleTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
                                int count);
-    static void Rot_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
-    static void RotTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
-                             int count);
     static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
 
+    static void Affine_vpts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
+
     static const MapPtsProc gMapPtsProcs[];
 
     friend class SkPerspIter;
diff --git a/include/core/SkOnce.h b/include/core/SkOnce.h
index c26c49f..c7a87f7 100644
--- a/include/core/SkOnce.h
+++ b/include/core/SkOnce.h
@@ -28,8 +28,9 @@
 // OnceTest.cpp also should serve as a few other simple examples.
 
 #include "SkAtomics.h"
+#include "SkSpinlock.h"
 
-// This must be used in a global scope, not in fuction scope or as a class member.
+// This must be used in a global scope, not in function scope or as a class member.
 #define SK_DECLARE_STATIC_ONCE(name) namespace {} static SkOnceFlag name
 
 class SkOnceFlag;
@@ -53,24 +54,12 @@
 public:
     bool* mutableDone() { return &fDone; }
 
-    void acquire() {
-        // To act as a mutex, this needs an acquire barrier on success.
-        // sk_atomic_cas doesn't guarantee this ...
-        while (!sk_atomic_cas(&fSpinlock, 0, 1)) {
-            // spin
-        }
-        // ... so make sure to issue one of our own.
-        SkAssertResult(sk_acquire_load(&fSpinlock));
-    }
-
-    void release() {
-        // To act as a mutex, this needs a release barrier.  sk_atomic_cas guarantees this.
-        SkAssertResult(sk_atomic_cas(&fSpinlock, 1, 0));
-    }
+    void acquire() { fSpinlock.acquire(); }
+    void release() { fSpinlock.release(); }
 
 private:
     bool fDone;
-    int32_t fSpinlock;
+    SkPODSpinlock fSpinlock;
 };
 
 // We've pulled a pretty standard double-checked locking implementation apart
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index 5f02662..e4be06e 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -14,12 +14,11 @@
 #include "SkMatrix.h"
 #include "SkXfermode.h"
 
-// TODO: clean up Skia internals so we can remove this and only keep it for clients
-#define SK_SUPPORT_LEGACY_FILTERLEVEL_ENUM
-
 class SkAnnotation;
+class SkAutoDescriptor;
 class SkAutoGlyphCache;
 class SkColorFilter;
+class SkData;
 class SkDescriptor;
 struct SkDeviceProperties;
 class SkReadBuffer;
@@ -337,23 +336,6 @@
      */
     void setFilterQuality(SkFilterQuality quality);
 
-    /**
-     *  If the predicate is true, set the filterLevel to Low, else set it to
-     *  None.
-     */
-    SK_ATTR_DEPRECATED("use setFilterLevel")
-    void setFilterBitmap(bool doFilter) {
-        this->setFilterLevel(doFilter ? kLow_FilterLevel : kNone_FilterLevel);
-    }
-
-    /**
-     *  Returns true if getFilterLevel() returns anything other than None.
-     */
-    SK_ATTR_DEPRECATED("use getFilterLevel")
-    bool isFilterBitmap() const {
-        return kNone_FilterLevel != this->getFilterLevel();
-    }
-
     /** Styles apply to rect, oval, path, and text.
         Bitmaps are always drawn in "fill", and lines are always drawn in
         "stroke".
@@ -1080,6 +1062,13 @@
     SkScalar measure_text(SkGlyphCache*, const char* text, size_t length,
                           int* count, SkRect* bounds) const;
 
+    /*
+     * Allocs an SkDescriptor on the heap and return it to the caller as a refcnted
+     * SkData.  Caller is responsible for managing the lifetime of this object.
+     */
+    void getScalerContextDescriptor(SkAutoDescriptor*, const SkDeviceProperties* deviceProperties,
+                                    const SkMatrix*, bool ignoreGamma) const;
+
     SkGlyphCache* detachCache(const SkDeviceProperties* deviceProperties, const SkMatrix*,
                               bool ignoreGamma) const;
 
@@ -1087,6 +1076,13 @@
                         void (*proc)(SkTypeface*, const SkDescriptor*, void*),
                         void* context, bool ignoreGamma = false) const;
 
+    /*
+     * The luminance color is used to determine which Gamma Canonical color to map to.  This is
+     * really only used by backends which want to cache glyph masks, and need some way to know if
+     * they need to generate new masks based off a given color.
+     */
+    SkColor computeLuminanceColor() const;
+
     static void Term();
 
     enum {
@@ -1134,10 +1130,13 @@
     friend class SkGraphics; // So Term() can be called.
     friend class SkPDFDevice;
     friend class GrBitmapTextContext;
+    friend class GrAtlasTextContext;
     friend class GrDistanceFieldTextContext;
     friend class GrStencilAndCoverTextContext;
     friend class GrPathRendering;
+    friend class GrTextContext;
     friend class GrGLPathRendering;
+    friend class SkScalerContext;
     friend class SkTextToPathIter;
     friend class SkCanonicalizePaint;
 };
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 7444cc9..9dbc97b 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -581,7 +581,8 @@
      */
     bool isRect(SkRect* rect, bool* isClosed = NULL, Direction* direction = NULL) const;
 
-    /** Returns true if the path specifies a pair of nested rectangles. If so, and if
+    /** Returns true if the path specifies a pair of nested rectangles, or would draw a
+        pair of nested rectangles when filled. If so, and if
         rect is not null, set rect[0] to the outer rectangle and rect[1] to the inner
         rectangle. If so, and dirs is not null, set dirs[0] to the direction of
         the outer rectangle and dirs[1] to the direction of the inner rectangle. If
@@ -592,7 +593,7 @@
         @param dirs If not null, returns the direction of the rects
         @return true if the path describes a pair of nested rectangles
     */
-    bool isNestedRects(SkRect rect[2], Direction dirs[2] = NULL) const;
+    bool isNestedFillRects(SkRect rect[2], Direction dirs[2] = NULL) const;
 
     /**
      *  Add a closed rectangle contour to the path
@@ -917,7 +918,6 @@
         const uint8_t*  fVerbStop;
         const SkScalar* fConicWeights;
         SkPoint         fMoveTo;
-        SkPoint         fLastPt;
     };
 
     /**
@@ -1033,6 +1033,8 @@
         ed.setBounds(rect);
     }
 
+    void setPt(int index, SkScalar x, SkScalar y);
+
     friend class SkAutoPathBoundsUpdate;
     friend class SkAutoDisableOvalCheck;
     friend class SkAutoDisableDirectionCheck;
diff --git a/include/core/SkPathEffect.h b/include/core/SkPathEffect.h
index 7e2d1ae..09070bd 100644
--- a/include/core/SkPathEffect.h
+++ b/include/core/SkPathEffect.h
@@ -163,7 +163,7 @@
 protected:
     SkPairPathEffect(SkPathEffect* pe0, SkPathEffect* pe1);
 
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     // these are visible to our subclasses
     SkPathEffect* fPE0, *fPE1;
@@ -191,13 +191,13 @@
     }
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const override;
 
     SK_TO_STRING_OVERRIDE()    
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposePathEffect)
 
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
-    bool exposedInAndroidJavaAPI() const SK_OVERRIDE { return true; }
+    bool exposedInAndroidJavaAPI() const override { return true; }
 #endif
 
 protected:
@@ -228,13 +228,13 @@
     }
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const override;
 
     SK_TO_STRING_OVERRIDE()    
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSumPathEffect)
 
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
-    bool exposedInAndroidJavaAPI() const SK_OVERRIDE { return true; }
+    bool exposedInAndroidJavaAPI() const override { return true; }
 #endif
 
 protected:
diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h
index 346d65b..9a2b65b 100644
--- a/include/core/SkPicture.h
+++ b/include/core/SkPicture.h
@@ -10,6 +10,7 @@
 #define SkPicture_DEFINED
 
 #include "SkImageDecoder.h"
+#include "SkLazyPtr.h"
 #include "SkRefCnt.h"
 #include "SkTDArray.h"
 
@@ -41,8 +42,7 @@
 */
 class SK_API SkPicture : public SkNVRefCnt<SkPicture> {
 public:
-    // AccelData provides a base class for device-specific acceleration
-    // data. It is added to the picture via EXPERIMENTAL_addAccelData.
+    // AccelData provides a base class for device-specific acceleration data.
     class AccelData : public SkRefCnt {
     public:
         typedef uint8_t Domain;
@@ -60,9 +60,6 @@
     };
 
     /**  PRIVATE / EXPERIMENTAL -- do not call */
-    void EXPERIMENTAL_addAccelData(const AccelData*) const;
-
-    /**  PRIVATE / EXPERIMENTAL -- do not call */
     const AccelData* EXPERIMENTAL_getAccelData(AccelData::Key) const;
 
     /**
@@ -136,7 +133,7 @@
 
     /** Return a non-zero, unique value representing the picture.
      */
-    uint32_t uniqueID() const { return fUniqueID; }
+    uint32_t uniqueID() const;
 
     /**
      *  Serialize to a stream. If non NULL, serializer will be used to serialize
@@ -243,31 +240,43 @@
     // V37: Added shadow only option to SkDropShadowImageFilter (last version to record CLEAR)
     // V38: Added PictureResolution option to SkPictureImageFilter
     // V39: Added FilterLevel option to SkPictureImageFilter
+    // V40: Remove UniqueID serialization from SkImageFilter.
+    // V41: Added serialization of SkBitmapSource's filterQuality parameter
 
     // Note: If the picture version needs to be increased then please follow the
     // steps to generate new SKPs in (only accessible to Googlers): http://goo.gl/qATVcw
 
     // Only SKPs within the min/current picture version range (inclusive) can be read.
     static const uint32_t MIN_PICTURE_VERSION = 35;     // Produced by Chrome M39.
-    static const uint32_t CURRENT_PICTURE_VERSION = 39;
+    static const uint32_t CURRENT_PICTURE_VERSION = 41;
+
+    static_assert(MIN_PICTURE_VERSION <= 41,
+                  "Remove kFontFileName and related code from SkFontDescriptor.cpp.");
 
     void createHeader(SkPictInfo* info) const;
     static bool IsValidPictInfo(const SkPictInfo& info);
 
-    // Takes ownership of the SkRecord and (optional) SnapshotArray, refs the (optional) BBH.
-    SkPicture(const SkRect& cullRect, SkRecord*, SnapshotArray*, SkBBoxHierarchy*);
+    // Takes ownership of the (optional) SnapshotArray.
+    // For performance, we take ownership of the caller's refs on the SkRecord, BBH, and AccelData.
+    SkPicture(const SkRect& cullRect,
+              SkRecord*,
+              SnapshotArray*,
+              SkBBoxHierarchy*,
+              AccelData*,
+              size_t approxBytesUsedBySubPictures);
 
     static SkPicture* Forwardport(const SkPictInfo&, const SkPictureData*);
     static SkPictureData* Backport(const SkRecord&, const SkPictInfo&,
                                    SkPicture const* const drawablePics[], int drawableCount);
 
     // uint32_t fRefCnt; from SkNVRefCnt<SkPicture>
-    const uint32_t                        fUniqueID;
+    mutable uint32_t                      fUniqueID;
     const SkRect                          fCullRect;
-    mutable SkAutoTUnref<const AccelData> fAccelData;
     SkAutoTUnref<const SkRecord>          fRecord;
-    SkAutoTUnref<const SkBBoxHierarchy>   fBBH;
     SkAutoTDelete<const SnapshotArray>    fDrawablePicts;
+    SkAutoTUnref<const SkBBoxHierarchy>   fBBH;
+    SkAutoTUnref<const AccelData>         fAccelData;
+    const size_t                          fApproxBytesUsedBySubPictures;
 
     // helpers for fDrawablePicts
     int drawableCount() const;
@@ -282,14 +291,12 @@
 
         bool suitableForGpuRasterization(const char** reason, int sampleCount) const;
 
-        bool        fWillPlaybackBitmaps;
-        bool        fHasText;
-        int         fNumPaintWithPathEffectUses;
-        int         fNumFastPathDashEffects;
-        int         fNumAAConcavePaths;
-        int         fNumAAHairlineConcavePaths;
-        int         fNumAADFEligibleConcavePaths;
-    } fAnalysis;
+        uint8_t     fNumSlowPathsAndDashEffects;
+        bool        fWillPlaybackBitmaps : 1;
+        bool        fHasText             : 1;
+    };
+    SkLazyPtr<Analysis> fAnalysis;
+    const Analysis& analysis() const;
 
     friend class SkPictureRecorder;            // SkRecord-based constructor.
     friend class GrLayerHoister;               // access to fRecord
@@ -297,6 +304,6 @@
     friend class SkPictureUtils;
     friend class SkRecordedDrawable;
 };
-SK_COMPILE_ASSERT(sizeof(SkPicture) <= 96, SkPictureSize);
+SK_COMPILE_ASSERT(sizeof(SkPicture) <= 88, SkPictureSize);
 
 #endif
diff --git a/include/core/SkPictureRecorder.h b/include/core/SkPictureRecorder.h
index b25e26b..2cf7909 100644
--- a/include/core/SkPictureRecorder.h
+++ b/include/core/SkPictureRecorder.h
@@ -67,7 +67,7 @@
      *  reflect their current state, but will not contain a live reference to the drawables
      *  themselves.
      */
-    SkPicture* endRecordingAsPicture();
+    SkPicture* SK_WARN_UNUSED_RESULT endRecordingAsPicture();
 
     /**
      *  Signal that the caller is done recording. This invalidates the canvas returned by
@@ -79,10 +79,10 @@
      *  and therefore this drawable will reflect the current state of those nested drawables anytime
      *  it is drawn or a new picture is snapped from it (by calling drawable->newPictureSnapshot()).
      */
-    SkDrawable* endRecordingAsDrawable();
+    SkDrawable* SK_WARN_UNUSED_RESULT endRecordingAsDrawable();
 
     // Legacy API -- use endRecordingAsPicture instead.
-    SkPicture* endRecording() { return this->endRecordingAsPicture(); }
+    SkPicture* SK_WARN_UNUSED_RESULT endRecording() { return this->endRecordingAsPicture(); }
 
 private:
     void reset();
@@ -96,6 +96,7 @@
     friend class SkPictureRecorderReplayTester; // for unit testing
     void partialReplay(SkCanvas* canvas) const;
 
+    bool                          fActivelyRecording;
     uint32_t                      fFlags;
     SkRect                        fCullRect;
     SkAutoTUnref<SkBBoxHierarchy> fBBH;
diff --git a/include/core/SkPoint.h b/include/core/SkPoint.h
index 4a97391..52d01ae 100644
--- a/include/core/SkPoint.h
+++ b/include/core/SkPoint.h
@@ -348,6 +348,16 @@
         fY -= v.fY;
     }
 
+    SkPoint operator*(SkScalar scale) const {
+        return Make(fX * scale, fY * scale);
+    }
+    
+    SkPoint& operator*=(SkScalar scale) {
+        fX *= scale;
+        fY *= scale;
+        return *this;
+    }
+    
     /**
      *  Returns true if both X and Y are finite (not infinity or NaN)
      */
diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h
index cf111af..37e6c59 100644
--- a/include/core/SkPostConfig.h
+++ b/include/core/SkPostConfig.h
@@ -232,18 +232,6 @@
          SK_ ## C3 ## 32_SHIFT == 24)
 #endif
 
-//////////////////////////////////////////////////////////////////////
-
-// TODO: rebaseline as needed so we can remove this flag entirely.
-//  - all platforms have int64_t now
-//  - we have slightly different fixed math results because of this check
-//    since we don't define this for linux/android
-#if defined(SK_BUILD_FOR_WIN32) || defined(SK_BUILD_FOR_MAC)
-#  ifndef SkLONGLONG
-#    define SkLONGLONG int64_t
-#  endif
-#endif
-
 //////////////////////////////////////////////////////////////////////////////////////////////
 #ifndef SK_BUILD_FOR_WINCE
 #  include <string.h>
@@ -296,32 +284,6 @@
 
 //////////////////////////////////////////////////////////////////////
 
-#ifndef SK_OVERRIDE
-#  if defined(_MSC_VER)
-#    define SK_OVERRIDE override
-#  elif defined(__clang__)
-     // Using __attribute__((override)) on clang does not appear to always work.
-     // Clang defaults to C++03 and warns about using override. Squelch that. Intentionally no
-     // push/pop here so all users of SK_OVERRIDE ignore the warning too. This is like passing
-     // -Wno-c++11-extensions, except that GCC won't die (because it won't see this pragma).
-#    pragma clang diagnostic ignored "-Wc++11-extensions"
-#
-#    if __has_feature(cxx_override_control)
-#      define SK_OVERRIDE override
-#    elif defined(__has_extension) && __has_extension(cxx_override_control)
-#      define SK_OVERRIDE override
-#    endif
-   // if GCC >= 4.7
-#  elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
-#    define SK_OVERRIDE override
-#  endif
-#  ifndef SK_OVERRIDE
-#    define SK_OVERRIDE
-#  endif
-#endif
-
-//////////////////////////////////////////////////////////////////////
-
 #if !defined(SK_UNUSED)
 #  define SK_UNUSED SK_ATTRIBUTE(unused)
 #endif
@@ -392,7 +354,7 @@
 //////////////////////////////////////////////////////////////////////
 
 #ifndef SK_EGL
-#  if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_NACL)
+#  if defined(SK_BUILD_FOR_ANDROID)
 #    define SK_EGL 1
 #  else
 #    define SK_EGL 0
@@ -409,4 +371,10 @@
 #  define SK_GAMMA_EXPONENT (2.2f)
 #endif
 
+//////////////////////////////////////////////////////////////////////
+
+#ifndef GR_TEST_UTILS
+#  define GR_TEST_UTILS 1
+#endif
+
 #endif // SkPostConfig_DEFINED
diff --git a/include/core/SkPreConfig.h b/include/core/SkPreConfig.h
index c733111..745ba90 100644
--- a/include/core/SkPreConfig.h
+++ b/include/core/SkPreConfig.h
@@ -23,7 +23,7 @@
 
 //////////////////////////////////////////////////////////////////////
 
-#if !defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_PALM) && !defined(SK_BUILD_FOR_WINCE) && !defined(SK_BUILD_FOR_WIN32) && !defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_SDL) && !defined(SK_BUILD_FOR_BREW) && !defined(SK_BUILD_FOR_NACL)
+#if !defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_PALM) && !defined(SK_BUILD_FOR_WINCE) && !defined(SK_BUILD_FOR_WIN32) && !defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_SDL) && !defined(SK_BUILD_FOR_BREW)
 
     #ifdef __APPLE__
         #include "TargetConditionals.h"
@@ -197,6 +197,13 @@
     #define SK_CPU_ARM64
 #endif
 
+// All 64-bit ARM chips have NEON.  Many 32-bit ARM chips do too.
+// TODO: Why don't we want NEON on iOS?
+#if !defined(SK_ARM_HAS_NEON) && !defined(SK_BUILD_FOR_IOS) && \
+    (defined(SK_CPU_ARM64) || defined(__ARM_NEON__))
+    #define SK_ARM_HAS_NEON
+#endif
+
 //////////////////////////////////////////////////////////////////////
 
 #if !defined(SKIA_IMPLEMENTATION)
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
index 8d68c97..fe276e6 100644
--- a/include/core/SkRect.h
+++ b/include/core/SkRect.h
@@ -162,17 +162,24 @@
     /**
      *  Return a new IRect, built as an offset of this rect.
      */
-    SkIRect makeOffset(int dx, int dy) const {
+    SkIRect makeOffset(int32_t dx, int32_t dy) const {
         return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy);
     }
 
     /**
      *  Return a new IRect, built as an inset of this rect.
      */
-    SkIRect makeInset(int dx, int dy) const {
+    SkIRect makeInset(int32_t dx, int32_t dy) const {
         return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy);
     }
 
+    /**
+     *  Return a new Rect, built as an outset of this rect.
+     */
+    SkIRect makeOutset(int32_t dx, int32_t dy) const {
+        return MakeLTRB(fLeft - dx, fTop - dy, fRight + dx, fBottom + dy);
+    }
+
     /** Offset set the rectangle by adding dx to its left and right,
         and adding dy to its top and bottom.
     */
@@ -274,7 +281,6 @@
         If either rectangle is empty, do nothing and return false.
     */
     bool SK_WARN_UNUSED_RESULT intersect(const SkIRect& r) {
-        SkASSERT(&r);
         return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
     }
 
@@ -608,7 +614,7 @@
     SkRect makeOffset(SkScalar dx, SkScalar dy) const {
         return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy);
     }
-
+    
     /**
      *  Return a new Rect, built as an inset of this rect.
      */
@@ -616,6 +622,13 @@
         return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy);
     }
 
+    /**
+     *  Return a new Rect, built as an outset of this rect.
+     */
+    SkRect makeOutset(SkScalar dx, SkScalar dy) const {
+        return MakeLTRB(fLeft - dx, fTop - dy, fRight + dx, fBottom + dy);
+    }
+
     /** Offset set the rectangle by adding dx to its left and right,
         and adding dy to its top and bottom.
     */
@@ -734,14 +747,22 @@
         if (fLeft >= fRight || fTop >= fBottom) {
             *this = r;
         } else {
-            fLeft   = SkMinScalar(fLeft, r.left());
-            fTop    = SkMinScalar(fTop, r.top());
-            fRight  = SkMaxScalar(fRight, r.right());
-            fBottom = SkMaxScalar(fBottom, r.bottom());
+            this->joinPossiblyEmptyRect(r);
         }
     }
 
     /**
+     * Joins the rectangle with another without checking if either are empty (may produce unexpected
+     * results if either rect is inverted).
+     */
+    void joinPossiblyEmptyRect(const SkRect& r) {
+        fLeft   = SkMinScalar(fLeft, r.left());
+        fTop    = SkMinScalar(fTop, r.top());
+        fRight  = SkMaxScalar(fRight, r.right());
+        fBottom = SkMaxScalar(fBottom, r.bottom());
+    }
+
+    /**
      *  Grow the rect to include the specified (x,y). After this call, the
      *  following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
      *
diff --git a/include/core/SkRefCnt.h b/include/core/SkRefCnt.h
index 9c62fe2..1632361 100644
--- a/include/core/SkRefCnt.h
+++ b/include/core/SkRefCnt.h
@@ -208,30 +208,7 @@
         return obj;
     }
 
-    /**
-     *  BlockRef<B> is a type which inherits from B, cannot be created,
-     *  cannot be deleted, and makes ref and unref private.
-     */
-    template<typename B> class BlockRef : public B {
-    private:
-        BlockRef();
-        ~BlockRef();
-        void ref() const;
-        void unref() const;
-    };
-
-    /** If T is const, the type returned from operator-> will also be const. */
-    typedef typename SkTConstType<BlockRef<T>, SkTIsConst<T>::value>::type BlockRefType;
-
-    /**
-     *  SkAutoTUnref assumes ownership of the ref. As a result, it is an error
-     *  for the user to ref or unref through SkAutoTUnref. Therefore
-     *  SkAutoTUnref::operator-> returns BlockRef<T>*. This prevents use of
-     *  skAutoTUnrefInstance->ref() and skAutoTUnrefInstance->unref().
-     */
-    BlockRefType *operator->() const {
-        return static_cast<BlockRefType*>(fObj);
-    }
+    T* operator->() const { return fObj; }
     operator T*() const { return fObj; }
 
 private:
diff --git a/include/core/SkRegion.h b/include/core/SkRegion.h
index 20366bc..eb0f136 100644
--- a/include/core/SkRegion.h
+++ b/include/core/SkRegion.h
@@ -59,7 +59,6 @@
      *  resulting region is non-empty.
      */
     bool set(const SkRegion& src) {
-        SkASSERT(&src);
         *this = src;
         return !this->isEmpty();
     }
diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h
index fae23eb..2c42eb8 100644
--- a/include/core/SkScalar.h
+++ b/include/core/SkScalar.h
@@ -175,9 +175,7 @@
 }
 
 static inline SkScalar SkScalarPin(SkScalar x, SkScalar min, SkScalar max) {
-    x = SkTMin(x, max);
-    x = SkTMax(x, min);
-    return x;
+    return SkTPin(x, min, max);
 }
 
 SkScalar SkScalarSinCos(SkScalar radians, SkScalar* cosValue);
@@ -186,7 +184,9 @@
 
 #define SkScalarMul(a, b)       ((SkScalar)(a) * (b))
 #define SkScalarMulAdd(a, b, c) ((SkScalar)(a) * (b) + (c))
-#define SkScalarDiv(a, b)       ((SkScalar)(a) / (b))
+#ifdef SK_SUPPORT_LEGACY_SCALAR_DIV
+    #define SkScalarDiv(a, b)       ((SkScalar)(a) / (b))
+#endif
 #define SkScalarMulDiv(a, b, c) ((SkScalar)(a) * (b) / (c))
 #define SkScalarInvert(x)       (SK_Scalar1 / (x))
 #define SkScalarFastInvert(x)   (SK_Scalar1 / (x))
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index bf9fca5..4c4dba3 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -65,8 +65,10 @@
         /** only draw within the original domain, return 0 everywhere else */
         kDecal_TileMode,
 #endif
+    };
 
-        kTileModeCount
+    enum {
+        kTileModeCount = kMirror_TileMode + 1
     };
 
     // override these in your subclass
@@ -244,19 +246,6 @@
                             //   to (0,0) as bitmap x coord, where angle = 0 is
                             //   bitmap left edge of bitmap = 2pi is the
                             //   right edge. Bitmap is 1 pixel tall. No extras
-        kTwoPointRadial_BitmapType,
-                            //<! Matrix transforms to space where (0,0) is
-                            //   the center of the starting circle.  The second
-                            //   circle will be centered (x, 0) where x  may be
-                            //   0. The post-matrix space is normalized such
-                            //   that 1 is the second radius - first radius.
-                            //   Three extra parameters are returned:
-                            //      0: x-offset of second circle center
-                            //         to first.
-                            //      1: radius of first circle in post-matrix
-                            //         space
-                            //      2: the second radius minus the first radius
-                            //         in pre-transformed space.
         kTwoPointConical_BitmapType,
                             //<! Matrix transforms to space where (0,0) is
                             //   the center of the starting circle.  The second
@@ -312,7 +301,7 @@
      *      fPoint[0] and fPoint[1] are the end-points of the gradient
      *  Radial:
      *      fPoint[0] and fRadius[0] are the center and radius
-     *  Radial2:
+     *  Conical:
      *      fPoint[0] and fRadius[0] are the center and radius of the 1st circle
      *      fPoint[1] and fRadius[1] are the center and radius of the 2nd circle
      *  Sweep:
@@ -324,7 +313,6 @@
         kColor_GradientType,
         kLinear_GradientType,
         kRadial_GradientType,
-        kRadial2_GradientType,
         kSweep_GradientType,
         kConical_GradientType,
         kLast_GradientType = kConical_GradientType
@@ -474,7 +462,7 @@
     SK_DEFINE_FLATTENABLE_TYPE(SkShader)
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     bool computeTotalInverse(const ContextRec&, SkMatrix* totalInverse) const;
 
diff --git a/include/core/SkSpinlock.h b/include/core/SkSpinlock.h
new file mode 100644
index 0000000..68a6ff7
--- /dev/null
+++ b/include/core/SkSpinlock.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This file is not part of the public Skia API.
+
+#ifndef SkSpinlock_DEFINED
+#define SkSpinlock_DEFINED
+
+#include "SkAtomics.h"
+
+#define SK_DECLARE_STATIC_SPINLOCK(name) namespace {} static SkPODSpinlock name
+
+// This class has no constructor and must be zero-initialized (the macro above does this).
+struct SkPODSpinlock {
+    void acquire() {
+        // To act as a mutex, we need an acquire barrier.
+        while(sk_atomic_exchange(&fLocked, true, sk_memory_order_acquire)) { /*spin*/ }
+    }
+    void release() {
+        // To act as a mutex, we need a release barrier.
+        sk_atomic_store(&fLocked, false, sk_memory_order_release);
+    }
+
+    bool fLocked;
+};
+
+// For non-global-static use cases, this is normally what you want.
+class SkSpinlock : public SkPODSpinlock {
+public:
+    SkSpinlock() { this->release(); }
+};
+
+#endif//SkSpinlock_DEFINED
diff --git a/include/core/SkStream.h b/include/core/SkStream.h
index 9e6c261..4c9c461 100644
--- a/include/core/SkStream.h
+++ b/include/core/SkStream.h
@@ -63,6 +63,20 @@
         return this->read(NULL, size);
     }
 
+    /**
+     *  Attempt to peek at size bytes.
+     *  If this stream supports peeking, and it can peek size bytes, copy size
+     *  bytes into buffer, and return true.
+     *  If the stream does not support peeking, or cannot peek size bytes,
+     *  return false and leave buffer unchanged.
+     *  The stream is guaranteed to be in the same visible state after this
+     *  call, regardless of success or failure.
+     *  @param buffer Must not be NULL. Destination to copy bytes.
+     *  @param size Number of bytes to copy.
+     *  @return Whether the peek was performed.
+     */
+    virtual bool peek(void* /* buffer */, size_t /* size */) const { return false; }
+
     /** Returns true when all the bytes in the stream have been read.
      *  This may return true early (when there are no more bytes to be read)
      *  or late (after the first unsuccessful read).
@@ -130,39 +144,39 @@
 /** SkStreamRewindable is a SkStream for which rewind and duplicate are required. */
 class SK_API SkStreamRewindable : public SkStream {
 public:
-    bool rewind() SK_OVERRIDE = 0;
-    SkStreamRewindable* duplicate() const SK_OVERRIDE = 0;
+    bool rewind() override = 0;
+    SkStreamRewindable* duplicate() const override = 0;
 };
 
 /** SkStreamSeekable is a SkStreamRewindable for which position, seek, move, and fork are required. */
 class SK_API SkStreamSeekable : public SkStreamRewindable {
 public:
-    SkStreamSeekable* duplicate() const SK_OVERRIDE = 0;
+    SkStreamSeekable* duplicate() const override = 0;
 
-    bool hasPosition() const SK_OVERRIDE { return true; }
-    size_t getPosition() const SK_OVERRIDE = 0;
-    bool seek(size_t position) SK_OVERRIDE = 0;
-    bool move(long offset) SK_OVERRIDE = 0;
-    SkStreamSeekable* fork() const SK_OVERRIDE = 0;
+    bool hasPosition() const override { return true; }
+    size_t getPosition() const override = 0;
+    bool seek(size_t position) override = 0;
+    bool move(long offset) override = 0;
+    SkStreamSeekable* fork() const override = 0;
 };
 
 /** SkStreamAsset is a SkStreamSeekable for which getLength is required. */
 class SK_API SkStreamAsset : public SkStreamSeekable {
 public:
-    SkStreamAsset* duplicate() const SK_OVERRIDE = 0;
-    SkStreamAsset* fork() const SK_OVERRIDE = 0;
+    SkStreamAsset* duplicate() const override = 0;
+    SkStreamAsset* fork() const override = 0;
 
-    bool hasLength() const SK_OVERRIDE { return true; }
-    size_t getLength() const SK_OVERRIDE = 0;
+    bool hasLength() const override { return true; }
+    size_t getLength() const override = 0;
 };
 
 /** SkStreamMemory is a SkStreamAsset for which getMemoryBase is required. */
 class SK_API SkStreamMemory : public SkStreamAsset {
 public:
-    SkStreamMemory* duplicate() const SK_OVERRIDE = 0;
-    SkStreamMemory* fork() const SK_OVERRIDE = 0;
+    SkStreamMemory* duplicate() const override = 0;
+    SkStreamMemory* fork() const override = 0;
 
-    const void* getMemoryBase() SK_OVERRIDE = 0;
+    const void* getMemoryBase() override = 0;
 };
 
 class SK_API SkWStream : SkNoncopyable {
@@ -245,20 +259,20 @@
      */
     void setPath(const char path[]);
 
-    size_t read(void* buffer, size_t size) SK_OVERRIDE;
-    bool isAtEnd() const SK_OVERRIDE;
+    size_t read(void* buffer, size_t size) override;
+    bool isAtEnd() const override;
 
-    bool rewind() SK_OVERRIDE;
-    SkStreamAsset* duplicate() const SK_OVERRIDE;
+    bool rewind() override;
+    SkStreamAsset* duplicate() const override;
 
-    size_t getPosition() const SK_OVERRIDE;
-    bool seek(size_t position) SK_OVERRIDE;
-    bool move(long offset) SK_OVERRIDE;
-    SkStreamAsset* fork() const SK_OVERRIDE;
+    size_t getPosition() const override;
+    bool seek(size_t position) override;
+    bool move(long offset) override;
+    SkStreamAsset* fork() const override;
 
-    size_t getLength() const SK_OVERRIDE;
+    size_t getLength() const override;
 
-    const void* getMemoryBase() SK_OVERRIDE;
+    const void* getMemoryBase() override;
 
 private:
     SkFILE*     fFILE;
@@ -315,22 +329,23 @@
 
     void skipToAlign4();
     const void* getAtPos();
-    size_t peek() const { return fOffset; }
 
-    size_t read(void* buffer, size_t size) SK_OVERRIDE;
-    bool isAtEnd() const SK_OVERRIDE;
+    size_t read(void* buffer, size_t size) override;
+    bool isAtEnd() const override;
 
-    bool rewind() SK_OVERRIDE;
-    SkMemoryStream* duplicate() const SK_OVERRIDE;
+    bool peek(void* buffer, size_t size) const override;
 
-    size_t getPosition() const SK_OVERRIDE;
-    bool seek(size_t position) SK_OVERRIDE;
-    bool move(long offset) SK_OVERRIDE;
-    SkMemoryStream* fork() const SK_OVERRIDE;
+    bool rewind() override;
+    SkMemoryStream* duplicate() const override;
 
-    size_t getLength() const SK_OVERRIDE;
+    size_t getPosition() const override;
+    bool seek(size_t position) override;
+    bool move(long offset) override;
+    SkMemoryStream* fork() const override;
 
-    const void* getMemoryBase() SK_OVERRIDE;
+    size_t getLength() const override;
+
+    const void* getMemoryBase() override;
 
 private:
     SkData* fData;
@@ -352,9 +367,9 @@
     */
     bool isValid() const { return fFILE != NULL; }
 
-    bool write(const void* buffer, size_t size) SK_OVERRIDE;
-    void flush() SK_OVERRIDE;
-    size_t bytesWritten() const SK_OVERRIDE;
+    bool write(const void* buffer, size_t size) override;
+    void flush() override;
+    size_t bytesWritten() const override;
 
 private:
     SkFILE* fFILE;
@@ -367,8 +382,8 @@
     SK_DECLARE_INST_COUNT(SkMemoryWStream)
 
     SkMemoryWStream(void* buffer, size_t size);
-    bool write(const void* buffer, size_t size) SK_OVERRIDE;
-    size_t bytesWritten() const SK_OVERRIDE { return fBytesWritten; }
+    bool write(const void* buffer, size_t size) override;
+    size_t bytesWritten() const override { return fBytesWritten; }
 
 private:
     char*   fBuffer;
@@ -385,8 +400,8 @@
     SkDynamicMemoryWStream();
     virtual ~SkDynamicMemoryWStream();
 
-    bool write(const void* buffer, size_t size) SK_OVERRIDE;
-    size_t bytesWritten() const SK_OVERRIDE { return fBytesWritten; }
+    bool write(const void* buffer, size_t size) override;
+    size_t bytesWritten() const override { return fBytesWritten; }
     // random access write
     // modifies stream and returns true if offset + size is less than or equal to getOffset()
     bool write(const void* buffer, size_t offset, size_t size);
@@ -432,9 +447,9 @@
     SK_DECLARE_INST_COUNT(SkDebugWStream)
 
     // overrides
-    bool write(const void* buffer, size_t size) SK_OVERRIDE;
-    void newline() SK_OVERRIDE;
-    size_t bytesWritten() const SK_OVERRIDE { return fBytesWritten; }
+    bool write(const void* buffer, size_t size) override;
+    void newline() override;
+    size_t bytesWritten() const override { return fBytesWritten; }
 
 private:
     size_t fBytesWritten;
diff --git a/include/core/SkString.h b/include/core/SkString.h
index 7766c57..e0a51c6 100644
--- a/include/core/SkString.h
+++ b/include/core/SkString.h
@@ -238,7 +238,6 @@
     Rec* fRec;
 
 #ifdef SK_DEBUG
-    const char* fStr;
     void validate() const;
 #else
     void validate() const {}
diff --git a/include/core/SkStrokeRec.h b/include/core/SkStrokeRec.h
index 4858fce..b56dacb 100644
--- a/include/core/SkStrokeRec.h
+++ b/include/core/SkStrokeRec.h
@@ -64,6 +64,11 @@
         fMiterLimit = miterLimit;
     }
 
+    void setResScale(SkScalar rs) {
+        SkASSERT(rs > 0 && SkScalarIsFinite(rs));
+        fResScale = rs;
+    }
+
     /**
      *  Returns true if this specifes any thick stroking, i.e. applyToPath()
      *  will return true.
@@ -90,12 +95,20 @@
      */
     void applyToPaint(SkPaint* paint) const;
 
-    bool operator==(const SkStrokeRec& other) const {
-            return fWidth == other.fWidth &&
-                   fMiterLimit == other.fMiterLimit &&
-                   fCap == other.fCap &&
-                   fJoin == other.fJoin &&
-                   fStrokeAndFill == other.fStrokeAndFill;
+    /**
+     * Compare if two SkStrokeRecs have an equal effect on a path.
+     * Equal SkStrokeRecs produce equal paths. Equality of produced
+     * paths does not take the ResScale parameter into account.
+     */
+    bool hasEqualEffect(const SkStrokeRec& other) const {
+        if (!this->needToApply()) {
+            return this->getStyle() == other.getStyle();
+        }
+        return fWidth == other.fWidth &&
+               fMiterLimit == other.fMiterLimit &&
+               fCap == other.fCap &&
+               fJoin == other.fJoin &&
+               fStrokeAndFill == other.fStrokeAndFill;
     }
 
 private:
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
index a87ecce..2df9865 100644
--- a/include/core/SkSurface.h
+++ b/include/core/SkSurface.h
@@ -93,6 +93,13 @@
     static SkSurface* NewRenderTargetDirect(GrRenderTarget* target) {
         return NewRenderTargetDirect(target, NULL);
     }
+
+    /**
+     *  Used to wrap a pre-existing backend 3D API texture in a SkSurface. The kRenderTarget flag
+     *  must be set on GrBackendTextureDesc for this to succeed.
+     */
+    static SkSurface* NewWrappedRenderTarget(GrContext*, GrBackendTextureDesc,
+                                             const SkSurfaceProps*);
     
     /**
      *  Return a new surface whose contents will be drawn to an offscreen
diff --git a/include/core/SkTArray.h b/include/core/SkTArray.h
index dea0e38..a035f6e 100644
--- a/include/core/SkTArray.h
+++ b/include/core/SkTArray.h
@@ -300,7 +300,7 @@
         return fItemArray ? fItemArray + fCount : NULL;
     }
     const T* end() const {
-        return fItemArray ? fItemArray + fCount : NULL;;
+        return fItemArray ? fItemArray + fCount : NULL;
     }
 
    /**
diff --git a/include/core/SkTDArray.h b/include/core/SkTDArray.h
index 31a11c7..337b6fd 100644
--- a/include/core/SkTDArray.h
+++ b/include/core/SkTDArray.h
@@ -136,7 +136,7 @@
      *  Sets the number of elements in the array.
      *  If the array does not have space for count elements, it will increase
      *  the storage allocated to some amount greater than that required.
-     *  It will never shrink the shrink the storage.
+     *  It will never shrink the storage.
      */
     void setCount(int count) {
         SkASSERT(count >= 0);
diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h
index b18556b..0488a29 100644
--- a/include/core/SkTemplates.h
+++ b/include/core/SkTemplates.h
@@ -132,7 +132,7 @@
     ~SkAutoTDelete() { SkDELETE(fObj); }
 
     T* get() const { return fObj; }
-    operator T*() { return fObj; }
+    operator T*() const { return fObj; }
     T& operator*() const { SkASSERT(fObj); return *fObj; }
     T* operator->() const { SkASSERT(fObj); return fObj; }
 
@@ -411,17 +411,13 @@
 
 template <size_t N, typename T> class SkAutoSTMalloc : SkNoncopyable {
 public:
-    SkAutoSTMalloc() {
-        fPtr = NULL;
-    }
+    SkAutoSTMalloc() : fPtr(fTStorage) {}
 
     SkAutoSTMalloc(size_t count) {
         if (count > N) {
             fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
-        } else if (count) {
-            fPtr = fTStorage;
         } else {
-            fPtr = NULL;
+            fPtr = fTStorage;
         }
     }
 
@@ -437,11 +433,9 @@
             sk_free(fPtr);
         }
         if (count > N) {
-            fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
-        } else if (count) {
-            fPtr = fTStorage;
+            fPtr = (T*)sk_malloc_throw(count * sizeof(T));
         } else {
-            fPtr = NULL;
+            fPtr = fTStorage;
         }
         return fPtr;
     }
@@ -464,6 +458,20 @@
         return fPtr[index];
     }
 
+    // Reallocs the array, can be used to shrink the allocation.  Makes no attempt to be intelligent
+    void realloc(size_t count) {
+        if (count > N) {
+            if (fPtr == fTStorage) {
+                fPtr = (T*)sk_malloc_throw(count * sizeof(T));
+                memcpy(fPtr, fTStorage, N * sizeof(T));
+            } else {
+                fPtr = (T*)sk_realloc_throw(fPtr, count * sizeof(T));
+            }
+        } else if (fPtr != fTStorage) {
+            fPtr = (T*)sk_realloc_throw(fPtr, count * sizeof(T));
+        }
+    }
+
 private:
     T*          fPtr;
     union {
diff --git a/include/core/SkTextBlob.h b/include/core/SkTextBlob.h
index 1f04d38..b1ae456 100644
--- a/include/core/SkTextBlob.h
+++ b/include/core/SkTextBlob.h
@@ -30,7 +30,7 @@
     /**
      *  Return a non-zero, unique value representing the text blob.
      */
-    uint32_t uniqueID() const;
+    uint32_t uniqueID() const { return fUniqueID; }
 
     /**
      *  Serialize to a buffer.
@@ -68,6 +68,7 @@
         const SkPoint& offset() const;
         void applyFontToPaint(SkPaint*) const;
         GlyphPositioning positioning() const;
+        bool isLCD() const;
 
     private:
         const RunRecord* fCurrentRun;
@@ -91,13 +92,16 @@
 
     static unsigned ScalarsPerGlyph(GlyphPositioning pos);
 
+    friend class GrAtlasTextContext;
+    friend class GrTextBlobCache;
+    friend class GrTextContext;
     friend class SkBaseDevice;
     friend class SkTextBlobBuilder;
     friend class TextBlobTester;
 
     const int        fRunCount;
     const SkRect     fBounds;
-    mutable uint32_t fUniqueID;
+    const uint32_t fUniqueID;
 
     SkDEBUGCODE(size_t fStorageSize;)
 
diff --git a/include/core/SkTime.h b/include/core/SkTime.h
index 51616d4..a865634 100644
--- a/include/core/SkTime.h
+++ b/include/core/SkTime.h
@@ -12,12 +12,16 @@
 
 #include "SkTypes.h"
 
+class SkString;
+
 /** \class SkTime
     Platform-implemented utilities to return time of day, and millisecond counter.
 */
 class SkTime {
 public:
     struct DateTime {
+        int16_t  fTimeZoneMinutes;  // The number of minutes that GetDateTime()
+                                    // is ahead of or behind UTC.
         uint16_t fYear;          //!< e.g. 2005
         uint8_t  fMonth;         //!< 1..12
         uint8_t  fDayOfWeek;     //!< 0..6, 0==Sunday
@@ -25,6 +29,8 @@
         uint8_t  fHour;          //!< 0..23
         uint8_t  fMinute;        //!< 0..59
         uint8_t  fSecond;        //!< 0..59
+
+        void toISO8601(SkString* dst) const;
     };
     static void GetDateTime(DateTime*);
 
diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h
index 6a6d724..7d8b892 100644
--- a/include/core/SkTypeface.h
+++ b/include/core/SkTypeface.h
@@ -10,9 +10,10 @@
 #ifndef SkTypeface_DEFINED
 #define SkTypeface_DEFINED
 
-#include "SkAdvancedTypefaceMetrics.h"
 #include "SkFontStyle.h"
 #include "SkLazyPtr.h"
+#include "SkRect.h"
+#include "SkString.h"
 #include "SkWeakRefCnt.h"
 
 class SkDescriptor;
@@ -307,6 +308,16 @@
     }
 
 protected:
+    // The type of advance data wanted.
+    enum PerGlyphInfo {
+        kNo_PerGlyphInfo         = 0x0, // Don't populate any per glyph info.
+        kHAdvance_PerGlyphInfo   = 0x1, // Populate horizontal advance data.
+        kVAdvance_PerGlyphInfo   = 0x2, // Populate vertical advance data.
+        kGlyphNames_PerGlyphInfo = 0x4, // Populate glyph names (Type 1 only).
+        kToUnicode_PerGlyphInfo  = 0x8  // Populate ToUnicode table, ignored
+        // for Type 1 fonts
+    };
+
     /** uniqueID must be unique and non-zero
     */
     SkTypeface(const SkFontStyle& style, SkFontID uniqueID, bool isFixedPitch = false);
@@ -321,7 +332,7 @@
     virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const = 0;
     virtual void onFilterRec(SkScalerContextRec*) const = 0;
     virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+                        PerGlyphInfo,
                         const uint32_t* glyphIDs,
                         uint32_t glyphIDsCount) const = 0;
 
@@ -368,7 +379,7 @@
      @return The returned object has already been referenced.
      */
     SkAdvancedTypefaceMetrics* getAdvancedTypefaceMetrics(
-                          SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+                          PerGlyphInfo,
                           const uint32_t* glyphIDs = NULL,
                           uint32_t glyphIDsCount = 0) const;
 
diff --git a/include/core/SkTypes.h b/include/core/SkTypes.h
index 39923c5..1b0b12c 100644
--- a/include/core/SkTypes.h
+++ b/include/core/SkTypes.h
@@ -14,6 +14,12 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#if defined(SK_ARM_HAS_NEON)
+    #include <arm_neon.h>
+#elif SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
+    #include <immintrin.h>
+#endif
+
 /** \file SkTypes.h
 */
 
@@ -73,7 +79,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifdef SK_OVERRIDE_GLOBAL_NEW
+#ifdef override_GLOBAL_NEW
 #include <new>
 
 inline void* operator new(size_t size) {
@@ -138,7 +144,7 @@
     #define SK_TO_STRING_NONVIRT() void toString(SkString* str) const;
     #define SK_TO_STRING_VIRT() virtual void toString(SkString* str) const;
     #define SK_TO_STRING_PUREVIRT() virtual void toString(SkString* str) const = 0;
-    #define SK_TO_STRING_OVERRIDE() void toString(SkString* str) const SK_OVERRIDE;
+    #define SK_TO_STRING_OVERRIDE() void toString(SkString* str) const override;
 #endif
 
 template <bool>
@@ -290,9 +296,9 @@
     #define SK_OFFSETOF(type, field)    (size_t)((char*)&(((type*)1)->field) - (char*)1)
 #endif
 
-/** Returns the number of entries in an array (not a pointer)
-*/
-#define SK_ARRAY_COUNT(array)       (sizeof(array) / sizeof(array[0]))
+/** Returns the number of entries in an array (not a pointer) */
+template <typename T, size_t N> char (&SkArrayCountHelper(T (&array)[N]))[N];
+#define SK_ARRAY_COUNT(array) (sizeof(SkArrayCountHelper(array)))
 
 #define SkAlign2(x)     (((x) + 1) >> 1 << 1)
 #define SkIsAlign2(x)   (0 == ((x) & 1))
@@ -400,16 +406,13 @@
     return value;
 }
 
-/** Returns signed 32 bit value pinned between min and max, inclusively
-*/
+template <typename T> static inline const T& SkTPin(const T& x, const T& min, const T& max) {
+    return SkTMax(SkTMin(x, max), min);
+}
+
+/** Returns signed 32 bit value pinned between min and max, inclusively. */
 static inline int32_t SkPin32(int32_t value, int32_t min, int32_t max) {
-    if (value < min) {
-        value = min;
-    }
-    if (value > max) {
-        value = max;
-    }
-    return value;
+    return SkTPin(value, min, max);
 }
 
 static inline uint32_t SkSetClearShift(uint32_t bits, bool cond,
@@ -448,7 +451,7 @@
 
 /** \class SkNoncopyable
 
-SkNoncopyable is the base class for objects that may do not want to
+SkNoncopyable is the base class for objects that do not want to
 be copied. It hides its copy-constructor and its assignment-operator.
 */
 class SK_API SkNoncopyable {
diff --git a/include/core/SkUserConfig.h b/include/core/SkUserConfig.h
index c8cc198..3f0d2a5 100644
--- a/include/core/SkUserConfig.h
+++ b/include/core/SkUserConfig.h
@@ -117,11 +117,6 @@
  */
 //#define SK_DEFAULT_IMAGE_CACHE_LIMIT (1024 * 1024)
 
-/*  If zlib is not available or you don't want to support flate compression
-    in PDF generation, define SK_NO_FLATE.
- */
-//#define SK_NO_FLATE
-
 /*  Define this to allow PDF scalars above 32k.  The PDF/A spec doesn't allow
     them, but modern PDF interpreters should handle them just fine.
  */
@@ -166,14 +161,14 @@
 //#define SK_SUPPORT_GPU 1
 
 
-/* The PDF generation code uses Path Ops to generate inverse fills and complex
- * clipping paths, but at this time, Path Ops is not release ready yet. So,
- * the code is hidden behind this #define guard. If you are feeling adventurous
- * and want the latest and greatest PDF generation code, uncomment the #define.
+/* The PDF generation code uses Path Ops to handle complex clipping paths,
+ * but at this time, Path Ops is not release ready yet. So, the code is
+ * hidden behind this #define guard. If you are feeling adventurous and
+ * want the latest and greatest PDF generation code, uncomment the #define.
  * When Path Ops is release ready, the define guards and this user config
  * define should be removed entirely.
  */
-//#define SK_PDF_USE_PATHOPS
+//#define SK_PDF_USE_PATHOPS_CLIPPING
 
 #endif
 
@@ -210,19 +205,18 @@
 #define SK_GAMMA_CONTRAST 0.0
 #define SK_GAMMA_EXPONENT 1.4
 #define SK_IGNORE_ETC1_SUPPORT
+#define SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
 #define SK_INTERNAL
-#define SK_LEGACY_DRAWPICTURECALLBACK
-#define SK_OVERRIDE override
+#define SK_PRINT_CODEC_MESSAGES
 #define SK_SCALAR_TO_FLOAT_EXCLUDED
 #define SK_SFNTLY_SUBSETTER "sample/chromium/font_subsetter.h"
 #define SK_SUPPORT_GPU 1
 #define SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
 #define SK_SUPPORT_LEGACY_GETDEVICE
-#define SK_SUPPORT_LEGACY_GRADIENT_FACTORIES
 #define SK_SUPPORT_LEGACY_PUBLIC_IMAGEINFO_FIELDS
+#define SK_SUPPORT_LEGACY_SCALAR_DIV
 #define SK_SUPPORT_OPENCL 0
 #define SK_SUPPORT_PDF
 #define SK_USE_FREETYPE_EMBOLDEN
-#define SkLONGLONG int64_t
 
 #endif // SkUserConfig_Android_DEFINED
diff --git a/include/core/SkUtils.h b/include/core/SkUtils.h
index d522ae0..3c24b1f 100644
--- a/include/core/SkUtils.h
+++ b/include/core/SkUtils.h
@@ -12,12 +12,31 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+// Determined empirically using bench/MemsetBench.cpp on a Nexus 7, Nexus 9, and desktop.
+#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 || defined(SK_ARM_HAS_NEON)
+    // Platforms where we can assume an autovectorizer will give us a good inline memset.
+    #define SK_SMALL_MEMSET 1000
+#else
+    // Platforms like Chrome on ARMv7 that don't typically compile with NEON globally.
+    #define SK_SMALL_MEMSET 10
+#endif
+
+
 /** Similar to memset(), but it assigns a 16bit value into the buffer.
     @param buffer   The memory to have value copied into it
     @param value    The 16bit value to be copied into buffer
     @param count    The number of times value should be copied into the buffer.
 */
-void sk_memset16(uint16_t dst[], uint16_t value, int count);
+void sk_memset16_large(uint16_t dst[], uint16_t value, int count);
+inline void sk_memset16(uint16_t dst[], uint16_t value, int count) {
+    if (count <= SK_SMALL_MEMSET) {
+        for (int i = 0; i < count; i++) {
+            dst[i] = value;
+        }
+    } else {
+        sk_memset16_large(dst, value, count);
+    }
+}
 typedef void (*SkMemset16Proc)(uint16_t dst[], uint16_t value, int count);
 SkMemset16Proc SkMemset16GetPlatformProc();
 
@@ -26,10 +45,22 @@
     @param value    The 32bit value to be copied into buffer
     @param count    The number of times value should be copied into the buffer.
 */
-void sk_memset32(uint32_t dst[], uint32_t value, int count);
+void sk_memset32_large(uint32_t dst[], uint32_t value, int count);
+inline void sk_memset32(uint32_t dst[], uint32_t value, int count) {
+    if (count <= SK_SMALL_MEMSET) {
+        for (int i = 0; i < count; i++) {
+            dst[i] = value;
+        }
+    } else {
+        sk_memset32_large(dst, value, count);
+    }
+}
+
 typedef void (*SkMemset32Proc)(uint32_t dst[], uint32_t value, int count);
 SkMemset32Proc SkMemset32GetPlatformProc();
 
+#undef SK_SMALL_MEMSET
+
 /** Similar to memcpy(), but it copies count 32bit values from src to dst.
     @param dst      The memory to have value copied into it
     @param src      The memory to have value copied from it
diff --git a/include/core/SkWeakRefCnt.h b/include/core/SkWeakRefCnt.h
index 3eb8ea5..91795f7 100644
--- a/include/core/SkWeakRefCnt.h
+++ b/include/core/SkWeakRefCnt.h
@@ -147,7 +147,7 @@
         on the object and releases the implicit weak reference held
         collectively by the strong references.
     */
-    void internal_dispose() const SK_OVERRIDE {
+    void internal_dispose() const override {
         weak_dispose();
         weak_unref();
     }
diff --git a/include/device/xps/SkXPSDevice.h b/include/device/xps/SkXPSDevice.h
index 514259d..9efb6f3 100644
--- a/include/device/xps/SkXPSDevice.h
+++ b/include/device/xps/SkXPSDevice.h
@@ -71,54 +71,54 @@
     virtual bool endPortfolio();
 
 protected:
-    void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE;
+    void drawPaint(const SkDraw&, const SkPaint& paint) override;
 
     virtual void drawPoints(
         const SkDraw&,
         SkCanvas::PointMode mode,
         size_t count, const SkPoint[],
-        const SkPaint& paint) SK_OVERRIDE;
+        const SkPaint& paint) override;
 
     virtual void drawRect(
         const SkDraw&,
         const SkRect& r,
-        const SkPaint& paint) SK_OVERRIDE;
+        const SkPaint& paint) override;
 
     virtual void drawRRect(
         const SkDraw&,
         const SkRRect&,
-        const SkPaint& paint) SK_OVERRIDE;
+        const SkPaint& paint) override;
 
     virtual void drawPath(
         const SkDraw&,
         const SkPath& platonicPath,
         const SkPaint& paint,
         const SkMatrix* prePathMatrix,
-        bool pathIsMutable) SK_OVERRIDE;
+        bool pathIsMutable) override;
 
     virtual void drawBitmap(
         const SkDraw&,
         const SkBitmap& bitmap,
         const SkMatrix& matrix,
-        const SkPaint& paint) SK_OVERRIDE;
+        const SkPaint& paint) override;
 
     virtual void drawSprite(
         const SkDraw&,
         const SkBitmap& bitmap,
         int x, int y,
-        const SkPaint& paint) SK_OVERRIDE;
+        const SkPaint& paint) override;
 
     virtual void drawText(
         const SkDraw&,
         const void* text, size_t len,
         SkScalar x, SkScalar y,
-        const SkPaint& paint) SK_OVERRIDE;
+        const SkPaint& paint) override;
 
     virtual void drawPosText(
         const SkDraw&,
         const void* text, size_t len,
         const SkScalar pos[], int scalarsPerPos,
-        const SkPoint& offset, const SkPaint& paint) SK_OVERRIDE;
+        const SkPoint& offset, const SkPaint& paint) override;
 
     virtual void drawVertices(
         const SkDraw&,
@@ -127,15 +127,13 @@
         const SkPoint texs[], const SkColor colors[],
         SkXfermode* xmode,
         const uint16_t indices[], int indexCount,
-        const SkPaint& paint) SK_OVERRIDE;
+        const SkPaint& paint) override;
 
     virtual void drawDevice(
         const SkDraw&,
         SkBaseDevice* device,
         int x, int y,
-        const SkPaint& paint) SK_OVERRIDE;
-
-    bool allowImageFilter(const SkImageFilter*) SK_OVERRIDE;
+        const SkPaint& paint) override;
 
 private:
     class TypefaceUse : ::SkNoncopyable {
@@ -311,7 +309,7 @@
         const SkVector& ppuScale,
         IXpsOMPath* shadedPath);
 
-    SkBaseDevice* onCreateCompatibleDevice(const CreateInfo&) SK_OVERRIDE;
+    SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
 
     // Disable the default copy and assign implementation.
     SkXPSDevice(const SkXPSDevice&);
diff --git a/include/effects/Sk1DPathEffect.h b/include/effects/Sk1DPathEffect.h
index e5a3b5c..6b3ec6d 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 SkRect*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const override;
 
 protected:
     /** Called at the start of each contour, returns the initial offset
@@ -32,7 +32,7 @@
     virtual SkScalar next(SkPath* dst, SkScalar dist, SkPathMeasure&) const = 0;
 
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
-    bool exposedInAndroidJavaAPI() const SK_OVERRIDE { return true; }
+    bool exposedInAndroidJavaAPI() const override { return true; }
 #endif
 
 private:
@@ -62,18 +62,18 @@
     }
 
     virtual bool filterPath(SkPath*, const SkPath&,
-                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPath1DPathEffect)
 
 protected:
     SkPath1DPathEffect(const SkPath& path, SkScalar advance, SkScalar phase, Style);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     // overrides from Sk1DPathEffect
-    SkScalar begin(SkScalar contourLength) const SK_OVERRIDE;
-    SkScalar next(SkPath*, SkScalar, SkPathMeasure&) const SK_OVERRIDE;
+    SkScalar begin(SkScalar contourLength) const override;
+    SkScalar next(SkPath*, SkScalar, SkPathMeasure&) const override;
 
 private:
     SkPath      fPath;          // copied from constructor
diff --git a/include/effects/Sk2DPathEffect.h b/include/effects/Sk2DPathEffect.h
index 7971ef4..7fbf94b 100644
--- a/include/effects/Sk2DPathEffect.h
+++ b/include/effects/Sk2DPathEffect.h
@@ -14,7 +14,7 @@
 
 class SK_API Sk2DPathEffect : public SkPathEffect {
 public:
-    bool filterPath(SkPath*, const SkPath&, SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
+    bool filterPath(SkPath*, const SkPath&, SkStrokeRec*, const SkRect*) const override;
 
 protected:
     /** New virtual, to be overridden by subclasses.
@@ -37,7 +37,7 @@
 
     // protected so that subclasses can call this during unflattening
     explicit Sk2DPathEffect(const SkMatrix& mat);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     SK_TO_STRING_OVERRIDE()
 
@@ -60,7 +60,7 @@
     }
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLine2DPathEffect)
@@ -68,9 +68,9 @@
 protected:
     SkLine2DPathEffect(SkScalar width, const SkMatrix& matrix)
         : Sk2DPathEffect(matrix), fWidth(width) {}
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
-    void nextSpan(int u, int v, int ucount, SkPath*) const SK_OVERRIDE;
+    void nextSpan(int u, int v, int ucount, SkPath*) const override;
 
 private:
     SkScalar fWidth;
@@ -93,9 +93,9 @@
 
 protected:
     SkPath2DPathEffect(const SkMatrix&, const SkPath&);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
-    void next(const SkPoint&, int u, int v, SkPath*) const SK_OVERRIDE;
+    void next(const SkPoint&, int u, int v, SkPath*) const override;
 
 private:
     SkPath  fPath;
diff --git a/include/effects/SkArcToPathEffect.h b/include/effects/SkArcToPathEffect.h
index 7a96cd7..6ae7e36 100644
--- a/include/effects/SkArcToPathEffect.h
+++ b/include/effects/SkArcToPathEffect.h
@@ -22,14 +22,14 @@
         return SkNEW_ARGS(SkArcToPathEffect, (radius));
     }
 
-    bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
+    bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkArcToPathEffect)
 
 protected:
     explicit SkArcToPathEffect(SkScalar radius);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     SkScalar fRadius;
diff --git a/include/effects/SkBitmapSource.h b/include/effects/SkBitmapSource.h
index 327ff64..55b5bed 100644
--- a/include/effects/SkBitmapSource.h
+++ b/include/effects/SkBitmapSource.h
@@ -16,26 +16,31 @@
     static SkBitmapSource* Create(const SkBitmap& bitmap) {
         return SkNEW_ARGS(SkBitmapSource, (bitmap));
     }
-    static SkBitmapSource* Create(const SkBitmap& bitmap, const SkRect& srcRect,
-                                  const SkRect& dstRect) {
-        return SkNEW_ARGS(SkBitmapSource, (bitmap, srcRect, dstRect));
+    static SkBitmapSource* Create(const SkBitmap& bitmap,
+                                  const SkRect& srcRect, const SkRect& dstRect,
+                                  SkFilterQuality filterQuality = kHigh_SkFilterQuality) {
+        return SkNEW_ARGS(SkBitmapSource, (bitmap, srcRect, dstRect, filterQuality));
     }
-    void computeFastBounds(const SkRect& src, SkRect* dst) const SK_OVERRIDE;
+    void computeFastBounds(const SkRect& src, SkRect* dst) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBitmapSource)
 
 protected:
     explicit SkBitmapSource(const SkBitmap& bitmap);
-    SkBitmapSource(const SkBitmap& bitmap, const SkRect& srcRect, const SkRect& dstRect);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    SkBitmapSource(const SkBitmap& bitmap,
+                   const SkRect& srcRect, const SkRect& dstRect,
+                   SkFilterQuality filterQuality);
+    void flatten(SkWriteBuffer&) const override;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* offset) const override;
 
 private:
     SkBitmap fBitmap;
     SkRect   fSrcRect, fDstRect;
+    SkFilterQuality fFilterQuality;
+
     typedef SkImageFilter INHERITED;
 };
 
diff --git a/include/effects/SkBlurDrawLooper.h b/include/effects/SkBlurDrawLooper.h
index 5e6b9df..26bcd52 100644
--- a/include/effects/SkBlurDrawLooper.h
+++ b/include/effects/SkBlurDrawLooper.h
@@ -42,9 +42,9 @@
 
     virtual ~SkBlurDrawLooper();
 
-    SkDrawLooper::Context* createContext(SkCanvas*, void* storage) const SK_OVERRIDE;
+    SkDrawLooper::Context* createContext(SkCanvas*, void* storage) const override;
 
-    size_t contextSize() const SK_OVERRIDE { return sizeof(BlurDrawLooperContext); }
+    size_t contextSize() const override { return sizeof(BlurDrawLooperContext); }
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurDrawLooper)
@@ -53,9 +53,9 @@
     SkBlurDrawLooper(SkColor color, SkScalar sigma, SkScalar dx, SkScalar dy,
                      uint32_t flags);
 
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
-    bool asABlurShadow(BlurShadowRec*) const SK_OVERRIDE;
+    bool asABlurShadow(BlurShadowRec*) const override;
 
 private:
     SkMaskFilter*   fBlur;
@@ -74,7 +74,7 @@
     public:
         explicit BlurDrawLooperContext(const SkBlurDrawLooper* looper);
 
-        bool next(SkCanvas* canvas, SkPaint* paint) SK_OVERRIDE;
+        bool next(SkCanvas* canvas, SkPaint* paint) override;
 
     private:
         const SkBlurDrawLooper* fLooper;
diff --git a/include/effects/SkBlurImageFilter.h b/include/effects/SkBlurImageFilter.h
index d6b5066..ec29c8e 100644
--- a/include/effects/SkBlurImageFilter.h
+++ b/include/effects/SkBlurImageFilter.h
@@ -16,11 +16,11 @@
     static SkBlurImageFilter* Create(SkScalar sigmaX,
                                      SkScalar sigmaY,
                                      SkImageFilter* input = NULL,
-                                     const CropRect* cropRect = NULL, uint32_t uniqueID = 0) {
-        return SkNEW_ARGS(SkBlurImageFilter, (sigmaX, sigmaY, input, cropRect, uniqueID));
+                                     const CropRect* cropRect = NULL) {
+        return SkNEW_ARGS(SkBlurImageFilter, (sigmaX, sigmaY, input, cropRect));
     }
 
-    void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
+    void computeFastBounds(const SkRect&, SkRect*) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurImageFilter)
@@ -29,18 +29,17 @@
     SkBlurImageFilter(SkScalar sigmaX,
                       SkScalar sigmaY,
                       SkImageFilter* input,
-                      const CropRect* cropRect,
-                      uint32_t uniqueID);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+                      const CropRect* cropRect);
+    void flatten(SkWriteBuffer&) const override;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* offset) const override;
     virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
-                                SkIRect* dst) const SK_OVERRIDE;
+                                SkIRect* dst) const override;
 
-    bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    bool canFilterImageGPU() const override { return true; }
     virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
-                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                                SkBitmap* result, SkIPoint* offset) const override;
 
 private:
     SkSize   fSigma;
diff --git a/include/effects/SkColorCubeFilter.h b/include/effects/SkColorCubeFilter.h
index 4500a0c..4999bc5 100644
--- a/include/effects/SkColorCubeFilter.h
+++ b/include/effects/SkColorCubeFilter.h
@@ -21,11 +21,11 @@
      */
     static SkColorFilter* Create(SkData* cubeData, int cubeDimension);
 
-    void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const SK_OVERRIDE;
-    uint32_t getFlags() const SK_OVERRIDE;
+    void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override;
+    uint32_t getFlags() const override;
 
 #if SK_SUPPORT_GPU
-   bool asFragmentProcessors(GrContext*, SkTDArray<GrFragmentProcessor*>*) const SK_OVERRIDE;
+   bool asFragmentProcessors(GrContext*, SkTDArray<GrFragmentProcessor*>*) const override;
 #endif
 
     SK_TO_STRING_OVERRIDE()
@@ -33,7 +33,7 @@
 
 protected:
     SkColorCubeFilter(SkData* cubeData, int cubeDimension);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     /** The cache is initialized on-demand when getProcessingLuts is called.
diff --git a/include/effects/SkColorFilterImageFilter.h b/include/effects/SkColorFilterImageFilter.h
index c8b1661..0076a87 100644
--- a/include/effects/SkColorFilterImageFilter.h
+++ b/include/effects/SkColorFilterImageFilter.h
@@ -16,26 +16,24 @@
 public:
     static SkColorFilterImageFilter* Create(SkColorFilter* cf,
                                             SkImageFilter* input = NULL,
-                                            const CropRect* cropRect = NULL,
-                                            uint32_t uniqueID = 0);
+                                            const CropRect* cropRect = NULL);
     virtual ~SkColorFilterImageFilter();
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterImageFilter)
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* loc) const override;
 
-    bool onIsColorFilterNode(SkColorFilter**) const SK_OVERRIDE;
+    bool onIsColorFilterNode(SkColorFilter**) const override;
 
 private:
     SkColorFilterImageFilter(SkColorFilter* cf,
                              SkImageFilter* input,
-                             const CropRect* cropRect,
-                             uint32_t uniqueID);
+                             const CropRect* cropRect);
     SkColorFilter*  fColorFilter;
 
     typedef SkImageFilter INHERITED;
diff --git a/include/effects/SkColorMatrix.h b/include/effects/SkColorMatrix.h
index 7ac4579..a38585c 100644
--- a/include/effects/SkColorMatrix.h
+++ b/include/effects/SkColorMatrix.h
@@ -12,7 +12,10 @@
 
 class SK_API SkColorMatrix {
 public:
-    SkScalar    fMat[20];
+    enum {
+        kCount = 20
+    };
+    SkScalar    fMat[kCount];
 
     enum Elem {
         kR_Scale    = 0,
diff --git a/include/effects/SkColorMatrixFilter.h b/include/effects/SkColorMatrixFilter.h
index 58f37ff..4709273 100644
--- a/include/effects/SkColorMatrixFilter.h
+++ b/include/effects/SkColorMatrixFilter.h
@@ -20,15 +20,13 @@
         return SkNEW_ARGS(SkColorMatrixFilter, (array));
     }
 
-    // overrides from SkColorFilter
-    void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const SK_OVERRIDE;
-    void filterSpan16(const uint16_t src[], int count, uint16_t[]) const SK_OVERRIDE;
-    uint32_t getFlags() const SK_OVERRIDE;
-    bool asColorMatrix(SkScalar matrix[20]) const SK_OVERRIDE;
-    SkColorFilter* newComposed(const SkColorFilter*) const SK_OVERRIDE;
+    void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override;
+    uint32_t getFlags() const override;
+    bool asColorMatrix(SkScalar matrix[20]) const override;
+    SkColorFilter* newComposed(const SkColorFilter*) const override;
 
 #if SK_SUPPORT_GPU
-    bool asFragmentProcessors(GrContext*, SkTDArray<GrFragmentProcessor*>*) const SK_OVERRIDE;
+    bool asFragmentProcessors(GrContext*, SkTDArray<GrFragmentProcessor*>*) const override;
 #endif
 
     struct State {
@@ -43,10 +41,11 @@
 protected:
     explicit SkColorMatrixFilter(const SkColorMatrix&);
     explicit SkColorMatrixFilter(const SkScalar array[20]);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
-    SkColorMatrix fMatrix;
+    SkColorMatrix   fMatrix;
+    float           fTranspose[SkColorMatrix::kCount]; // for Sk4s
 
     typedef void (*Proc)(const State&, unsigned r, unsigned g, unsigned b,
                          unsigned a, int32_t result[4]);
diff --git a/include/effects/SkComposeImageFilter.h b/include/effects/SkComposeImageFilter.h
index 361ca2b..e3dc1ff 100644
--- a/include/effects/SkComposeImageFilter.h
+++ b/include/effects/SkComposeImageFilter.h
@@ -24,7 +24,7 @@
         SkImageFilter* inputs[2] = { outer, inner };
         return SkNEW_ARGS(SkComposeImageFilter, (inputs));
     }
-    void computeFastBounds(const SkRect& src, SkRect* dst) const SK_OVERRIDE;
+    void computeFastBounds(const SkRect& src, SkRect* dst) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeImageFilter)
@@ -35,8 +35,8 @@
         SkASSERT(inputs[1]);
     }
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
-    bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* loc) const override;
+    bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const override;
 
 private:
     typedef SkImageFilter INHERITED;
diff --git a/include/effects/SkCornerPathEffect.h b/include/effects/SkCornerPathEffect.h
index 636de40..1935790 100644
--- a/include/effects/SkCornerPathEffect.h
+++ b/include/effects/SkCornerPathEffect.h
@@ -26,18 +26,18 @@
     virtual ~SkCornerPathEffect();
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkCornerPathEffect)
 
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
-    bool exposedInAndroidJavaAPI() const SK_OVERRIDE { return true; }
+    bool exposedInAndroidJavaAPI() const override { return true; }
 #endif
 
 protected:
     explicit SkCornerPathEffect(SkScalar radius);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     SkScalar    fRadius;
diff --git a/include/effects/SkDashPathEffect.h b/include/effects/SkDashPathEffect.h
index dcb7577..0fd00d7 100644
--- a/include/effects/SkDashPathEffect.h
+++ b/include/effects/SkDashPathEffect.h
@@ -43,24 +43,24 @@
     virtual ~SkDashPathEffect();
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const override;
 
     virtual bool asPoints(PointData* results, const SkPath& src,
                           const SkStrokeRec&, const SkMatrix&,
-                          const SkRect*) const SK_OVERRIDE;
+                          const SkRect*) const override;
 
-    DashType asADash(DashInfo* info) const SK_OVERRIDE;
+    DashType asADash(DashInfo* info) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDashPathEffect)
 
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
-    bool exposedInAndroidJavaAPI() const SK_OVERRIDE { return true; }
+    bool exposedInAndroidJavaAPI() const override { return true; }
 #endif
 
 protected:
     SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     SkScalar*   fIntervals;
diff --git a/include/effects/SkDiscretePathEffect.h b/include/effects/SkDiscretePathEffect.h
index ea44d65..d05d181 100644
--- a/include/effects/SkDiscretePathEffect.h
+++ b/include/effects/SkDiscretePathEffect.h
@@ -37,20 +37,20 @@
     }
 
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
+                            SkStrokeRec*, const SkRect*) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiscretePathEffect)
 
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
-    bool exposedInAndroidJavaAPI() const SK_OVERRIDE { return true; }
+    bool exposedInAndroidJavaAPI() const override { return true; }
 #endif
 
 protected:
     SkDiscretePathEffect(SkScalar segLength,
                          SkScalar deviation,
                          uint32_t seedAssist);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     SkScalar fSegLength, fPerterb;
diff --git a/include/effects/SkDisplacementMapEffect.h b/include/effects/SkDisplacementMapEffect.h
index e55b324..9513a54 100644
--- a/include/effects/SkDisplacementMapEffect.h
+++ b/include/effects/SkDisplacementMapEffect.h
@@ -27,8 +27,7 @@
                                            ChannelSelectorType yChannelSelector,
                                            SkScalar scale, SkImageFilter* displacement,
                                            SkImageFilter* color = NULL,
-                                           const CropRect* cropRect = NULL,
-                                           uint32_t uniqueID = 0);
+                                           const CropRect* cropRect = NULL);
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDisplacementMapEffect)
 
@@ -36,16 +35,16 @@
                                const SkBitmap& src,
                                const Context& ctx,
                                SkBitmap* dst,
-                               SkIPoint* offset) const SK_OVERRIDE;
-    void computeFastBounds(const SkRect& src, SkRect* dst) const SK_OVERRIDE;
+                               SkIPoint* offset) const override;
+    void computeFastBounds(const SkRect& src, SkRect* dst) const override;
 
     virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
-                                SkIRect* dst) const SK_OVERRIDE;
+                                SkIRect* dst) const override;
 
 #if SK_SUPPORT_GPU
-    bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    bool canFilterImageGPU() const override { return true; }
     virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
-                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                                SkBitmap* result, SkIPoint* offset) const override;
 #endif
 
     SK_TO_STRING_OVERRIDE()
@@ -54,9 +53,8 @@
     SkDisplacementMapEffect(ChannelSelectorType xChannelSelector,
                             ChannelSelectorType yChannelSelector,
                             SkScalar scale, SkImageFilter* inputs[2],
-                            const CropRect* cropRect,
-                            uint32_t uniqueID);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+                            const CropRect* cropRect);
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     ChannelSelectorType fXChannelSelector;
diff --git a/include/effects/SkDropShadowImageFilter.h b/include/effects/SkDropShadowImageFilter.h
index 5eb6409..3ff3d80 100644
--- a/include/effects/SkDropShadowImageFilter.h
+++ b/include/effects/SkDropShadowImageFilter.h
@@ -20,46 +20,26 @@
 
     static const int kShadowModeCount = kLast_ShadowMode+1;
 
-    /** @deprecated use another Create function below instead */
-    static SkDropShadowImageFilter* Create(SkScalar dx, SkScalar dy,
-                                           SkScalar sigmaX, SkScalar sigmaY, SkColor color,
-                                           SkImageFilter* input = NULL,
-                                           const CropRect* cropRect = NULL,
-                                           uint32_t uniqueID = 0) {
-        return SkNEW_ARGS(SkDropShadowImageFilter, (dx, dy, sigmaX, sigmaY, color,
-                                                    kDrawShadowAndForeground_ShadowMode,
-                                                    input, cropRect, uniqueID));
-    }
-
     static SkDropShadowImageFilter* Create(SkScalar dx, SkScalar dy,
                                            SkScalar sigmaX, SkScalar sigmaY, SkColor color,
                                            ShadowMode shadowMode,
-                                           SkImageFilter* input,
-                                           const CropRect* cropRect,
-                                           uint32_t uniqueID) {
+                                           SkImageFilter* input = NULL,
+                                           const CropRect* cropRect = NULL) {
         return SkNEW_ARGS(SkDropShadowImageFilter, (dx, dy, sigmaX, sigmaY, color,
-                                                    shadowMode, input, cropRect, uniqueID));
+                                                    shadowMode, input, cropRect));
     }
 
-    static SkDropShadowImageFilter* Create(SkScalar dx, SkScalar dy,
-                                           SkScalar sigmaX, SkScalar sigmaY, SkColor color,
-                                           ShadowMode shadowMode) {
-        return SkNEW_ARGS(SkDropShadowImageFilter, (dx, dy, sigmaX, sigmaY, color,
-                                                    shadowMode, NULL, NULL, 0));
-    }
-
-    void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
+    void computeFastBounds(const SkRect&, SkRect*) const override;
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDropShadowImageFilter)
 
 protected:
     SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor,
-                            ShadowMode shadowMode, SkImageFilter* input, const CropRect* cropRect,
-                            uint32_t uniqueID);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
-    bool onFilterImage(Proxy*, const SkBitmap& source, const Context&, SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
+                            ShadowMode shadowMode, SkImageFilter* input, const CropRect* cropRect);
+    void flatten(SkWriteBuffer&) const override;
+    bool onFilterImage(Proxy*, const SkBitmap& source, const Context&, SkBitmap* result, SkIPoint* loc) const override;
     virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
-                                SkIRect* dst) const SK_OVERRIDE;
+                                SkIRect* dst) const override;
 
 private:
     SkScalar fDx, fDy, fSigmaX, fSigmaY;
diff --git a/include/effects/SkEmbossMaskFilter.h b/include/effects/SkEmbossMaskFilter.h
index 3fa15fd..41dfa2a 100644
--- a/include/effects/SkEmbossMaskFilter.h
+++ b/include/effects/SkEmbossMaskFilter.h
@@ -27,17 +27,17 @@
 
     // overrides from SkMaskFilter
     //  This method is not exported to java.
-    SkMask::Format getFormat() const SK_OVERRIDE;
+    SkMask::Format getFormat() const override;
     //  This method is not exported to java.
     virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
-                            SkIPoint* margin) const SK_OVERRIDE;
+                            SkIPoint* margin) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmbossMaskFilter)
 
 protected:
     SkEmbossMaskFilter(SkScalar blurSigma, const Light& light);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     Light       fLight;
diff --git a/include/effects/SkGradientShader.h b/include/effects/SkGradientShader.h
index af88080..ae11e2d 100644
--- a/include/effects/SkGradientShader.h
+++ b/include/effects/SkGradientShader.h
@@ -53,15 +53,6 @@
         return CreateLinear(pts, colors, pos, count, mode, 0, NULL);
     }
 
-#ifdef SK_SUPPORT_LEGACY_GRADIENT_FACTORIES
-    static SkShader* CreateLinear(const SkPoint pts[2],
-                                  const SkColor colors[], const SkScalar pos[], int count,
-                                  SkShader::TileMode mode, void* /*ignored*/,
-                                  uint32_t flags, const SkMatrix* localMatrix) {
-        return CreateLinear(pts, colors, pos, count, mode, flags, localMatrix);
-    }
-#endif
-
     /** Returns a shader that generates a radial gradient given the center and radius.
         <p />
         CreateRadial returns a shader with a reference count of 1.
@@ -89,59 +80,6 @@
         return CreateRadial(center, radius, colors, pos, count, mode, 0, NULL);
     }
 
-#ifdef SK_SUPPORT_LEGACY_GRADIENT_FACTORIES
-    static SkShader* CreateRadial(const SkPoint& center, SkScalar radius,
-                                  const SkColor colors[], const SkScalar pos[], int count,
-                                  SkShader::TileMode mode, void* /*ignored*/,
-                                  uint32_t flags, const SkMatrix* localMatrix) {
-        return CreateRadial(center, radius, colors, pos, count, mode, flags, localMatrix);
-    }
-#endif
-
-    /** Returns a shader that generates a radial gradient given the start position, start radius, end position and end radius.
-        <p />
-        CreateTwoPointRadial returns a shader with a reference count of 1.
-        The caller should decrement the shader's reference count when done with the shader.
-        It is an error for colorCount to be < 2, for startRadius or endRadius to be < 0, or for
-        startRadius to be equal to endRadius.
-        @param  start   The center of the start circle for this gradient
-        @param  startRadius  Must be positive.  The radius of the start circle for this gradient.
-        @param  end     The center of the end circle for this gradient
-        @param  endRadius  Must be positive. The radius of the end circle for this gradient.
-        @param  colors  The array[count] of colors, to be distributed between the center and edge of the circle
-        @param  pos     May be NULL. The array[count] of SkScalars, or NULL, of the relative position of
-                        each corresponding color in the colors array. If this is NULL,
-                        the the colors are distributed evenly between the center and edge of the circle.
-                        If this is not null, the values must begin with 0, end with 1.0, and
-                        intermediate values must be strictly increasing.
-        @param  count   Must be >= 2. The number of colors (and pos if not NULL) entries
-        @param  mode    The tiling mode
-    */
-    static SkShader* CreateTwoPointRadial(const SkPoint& start, SkScalar startRadius,
-                                          const SkPoint& end, SkScalar endRadius,
-                                          const SkColor colors[], const SkScalar pos[], int count,
-                                          SkShader::TileMode mode,
-                                          uint32_t flags, const SkMatrix* localMatrix);
-
-    static SkShader* CreateTwoPointRadial(const SkPoint& start, SkScalar startRadius,
-                                          const SkPoint& end, SkScalar endRadius,
-                                          const SkColor colors[], const SkScalar pos[], int count,
-                                          SkShader::TileMode mode) {
-        return CreateTwoPointRadial(start, startRadius, end, endRadius, colors, pos, count, mode,
-                                    0, NULL);
-    }
-
-#ifdef SK_SUPPORT_LEGACY_GRADIENT_FACTORIES
-    static SkShader* CreateTwoPointRadial(const SkPoint& start, SkScalar startRadius,
-                                          const SkPoint& end, SkScalar endRadius,
-                                          const SkColor colors[], const SkScalar pos[], int count,
-                                          SkShader::TileMode mode, void* /*ignored*/,
-                                          uint32_t flags, const SkMatrix* localMatrix) {
-        return CreateTwoPointRadial(start, startRadius, end, endRadius, colors, pos, count, mode,
-                                    flags, localMatrix);
-    }
-#endif
-
     /**
      *  Returns a shader that generates a conical gradient given two circles, or
      *  returns NULL if the inputs are invalid. The gradient interprets the
@@ -162,17 +100,6 @@
                                      0, NULL);
     }
 
-#ifdef SK_SUPPORT_LEGACY_GRADIENT_FACTORIES
-    static SkShader* CreateTwoPointConical(const SkPoint& start, SkScalar startRadius,
-                                           const SkPoint& end, SkScalar endRadius,
-                                           const SkColor colors[], const SkScalar pos[], int count,
-                                           SkShader::TileMode mode, void* /*ignored*/,
-                                           uint32_t flags, const SkMatrix* localMatrix) {
-        return CreateTwoPointConical(start, startRadius, end, endRadius, colors, pos, count, mode,
-                                    flags, localMatrix);
-    }
-#endif
-
     /** Returns a shader that generates a sweep gradient given a center.
         <p />
         CreateSweep returns a shader with a reference count of 1.
@@ -197,15 +124,6 @@
         return CreateSweep(cx, cy, colors, pos, count, 0, NULL);
     }
 
-#ifdef SK_SUPPORT_LEGACY_GRADIENT_FACTORIES
-    static SkShader* CreateSweep(SkScalar cx, SkScalar cy,
-                                 const SkColor colors[], const SkScalar pos[], int count,
-                                 void* /*ignored*/,
-                                 uint32_t flags, const SkMatrix* localMatrix) {
-        return CreateSweep(cx, cy, colors, pos, count, flags, localMatrix);
-    }
-#endif
-
     SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 };
 
diff --git a/include/effects/SkLayerDrawLooper.h b/include/effects/SkLayerDrawLooper.h
index 9aac01e..d93d08a 100644
--- a/include/effects/SkLayerDrawLooper.h
+++ b/include/effects/SkLayerDrawLooper.h
@@ -73,21 +73,21 @@
         LayerInfo();
     };
 
-    SkDrawLooper::Context* createContext(SkCanvas*, void* storage) const SK_OVERRIDE;
+    SkDrawLooper::Context* createContext(SkCanvas*, void* storage) const override;
 
-    size_t contextSize() const SK_OVERRIDE { return sizeof(LayerDrawLooperContext); }
+    size_t contextSize() const override { return sizeof(LayerDrawLooperContext); }
 
-    bool asABlurShadow(BlurShadowRec* rec) const SK_OVERRIDE;
+    bool asABlurShadow(BlurShadowRec* rec) const override;
 
     SK_TO_STRING_OVERRIDE()
 
-    Factory getFactory() const SK_OVERRIDE { return CreateProc; }
+    Factory getFactory() const override { return CreateProc; }
     static SkFlattenable* CreateProc(SkReadBuffer& buffer);
 
 protected:
     SkLayerDrawLooper();
 
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     struct Rec {
@@ -105,7 +105,7 @@
         explicit LayerDrawLooperContext(const SkLayerDrawLooper* looper);
 
     protected:
-        bool next(SkCanvas*, SkPaint* paint) SK_OVERRIDE;
+        bool next(SkCanvas*, SkPaint* paint) override;
 
     private:
         Rec* fCurrRec;
diff --git a/include/effects/SkLayerRasterizer.h b/include/effects/SkLayerRasterizer.h
index 6ea873c..c6ac1de 100644
--- a/include/effects/SkLayerRasterizer.h
+++ b/include/effects/SkLayerRasterizer.h
@@ -69,12 +69,12 @@
 protected:
     SkLayerRasterizer();
     SkLayerRasterizer(SkDeque* layers);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     // override from SkRasterizer
     virtual bool onRasterize(const SkPath& path, const SkMatrix& matrix,
                              const SkIRect* clipBounds,
-                             SkMask* mask, SkMask::CreateMode mode) const SK_OVERRIDE;
+                             SkMask* mask, SkMask::CreateMode mode) const override;
 
 private:
     const SkDeque* const fLayers;
diff --git a/include/effects/SkLerpXfermode.h b/include/effects/SkLerpXfermode.h
index 4fcc06b..8ba4230 100644
--- a/include/effects/SkLerpXfermode.h
+++ b/include/effects/SkLerpXfermode.h
@@ -22,17 +22,17 @@
 
     // overrides from SkXfermode
     virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) const SK_OVERRIDE;
+                        const SkAlpha aa[]) const override;
     virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) const SK_OVERRIDE;
+                        const SkAlpha aa[]) const override;
     virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) const SK_OVERRIDE;
+                        const SkAlpha aa[]) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLerpXfermode)
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     SkLerpXfermode(unsigned scale256);
diff --git a/include/effects/SkLightingImageFilter.h b/include/effects/SkLightingImageFilter.h
index d06af93..e5048ee 100644
--- a/include/effects/SkLightingImageFilter.h
+++ b/include/effects/SkLightingImageFilter.h
@@ -73,9 +73,8 @@
     SkLightingImageFilter(SkLight* light,
                           SkScalar surfaceScale,
                           SkImageFilter* input,
-                          const CropRect* cropRect,
-                          uint32_t uniqueID);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+                          const CropRect* cropRect);
+    void flatten(SkWriteBuffer&) const override;
     const SkLight* light() const { return fLight.get(); }
     SkScalar surfaceScale() const { return fSurfaceScale; }
 
diff --git a/include/effects/SkLumaColorFilter.h b/include/effects/SkLumaColorFilter.h
index c970e8c..4e8ac79 100644
--- a/include/effects/SkLumaColorFilter.h
+++ b/include/effects/SkLumaColorFilter.h
@@ -25,17 +25,17 @@
 public:
     static SkColorFilter* Create();
 
-    void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const SK_OVERRIDE;
+    void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override;
 
 #if SK_SUPPORT_GPU
-    bool asFragmentProcessors(GrContext*, SkTDArray<GrFragmentProcessor*>*) const SK_OVERRIDE;
+    bool asFragmentProcessors(GrContext*, SkTDArray<GrFragmentProcessor*>*) const override;
 #endif
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLumaColorFilter)
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     SkLumaColorFilter();
diff --git a/include/effects/SkMagnifierImageFilter.h b/include/effects/SkMagnifierImageFilter.h
index a9cc66a..6df95b2 100644
--- a/include/effects/SkMagnifierImageFilter.h
+++ b/include/effects/SkMagnifierImageFilter.h
@@ -21,13 +21,13 @@
 
 protected:
     SkMagnifierImageFilter(const SkRect& srcRect, SkScalar inset, SkImageFilter* input);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* offset) const override;
 #if SK_SUPPORT_GPU
     virtual bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
-                                     const SkIRect& bounds) const SK_OVERRIDE;
+                                     const SkIRect& bounds) const override;
 #endif
 
 private:
diff --git a/include/effects/SkMatrixConvolutionImageFilter.h b/include/effects/SkMatrixConvolutionImageFilter.h
index 327f98f..3bbfc4f 100644
--- a/include/effects/SkMatrixConvolutionImageFilter.h
+++ b/include/effects/SkMatrixConvolutionImageFilter.h
@@ -60,8 +60,7 @@
                                                   TileMode tileMode,
                                                   bool convolveAlpha,
                                                   SkImageFilter* input = NULL,
-                                                  const CropRect* cropRect = NULL,
-                                                  uint32_t uniqueID = 0);
+                                                  const CropRect* cropRect = NULL);
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMatrixConvolutionImageFilter)
@@ -75,18 +74,17 @@
                                    TileMode tileMode,
                                    bool convolveAlpha,
                                    SkImageFilter* input,
-                                   const CropRect* cropRect,
-                                   uint32_t uniqueID);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+                                   const CropRect* cropRect);
+    void flatten(SkWriteBuffer&) const override;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
-    bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* loc) const override;
+    bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const override;
 
 
 #if SK_SUPPORT_GPU
     virtual bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
-                                     const SkIRect& bounds) const SK_OVERRIDE;
+                                     const SkIRect& bounds) const override;
 #endif
 
 private:
diff --git a/include/effects/SkMergeImageFilter.h b/include/effects/SkMergeImageFilter.h
index ba2849b..5bb6826 100644
--- a/include/effects/SkMergeImageFilter.h
+++ b/include/effects/SkMergeImageFilter.h
@@ -18,17 +18,15 @@
 
     static SkMergeImageFilter* Create(SkImageFilter* first, SkImageFilter* second,
                                       SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode,
-                                      const CropRect* cropRect = NULL,
-                                      uint32_t uniqueID = 0) {
+                                      const CropRect* cropRect = NULL) {
         SkImageFilter* inputs[2] = { first, second };
         SkXfermode::Mode modes[2] = { mode, mode };
-        return SkNEW_ARGS(SkMergeImageFilter, (inputs, 2, modes, cropRect, uniqueID));
+        return SkNEW_ARGS(SkMergeImageFilter, (inputs, 2, modes, cropRect));
     }
     static SkMergeImageFilter* Create(SkImageFilter* filters[], int count,
                                       const SkXfermode::Mode modes[] = NULL,
-                                      const CropRect* cropRect = NULL,
-                                      uint32_t uniqueID = 0) {
-        return SkNEW_ARGS(SkMergeImageFilter, (filters, count, modes, cropRect, uniqueID));
+                                      const CropRect* cropRect = NULL) {
+        return SkNEW_ARGS(SkMergeImageFilter, (filters, count, modes, cropRect));
     }
 
     SK_TO_STRING_OVERRIDE()
@@ -37,12 +35,11 @@
 protected:
     SkMergeImageFilter(SkImageFilter* filters[], int count,
                        const SkXfermode::Mode modes[],
-                       const CropRect* cropRect,
-                       uint32_t uniqueID);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+                       const CropRect* cropRect);
+    void flatten(SkWriteBuffer&) const override;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* loc) const override;
 
 private:
     uint8_t*            fModes; // SkXfermode::Mode
diff --git a/include/effects/SkModeColorFilter.h b/include/effects/SkModeColorFilter.h
index 4bb7a43..a2cb841 100644
--- a/include/effects/SkModeColorFilter.h
+++ b/include/effects/SkModeColorFilter.h
@@ -25,13 +25,12 @@
     SkXfermode::Mode getMode() const { return fMode; }
     SkPMColor getPMColor() const { return fPMColor; }
 
-    bool asColorMode(SkColor*, SkXfermode::Mode*) const SK_OVERRIDE;
-    uint32_t getFlags() const SK_OVERRIDE;
-    void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) const SK_OVERRIDE;
-    void filterSpan16(const uint16_t shader[], int count, uint16_t result[]) const SK_OVERRIDE;
+    bool asColorMode(SkColor*, SkXfermode::Mode*) const override;
+    uint32_t getFlags() const override;
+    void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) const override;
 
 #ifndef SK_IGNORE_TO_STRING
-    void toString(SkString* str) const SK_OVERRIDE {
+    void toString(SkString* str) const override {
         str->append("SkModeColorFilter: color: 0x");
         str->appendHex(fColor);
         str->append(" mode: ");
@@ -40,12 +39,12 @@
 #endif
 
 #if SK_SUPPORT_GPU
-    bool asFragmentProcessors(GrContext*, SkTDArray<GrFragmentProcessor*>*) const SK_OVERRIDE;
+    bool asFragmentProcessors(GrContext*, SkTDArray<GrFragmentProcessor*>*) const override;
 #endif
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkModeColorFilter)
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     SkColor             fColor;
@@ -53,7 +52,6 @@
     // cache
     SkPMColor           fPMColor;
     SkXfermodeProc      fProc;
-    SkXfermodeProc16    fProc16;
 
     void updateCache();
 
diff --git a/include/effects/SkMorphologyImageFilter.h b/include/effects/SkMorphologyImageFilter.h
index 901955b..7d409e8 100644
--- a/include/effects/SkMorphologyImageFilter.h
+++ b/include/effects/SkMorphologyImageFilter.h
@@ -15,8 +15,8 @@
 
 class SK_API SkMorphologyImageFilter : public SkImageFilter {
 public:
-    void computeFastBounds(const SkRect& src, SkRect* dst) const SK_OVERRIDE;
-    bool onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const SK_OVERRIDE;
+    void computeFastBounds(const SkRect& src, SkRect* dst) const override;
+    bool onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const override;
 
     /**
      * All morphology procs have the same signature: src is the source buffer, dst the
@@ -30,13 +30,13 @@
 
 protected:
     SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input,
-                            const CropRect* cropRect, uint32_t uniqueID);
+                            const CropRect* cropRect);
     bool filterImageGeneric(Proc procX, Proc procY,
                             Proxy*, const SkBitmap& src, const Context&,
                             SkBitmap* result, SkIPoint* offset) const;
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 #if SK_SUPPORT_GPU
-    bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    bool canFilterImageGPU() const override { return true; }
     bool filterImageGPUGeneric(bool dilate, Proxy* proxy, const SkBitmap& src,
                                const Context& ctm, SkBitmap* result,
                                SkIPoint* offset) const;
@@ -53,27 +53,26 @@
 public:
     static SkDilateImageFilter* Create(int radiusX, int radiusY,
                                        SkImageFilter* input = NULL,
-                                       const CropRect* cropRect = NULL,
-                                       uint32_t uniqueID = 0) {
+                                       const CropRect* cropRect = NULL) {
         if (radiusX < 0 || radiusY < 0) {
             return NULL;
         }
-        return SkNEW_ARGS(SkDilateImageFilter, (radiusX, radiusY, input, cropRect, uniqueID));
+        return SkNEW_ARGS(SkDilateImageFilter, (radiusX, radiusY, input, cropRect));
     }
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* offset) const override;
 #if SK_SUPPORT_GPU
     virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context&,
-                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                                SkBitmap* result, SkIPoint* offset) const override;
 #endif
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDilateImageFilter)
 
 protected:
-    SkDilateImageFilter(int radiusX, int radiusY, SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID)
-        : INHERITED(radiusX, radiusY, input, cropRect, uniqueID) {}
+    SkDilateImageFilter(int radiusX, int radiusY, SkImageFilter* input, const CropRect* cropRect)
+        : INHERITED(radiusX, radiusY, input, cropRect) {}
 private:
     typedef SkMorphologyImageFilter INHERITED;
 };
@@ -82,27 +81,26 @@
 public:
     static SkErodeImageFilter* Create(int radiusX, int radiusY,
                                       SkImageFilter* input = NULL,
-                                      const CropRect* cropRect = NULL,
-                                      uint32_t uniqueID = 0) {
+                                      const CropRect* cropRect = NULL) {
         if (radiusX < 0 || radiusY < 0) {
             return NULL;
         }
-        return SkNEW_ARGS(SkErodeImageFilter, (radiusX, radiusY, input, cropRect, uniqueID));
+        return SkNEW_ARGS(SkErodeImageFilter, (radiusX, radiusY, input, cropRect));
     }
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* offset) const override;
 #if SK_SUPPORT_GPU
     virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context&,
-                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                                SkBitmap* result, SkIPoint* offset) const override;
 #endif
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkErodeImageFilter)
 
 protected:
-    SkErodeImageFilter(int radiusX, int radiusY, SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID)
-        : INHERITED(radiusX, radiusY, input, cropRect, uniqueID) {}
+    SkErodeImageFilter(int radiusX, int radiusY, SkImageFilter* input, const CropRect* cropRect)
+        : INHERITED(radiusX, radiusY, input, cropRect) {}
 
 private:
     typedef SkMorphologyImageFilter INHERITED;
diff --git a/include/effects/SkOffsetImageFilter.h b/include/effects/SkOffsetImageFilter.h
index ed2878a..17c9627 100644
--- a/include/effects/SkOffsetImageFilter.h
+++ b/include/effects/SkOffsetImageFilter.h
@@ -16,24 +16,23 @@
 
 public:
     static SkOffsetImageFilter* Create(SkScalar dx, SkScalar dy, SkImageFilter* input = NULL,
-                                       const CropRect* cropRect = NULL,
-                                       uint32_t uniqueID = 0) {
+                                       const CropRect* cropRect = NULL) {
         if (!SkScalarIsFinite(dx) || !SkScalarIsFinite(dy)) {
             return NULL;
         }
-        return SkNEW_ARGS(SkOffsetImageFilter, (dx, dy, input, cropRect, uniqueID));
+        return SkNEW_ARGS(SkOffsetImageFilter, (dx, dy, input, cropRect));
     }
-    void computeFastBounds(const SkRect& src, SkRect* dst) const SK_OVERRIDE;
+    void computeFastBounds(const SkRect& src, SkRect* dst) const override;
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkOffsetImageFilter)
 
 protected:
-    SkOffsetImageFilter(SkScalar dx, SkScalar dy, SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    SkOffsetImageFilter(SkScalar dx, SkScalar dy, SkImageFilter* input, const CropRect* cropRect);
+    void flatten(SkWriteBuffer&) const override;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
-    bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* loc) const override;
+    bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const override;
 
 private:
     SkVector fOffset;
diff --git a/include/effects/SkPaintFlagsDrawFilter.h b/include/effects/SkPaintFlagsDrawFilter.h
index 0004057..10dd3c4 100644
--- a/include/effects/SkPaintFlagsDrawFilter.h
+++ b/include/effects/SkPaintFlagsDrawFilter.h
@@ -14,7 +14,7 @@
 public:
     SkPaintFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags);
 
-    bool filter(SkPaint*, Type) SK_OVERRIDE;
+    bool filter(SkPaint*, Type) override;
 
 private:
     uint16_t    fClearFlags;    // user specified
diff --git a/include/effects/SkPerlinNoiseShader.h b/include/effects/SkPerlinNoiseShader.h
index b318ef1..fe1c9c5 100644
--- a/include/effects/SkPerlinNoiseShader.h
+++ b/include/effects/SkPerlinNoiseShader.h
@@ -72,15 +72,15 @@
     }
 
 
-    size_t contextSize() const SK_OVERRIDE;
+    size_t contextSize() const override;
 
     class PerlinNoiseShaderContext : public SkShader::Context {
     public:
         PerlinNoiseShaderContext(const SkPerlinNoiseShader& shader, const ContextRec&);
         virtual ~PerlinNoiseShaderContext();
 
-        void shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE;
-        void shadeSpan16(int x, int y, uint16_t[], int count) SK_OVERRIDE;
+        void shadeSpan(int x, int y, SkPMColor[], int count) override;
+        void shadeSpan16(int x, int y, uint16_t[], int count) override;
 
     private:
         SkPMColor shade(const SkPoint& point, StitchData& stitchData) const;
@@ -98,14 +98,14 @@
 
     virtual bool asFragmentProcessor(GrContext* context, const SkPaint&, const SkMatrix& viewM,
                                      const SkMatrix*, GrColor*,
-                                     GrFragmentProcessor**) const SK_OVERRIDE;
+                                     GrFragmentProcessor**) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPerlinNoiseShader)
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
+    Context* onCreateContext(const ContextRec&, void* storage) const override;
 
 private:
     SkPerlinNoiseShader(SkPerlinNoiseShader::Type type, SkScalar baseFrequencyX,
diff --git a/include/effects/SkPictureImageFilter.h b/include/effects/SkPictureImageFilter.h
index e6579eab..d62f03f 100644
--- a/include/effects/SkPictureImageFilter.h
+++ b/include/effects/SkPictureImageFilter.h
@@ -16,19 +16,18 @@
     /**
      *  Refs the passed-in picture.
      */
-    static SkPictureImageFilter* Create(const SkPicture* picture, int32_t uniqueID = 0) {
-        return SkNEW_ARGS(SkPictureImageFilter, (picture, uniqueID));
+    static SkPictureImageFilter* Create(const SkPicture* picture) {
+        return SkNEW_ARGS(SkPictureImageFilter, (picture));
     }
 
     /**
      *  Refs the passed-in picture. cropRect can be used to crop or expand the destination rect when
      *  the picture is drawn. (No scaling is implied by the dest rect; only the CTM is applied.)
      */
-    static SkPictureImageFilter* Create(const SkPicture* picture, const SkRect& cropRect,
-                                        uint32_t uniqueID = 0) {
-        return SkNEW_ARGS(SkPictureImageFilter, (picture, cropRect, uniqueID,
+    static SkPictureImageFilter* Create(const SkPicture* picture, const SkRect& cropRect) {
+        return SkNEW_ARGS(SkPictureImageFilter, (picture, cropRect,
                                                  kDeviceSpace_PictureResolution,
-                                                 SkPaint::kLow_FilterLevel));
+                                                 kLow_SkFilterQuality));
     }
 
     /**
@@ -40,12 +39,17 @@
      */
     static SkPictureImageFilter* CreateForLocalSpace(const SkPicture* picture,
                                                      const SkRect& cropRect,
-                                                     SkPaint::FilterLevel filterLevel,
-                                                     uint32_t uniqueID = 0) {
-        return SkNEW_ARGS(SkPictureImageFilter, (picture, cropRect, uniqueID,
-                                                 kLocalSpace_PictureResolution, filterLevel));
+                                                     SkFilterQuality filterQuality) {
+        return SkNEW_ARGS(SkPictureImageFilter, (picture, cropRect,
+                                                 kLocalSpace_PictureResolution, filterQuality));
     }
-
+#ifdef SK_SUPPORT_LEGACY_FILTERLEVEL_ENUM
+    static SkPictureImageFilter* CreateForLocalSpace(const SkPicture* picture,
+                                                     const SkRect& cropRect,
+                                                     SkPaint::FilterLevel filterLevel) {
+        return CreateForLocalSpace(picture, cropRect, (SkFilterQuality)filterLevel);
+    }
+#endif
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPictureImageFilter)
 
@@ -55,9 +59,9 @@
         kLocalSpace_PictureResolution
     };
 
-    explicit SkPictureImageFilter(const SkPicture* picture, uint32_t uniqueID);
-    SkPictureImageFilter(const SkPicture* picture, const SkRect& cropRect, uint32_t uniqueID,
-                         PictureResolution, SkPaint::FilterLevel);
+    explicit SkPictureImageFilter(const SkPicture* picture);
+    SkPictureImageFilter(const SkPicture* picture, const SkRect& cropRect,
+                         PictureResolution, SkFilterQuality);
     virtual ~SkPictureImageFilter();
     /*  Constructs an SkPictureImageFilter object from an SkReadBuffer.
      *  Note: If the SkPictureImageFilter object construction requires bitmap
@@ -65,9 +69,9 @@
      *  SkReadBuffer::setBitmapDecoder() before calling this constructor.
      *  @param SkReadBuffer Serialized picture data.
      */
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* offset) const override;
 
 private:
 
@@ -80,7 +84,7 @@
     const SkPicture*      fPicture;
     SkRect                fCropRect;
     PictureResolution     fPictureResolution;
-    SkPaint::FilterLevel  fFilterLevel;
+    SkFilterQuality       fFilterQuality;
     typedef SkImageFilter INHERITED;
 };
 
diff --git a/include/effects/SkPixelXorXfermode.h b/include/effects/SkPixelXorXfermode.h
index ccc6145..3e6fa84 100644
--- a/include/effects/SkPixelXorXfermode.h
+++ b/include/effects/SkPixelXorXfermode.h
@@ -26,10 +26,10 @@
 
 protected:
     explicit SkPixelXorXfermode(SkColor opColor) : fOpColor(opColor) {}
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     // override from SkXfermode
-    SkPMColor xferColor(SkPMColor src, SkPMColor dst) const SK_OVERRIDE;
+    SkPMColor xferColor(SkPMColor src, SkPMColor dst) const override;
 
 private:
     SkColor fOpColor;
diff --git a/include/effects/SkRectShaderImageFilter.h b/include/effects/SkRectShaderImageFilter.h
index a2d42b3..64b42c7 100644
--- a/include/effects/SkRectShaderImageFilter.h
+++ b/include/effects/SkRectShaderImageFilter.h
@@ -28,20 +28,20 @@
     SK_ATTR_DEPRECATED("use Create(SkShader*, const CropRect*)")
     static SkRectShaderImageFilter* Create(SkShader* s, const SkRect& rect);
 
-    static SkRectShaderImageFilter* Create(SkShader* s, const CropRect* rect = NULL, uint32_t uniqueID = 0);
+    static SkRectShaderImageFilter* Create(SkShader* s, const CropRect* rect = NULL);
     virtual ~SkRectShaderImageFilter();
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRectShaderImageFilter)
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* loc) const override;
 
 private:
-    SkRectShaderImageFilter(SkShader* s, const CropRect* rect, uint32_t uniqueID = 0);
+    SkRectShaderImageFilter(SkShader* s, const CropRect* rect);
     SkShader*  fShader;
 
     typedef SkImageFilter INHERITED;
diff --git a/include/effects/SkTableColorFilter.h b/include/effects/SkTableColorFilter.h
index 5714d07..e4d42dc 100644
--- a/include/effects/SkTableColorFilter.h
+++ b/include/effects/SkTableColorFilter.h
@@ -1,3 +1,9 @@
+/*
+* Copyright 2015 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
 
 #ifndef SkTableColorFilter_DEFINED
 #define SkTableColorFilter_DEFINED
diff --git a/include/effects/SkTableMaskFilter.h b/include/effects/SkTableMaskFilter.h
index 444ca01..6084ec4 100644
--- a/include/effects/SkTableMaskFilter.h
+++ b/include/effects/SkTableMaskFilter.h
@@ -45,9 +45,9 @@
         return SkNEW_ARGS(SkTableMaskFilter, (table));
     }
 
-    SkMask::Format getFormat() const SK_OVERRIDE;
+    SkMask::Format getFormat() const override;
     virtual bool filterMask(SkMask*, const SkMask&, const SkMatrix&,
-                            SkIPoint*) const SK_OVERRIDE;
+                            SkIPoint*) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTableMaskFilter)
@@ -55,7 +55,7 @@
 protected:
     SkTableMaskFilter();
     explicit SkTableMaskFilter(const uint8_t table[256]);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     uint8_t fTable[256];
diff --git a/include/effects/SkTestImageFilters.h b/include/effects/SkTestImageFilters.h
index 5e375ae..5c6661d 100644
--- a/include/effects/SkTestImageFilters.h
+++ b/include/effects/SkTestImageFilters.h
@@ -24,10 +24,10 @@
 protected:
     SkDownSampleImageFilter(SkScalar scale, SkImageFilter* input)
       : INHERITED(1, &input), fScale(scale) {}
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* loc) const override;
 
 private:
     SkScalar fScale;
diff --git a/include/effects/SkTileImageFilter.h b/include/effects/SkTileImageFilter.h
index 82673d6..a010205 100644
--- a/include/effects/SkTileImageFilter.h
+++ b/include/effects/SkTileImageFilter.h
@@ -20,21 +20,21 @@
         @param input    Input from which the subregion defined by srcRect will be tiled
     */
     static SkTileImageFilter* Create(const SkRect& srcRect, const SkRect& dstRect,
-                                     SkImageFilter* input, uint32_t uniqueID = 0);
+                                     SkImageFilter* input);
 
     virtual bool onFilterImage(Proxy* proxy, const SkBitmap& src, const Context& ctx,
-                               SkBitmap* dst, SkIPoint* offset) const SK_OVERRIDE;
+                               SkBitmap* dst, SkIPoint* offset) const override;
     virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
-                                SkIRect* dst) const SK_OVERRIDE;
+                                SkIRect* dst) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTileImageFilter)
 
 protected:
-    SkTileImageFilter(const SkRect& srcRect, const SkRect& dstRect, SkImageFilter* input, uint32_t uniqueID)
-        : INHERITED(1, &input, NULL, uniqueID), fSrcRect(srcRect), fDstRect(dstRect) {}
+    SkTileImageFilter(const SkRect& srcRect, const SkRect& dstRect, SkImageFilter* input)
+        : INHERITED(1, &input, NULL), fSrcRect(srcRect), fDstRect(dstRect) {}
 
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer& buffer) const override;
 
 private:
     SkRect fSrcRect;
diff --git a/include/effects/SkTransparentShader.h b/include/effects/SkTransparentShader.h
index 254338c..11aa086 100644
--- a/include/effects/SkTransparentShader.h
+++ b/include/effects/SkTransparentShader.h
@@ -14,16 +14,16 @@
 public:
     SkTransparentShader() {}
 
-    size_t contextSize() const SK_OVERRIDE;
+    size_t contextSize() const override;
 
     class TransparentShaderContext : public SkShader::Context {
     public:
         TransparentShaderContext(const SkTransparentShader& shader, const ContextRec&);
         virtual ~TransparentShaderContext();
 
-        uint32_t getFlags() const SK_OVERRIDE;
-        void shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE;
-        void shadeSpan16(int x, int y, uint16_t span[], int count) SK_OVERRIDE;
+        uint32_t getFlags() const override;
+        void shadeSpan(int x, int y, SkPMColor[], int count) override;
+        void shadeSpan16(int x, int y, uint16_t span[], int count) override;
 
     private:
         const SkBitmap* fDevice;
@@ -35,10 +35,10 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTransparentShader)
 
 protected:
-    Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
+    Context* onCreateContext(const ContextRec&, void* storage) const override;
 
     // we don't need to flatten anything at all
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE {}
+    void flatten(SkWriteBuffer&) const override {}
 
 private:
     typedef SkShader INHERITED;
diff --git a/include/effects/SkXfermodeImageFilter.h b/include/effects/SkXfermodeImageFilter.h
index 47c44b8..2115ee4 100644
--- a/include/effects/SkXfermodeImageFilter.h
+++ b/include/effects/SkXfermodeImageFilter.h
@@ -25,10 +25,9 @@
 
     static SkXfermodeImageFilter* Create(SkXfermode* mode, SkImageFilter* background,
                                          SkImageFilter* foreground = NULL,
-                                         const CropRect* cropRect = NULL,
-                                         uint32_t uniqueID = 0) {
+                                         const CropRect* cropRect = NULL) {
         SkImageFilter* inputs[2] = { background, foreground };
-        return SkNEW_ARGS(SkXfermodeImageFilter, (mode, inputs, cropRect, uniqueID));
+        return SkNEW_ARGS(SkXfermodeImageFilter, (mode, inputs, cropRect));
     }
 
     SK_TO_STRING_OVERRIDE()
@@ -38,17 +37,17 @@
                                const SkBitmap& src,
                                const Context& ctx,
                                SkBitmap* dst,
-                               SkIPoint* offset) const SK_OVERRIDE;
+                               SkIPoint* offset) const override;
 #if SK_SUPPORT_GPU
-    bool canFilterImageGPU() const SK_OVERRIDE;
+    bool canFilterImageGPU() const override;
     virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
-                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                                SkBitmap* result, SkIPoint* offset) const override;
 #endif
 
 protected:
     SkXfermodeImageFilter(SkXfermode* mode, SkImageFilter* inputs[2],
-                          const CropRect* cropRect, uint32_t uniqueID);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+                          const CropRect* cropRect);
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     SkXfermode* fMode;
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 6a225c2..a6fdf75 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -13,20 +13,18 @@
 #include "GrPaint.h"
 #include "GrPathRendererChain.h"
 #include "GrRenderTarget.h"
-#include "GrTexture.h"
+#include "GrTextureProvider.h"
 #include "SkMatrix.h"
 #include "SkPathEffect.h"
 #include "SkTypes.h"
 
 class GrAARectRenderer;
+class GrBatchFontCache;
 class GrDrawTarget;
-class GrFontCache;
 class GrFragmentProcessor;
 class GrGpu;
 class GrGpuTraceMarker;
 class GrIndexBuffer;
-class GrIndexBufferAllocPool;
-class GrInOrderDrawBuffer;
 class GrLayerCache;
 class GrOvalRenderer;
 class GrPath;
@@ -34,26 +32,28 @@
 class GrPipelineBuilder;
 class GrResourceEntry;
 class GrResourceCache;
+class GrResourceProvider;
 class GrTestTarget;
+class GrTextBlobCache;
 class GrTextContext;
 class GrTextureParams;
 class GrVertexBuffer;
-class GrVertexBufferAllocPool;
 class GrStrokeInfo;
 class GrSoftwarePathRenderer;
-class SkStrokeRec;
+class SkGpuDevice;
 
 class SK_API GrContext : public SkRefCnt {
 public:
     SK_DECLARE_INST_COUNT(GrContext)
 
     struct Options {
-        Options() : fDrawPathToCompressedTexture(false) { }
+        Options() : fDrawPathToCompressedTexture(false), fSuppressPrints(false) { }
 
         // EXPERIMENTAL
         // May be removed in the future, or may become standard depending
         // on the outcomes of a variety of internal tests.
         bool fDrawPathToCompressedTexture;
+        bool fSuppressPrints;
     };
 
     /**
@@ -111,7 +111,6 @@
      * GrGpuResources it created is to destroy them.
      */
     void abandonContext();
-    void contextDestroyed() { this->abandonContext(); }  //  legacy alias
 
     ///////////////////////////////////////////////////////////////////////////
     // Resource Cache
@@ -147,6 +146,9 @@
      */
     void setResourceCacheLimits(int maxResources, size_t maxResourceBytes);
 
+    GrTextureProvider* textureProvider() { return fTextureProvider; }
+    const GrTextureProvider* textureProvider() const { return fTextureProvider; }
+
     /**
      * Frees GPU created by the context. Can be called to reduce GPU memory
      * pressure.
@@ -154,131 +156,14 @@
     void freeGpuResources();
 
     /**
-     * This method should be called whenever a GrResource is unreffed or
-     * switched from exclusive to non-exclusive. This
-     * gives the resource cache a chance to discard unneeded resources.
-     * Note: this entry point will be removed once totally ref-driven
-     * cache maintenance is implemented.
-     */
-    void purgeCache();
-
-    /**
      * Purge all the unlocked resources from the cache.
      * This entry point is mainly meant for timing texture uploads
      * and is not defined in normal builds of Skia.
      */
     void purgeAllUnlockedResources();
 
-    /**
-     * Sets a unique key on the resource. Upon key collision this resource takes the place of the
-     * previous resource that had the key.
-     */
-    void addResourceToCache(const GrUniqueKey&, GrGpuResource*);
-
-    /**
-     * Finds a resource in the cache, based on the specified key. This is intended for use in
-     * conjunction with addResourceToCache(). The return value will be NULL if not found. The
-     * caller must balance with a call to unref().
-     */
-    GrGpuResource* findAndRefCachedResource(const GrUniqueKey&);
-
-    /** Helper for casting resource to a texture. Caller must be sure that the resource cached
-        with the key is either NULL or a texture and not another resource type. */
-    GrTexture* findAndRefCachedTexture(const GrUniqueKey& key) {
-        GrGpuResource* resource = this->findAndRefCachedResource(key);
-        if (resource) {
-            GrTexture* texture = static_cast<GrSurface*>(resource)->asTexture();
-            SkASSERT(texture);
-            return texture;
-        }
-        return NULL;
-    }
-
-    /**
-     * Determines whether a resource is in the cache. If the resource is found it
-     * will not be locked or returned. This call does not affect the priority of
-     * the resource for deletion.
-     */
-    bool isResourceInCache(const GrUniqueKey& key) const;
-
-    /**
-     * Creates a new text rendering context that is optimal for the
-     * render target and the context. Caller assumes the ownership
-     * of the returned object. The returned object must be deleted
-     * before the context is destroyed.
-     */
-    GrTextContext* createTextContext(GrRenderTarget*,
-                                     const SkDeviceProperties&,
-                                     bool enableDistanceFieldFonts);
-
-    ///////////////////////////////////////////////////////////////////////////
-    // Textures
-
-    /**
-     * Creates a new texture in the resource cache and returns it. The caller owns a
-     * ref on the returned texture which must be balanced by a call to unref.
-     *
-     * @param desc      Description of the texture properties.
-     * @param budgeted  Does the texture count against the resource cache budget?
-     * @param srcData   Pointer to the pixel values (optional).
-     * @param rowBytes  The number of bytes between rows of the texture. Zero
-     *                  implies tightly packed rows. For compressed pixel configs, this
-     *                  field is ignored.
-     */
-    GrTexture* createTexture(const GrSurfaceDesc& desc, bool budgeted, const void* srcData,
-                             size_t rowBytes);
-
-    GrTexture* createTexture(const GrSurfaceDesc& desc, bool budgeted) {
-        return this->createTexture(desc, budgeted, NULL, 0);
-    }
-
-    /**
-     * DEPRECATED: use createTexture().
-     */
-    GrTexture* createUncachedTexture(const GrSurfaceDesc& desc, void* srcData, size_t rowBytes) {
-        return this->createTexture(desc, false, srcData, rowBytes);
-    }
-
-    /**
-     * Enum that determines how closely a returned scratch texture must match
-     * a provided GrSurfaceDesc. TODO: Remove this. createTexture() should be used
-     * for exact match and refScratchTexture() should be replaced with createApproxTexture().
-     */
-    enum ScratchTexMatch {
-        /**
-         * Finds a texture that exactly matches the descriptor.
-         */
-        kExact_ScratchTexMatch,
-        /**
-         * Finds a texture that approximately matches the descriptor. Will be
-         * at least as large in width and height as desc specifies. If desc
-         * specifies that texture is a render target then result will be a
-         * render target. If desc specifies a render target and doesn't set the
-         * no stencil flag then result will have a stencil. Format and aa level
-         * will always match.
-         */
-        kApprox_ScratchTexMatch
-    };
-
-    /**
-     * Returns a texture matching the desc. It's contents are unknown. The caller
-     * owns a ref on the returned texture and must balance with a call to unref.
-     * It is guaranteed that the same texture will not be returned in subsequent
-     * calls until all refs to the texture are dropped.
-     *
-     * Textures created by createTexture() hide the complications of
-     * tiling non-power-of-two textures on APIs that don't support this (e.g.
-     * unextended GLES2). NPOT scratch textures are not tilable on such APIs.
-     *
-     * internalFlag is a temporary workaround until changes in the internal
-     * architecture are complete. Use the default value.
-     *
-     * TODO: Once internal flag can be removed, this should be replaced with
-     * createApproxTexture() and exact textures should be created with
-     * createTexture().
-     */
-    GrTexture* refScratchTexture(const GrSurfaceDesc&, ScratchTexMatch match,
-                                 bool internalFlag = false);
+    //////////////////////////////////////////////////////////////////////////
+    /// Texture and Render Target Queries
 
     /**
      * Can the provided configuration act as a texture?
@@ -335,33 +220,6 @@
     int getRecommendedSampleCount(GrPixelConfig config, SkScalar dpi) const;
 
     ///////////////////////////////////////////////////////////////////////////
-    // Backend Surfaces
-
-    /**
-     * Wraps an existing texture with a GrTexture object.
-     *
-     * OpenGL: if the object is a texture Gr may change its GL texture params
-     *         when it is drawn.
-     *
-     * @param  desc     description of the object to create.
-     *
-     * @return GrTexture object or NULL on failure.
-     */
-    GrTexture* wrapBackendTexture(const GrBackendTextureDesc& desc);
-
-    /**
-     * Wraps an existing render target with a GrRenderTarget object. It is
-     * similar to wrapBackendTexture but can be used to draw into surfaces
-     * that are not also textures (e.g. FBO 0 in OpenGL, or an MSAA buffer that
-     * the client will resolve to a texture).
-     *
-     * @param  desc     description of the object to create.
-     *
-     * @return GrTexture object or NULL on failure.
-     */
-     GrRenderTarget* wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc);
-
-    ///////////////////////////////////////////////////////////////////////////
     // Draws
 
     /**
@@ -655,20 +513,24 @@
      */
     void discardRenderTarget(GrRenderTarget*);
 
-#ifdef SK_DEVELOPER
-    void dumpFontCache() const;
-#endif
+    /**
+     * An ID associated with this context, guaranteed to be unique.
+     */
+    uint32_t uniqueID() { return fUniqueID; }
 
     ///////////////////////////////////////////////////////////////////////////
     // Functions intended for internal use only.
     GrGpu* getGpu() { return fGpu; }
     const GrGpu* getGpu() const { return fGpu; }
-    GrFontCache* getFontCache() { return fFontCache; }
+    GrBatchFontCache* getBatchFontCache() { return fBatchFontCache; }
     GrLayerCache* getLayerCache() { return fLayerCache.get(); }
+    GrTextBlobCache* getTextBlobCache() { return fTextBlobCache; }
     GrDrawTarget* getTextTarget();
-    const GrIndexBuffer* getQuadIndexBuffer() const;
     GrAARectRenderer* getAARectRenderer() { return fAARectRenderer; }
+    GrResourceProvider* resourceProvider() { return fResourceProvider; }
+    const GrResourceProvider* resourceProvider() const { return fResourceProvider; }
     GrResourceCache* getResourceCache() { return fResourceCache; }
+    bool suppressPrints() const { return fOptions.fSuppressPrints; }
 
     // Called by tests that draw directly to the context via GrDrawTarget
     void getTestTarget(GrTestTarget*);
@@ -681,7 +543,7 @@
                     const GrPipelineBuilder*,
                     const SkMatrix& viewMatrix,
                     const SkPath& path,
-                    const SkStrokeRec& stroke,
+                    const GrStrokeInfo& stroke,
                     bool allowSW,
                     GrPathRendererChain::DrawType drawType = GrPathRendererChain::kColor_DrawType,
                     GrPathRendererChain::StencilSupport* stencilSupport = NULL);
@@ -702,17 +564,22 @@
 
 private:
     GrGpu*                          fGpu;
-
     GrResourceCache*                fResourceCache;
-    GrFontCache*                    fFontCache;
+    // this union exists because the inheritance of GrTextureProvider->GrResourceProvider
+    // is in a private header.
+    union { 
+        GrResourceProvider*         fResourceProvider;
+        GrTextureProvider*          fTextureProvider;
+    };
+
+    GrBatchFontCache*               fBatchFontCache;
     SkAutoTDelete<GrLayerCache>     fLayerCache;
+    SkAutoTDelete<GrTextBlobCache>  fTextBlobCache;
 
     GrPathRendererChain*            fPathRendererChain;
     GrSoftwarePathRenderer*         fSoftwarePathRenderer;
 
-    GrVertexBufferAllocPool*        fDrawBufferVBAllocPool;
-    GrIndexBufferAllocPool*         fDrawBufferIBAllocPool;
-    GrInOrderDrawBuffer*            fDrawBuffer;
+    GrDrawTarget*                   fDrawBuffer;
 
     // Set by OverbudgetCB() to request that GrContext flush before exiting a draw.
     bool                            fFlushToReduceCacheSize;
@@ -733,14 +600,13 @@
     int                             fMaxTextureSizeOverride;
 
     const Options                   fOptions;
+    const uint32_t                  fUniqueID;
 
     GrContext(const Options&); // init must be called after the constructor.
     bool init(GrBackend, GrBackendContext);
     void initMockContext();
     void initCommon();
 
-    void setupDrawBuffer();
-
     class AutoCheckFlush;
     // Sets the paint and returns the target to draw into.
     GrDrawTarget* prepareToDraw(GrPipelineBuilder*,
@@ -760,7 +626,19 @@
                           const SkPath&,
                           const GrStrokeInfo&);
 
-    GrTexture* internalRefScratchTexture(const GrSurfaceDesc&, uint32_t flags);
+    /**
+     * Creates a new text rendering context that is optimal for the
+     * render target and the context. Caller assumes the ownership
+     * of the returned object. The returned object must be deleted
+     * before the context is destroyed.
+     * TODO we can possibly bury this behind context, but we need to be able to use the
+     * drawText_asPaths logic on SkGpuDevice
+     */
+    GrTextContext* createTextContext(GrRenderTarget*,
+                                     SkGpuDevice*,
+                                     const SkDeviceProperties&,
+                                     bool enableDistanceFieldFonts);
+
 
     /**
      * These functions create premul <-> unpremul effects if it is possible to generate a pair
@@ -776,6 +654,15 @@
      */
     static void OverBudgetCB(void* data);
 
+    /**
+     * A callback similar to the above for use by the TextBlobCache
+     * TODO move textblob draw calls below context so we can use the call above.
+     */
+    static void TextBlobCacheOverBudgetCB(void* data);
+
+    // TODO see note on createTextContext
+    friend class SkGpuDevice;
+
     typedef SkRefCnt INHERITED;
 };
 
diff --git a/include/gpu/GrCoordTransform.h b/include/gpu/GrCoordTransform.h
index 09f4ac9..4d96cd9 100644
--- a/include/gpu/GrCoordTransform.h
+++ b/include/gpu/GrCoordTransform.h
@@ -125,7 +125,7 @@
     static inline SkMatrix MakeDivByTextureWHMatrix(const GrTexture* texture) {
         SkASSERT(texture);
         SkMatrix mat;
-        mat.setIDiv(texture->width(), texture->height());
+        (void)mat.setIDiv(texture->width(), texture->height());
         return mat;
     }
 
diff --git a/include/gpu/GrFragmentProcessor.h b/include/gpu/GrFragmentProcessor.h
index 8ebc94e..7914ad0 100644
--- a/include/gpu/GrFragmentProcessor.h
+++ b/include/gpu/GrFragmentProcessor.h
@@ -11,7 +11,7 @@
 #include "GrProcessor.h"
 
 class GrCoordTransform;
-class GrGLCaps;
+class GrGLSLCaps;
 class GrGLFragmentProcessor;
 class GrProcessorKeyBuilder;
 
@@ -28,7 +28,7 @@
         , fUsesLocalCoords(false) {}
 
     /** Implemented using GLFragmentProcessor::GenKey as described in this class's comment. */
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
                                    GrProcessorKeyBuilder* b) const = 0;
 
     /** Returns a new instance of the appropriate *GL* implementation class
diff --git a/include/gpu/GrGpuResource.h b/include/gpu/GrGpuResource.h
index e2f23e0..5a35ab7 100644
--- a/include/gpu/GrGpuResource.h
+++ b/include/gpu/GrGpuResource.h
@@ -33,8 +33,15 @@
  *
  * The latter two ref types are private and intended only for Gr core code.
  *
- * When an item is purgeable DERIVED:notifyIsPurgeable() will be called (static poly morphism using
- * CRTP). GrIORef and GrGpuResource are separate classes for organizational reasons and to be
+ * When all the ref/io counts reach zero DERIVED::notifyAllCntsAreZero() will be called (static poly
+ * morphism using CRTP). Similarly when the ref (but not necessarily pending read/write) count
+ * reaches 0 DERIVED::notifyRefCountIsZero() will be called. In the case when an unref() causes both
+ * the ref cnt to reach zero and the other counts are zero, notifyRefCountIsZero() will be called
+ * before notifyIsPurgeable(). Moreover, if notifyRefCountIsZero() returns false then
+ * notifyAllRefCntsAreZero() won't be called at all. notifyRefCountIsZero() must return false if the
+ * object may be deleted after notifyRefCntIsZero() returns.
+ *
+ * GrIORef and GrGpuResource are separate classes for organizational reasons and to be
  * able to give access via friendship to only the functions related to pending IO operations.
  */
 template <typename DERIVED> class GrIORef : public SkNoncopyable {
@@ -52,8 +59,14 @@
 
     void unref() const {
         this->validate();
-        --fRefCnt;
-        this->didUnref();
+        
+        if (!(--fRefCnt)) {
+            if (!static_cast<const DERIVED*>(this)->notifyRefCountIsZero()) {
+                return;
+            }
+        }
+
+        this->didRemoveRefOrPendingIO(kRef_CntType);
     }
 
     void validate() const {
@@ -68,6 +81,12 @@
 protected:
     GrIORef() : fRefCnt(1), fPendingReads(0), fPendingWrites(0) { }
 
+    enum CntType {
+        kRef_CntType,
+        kPendingRead_CntType,
+        kPendingWrite_CntType,
+    };
+
     bool isPurgeable() const { return !this->internalHasRef() && !this->internalHasPendingIO(); }
 
     bool internalHasPendingRead() const { return SkToBool(fPendingReads); }
@@ -85,7 +104,7 @@
     void completedRead() const {
         this->validate();
         --fPendingReads;
-        this->didUnref();
+        this->didRemoveRefOrPendingIO(kPendingRead_CntType);
     }
 
     void addPendingWrite() const {
@@ -96,13 +115,13 @@
     void completedWrite() const {
         this->validate();
         --fPendingWrites;
-        this->didUnref();
+        this->didRemoveRefOrPendingIO(kPendingWrite_CntType);
     }
 
 private:
-    void didUnref() const {
+    void didRemoveRefOrPendingIO(CntType cntTypeRemoved) const {
         if (0 == fPendingReads && 0 == fPendingWrites && 0 == fRefCnt) {
-            static_cast<const DERIVED*>(this)->notifyIsPurgeable();
+            static_cast<const DERIVED*>(this)->notifyAllCntsAreZero(cntTypeRemoved);
         }
     }
 
@@ -271,7 +290,8 @@
     // See comments in CacheAccess and ResourcePriv.
     void setUniqueKey(const GrUniqueKey&);
     void removeUniqueKey();
-    void notifyIsPurgeable() const;
+    void notifyAllCntsAreZero(CntType) const;
+    bool notifyRefCountIsZero() const;
     void removeScratchKey();
     void makeBudgeted();
     void makeUnbudgeted();
@@ -304,7 +324,7 @@
     SkAutoTUnref<const SkData>  fData;
 
     typedef GrIORef<GrGpuResource> INHERITED;
-    friend class GrIORef<GrGpuResource>; // to access notifyIsPurgeable.
+    friend class GrIORef<GrGpuResource>; // to access notifyAllCntsAreZero and notifyRefCntIsZero.
 };
 
 #endif
diff --git a/include/gpu/GrInvariantOutput.h b/include/gpu/GrInvariantOutput.h
index 4977333..5bf929f 100644
--- a/include/gpu/GrInvariantOutput.h
+++ b/include/gpu/GrInvariantOutput.h
@@ -79,6 +79,7 @@
     };
 
     void mulByUnknownOpaqueFourComponents() {
+        SkDEBUGCODE(this->validate());
         if (this->isOpaque()) {
             fValidFlags = kA_GrColorComponentFlag;
             fIsSingleComponent = false;
@@ -87,26 +88,32 @@
             // multiplied is opaque.
             this->mulByUnknownFourComponents();
         }
+        SkDEBUGCODE(this->validate());
     }
 
     void mulByUnknownFourComponents() {
+        SkDEBUGCODE(this->validate());
         if (this->hasZeroAlpha()) {
             this->internalSetToTransparentBlack();
         } else {
             this->internalSetToUnknown();
         }
+        SkDEBUGCODE(this->validate());
     }
 
     void mulByUnknownSingleComponent() {
+        SkDEBUGCODE(this->validate());
         if (this->hasZeroAlpha()) {
             this->internalSetToTransparentBlack();
         } else {
             // We don't need to change fIsSingleComponent in this case
             fValidFlags = 0;
         }
+        SkDEBUGCODE(this->validate());
     }
 
     void mulByKnownSingleComponent(uint8_t alpha) {
+        SkDEBUGCODE(this->validate());
         if (this->hasZeroAlpha() || 0 == alpha) {
             this->internalSetToTransparentBlack();
         } else {
@@ -116,20 +123,92 @@
                                          SkMulDiv255Round(GrColorUnpackG(fColor), alpha),
                                          SkMulDiv255Round(GrColorUnpackB(fColor), alpha),
                                          SkMulDiv255Round(GrColorUnpackA(fColor), alpha));
+                // We don't need to change fIsSingleComponent in this case
             }
         }
+        SkDEBUGCODE(this->validate());
+    }
+
+    void mulByKnownFourComponents(GrColor color) {
+        SkDEBUGCODE(this->validate());
+        uint32_t a;
+        if (GetAlphaAndCheckSingleChannel(color, &a)) {
+            this->mulByKnownSingleComponent(a);
+        } else {
+            if (color != 0xffffffff) {
+                fColor = GrColorPackRGBA(
+                    SkMulDiv255Round(GrColorUnpackR(fColor), GrColorUnpackR(color)),
+                    SkMulDiv255Round(GrColorUnpackG(fColor), GrColorUnpackG(color)),
+                    SkMulDiv255Round(GrColorUnpackB(fColor), GrColorUnpackB(color)),
+                    SkMulDiv255Round(GrColorUnpackA(fColor), a));
+                if (kRGBA_GrColorComponentFlags == fValidFlags) {
+                    fIsSingleComponent = GetAlphaAndCheckSingleChannel(fColor, &a);
+                }
+            }
+        }
+        SkDEBUGCODE(this->validate());
+    }
+
+    // Ignores the incoming color's RGB and muls its alpha by color.
+    void mulAlphaByKnownFourComponents(GrColor color) {
+        SkDEBUGCODE(this->validate());
+        uint32_t a;
+        if (GetAlphaAndCheckSingleChannel(color, &a)) {
+            this->mulAlphaByKnownSingleComponent(a);
+        } else if (fValidFlags & kA_GrColorComponentFlag) {
+            GrColor preAlpha = GrColorUnpackA(fColor);
+            if (0 == preAlpha) {
+                this->internalSetToTransparentBlack();
+            } else {
+                // We know that color has different component values
+                fIsSingleComponent = false;
+                fColor = GrColorPackRGBA(
+                    SkMulDiv255Round(preAlpha, GrColorUnpackR(color)),
+                    SkMulDiv255Round(preAlpha, GrColorUnpackG(color)),
+                    SkMulDiv255Round(preAlpha, GrColorUnpackB(color)),
+                    SkMulDiv255Round(preAlpha, a));
+                fValidFlags = kRGBA_GrColorComponentFlags;
+            }
+        } else {
+            fIsSingleComponent = false;
+            fValidFlags = 0;
+        }
+        SkDEBUGCODE(this->validate());
+    }
+
+    // Ignores the incoming color's RGB and muls its alpha by the alpha param and sets all channels
+    // equal to that value.
+    void mulAlphaByKnownSingleComponent(uint8_t alpha) {
+        SkDEBUGCODE(this->validate());
+        if (0 == alpha || this->hasZeroAlpha()) {
+            this->internalSetToTransparentBlack();
+        } else {
+            if (fValidFlags & kA_GrColorComponentFlag) {
+                GrColor a = GrColorUnpackA(fColor);
+                a = SkMulDiv255Round(alpha, a);
+                fColor = GrColorPackRGBA(a, a, a, a);
+                fValidFlags = kRGBA_GrColorComponentFlags;
+            } else {
+                fValidFlags = 0;
+            }
+            fIsSingleComponent = true;
+        }
+        SkDEBUGCODE(this->validate());
     }
 
     void invalidateComponents(uint8_t invalidateFlags, ReadInput readsInput) {
+        SkDEBUGCODE(this->validate());
         fValidFlags &= ~invalidateFlags;
         fIsSingleComponent = false;
         fNonMulStageFound = true;
         if (kWillNot_ReadInput == readsInput) {
             fWillUseInputColor = false;
         }
+        SkDEBUGCODE(this->validate());
     }
 
     void setToOther(uint8_t validFlags, GrColor color, ReadInput readsInput) {
+        SkDEBUGCODE(this->validate());
         fValidFlags = validFlags;
         fColor = color;
         fIsSingleComponent = false;
@@ -137,14 +216,28 @@
         if (kWillNot_ReadInput == readsInput) {
             fWillUseInputColor = false;
         }
+        if (kRGBA_GrColorComponentFlags == fValidFlags) {
+            uint32_t a;
+            if (GetAlphaAndCheckSingleChannel(color, &a)) {
+                fIsSingleComponent = true;
+            }
+        } else if (kA_GrColorComponentFlag & fValidFlags) {
+            // Assuming fColor is premul means if a is 0 the color must be all 0s.
+            if (!GrColorUnpackA(fColor)) {
+                this->internalSetToTransparentBlack();
+            }
+        }
+        SkDEBUGCODE(this->validate());
     }
 
     void setToUnknown(ReadInput readsInput) {
+        SkDEBUGCODE(this->validate());
         this->internalSetToUnknown();
         fNonMulStageFound= true;
         if (kWillNot_ReadInput == readsInput) {
             fWillUseInputColor = false;
         }
+        SkDEBUGCODE(this->validate());
     }
 
     // Temporary setter to handle LCD text correctly until we improve texture pixel config queries
@@ -165,6 +258,13 @@
 private:
     friend class GrProcOptInfo;
 
+    /** Extracts the alpha channel and returns true if r,g,b == a. */
+    static bool GetAlphaAndCheckSingleChannel(GrColor color, uint32_t* alpha) {
+        *alpha = GrColorUnpackA(color);
+        return *alpha == GrColorUnpackR(color) && *alpha == GrColorUnpackG(color) &&
+               *alpha == GrColorUnpackB(color);
+    }
+
     void reset(GrColor color, GrColorComponentFlags flags, bool isSingleComponent) {
         fColor = color;
         fValidFlags = flags;
diff --git a/include/gpu/GrPathRendererChain.h b/include/gpu/GrPathRendererChain.h
index 9c4b976..9b703da 100644
--- a/include/gpu/GrPathRendererChain.h
+++ b/include/gpu/GrPathRendererChain.h
@@ -15,9 +15,9 @@
 class GrDrawTarget;
 class GrPathRenderer;
 class GrPipelineBuilder;
+class GrStrokeInfo;
 class SkMatrix;
 class SkPath;
-class SkStrokeRec;
 
 /**
  * Keeps track of an ordered list of path renderers. When a path needs to be
@@ -61,7 +61,7 @@
                                     const GrPipelineBuilder*,
                                     const SkMatrix& viewMatrix,
                                     const SkPath& path,
-                                    const SkStrokeRec& rec,
+                                    const GrStrokeInfo& stroke,
                                     DrawType drawType,
                                     StencilSupport* stencilSupport);
 
diff --git a/include/gpu/GrProcessorUnitTest.h b/include/gpu/GrProcessorUnitTest.h
index 4b97483..5145a49 100644
--- a/include/gpu/GrProcessorUnitTest.h
+++ b/include/gpu/GrProcessorUnitTest.h
@@ -8,8 +8,7 @@
 #ifndef GrProcessorUnitTest_DEFINED
 #define GrProcessorUnitTest_DEFINED
 
-#include "GrColor.h"
-#include "SkRandom.h"
+#include "GrTestUtils.h"
 #include "SkTArray.h"
 #include "SkTypes.h"
 
@@ -23,71 +22,6 @@
     kAlphaTextureIdx = 1,
 };
 
-/**
- * A helper for use in GrProcessor::TestCreate functions.
- */
-const SkMatrix& TestMatrix(SkRandom*);
-
-}
-
-static inline GrColor GrRandomColor(SkRandom* random) {
-    // There are only a few cases of random colors which interest us
-    enum ColorMode {
-        kAllOnes_ColorMode,
-        kAllZeros_ColorMode,
-        kAlphaOne_ColorMode,
-        kRandom_ColorMode,
-        kLast_ColorMode = kRandom_ColorMode
-    };
-
-    ColorMode colorMode = ColorMode(random->nextULessThan(kLast_ColorMode + 1));
-    GrColor color;
-    switch (colorMode) {
-        case kAllOnes_ColorMode:
-            color = GrColorPackRGBA(0xFF, 0xFF, 0xFF, 0xFF);
-            break;
-        case kAllZeros_ColorMode:
-            color = GrColorPackRGBA(0, 0, 0, 0);
-            break;
-        case kAlphaOne_ColorMode:
-            color = GrColorPackRGBA(random->nextULessThan(256),
-                                    random->nextULessThan(256),
-                                    random->nextULessThan(256),
-                                    0xFF);
-            break;
-        case kRandom_ColorMode:
-            uint8_t alpha = random->nextULessThan(256);
-            color = GrColorPackRGBA(random->nextRangeU(0, alpha),
-                                    random->nextRangeU(0, alpha),
-                                    random->nextRangeU(0, alpha),
-                                    alpha);
-            break;
-    }
-    GrColorIsPMAssert(color);
-    return color;
-}
-
-static inline uint8_t GrRandomCoverage(SkRandom* random) {
-    enum CoverageMode {
-        kZero_CoverageMode,
-        kAllOnes_CoverageMode,
-        kRandom_CoverageMode,
-        kLast_CoverageMode = kRandom_CoverageMode
-    };
-
-    CoverageMode colorMode = CoverageMode(random->nextULessThan(kLast_CoverageMode + 1));
-    uint8_t coverage;
-    switch (colorMode) {
-        case kZero_CoverageMode:
-            coverage = 0;
-        case kAllOnes_CoverageMode:
-            coverage = 0xff;
-            break;
-        case kRandom_CoverageMode:
-            coverage = random->nextULessThan(256);
-            break;
-    }
-    return coverage;
 }
 
 #if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
diff --git a/include/gpu/GrRenderTarget.h b/include/gpu/GrRenderTarget.h
index 40e006a..5984bf7 100644
--- a/include/gpu/GrRenderTarget.h
+++ b/include/gpu/GrRenderTarget.h
@@ -11,7 +11,7 @@
 #include "GrSurface.h"
 #include "SkRect.h"
 
-class GrStencilBuffer;
+class GrStencilAttachment;
 class GrRenderTargetPriv;
 
 /**
@@ -26,8 +26,8 @@
     SK_DECLARE_INST_COUNT(GrRenderTarget)
 
     // GrSurface overrides
-    GrRenderTarget* asRenderTarget() SK_OVERRIDE { return this; }
-    const GrRenderTarget* asRenderTarget() const  SK_OVERRIDE { return this; }
+    GrRenderTarget* asRenderTarget() override { return this; }
+    const GrRenderTarget* asRenderTarget() const  override { return this; }
 
     // GrRenderTarget
     /**
@@ -96,13 +96,13 @@
 protected:
     GrRenderTarget(GrGpu* gpu, LifeCycle lifeCycle, const GrSurfaceDesc& desc)
         : INHERITED(gpu, lifeCycle, desc)
-        , fStencilBuffer(NULL) {
+        , fStencilAttachment(NULL) {
         fResolveRect.setLargestInverted();
     }
 
     // override of GrResource
-    void onAbandon() SK_OVERRIDE;
-    void onRelease() SK_OVERRIDE;
+    void onAbandon() override;
+    void onRelease() override;
 
 private:
     // Checked when this object is asked to attach a stencil buffer.
@@ -110,9 +110,9 @@
 
     friend class GrRenderTargetPriv;
 
-    GrStencilBuffer*  fStencilBuffer;
+    GrStencilAttachment*  fStencilAttachment;
 
-    SkIRect           fResolveRect;
+    SkIRect               fResolveRect;
 
     typedef GrSurface INHERITED;
 };
diff --git a/include/gpu/GrResourceKey.h b/include/gpu/GrResourceKey.h
index aecdc70..50a7145 100644
--- a/include/gpu/GrResourceKey.h
+++ b/include/gpu/GrResourceKey.h
@@ -10,6 +10,7 @@
 #define GrResourceKey_DEFINED
 
 #include "GrTypes.h"
+#include "SkOnce.h"
 #include "SkTemplates.h"
 
 uint32_t GrResourceKeyHash(const uint32_t* data, size_t size);
@@ -266,6 +267,24 @@
     };
 };
 
+/**
+ * It is common to need a frequently reused GrUniqueKey where the only requirement is that the key
+ * is unique. These macros create such a key in a thread safe manner so the key can be truly global
+ * and only constructed once.
+ */
+
+/** Place outside of function/class definitions. */
+#define GR_DECLARE_STATIC_UNIQUE_KEY(name) SK_DECLARE_STATIC_ONCE(name##_once)
+
+/** Place inside function where the key is used. */
+#define GR_DEFINE_STATIC_UNIQUE_KEY(name)                           \
+    static GrUniqueKey name;                                        \
+    SkOnce(&name##_once, gr_init_static_unique_key_once, &name)
+
+static inline void gr_init_static_unique_key_once(GrUniqueKey* key) {
+    GrUniqueKey::Builder builder(key, GrUniqueKey::GenerateDomain(), 0);
+}
+
 // The cache listens for these messages to purge junk resources proactively.
 class GrUniqueKeyInvalidatedMessage {
 public:
diff --git a/include/gpu/GrTestUtils.h b/include/gpu/GrTestUtils.h
new file mode 100644
index 0000000..6fed7e1
--- /dev/null
+++ b/include/gpu/GrTestUtils.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrTestUtils_DEFINED
+#define GrTestUtils_DEFINED
+
+#include "SkTypes.h"
+
+#ifdef GR_TEST_UTILS
+
+#include "GrColor.h"
+#include "SkRandom.h"
+#include "SkStrokeRec.h"
+
+class SkMatrix;
+class SkPath;
+class SkRRect;
+struct SkRect;
+
+namespace GrTest {
+/**
+ * A helper for use in Test functions.
+ */
+const SkMatrix& TestMatrix(SkRandom*);
+const SkMatrix& TestMatrixPreservesRightAngles(SkRandom*);
+const SkMatrix& TestMatrixRectStaysRect(SkRandom*);
+const SkMatrix& TestMatrixInvertible(SkRandom*);
+const SkRect& TestRect(SkRandom*);
+const SkRect& TestSquare(SkRandom*);
+const SkRRect& TestRRectSimple(SkRandom*);
+const SkPath& TestPath(SkRandom*);
+const SkPath& TestPathConvex(SkRandom*);
+SkStrokeRec TestStrokeRec(SkRandom*);
+
+}
+
+static inline GrColor GrRandomColor(SkRandom* random) {
+    // There are only a few cases of random colors which interest us
+    enum ColorMode {
+        kAllOnes_ColorMode,
+        kAllZeros_ColorMode,
+        kAlphaOne_ColorMode,
+        kRandom_ColorMode,
+        kLast_ColorMode = kRandom_ColorMode
+    };
+
+    ColorMode colorMode = ColorMode(random->nextULessThan(kLast_ColorMode + 1));
+    GrColor color;
+    switch (colorMode) {
+        case kAllOnes_ColorMode:
+            color = GrColorPackRGBA(0xFF, 0xFF, 0xFF, 0xFF);
+            break;
+        case kAllZeros_ColorMode:
+            color = GrColorPackRGBA(0, 0, 0, 0);
+            break;
+        case kAlphaOne_ColorMode:
+            color = GrColorPackRGBA(random->nextULessThan(256),
+                                    random->nextULessThan(256),
+                                    random->nextULessThan(256),
+                                    0xFF);
+            break;
+        case kRandom_ColorMode: {
+                uint8_t alpha = random->nextULessThan(256);
+                color = GrColorPackRGBA(random->nextRangeU(0, alpha),
+                                        random->nextRangeU(0, alpha),
+                                        random->nextRangeU(0, alpha),
+                                        alpha);
+            break;
+        }
+    }
+    GrColorIsPMAssert(color);
+    return color;
+}
+
+static inline uint8_t GrRandomCoverage(SkRandom* random) {
+    enum CoverageMode {
+        kZero_CoverageMode,
+        kAllOnes_CoverageMode,
+        kRandom_CoverageMode,
+        kLast_CoverageMode = kRandom_CoverageMode
+    };
+
+    CoverageMode colorMode = CoverageMode(random->nextULessThan(kLast_CoverageMode + 1));
+    uint8_t coverage;
+    switch (colorMode) {
+        case kZero_CoverageMode:
+            coverage = 0;
+        case kAllOnes_CoverageMode:
+            coverage = 0xff;
+            break;
+        case kRandom_CoverageMode:
+            coverage = random->nextULessThan(256);
+            break;
+    }
+    return coverage;
+}
+
+#endif
+#endif
diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h
index ccf9580..d565f2f 100644
--- a/include/gpu/GrTexture.h
+++ b/include/gpu/GrTexture.h
@@ -18,8 +18,8 @@
 
 class GrTexture : virtual public GrSurface {
 public:
-    GrTexture* asTexture() SK_OVERRIDE { return this; }
-    const GrTexture* asTexture() const SK_OVERRIDE { return this; }
+    GrTexture* asTexture() override { return this; }
+    const GrTexture* asTexture() const override { return this; }
 
     /**
      *  Return the native ID or handle to the texture, depending on the
@@ -50,7 +50,7 @@
     void validateDesc() const;
 
 private:
-    size_t onGpuMemorySize() const SK_OVERRIDE;
+    size_t onGpuMemorySize() const override;
     void dirtyMipMaps(bool mipMapsDirty);
 
     enum MipMapsStatus {
diff --git a/include/gpu/GrTextureProvider.h b/include/gpu/GrTextureProvider.h
new file mode 100644
index 0000000..3f8c760
--- /dev/null
+++ b/include/gpu/GrTextureProvider.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrTextureProvider_DEFINED
+#define GrTextureProvider_DEFINED
+
+#include "GrTexture.h"
+
+class SK_API GrTextureProvider {
+public:
+    ///////////////////////////////////////////////////////////////////////////
+    // Textures
+
+    /**
+     * Creates a new texture in the resource cache and returns it. The caller owns a
+     * ref on the returned texture which must be balanced by a call to unref.
+     *
+     * @param desc      Description of the texture properties.
+     * @param budgeted  Does the texture count against the resource cache budget?
+     * @param srcData   Pointer to the pixel values (optional).
+     * @param rowBytes  The number of bytes between rows of the texture. Zero
+     *                  implies tightly packed rows. For compressed pixel configs, this
+     *                  field is ignored.
+     */
+    GrTexture* createTexture(const GrSurfaceDesc& desc, bool budgeted, const void* srcData,
+                             size_t rowBytes);
+
+    /** Shortcut for creating a texture with no initial data to upload. */
+    GrTexture* createTexture(const GrSurfaceDesc& desc, bool budgeted) {
+        return this->createTexture(desc, budgeted, NULL, 0);
+    }
+
+    /** Assigns a unique key to the texture. The texture will be findable via this key using
+        findTextureByUniqueKey(). If an existing texture has this key, it's key will be removed. */
+    void assignUniqueKeyToTexture(const GrUniqueKey& key, GrTexture* texture) {
+        this->assignUniqueKeyToResource(key, texture);
+    }
+
+    /** Finds a texture by unique key. If the texture is found it is ref'ed and returned. */
+    GrTexture* findAndRefTextureByUniqueKey(const GrUniqueKey& key) {
+        GrGpuResource* resource = this->findAndRefResourceByUniqueKey(key);
+        if (resource) {
+            GrTexture* texture = static_cast<GrSurface*>(resource)->asTexture();
+            SkASSERT(texture);
+            return texture;
+        }
+        return NULL;
+    }
+
+    /**
+     * Determines whether a texture is associated with the unique key. If the texture is found it
+     * will not be locked or returned. This call does not affect the priority of the resource for
+     * deletion.
+     */
+    bool existsTextureWithUniqueKey(const GrUniqueKey& key) const {
+        return this->existsResourceWithUniqueKey(key);
+    }
+
+    /**
+     * Enum that determines how closely a returned scratch texture must match
+     * a provided GrSurfaceDesc. TODO: Remove this. createTexture() should be used
+     * for exact match and refScratchTexture() should be replaced with createApproxTexture().
+     */
+    enum ScratchTexMatch {
+        /**
+         * Finds a texture that exactly matches the descriptor.
+         */
+        kExact_ScratchTexMatch,
+        /**
+         * Finds a texture that approximately matches the descriptor. Will be
+         * at least as large in width and height as desc specifies. If desc
+         * specifies that texture is a render target then result will be a
+         * render target. If desc specifies a render target and doesn't set the
+         * no stencil flag then result will have a stencil. Format and aa level
+         * will always match.
+         */
+        kApprox_ScratchTexMatch
+    };
+
+    /**
+     * Returns a texture matching the desc. It's contents are unknown. The caller
+     * owns a ref on the returned texture and must balance with a call to unref.
+     * It is guaranteed that the same texture will not be returned in subsequent
+     * calls until all refs to the texture are dropped.
+     *
+     * internalFlag is a temporary workaround until changes in the internal
+     * architecture are complete. Use the default value.
+     *
+     * TODO: Once internal flag can be removed, this should be replaced with
+     * createApproxTexture() and exact textures should be created with
+     * createTexture().
+     */
+    GrTexture* refScratchTexture(const GrSurfaceDesc&, ScratchTexMatch match,
+                                 bool internalFlag = false);
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Wrapped Backend Surfaces
+
+    /**
+     * Wraps an existing texture with a GrTexture object.
+     *
+     * OpenGL: if the object is a texture Gr may change its GL texture params
+     *         when it is drawn.
+     *
+     * @param  desc     description of the object to create.
+     *
+     * @return GrTexture object or NULL on failure.
+     */
+    GrTexture* wrapBackendTexture(const GrBackendTextureDesc& desc);
+
+    /**
+     * Wraps an existing render target with a GrRenderTarget object. It is
+     * similar to wrapBackendTexture but can be used to draw into surfaces
+     * that are not also textures (e.g. FBO 0 in OpenGL, or an MSAA buffer that
+     * the client will resolve to a texture).
+     *
+     * @param  desc     description of the object to create.
+     *
+     * @return GrTexture object or NULL on failure.
+     */
+     GrRenderTarget* wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc);
+
+protected:
+    GrTextureProvider(GrGpu* gpu, GrResourceCache* cache) : fCache(cache), fGpu(gpu) {}
+
+    /**
+     * Assigns a unique key to a resource. If the key is associated with another resource that
+     * association is removed and replaced by this resource.
+     */
+    void assignUniqueKeyToResource(const GrUniqueKey&, GrGpuResource*);
+
+    /**
+     * Finds a resource in the cache, based on the specified key. This is intended for use in
+     * conjunction with addResourceToCache(). The return value will be NULL if not found. The
+     * caller must balance with a call to unref().
+     */
+    GrGpuResource* findAndRefResourceByUniqueKey(const GrUniqueKey&);
+
+    /**
+     * Determines whether a resource is in the cache. If the resource is found it
+     * will not be locked or returned. This call does not affect the priority of
+     * the resource for deletion.
+     */
+    bool existsResourceWithUniqueKey(const GrUniqueKey& key) const;
+
+    GrTexture* internalRefScratchTexture(const GrSurfaceDesc&, uint32_t flags);
+
+    void abandon() {
+        fCache = NULL;
+        fGpu = NULL;
+    }
+
+    GrResourceCache* cache() { return fCache; }
+    const GrResourceCache* cache() const { return fCache; }
+
+    GrGpu* gpu() { return fGpu; }
+    const GrGpu* gpu() const { return fGpu; }
+
+private:
+    bool isAbandoned() const {
+        SkASSERT(SkToBool(fGpu) == SkToBool(fCache));
+        return !SkToBool(fCache);
+    }
+
+    GrResourceCache* fCache;
+    GrGpu* fGpu;
+};
+
+#endif
diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h
index 3f02c62..c1d324b 100644
--- a/include/gpu/GrTypes.h
+++ b/include/gpu/GrTypes.h
@@ -23,6 +23,9 @@
     inline X operator | (X a, X b) { \
         return (X) (+a | +b); \
     } \
+    inline X& operator |= (X& a, X b) { \
+        return (a = a | b); \
+    } \
     \
     inline X operator & (X a, X b) { \
         return (X) (+a & +b); \
@@ -38,6 +41,7 @@
 
 #define GR_DECL_BITFIELD_OPS_FRIENDS(X) \
     friend X operator | (X a, X b); \
+    friend X& operator |= (X& a, X b); \
     \
     friend X operator & (X a, X b); \
     \
@@ -142,7 +146,8 @@
     kTriangleFan_GrPrimitiveType,
     kPoints_GrPrimitiveType,
     kLines_GrPrimitiveType,     // 1 pix wide only
-    kLineStrip_GrPrimitiveType  // 1 pix wide only
+    kLineStrip_GrPrimitiveType, // 1 pix wide only
+    kLast_GrPrimitiveType = kLineStrip_GrPrimitiveType
 };
 
 static inline bool GrIsPrimTypeLines(GrPrimitiveType type) {
@@ -390,10 +395,6 @@
      */
     kRenderTarget_GrSurfaceFlag     = 0x1,
     /**
-     * DEPRECATED. This has no effect.
-     */
-    kNoStencil_GrSurfaceFlag        = 0x2,
-    /**
      * Indicates that all allocations (color buffer, FBO completeness, etc)
      * should be verified.
      */
@@ -402,13 +403,6 @@
 
 GR_MAKE_BITFIELD_OPS(GrSurfaceFlags)
 
-// Legacy aliases
-typedef GrSurfaceFlags GrTextureFlags;
-static const GrSurfaceFlags kNone_GrTextureFlags = kNone_GrSurfaceFlags;
-static const GrSurfaceFlags kRenderTarget_GrTextureFlagBit = kRenderTarget_GrSurfaceFlag;
-static const GrSurfaceFlags kNoStencil_GrTextureFlagBit = kNoStencil_GrSurfaceFlag;
-static const GrSurfaceFlags kCheckAllocation_GrTextureFlagBit = kCheckAllocation_GrSurfaceFlag;
-
 /**
  * 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
diff --git a/include/gpu/GrTypesPriv.h b/include/gpu/GrTypesPriv.h
index 412de89..eaf1aa4 100644
--- a/include/gpu/GrTypesPriv.h
+++ b/include/gpu/GrTypesPriv.h
@@ -264,4 +264,14 @@
     SkIRect fRect;
 };
 
+#ifdef SK_DEBUG
+// Takes a pointer to a GrContext, and will suppress prints if required
+#define GrContextDebugf(context, ...) \
+    if (!context->suppressPrints()) {         \
+        SkDebugf(__VA_ARGS__);      \
+    }
+#else
+#define GrContextDebugf(context, ...)
+#endif
+
 #endif
diff --git a/include/gpu/GrXferProcessor.h b/include/gpu/GrXferProcessor.h
index 87663b1..fa9dca2 100644
--- a/include/gpu/GrXferProcessor.h
+++ b/include/gpu/GrXferProcessor.h
@@ -14,17 +14,51 @@
 #include "GrTypes.h"
 #include "SkXfermode.h"
 
-class GrDrawTargetCaps;
-class GrGLCaps;
+class GrShaderCaps;
+class GrGLSLCaps;
 class GrGLXferProcessor;
 class GrProcOptInfo;
 
 /**
+ * Equations for alpha-blending.
+ */
+enum GrBlendEquation {
+    // Basic blend equations.
+    kAdd_GrBlendEquation,             //<! Cs*S + Cd*D
+    kSubtract_GrBlendEquation,        //<! Cs*S - Cd*D
+    kReverseSubtract_GrBlendEquation, //<! Cd*D - Cs*S
+
+    // Advanced blend equations. These are described in the SVG and PDF specs.
+    kScreen_GrBlendEquation,
+    kOverlay_GrBlendEquation,
+    kDarken_GrBlendEquation,
+    kLighten_GrBlendEquation,
+    kColorDodge_GrBlendEquation,
+    kColorBurn_GrBlendEquation,
+    kHardLight_GrBlendEquation,
+    kSoftLight_GrBlendEquation,
+    kDifference_GrBlendEquation,
+    kExclusion_GrBlendEquation,
+    kMultiply_GrBlendEquation,
+    kHSLHue_GrBlendEquation,
+    kHSLSaturation_GrBlendEquation,
+    kHSLColor_GrBlendEquation,
+    kHSLLuminosity_GrBlendEquation,
+
+    kFirstAdvancedGrBlendEquation = kScreen_GrBlendEquation,
+    kLast_GrBlendEquation = kHSLLuminosity_GrBlendEquation
+};
+
+static const int kGrBlendEquationCnt = kLast_GrBlendEquation + 1;
+
+inline bool GrBlendEquationIsAdvanced(GrBlendEquation equation) {
+    return equation >= kFirstAdvancedGrBlendEquation;
+}
+
+/**
  * Coeffecients for alpha-blending.
  */
 enum GrBlendCoeff {
-    kInvalid_GrBlendCoeff = -1,
-
     kZero_GrBlendCoeff,    //<! 0
     kOne_GrBlendCoeff,     //<! 1
     kSC_GrBlendCoeff,      //<! src color
@@ -44,7 +78,18 @@
     kS2A_GrBlendCoeff,
     kIS2A_GrBlendCoeff,
 
-    kTotalGrBlendCoeffCount
+    kLast_GrBlendCoeff = kIS2A_GrBlendCoeff
+};
+
+static const int kGrBlendCoeffCnt = kLast_GrBlendCoeff + 1;
+
+/**
+ * Barriers for blending. When a shader reads the dst directly, an Xfer barrier is sometimes
+ * required after a pixel has been written, before it can be safely read again.
+ */
+enum GrXferBarrierType {
+    kTexture_GrXferBarrierType, //<! Required when a shader reads and renders to the same texture.
+    kBlend_GrXferBarrierType,   //<! Required by certain blend extensions.
 };
 
 /**
@@ -66,7 +111,7 @@
      * Sets a unique key on the GrProcessorKeyBuilder calls onGetGLProcessorKey(...) to get the
      * specific subclass's key.
      */ 
-    void getGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const;
+    void getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const;
 
     /** Returns a new instance of the appropriate *GL* implementation class
         for the given GrXferProcessor; caller is responsible for deleting
@@ -118,22 +163,42 @@
      * A caller who calls this function on a XP is required to honor the returned OptFlags
      * and color values for its draw.
      */
-    virtual OptFlags getOptimizations(const GrProcOptInfo& colorPOI,
-                                      const GrProcOptInfo& coveragePOI,
-                                      bool doesStencilWrite,
-                                      GrColor* overrideColor,
-                                      const GrDrawTargetCaps& caps) = 0;
+    OptFlags getOptimizations(const GrProcOptInfo& colorPOI,
+                              const GrProcOptInfo& coveragePOI,
+                              bool doesStencilWrite,
+                              GrColor* overrideColor,
+                              const GrDrawTargetCaps& caps);
+
+    /**
+     * Returns whether this XP will require an Xfer barrier on the given rt. If true, outBarrierType
+     * is updated to contain the type of barrier needed.
+     */
+    bool willNeedXferBarrier(const GrRenderTarget* rt,
+                             const GrDrawTargetCaps& caps,
+                             GrXferBarrierType* outBarrierType) const;
 
     struct BlendInfo {
-        BlendInfo() : fWriteColor(true) {}
+        void reset() {
+            fEquation = kAdd_GrBlendEquation;
+            fSrcBlend = kOne_GrBlendCoeff;
+            fDstBlend = kZero_GrBlendCoeff;
+            fBlendConstant = 0;
+            fWriteColor = true;
+        }
 
-        GrBlendCoeff fSrcBlend;
-        GrBlendCoeff fDstBlend;
-        GrColor      fBlendConstant;
-        bool         fWriteColor;
+        SkDEBUGCODE(SkString dump() const;)
+
+        GrBlendEquation fEquation;
+        GrBlendCoeff    fSrcBlend;
+        GrBlendCoeff    fDstBlend;
+        GrColor         fBlendConstant;
+        bool            fWriteColor;
     };
 
-    virtual void getBlendInfo(BlendInfo* blendInfo) const = 0;
+    void getBlendInfo(BlendInfo* blendInfo) const {
+        blendInfo->reset();
+        this->onGetBlendInfo(blendInfo);
+    }
 
     bool willReadDstColor() const { return fWillReadDstColor; }
 
@@ -153,6 +218,11 @@
         return fDstCopyTextureOffset;
     }
 
+    /**
+     * Returns whether or not the XP will look at coverage when doing its blending.
+     */
+    bool readsCoverage() const { return fReadsCoverage; }
+
     /** 
      * Returns whether or not this xferProcossor will set a secondary output to be used with dual
      * source blending.
@@ -173,6 +243,9 @@
         if (this->fWillReadDstColor != that.fWillReadDstColor) {
             return false;
         }
+        if (this->fReadsCoverage != that.fReadsCoverage) {
+            return false;
+        }
         if (this->fDstCopy.getTexture() != that.fDstCopy.getTexture()) {
             return false;
         }
@@ -187,16 +260,40 @@
     GrXferProcessor(const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
 
 private:
+    virtual OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
+                                        const GrProcOptInfo& coveragePOI,
+                                        bool doesStencilWrite,
+                                        GrColor* overrideColor,
+                                        const GrDrawTargetCaps& caps) = 0;
+
     /**
      * Sets a unique key on the GrProcessorKeyBuilder that is directly associated with this xfer
      * processor's GL backend implementation.
      */
-    virtual void onGetGLProcessorKey(const GrGLCaps& caps,
+    virtual void onGetGLProcessorKey(const GrGLSLCaps& caps,
                                      GrProcessorKeyBuilder* b) const = 0;
 
+    /**
+     * If not using a texture barrier, retrieves whether the subclass will require a different type
+     * of barrier.
+     */
+    virtual bool onWillNeedXferBarrier(const GrRenderTarget*,
+                                       const GrDrawTargetCaps&,
+                                       GrXferBarrierType* outBarrierType SK_UNUSED) const {
+        return false;
+    }
+
+    /**
+     * Retrieves the hardware blend state required by this Xfer processor. The BlendInfo struct
+     * comes initialized to default values, so the Xfer processor only needs to set the state it
+     * needs. It may not even need to override this method at all.
+     */
+    virtual void onGetBlendInfo(BlendInfo*) const {}
+
     virtual bool onIsEqual(const GrXferProcessor&) const = 0;
 
     bool                    fWillReadDstColor;
+    bool                    fReadsCoverage;
     SkIPoint                fDstCopyTextureOffset;
     GrTextureAccess         fDstCopy;
 
@@ -246,13 +343,6 @@
     virtual void getInvariantOutput(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI,
                                     InvariantOutput*) const = 0;
 
-    /**
-     * Determines whether multiplying the computed per-pixel color by the pixel's fractional
-     * coverage before the blend will give the correct final destination color. In general it
-     * will not as coverage is applied after blending.
-     */
-    virtual bool canTweakAlphaForCoverage() const = 0;
-
     bool willNeedDstCopy(const GrDrawTargetCaps& caps, const GrProcOptInfo& colorPOI,
                          const GrProcOptInfo& coveragePOI) const;
 
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index 7cb614d..4de61af 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -78,16 +78,23 @@
 // Sets the color of GrPaint to the value of the parameter paintColor
 // Callers may subsequently modify the GrPaint. Setting constantColor indicates
 // that the final paint will draw the same color at every pixel. This allows
-// an optimization where the the color filter can be applied to the SkPaint's
-// color once while converting to GrPaint and then ignored.
-void SkPaint2GrPaintNoShader(GrContext* context, GrRenderTarget*, const SkPaint& skPaint,
+// an optimization where the color filter can be applied to the SkPaint's
+// color once while converting to GrPaint and then ignored. TODO: Remove this
+// bool and use the invariant info to automatically apply the color filter.
+bool SkPaint2GrPaintNoShader(GrContext* context, GrRenderTarget*, const SkPaint& skPaint,
                              GrColor paintColor, bool constantColor, GrPaint* grPaint);
 
 // This function is similar to skPaint2GrPaintNoShader but also converts
 // skPaint's shader to a GrFragmentProcessor if possible.
 // constantColor has the same meaning as in skPaint2GrPaintNoShader.
-void SkPaint2GrPaintShader(GrContext* context, GrRenderTarget*, const SkPaint& skPaint,
-                           const SkMatrix& viewM, bool constantColor, GrPaint* grPaint);
+bool SkPaint2GrPaint(GrContext* context, GrRenderTarget*, const SkPaint& skPaint,
+                     const SkMatrix& viewM, bool constantColor, GrPaint* grPaint);
+
+
+SkImageInfo GrMakeInfoFromTexture(GrTexture* tex, int w, int h, bool isOpaque);
+
+// Using the dreaded SkGrPixelRef ...
+void GrWrapTextureInBitmap(GrTexture* src, int w, int h, bool isOpaque, SkBitmap* dst);
 
 ////////////////////////////////////////////////////////////////////////////////
 // Classes
diff --git a/include/gpu/SkGrPixelRef.h b/include/gpu/SkGrPixelRef.h
index 4eab59c..eae19db 100644
--- a/include/gpu/SkGrPixelRef.h
+++ b/include/gpu/SkGrPixelRef.h
@@ -25,9 +25,9 @@
     virtual ~SkROLockPixelsPixelRef();
 
 protected:
-    bool onNewLockPixels(LockRec*) SK_OVERRIDE;
-    void onUnlockPixels() SK_OVERRIDE;
-    bool onLockPixelsAreWritable() const SK_OVERRIDE;   // return false;
+    bool onNewLockPixels(LockRec*) override;
+    void onUnlockPixels() override;
+    bool onLockPixelsAreWritable() const override;   // return false;
 
 private:
     SkBitmap    fBitmap;
@@ -47,13 +47,13 @@
     virtual ~SkGrPixelRef();
 
     // override from SkPixelRef
-    GrTexture* getTexture() SK_OVERRIDE;
+    GrTexture* getTexture() override;
 
 protected:
     // overrides from SkPixelRef
-    bool onReadPixels(SkBitmap* dst, const SkIRect* subset) SK_OVERRIDE;
+    bool onReadPixels(SkBitmap* dst, const SkIRect* subset) override;
     virtual SkPixelRef* deepCopy(SkColorType, SkColorProfileType,
-                                 const SkIRect* subset) SK_OVERRIDE;
+                                 const SkIRect* subset) override;
 
 private:
     GrSurface*  fSurface;
diff --git a/include/gpu/effects/GrConstColorProcessor.h b/include/gpu/effects/GrConstColorProcessor.h
new file mode 100644
index 0000000..b68841a
--- /dev/null
+++ b/include/gpu/effects/GrConstColorProcessor.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrColorProcessor_DEFINED
+#define GrColorProcessor_DEFINED
+
+#include "GrFragmentProcessor.h"
+
+class GrInvariantOutput;
+
+/**
+ * This is a simple GrFragmentProcessor that outputs a constant color. It may do one of the
+ * following with its input color: ignore it, or multiply it by the constant color, multiply its
+ * alpha by the constant color and ignore the input color's r, g, and b.
+ */
+class GrConstColorProcessor : public GrFragmentProcessor {
+public:
+    enum InputMode {
+        kIgnore_InputMode,
+        kModulateRGBA_InputMode,
+        kModulateA_InputMode,
+
+        kLastInputMode = kModulateA_InputMode
+    };
+    static const int kInputModeCnt = kLastInputMode + 1;
+
+    static GrFragmentProcessor* Create(GrColor color, InputMode mode) {
+        return SkNEW_ARGS(GrConstColorProcessor, (color, mode));
+    }
+
+    ~GrConstColorProcessor() override {}
+
+    const char* name() const override { return "Color"; }
+
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
+
+    GrGLFragmentProcessor* createGLInstance() const override;
+
+    GrColor color() const { return fColor; }
+
+    InputMode inputMode() const { return fMode; }
+
+private:
+    GrConstColorProcessor(GrColor color, InputMode mode) : fColor(color), fMode(mode) {
+        this->initClassID<GrConstColorProcessor>();
+    }
+
+    bool onIsEqual(const GrFragmentProcessor&) const override;
+
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
+
+    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+    GrColor     fColor;
+    InputMode   fMode;
+
+    typedef GrFragmentProcessor INHERITED;
+};
+
+#endif
diff --git a/include/gpu/effects/GrCoverageSetOpXP.h b/include/gpu/effects/GrCoverageSetOpXP.h
index 9435b84..fcae6d2 100644
--- a/include/gpu/effects/GrCoverageSetOpXP.h
+++ b/include/gpu/effects/GrCoverageSetOpXP.h
@@ -24,14 +24,12 @@
     static GrXPFactory* Create(SkRegion::Op regionOp, bool invertCoverage = false);
 
     bool supportsRGBCoverage(GrColor /*knownColor*/,
-                             uint32_t /*knownColorFlags*/) const SK_OVERRIDE {
+                             uint32_t /*knownColorFlags*/) const override {
         return true;
     }
 
-    bool canTweakAlphaForCoverage() const SK_OVERRIDE { return false; }
-
     void getInvariantOutput(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI,
-                            GrXPFactory::InvariantOutput*) const SK_OVERRIDE;
+                            GrXPFactory::InvariantOutput*) const override;
 
 private:
     GrCoverageSetOpXPFactory(SkRegion::Op regionOp, bool invertCoverage);
@@ -39,15 +37,15 @@
     GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
                                            const GrProcOptInfo& colorPOI,
                                            const GrProcOptInfo& coveragePOI,
-                                           const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE;
+                                           const GrDeviceCoordTexture* dstCopy) const override;
 
     bool willReadDstColor(const GrDrawTargetCaps& /*caps*/,
                           const GrProcOptInfo& /*colorPOI*/,
-                          const GrProcOptInfo& /*coveragePOI*/) const SK_OVERRIDE {
+                          const GrProcOptInfo& /*coveragePOI*/) const override {
         return false;
     }
 
-    bool onIsEqual(const GrXPFactory& xpfBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrXPFactory& xpfBase) const override {
         const GrCoverageSetOpXPFactory& xpf = xpfBase.cast<GrCoverageSetOpXPFactory>();
         return fRegionOp == xpf.fRegionOp;
     }
diff --git a/include/gpu/effects/GrPorterDuffXferProcessor.h b/include/gpu/effects/GrPorterDuffXferProcessor.h
index 3bcdf0e..d47fc0f 100644
--- a/include/gpu/effects/GrPorterDuffXferProcessor.h
+++ b/include/gpu/effects/GrPorterDuffXferProcessor.h
@@ -18,12 +18,10 @@
 public:
     static GrXPFactory* Create(SkXfermode::Mode mode); 
 
-    bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const SK_OVERRIDE;
-
-    bool canTweakAlphaForCoverage() const SK_OVERRIDE;
+    bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const override;
 
     void getInvariantOutput(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI,
-                            GrXPFactory::InvariantOutput*) const SK_OVERRIDE;
+                            GrXPFactory::InvariantOutput*) const override;
 
 private:
     GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst); 
@@ -31,13 +29,13 @@
     GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
                                            const GrProcOptInfo& colorPOI,
                                            const GrProcOptInfo& coveragePOI,
-                                           const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE;
+                                           const GrDeviceCoordTexture* dstCopy) const override;
 
     bool willReadDstColor(const GrDrawTargetCaps& caps,
                           const GrProcOptInfo& colorPOI,
-                          const GrProcOptInfo& coveragePOI) const SK_OVERRIDE;
+                          const GrProcOptInfo& coveragePOI) const override;
 
-    bool onIsEqual(const GrXPFactory& xpfBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrXPFactory& xpfBase) const override {
         const GrPorterDuffXPFactory& xpf = xpfBase.cast<GrPorterDuffXPFactory>();
         return (fSrcCoeff == xpf.fSrcCoeff && fDstCoeff == xpf.fDstCoeff);
     }
diff --git a/include/gpu/gl/GrGLFunctions.h b/include/gpu/gl/GrGLFunctions.h
index 3a94058..5c2cf7b 100644
--- a/include/gpu/gl/GrGLFunctions.h
+++ b/include/gpu/gl/GrGLFunctions.h
@@ -72,10 +72,12 @@
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBindFramebufferProc)(GrGLenum target, GrGLuint framebuffer);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBindRenderbufferProc)(GrGLenum target, GrGLuint renderbuffer);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBindTextureProc)(GrGLenum target, GrGLuint texture);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBlendColorProc)(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBindFragDataLocationProc)(GrGLuint program, GrGLuint colorNumber, const GrGLchar* name);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBindFragDataLocationIndexedProc)(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBindVertexArrayProc)(GrGLuint array);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBlendBarrierProc)();
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBlendColorProc)(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBlendEquationProc)(GrGLenum mode);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBlendFuncProc)(GrGLenum sfactor, GrGLenum dfactor);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBlitFramebufferProc)(GrGLint srcX0, GrGLint srcY0, GrGLint srcX1, GrGLint srcY1, GrGLint dstX0, GrGLint dstY0, GrGLint dstX1, GrGLint dstY1, GrGLbitfield mask, GrGLenum filter);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBufferDataProc)(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, GrGLenum usage);
@@ -187,6 +189,7 @@
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTexStorage2DProc)(GrGLenum target, GrGLsizei levels, GrGLenum internalformat, GrGLsizei width, GrGLsizei height);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDiscardFramebufferProc)(GrGLenum target, GrGLsizei numAttachments, const GrGLenum* attachments);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTexSubImage2DProc)(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, const GrGLvoid* pixels);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTextureBarrierProc)();
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform1fProc)(GrGLint location, GrGLfloat v0);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform1iProc)(GrGLint location, GrGLint v0);
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform1fvProc)(GrGLint location, GrGLsizei count, const GrGLfloat* v);
@@ -259,6 +262,8 @@
     // NV_path_rendering v1.3
     typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLProgramPathFragmentInputGenProc)(GrGLuint program, GrGLint location, GrGLenum genMode, GrGLint components,const GrGLfloat *coeffs);
     typedef GrGLenum (GR_GL_FUNCTION_TYPE* GrGLPathMemoryGlyphIndexArrayProc)(GrGLuint firstPathName, GrGLenum fontTarget, GrGLsizeiptr fontSize, const GrGLvoid *fontData, GrGLsizei faceIndex, GrGLuint firstGlyphIndex, GrGLsizei numGlyphs, GrGLuint pathParameterTemplate, GrGLfloat emScale);
+    // GL_NV_framebuffer_mixed_samples
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCoverageModulationProc)(GrGLenum components);
 }  // extern "C"
 
 #endif
diff --git a/include/gpu/gl/GrGLInterface.h b/include/gpu/gl/GrGLInterface.h
index 231702d..2e9ef6f 100644
--- a/include/gpu/gl/GrGLInterface.h
+++ b/include/gpu/gl/GrGLInterface.h
@@ -156,7 +156,9 @@
         GLPtr<GrGLBindRenderbufferProc> fBindRenderbuffer;
         GLPtr<GrGLBindTextureProc> fBindTexture;
         GLPtr<GrGLBindVertexArrayProc> fBindVertexArray;
+        GLPtr<GrGLBlendBarrierProc> fBlendBarrier;
         GLPtr<GrGLBlendColorProc> fBlendColor;
+        GLPtr<GrGLBlendEquationProc> fBlendEquation;
         GLPtr<GrGLBlendFuncProc> fBlendFunc;
         GLPtr<GrGLBlitFramebufferProc> fBlitFramebuffer;
         GLPtr<GrGLBufferDataProc> fBufferData;
@@ -288,6 +290,7 @@
         GLPtr<GrGLTexParameterivProc> fTexParameteriv;
         GLPtr<GrGLTexSubImage2DProc> fTexSubImage2D;
         GLPtr<GrGLTexStorage2DProc> fTexStorage2D;
+        GLPtr<GrGLTextureBarrierProc> fTextureBarrier;
         GLPtr<GrGLDiscardFramebufferProc> fDiscardFramebuffer;
         GLPtr<GrGLUniform1fProc> fUniform1f;
         GLPtr<GrGLUniform1iProc> fUniform1i;
@@ -348,6 +351,8 @@
         // NV_path_rendering v1.3
         GLPtr<GrGLProgramPathFragmentInputGenProc> fProgramPathFragmentInputGen;
         GLPtr<GrGLPathMemoryGlyphIndexArrayProc> fPathMemoryGlyphIndexArray;
+        // NV_framebuffer_mixed_samples
+        GLPtr<GrGLCoverageModulationProc> fCoverageModulation;
     } fFunctions;
 
     // Per-GL func callback
diff --git a/include/gpu/gl/SkGLContext.h b/include/gpu/gl/SkGLContext.h
index 57456c2..6ca7bf5 100644
--- a/include/gpu/gl/SkGLContext.h
+++ b/include/gpu/gl/SkGLContext.h
@@ -19,7 +19,7 @@
 public:
     SK_DECLARE_INST_COUNT(SkGLContext)
 
-    ~SkGLContext() SK_OVERRIDE;
+    ~SkGLContext() override;
 
     bool isValid() const { return NULL != gl(); }
 
diff --git a/include/gpu/gl/SkNullGLContext.h b/include/gpu/gl/SkNullGLContext.h
index 82b96a9..ca71dde 100644
--- a/include/gpu/gl/SkNullGLContext.h
+++ b/include/gpu/gl/SkNullGLContext.h
@@ -12,9 +12,9 @@
 
 class SK_API SkNullGLContext : public SkGLContext {
 public:
-    ~SkNullGLContext() SK_OVERRIDE;
-    void makeCurrent() const SK_OVERRIDE;
-    void swapBuffers() const SK_OVERRIDE {};
+    ~SkNullGLContext() override;
+    void makeCurrent() const override;
+    void swapBuffers() const override {};
 
     static SkNullGLContext* Create(GrGLStandard);
 
diff --git a/include/gpu/gl/angle/SkANGLEGLContext.h b/include/gpu/gl/angle/SkANGLEGLContext.h
index 6705248..f54f29e 100644
--- a/include/gpu/gl/angle/SkANGLEGLContext.h
+++ b/include/gpu/gl/angle/SkANGLEGLContext.h
@@ -14,9 +14,9 @@
 
 class SkANGLEGLContext : public SkGLContext {
 public:
-    ~SkANGLEGLContext() SK_OVERRIDE;
-    void makeCurrent() const SK_OVERRIDE;
-    void swapBuffers() const SK_OVERRIDE;
+    ~SkANGLEGLContext() override;
+    void makeCurrent() const override;
+    void swapBuffers() const override;
 
     static SkANGLEGLContext* Create(GrGLStandard forcedGpuAPI) {
         if (kGL_GrGLStandard == forcedGpuAPI) {
diff --git a/include/pathops/SkPathOps.h b/include/pathops/SkPathOps.h
index ba18f4b..a729e6f 100644
--- a/include/pathops/SkPathOps.h
+++ b/include/pathops/SkPathOps.h
@@ -8,20 +8,36 @@
 #define SkPathOps_DEFINED
 
 #include "SkPreConfig.h"
+#include "SkTArray.h"
+#include "SkTDArray.h"
 
 class SkPath;
 struct SkRect;
 
+
+// FIXME: remove this once the define in src/skia/SkUserConfig.h lands
+#ifndef SK_SUPPORT_LEGACY_PATHOP_ENUMS
+#define SK_SUPPORT_LEGACY_PATHOP_ENUMS
+#endif
+
 // FIXME: move everything below into the SkPath class
 /**
   *  The logical operations that can be performed when combining two paths.
   */
 enum SkPathOp {
-    kDifference_PathOp,         //!< subtract the op path from the first path
+    kDifference_SkPathOp,         //!< subtract the op path from the first path
+    kIntersect_SkPathOp,          //!< intersect the two paths
+    kUnion_SkPathOp,              //!< union (inclusive-or) the two paths
+    kXOR_SkPathOp,                //!< exclusive-or the two paths
+    kReverseDifference_SkPathOp,  //!< subtract the first path from the op path
+
+#ifdef SK_SUPPORT_LEGACY_PATHOP_ENUMS
+    kDifference_PathOp = 0,     //!< subtract the op path from the first path
     kIntersect_PathOp,          //!< intersect the two paths
     kUnion_PathOp,              //!< union (inclusive-or) the two paths
     kXOR_PathOp,                //!< exclusive-or the two paths
     kReverseDifference_PathOp,  //!< subtract the first path from the op path
+#endif
 };
 
 /** Set this path to the result of applying the Op to this path and the
@@ -35,9 +51,10 @@
 
     @param one The first operand (for difference, the minuend)
     @param two The second operand (for difference, the subtrahend)
+    @param op The operator to apply.
     @param result The product of the operands. The result may be one of the
                   inputs.
-    @return True if operation succeeded.
+    @return True if the operation succeeded.
   */
 bool SK_API Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result);
 
@@ -63,4 +80,31 @@
   */
 bool SK_API TightBounds(const SkPath& path, SkRect* result);
 
+/** Perform a series of path operations, optimized for unioning many paths together.
+  */
+class SK_API SkOpBuilder {
+public:
+    /** Add one or more paths and their operand. The builder is empty before the first
+        path is added, so the result of a single add is (emptyPath OP path).
+
+        @param path The second operand.
+        @param _operator The operator to apply to the existing and supplied paths.
+     */ 
+    void add(const SkPath& path, SkPathOp _operator);
+
+    /** Computes the sum of all paths and operands, and resets the builder to its
+        initial state.
+ 
+        @param result The product of the operands.
+        @return True if the operation succeeded.
+      */
+    bool resolve(SkPath* result);
+
+private:
+    SkTArray<SkPath> fPathRefs;
+    SkTDArray<SkPathOp> fOps;
+
+    void reset();
+};
+
 #endif
diff --git a/include/ports/SkAtomics_atomic.h b/include/ports/SkAtomics_atomic.h
index 6ae78a8..ddbf7c3 100644
--- a/include/ports/SkAtomics_atomic.h
+++ b/include/ports/SkAtomics_atomic.h
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #ifndef SkAtomics_atomic_DEFINED
 #define SkAtomics_atomic_DEFINED
 
@@ -37,4 +44,10 @@
     return __atomic_compare_exchange_n(ptr, expected, desired, false/*weak?*/, success, failure);
 }
 
+template <typename T>
+T sk_atomic_exchange(T* ptr, T val, sk_memory_order mo) {
+    // All values of mo are valid.
+    return __atomic_exchange_n(ptr, val, mo);
+}
+
 #endif//SkAtomics_atomic_DEFINED
diff --git a/include/ports/SkAtomics_std.h b/include/ports/SkAtomics_std.h
index e89d749..4c26858 100644
--- a/include/ports/SkAtomics_std.h
+++ b/include/ports/SkAtomics_std.h
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #ifndef SkAtomics_std_DEFINED
 #define SkAtomics_std_DEFINED
 
@@ -47,4 +54,11 @@
                                                         (std::memory_order)failure);
 }
 
+template <typename T>
+T sk_atomic_exchange(T* ptr, T val, sk_memory_order mo) {
+    // All values of mo are valid.
+    std::atomic<T>* ap = reinterpret_cast<std::atomic<T>*>(ptr);
+    return std::atomic_exchange_explicit(ap, val, (std::memory_order)mo);
+}
+
 #endif//SkAtomics_std_DEFINED
diff --git a/include/ports/SkAtomics_sync.h b/include/ports/SkAtomics_sync.h
index 66da4d3..7ca0b46 100644
--- a/include/ports/SkAtomics_sync.h
+++ b/include/ports/SkAtomics_sync.h
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #ifndef SkAtomics_sync_DEFINED
 #define SkAtomics_sync_DEFINED
 
@@ -48,4 +55,14 @@
     return false;
 }
 
+template <typename T>
+T sk_atomic_exchange(T* ptr, T val, sk_memory_order) {
+    // There is no __sync exchange.  Emulate it with a CAS loop.
+    T prev;
+    do {
+        prev = sk_atomic_load(ptr);
+    } while(!sk_atomic_compare_exchange(ptr, &prev, val));
+    return prev;
+}
+
 #endif//SkAtomics_sync_DEFINED
diff --git a/include/ports/SkFontMgr_indirect.h b/include/ports/SkFontMgr_indirect.h
index 1ab2750..0977eea 100644
--- a/include/ports/SkFontMgr_indirect.h
+++ b/include/ports/SkFontMgr_indirect.h
@@ -30,30 +30,30 @@
     { }
 
 protected:
-    int onCountFamilies() const SK_OVERRIDE;
-    void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE;
-    SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE;
+    int onCountFamilies() const override;
+    void onGetFamilyName(int index, SkString* familyName) const override;
+    SkFontStyleSet* onCreateStyleSet(int index) const override;
 
-    SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE;
+    SkFontStyleSet* onMatchFamily(const char familyName[]) const override;
 
     virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
-                                           const SkFontStyle& fontStyle) const SK_OVERRIDE;
+                                           const SkFontStyle& fontStyle) const override;
 
     virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
                                                     const SkFontStyle&,
                                                     const char* bcp47[],
                                                     int bcp47Count,
-                                                    SkUnichar character) const SK_OVERRIDE;
+                                                    SkUnichar character) const override;
 
     virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
-                                         const SkFontStyle& fontStyle) const SK_OVERRIDE;
+                                         const SkFontStyle& fontStyle) const override;
 
-    SkTypeface* onCreateFromStream(SkStreamAsset* stream, int ttcIndex) const SK_OVERRIDE;
-    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE;
-    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE;
+    SkTypeface* onCreateFromStream(SkStreamAsset* stream, int ttcIndex) const override;
+    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override;
+    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override;
 
     virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
-                                               unsigned styleBits) const SK_OVERRIDE;
+                                               unsigned styleBits) const override;
 
 private:
     SkTypeface* createTypefaceFromFontId(const SkFontIdentity& fontId) const;
diff --git a/include/ports/SkMutex_pthread.h b/include/ports/SkMutex_pthread.h
index 7452ece..a0ea274 100644
--- a/include/ports/SkMutex_pthread.h
+++ b/include/ports/SkMutex_pthread.h
@@ -73,9 +73,9 @@
     SkMutex& operator=(const SkMutex&);
 };
 
-#if defined(SK_DEBUG) && defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)
+#if defined(SK_DEBUG) && defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP)
     // When possible we want to use error-check mutexes in Debug builds.  See the note at the top.
-    #define SK_BASE_MUTEX_INIT { PTHREAD_ERRORCHECK_MUTEX_INITIALIZER, kNoOwner }
+    #define SK_BASE_MUTEX_INIT { PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, kNoOwner }
 #elif defined(SK_DEBUG)
     // Macs don't support PTHREAD_ERRORCHECK_MUTEX_INITIALIZER when targeting <10.7. We target 10.6.
     #define SK_BASE_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER, kNoOwner }
diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h
index d3ab73f..a833049 100644
--- a/include/utils/SkDeferredCanvas.h
+++ b/include/utils/SkDeferredCanvas.h
@@ -143,61 +143,58 @@
      */
     void silentFlush();
 
-    SkDrawFilter* setDrawFilter(SkDrawFilter* filter) SK_OVERRIDE;
+    SkDrawFilter* setDrawFilter(SkDrawFilter* filter) override;
 
 protected:
-    void willSave() SK_OVERRIDE;
-    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) SK_OVERRIDE;
-    void willRestore() SK_OVERRIDE;
+    void willSave() override;
+    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+    void willRestore() override;
 
-    void didConcat(const SkMatrix&) SK_OVERRIDE;
-    void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
+    void didConcat(const SkMatrix&) override;
+    void didSetMatrix(const SkMatrix&) override;
 
-    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
+    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
     virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                            const SkPaint&) SK_OVERRIDE;
+                            const SkPaint&) override;
     virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                               const SkPaint&) SK_OVERRIDE;
+                               const SkPaint&) override;
     virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                                SkScalar constY, const SkPaint&) SK_OVERRIDE;
+                                SkScalar constY, const SkPaint&) override;
     virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                                  const SkMatrix* matrix, const SkPaint&) SK_OVERRIDE;
+                                  const SkMatrix* matrix, const SkPaint&) override;
     virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                const SkPaint& paint) SK_OVERRIDE;
+                                const SkPaint& paint) override;
     virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
                              const SkPoint texCoords[4], SkXfermode* xmode,
-                             const SkPaint& paint) SK_OVERRIDE;
+                             const SkPaint& paint) override;
 
-    void onDrawPaint(const SkPaint&) SK_OVERRIDE;
-    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) SK_OVERRIDE;
-    void onDrawRect(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawOval(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawRRect(const SkRRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawPath(const SkPath&, const SkPaint&) SK_OVERRIDE;
-    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+    void onDrawPaint(const SkPaint&) override;
+    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
+    void onDrawRect(const SkRect&, const SkPaint&) override;
+    void onDrawOval(const SkRect&, const SkPaint&) override;
+    void onDrawRRect(const SkRRect&, const SkPaint&) override;
+    void onDrawPath(const SkPath&, const SkPaint&) override;
+    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
-                          DrawBitmapRectFlags flags) SK_OVERRIDE;
-#if 0
-    // rely on conversion to bitmap(for now)
-    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+                          DrawBitmapRectFlags flags) override;
+    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
-                         const SkPaint*) SK_OVERRIDE;
-#endif
+                         const SkPaint*) override;
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
-                          const SkPaint*) SK_OVERRIDE;
-    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) SK_OVERRIDE;
+                          const SkPaint*) override;
+    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
     void onDrawVertices(VertexMode vmode, int vertexCount,
                         const SkPoint vertices[], const SkPoint texs[],
                         const SkColor colors[], SkXfermode* xmode,
                         const uint16_t indices[], int indexCount,
-                        const SkPaint&) SK_OVERRIDE;
+                        const SkPaint&) override;
 
-    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRegion(const SkRegion&, SkRegion::Op) SK_OVERRIDE;
+    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRegion(const SkRegion&, SkRegion::Op) override;
 
-    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) SK_OVERRIDE;
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
 
 public:
     class NotificationClient {
@@ -232,7 +229,7 @@
     };
 
 protected:
-    SkCanvas* canvasForDrawIter() SK_OVERRIDE;
+    SkCanvas* canvasForDrawIter() override;
     SkDeferredDevice* getDeferredDevice() const;
 
 private:
diff --git a/include/utils/SkDumpCanvas.h b/include/utils/SkDumpCanvas.h
index c6b20eb..cd94501 100644
--- a/include/utils/SkDumpCanvas.h
+++ b/include/utils/SkDumpCanvas.h
@@ -75,60 +75,60 @@
 
     int getNestLevel() const { return fNestLevel; }
 
-    void beginCommentGroup(const char* description) SK_OVERRIDE;
-    void addComment(const char* kywd, const char* value) SK_OVERRIDE;
-    void endCommentGroup() SK_OVERRIDE;
+    void beginCommentGroup(const char* description) override;
+    void addComment(const char* kywd, const char* value) override;
+    void endCommentGroup() override;
 
 protected:
-    void willSave() SK_OVERRIDE;
-    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) SK_OVERRIDE;
-    void willRestore() SK_OVERRIDE;
+    void willSave() override;
+    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+    void willRestore() override;
 
-    void didConcat(const SkMatrix&) SK_OVERRIDE;
-    void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
+    void didConcat(const SkMatrix&) override;
+    void didSetMatrix(const SkMatrix&) override;
 
-    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
+    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
     virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                            const SkPaint&) SK_OVERRIDE;
+                            const SkPaint&) override;
     virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                               const SkPaint&) SK_OVERRIDE;
+                               const SkPaint&) override;
     virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                                SkScalar constY, const SkPaint&) SK_OVERRIDE;
+                                SkScalar constY, const SkPaint&) override;
     virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                                  const SkMatrix* matrix, const SkPaint&) SK_OVERRIDE;
+                                  const SkMatrix* matrix, const SkPaint&) override;
     virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                const SkPaint& paint) SK_OVERRIDE;
+                                const SkPaint& paint) override;
     virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
                              const SkPoint texCoords[4], SkXfermode* xmode,
-                             const SkPaint& paint) SK_OVERRIDE;
+                             const SkPaint& paint) override;
 
-    void onDrawPaint(const SkPaint&) SK_OVERRIDE;
-    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) SK_OVERRIDE;
-    void onDrawRect(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawOval(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawRRect(const SkRRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawPath(const SkPath&, const SkPaint&) SK_OVERRIDE;
-    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+    void onDrawPaint(const SkPaint&) override;
+    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
+    void onDrawRect(const SkRect&, const SkPaint&) override;
+    void onDrawOval(const SkRect&, const SkPaint&) override;
+    void onDrawRRect(const SkRRect&, const SkPaint&) override;
+    void onDrawPath(const SkPath&, const SkPaint&) override;
+    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
-                          DrawBitmapRectFlags flags) SK_OVERRIDE;
-    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+                          DrawBitmapRectFlags flags) override;
+    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
-                         const SkPaint*) SK_OVERRIDE;
+                         const SkPaint*) override;
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
-                          const SkPaint*) SK_OVERRIDE;
-    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) SK_OVERRIDE;
+                          const SkPaint*) override;
+    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
     void onDrawVertices(VertexMode vmode, int vertexCount,
                         const SkPoint vertices[], const SkPoint texs[],
                         const SkColor colors[], SkXfermode* xmode,
                         const uint16_t indices[], int indexCount,
-                        const SkPaint&) SK_OVERRIDE;
+                        const SkPaint&) override;
 
-    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRegion(const SkRegion&, SkRegion::Op) SK_OVERRIDE;
+    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRegion(const SkRegion&, SkRegion::Op) override;
 
-    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) SK_OVERRIDE;
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
 
     static const char* EdgeStyleToAAString(ClipEdgeStyle edgeStyle);
 
@@ -151,7 +151,7 @@
     // 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*) SK_OVERRIDE;
+                      const SkPaint*) override;
 
 private:
     void (*fProc)(const char*, void*);
diff --git a/include/utils/SkEventTracer.h b/include/utils/SkEventTracer.h
index 8d9fd11..f4f8676 100644
--- a/include/utils/SkEventTracer.h
+++ b/include/utils/SkEventTracer.h
@@ -29,10 +29,7 @@
 
     static SkEventTracer* GetInstance();
 
-    static void SetInstance(SkEventTracer* tracer) {
-        SkDELETE(SkEventTracer::gInstance);
-        SkEventTracer::gInstance = tracer;
-    }
+    static void SetInstance(SkEventTracer*);
 
     virtual ~SkEventTracer() { }
 
@@ -68,8 +65,6 @@
         updateTraceEventDuration(const uint8_t* categoryEnabledFlag,
                                  const char* name,
                                  SkEventTracer::Handle handle) = 0;
-private:
-    static SkEventTracer *gInstance;
 };
 
 #endif // SkEventTracer_DEFINED
diff --git a/include/utils/SkLuaCanvas.h b/include/utils/SkLuaCanvas.h
index e3541f2..0fd37da 100644
--- a/include/utils/SkLuaCanvas.h
+++ b/include/utils/SkLuaCanvas.h
@@ -21,52 +21,52 @@
     virtual ~SkLuaCanvas();
 
 protected:
-    void willSave() SK_OVERRIDE;
-    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) SK_OVERRIDE;
-    void willRestore() SK_OVERRIDE;
+    void willSave() override;
+    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+    void willRestore() override;
 
-    void didConcat(const SkMatrix&) SK_OVERRIDE;
-    void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
+    void didConcat(const SkMatrix&) override;
+    void didSetMatrix(const SkMatrix&) override;
 
-    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
+    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
     virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                            const SkPaint&) SK_OVERRIDE;
+                            const SkPaint&) override;
     virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                               const SkPaint&) SK_OVERRIDE;
+                               const SkPaint&) override;
     virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                                SkScalar constY, const SkPaint&) SK_OVERRIDE;
+                                SkScalar constY, const SkPaint&) override;
     virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                                  const SkMatrix* matrix, const SkPaint&) SK_OVERRIDE;
+                                  const SkMatrix* matrix, const SkPaint&) override;
     virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                const SkPaint& paint) SK_OVERRIDE;
+                                const SkPaint& paint) override;
 
-    void onDrawPaint(const SkPaint&) SK_OVERRIDE;
-    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) SK_OVERRIDE;
-    void onDrawRect(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawOval(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawRRect(const SkRRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawPath(const SkPath&, const SkPaint&) SK_OVERRIDE;
-    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+    void onDrawPaint(const SkPaint&) override;
+    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
+    void onDrawRect(const SkRect&, const SkPaint&) override;
+    void onDrawOval(const SkRect&, const SkPaint&) override;
+    void onDrawRRect(const SkRRect&, const SkPaint&) override;
+    void onDrawPath(const SkPath&, const SkPaint&) override;
+    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
-                          DrawBitmapRectFlags flags) SK_OVERRIDE;
-    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+                          DrawBitmapRectFlags flags) override;
+    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
-                         const SkPaint*) SK_OVERRIDE;
+                         const SkPaint*) override;
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
-                          const SkPaint*) SK_OVERRIDE;
-    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) SK_OVERRIDE;
+                          const SkPaint*) override;
+    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
     void onDrawVertices(VertexMode vmode, int vertexCount,
                         const SkPoint vertices[], const SkPoint texs[],
                         const SkColor colors[], SkXfermode* xmode,
                         const uint16_t indices[], int indexCount,
-                        const SkPaint&) SK_OVERRIDE;
+                        const SkPaint&) override;
 
-    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRegion(const SkRegion&, SkRegion::Op) SK_OVERRIDE;
+    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRegion(const SkRegion&, SkRegion::Op) override;
 
-    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) SK_OVERRIDE;
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
 
 private:
     lua_State*  fL;
diff --git a/include/utils/SkNWayCanvas.h b/include/utils/SkNWayCanvas.h
index 02d715b..85cd90d 100644
--- a/include/utils/SkNWayCanvas.h
+++ b/include/utils/SkNWayCanvas.h
@@ -23,64 +23,64 @@
     ///////////////////////////////////////////////////////////////////////////
     // These are forwarded to the N canvases we're referencing
 
-    SkDrawFilter* setDrawFilter(SkDrawFilter*) SK_OVERRIDE;
+    SkDrawFilter* setDrawFilter(SkDrawFilter*) override;
 
-    void beginCommentGroup(const char* description) SK_OVERRIDE;
-    void addComment(const char* kywd, const char* value) SK_OVERRIDE;
-    void endCommentGroup() SK_OVERRIDE;
+    void beginCommentGroup(const char* description) override;
+    void addComment(const char* kywd, const char* value) override;
+    void endCommentGroup() override;
 
 protected:
     SkTDArray<SkCanvas*> fList;
 
-    void willSave() SK_OVERRIDE;
-    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) SK_OVERRIDE;
-    void willRestore() SK_OVERRIDE;
+    void willSave() override;
+    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+    void willRestore() override;
 
-    void didConcat(const SkMatrix&) SK_OVERRIDE;
-    void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
+    void didConcat(const SkMatrix&) override;
+    void didSetMatrix(const SkMatrix&) override;
 
-    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
+    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
     virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                            const SkPaint&) SK_OVERRIDE;
+                            const SkPaint&) override;
     virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                               const SkPaint&) SK_OVERRIDE;
+                               const SkPaint&) override;
     virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                                SkScalar constY, const SkPaint&) SK_OVERRIDE;
+                                SkScalar constY, const SkPaint&) override;
     virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                                  const SkMatrix* matrix, const SkPaint&) SK_OVERRIDE;
+                                  const SkMatrix* matrix, const SkPaint&) override;
     virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                const SkPaint& paint) SK_OVERRIDE;
+                                const SkPaint& paint) override;
     virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
                              const SkPoint texCoords[4], SkXfermode* xmode,
-                             const SkPaint& paint) SK_OVERRIDE;
+                             const SkPaint& paint) override;
 
-    void onDrawPaint(const SkPaint&) SK_OVERRIDE;
-    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) SK_OVERRIDE;
-    void onDrawRect(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawOval(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawRRect(const SkRRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawPath(const SkPath&, const SkPaint&) SK_OVERRIDE;
-    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+    void onDrawPaint(const SkPaint&) override;
+    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
+    void onDrawRect(const SkRect&, const SkPaint&) override;
+    void onDrawOval(const SkRect&, const SkPaint&) override;
+    void onDrawRRect(const SkRRect&, const SkPaint&) override;
+    void onDrawPath(const SkPath&, const SkPaint&) override;
+    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
-                          DrawBitmapRectFlags flags) SK_OVERRIDE;
-    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+                          DrawBitmapRectFlags flags) override;
+    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
-                         const SkPaint*) SK_OVERRIDE;
+                         const SkPaint*) override;
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
-                          const SkPaint*) SK_OVERRIDE;
-    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) SK_OVERRIDE;
+                          const SkPaint*) override;
+    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
     void onDrawVertices(VertexMode vmode, int vertexCount,
                               const SkPoint vertices[], const SkPoint texs[],
                               const SkColor colors[], SkXfermode* xmode,
                               const uint16_t indices[], int indexCount,
-                              const SkPaint&) SK_OVERRIDE;
+                              const SkPaint&) override;
 
-    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRegion(const SkRegion&, SkRegion::Op) SK_OVERRIDE;
+    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRegion(const SkRegion&, SkRegion::Op) override;
 
-    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) SK_OVERRIDE;
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
 
     class Iter;
 
diff --git a/include/utils/SkNoSaveLayerCanvas.h b/include/utils/SkNoSaveLayerCanvas.h
index b692697..65cd4a8 100644
--- a/include/utils/SkNoSaveLayerCanvas.h
+++ b/include/utils/SkNoSaveLayerCanvas.h
@@ -22,7 +22,7 @@
 
 protected:
     virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
-                                            SaveFlags flags) SK_OVERRIDE {
+                                            SaveFlags flags) override {
         this->INHERITED::willSaveLayer(bounds, paint, flags);
         return kNoLayer_SaveLayerStrategy;
     }
diff --git a/include/utils/SkPaintFilterCanvas.h b/include/utils/SkPaintFilterCanvas.h
new file mode 100644
index 0000000..942fbda
--- /dev/null
+++ b/include/utils/SkPaintFilterCanvas.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPaintFilterCanvas_DEFINED
+#define SkPaintFilterCanvas_DEFINED
+
+#include "SkNWayCanvas.h"
+
+/** \class SkPaintFilterCanvas
+
+    A utility proxy base class for implementing paint filters.
+*/
+class SK_API SkPaintFilterCanvas : public SkNWayCanvas {
+public:
+    SkPaintFilterCanvas(int width, int height);
+
+    enum Type {
+        kPaint_Type,
+        kPoint_Type,
+        kBitmap_Type,
+        kRect_Type,
+        kRRect_Type,
+        kDRRect_Type,
+        kOval_Type,
+        kPath_Type,
+        kPicture_Type,
+        kText_Type,
+        kTextBlob_Type,
+        kVertices_Type,
+        kPatch_Type,
+
+        kTypeCount
+    };
+
+protected:
+    /**
+     *  Called with the paint that will be used to draw the specified type.
+     *  The implementation may modify the paint as they wish.
+     *
+     *  Note: The base implementation calls onFilterPaint() for top-level/explicit paints only.
+     *        To also filter encapsulated paints (e.g. SkPicture, SkTextBlob), clients may need to
+     *        override the relevant methods (i.e. drawPicture, drawTextBlob).
+     */
+    virtual void onFilterPaint(SkPaint* paint, Type type) const = 0;
+
+    void onDrawPaint(const SkPaint&) override;
+    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
+    void onDrawRect(const SkRect&, const SkPaint&) override;
+    void onDrawRRect(const SkRRect&, const SkPaint&) override;
+    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
+    void onDrawOval(const SkRect&, const SkPaint&) override;
+    void onDrawPath(const SkPath&, const SkPaint&) override;
+    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
+    void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
+                          DrawBitmapRectFlags flags) override;
+    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
+    void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
+                         const SkPaint*) override;
+    void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
+                          const SkPaint*) override;
+    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
+    void onDrawVertices(VertexMode vmode, int vertexCount,
+                              const SkPoint vertices[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint&) override;
+    void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+                             const SkPoint texCoords[4], SkXfermode* xmode,
+                             const SkPaint& paint) override;
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
+
+    void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+                    const SkPaint&) override;
+    void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+                       const SkPaint&) override;
+    void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+                        SkScalar constY, const SkPaint&) override;
+    void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+                          const SkMatrix* matrix, const SkPaint&) override;
+    void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+                        const SkPaint& paint) override;
+
+private:
+    class AutoPaintFilter;
+
+    typedef SkNWayCanvas INHERITED;
+};
+
+#endif
diff --git a/include/utils/SkPathUtils.h b/include/utils/SkPathUtils.h
deleted file mode 100644
index a27cbb8..0000000
--- a/include/utils/SkPathUtils.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *  CAUTION: EXPERIMENTAL CODE
- *
- *  This code is not to be used and will not be supported
- *  if it fails on you. DO NOT USE!
- *
- */
-
-#ifndef SkPathUtils_DEFINED
-#define SkPathUtils_DEFINED
-
-#include "SkPath.h"
-
-/*
- * The following methods return the boundary path given a 1-bit bitmap, specified
- * by width/height and stride. The bits are interpreted as 1 being "in" the path,
- * and 0 being "out". The bits are interpreted as MSB on the left, and LSB on the right.
- */
-
-class SK_API SkPathUtils {
-public:
-    /**
-       This variation iterates the binary data sequentially (as in scanline fashion)
-       and will add each run of 1's to the path as a rectangular path. Upon parsing
-       all binary data the path is simplified using the PathOps::Simplify() method.
-    */
-    static void BitsToPath_Path(SkPath* path, const char* bitmap,
-                            int w, int h, int rowBytes);
-
-    /**
-       This variation utilizes the SkRegion class to generate paths, adding
-       each run of 1's to the SkRegion as an SkIRect. Upon parsing the entirety
-       of the binary the SkRegion is converted to a Path via getBoundaryPath().
-    */
-    static void BitsToPath_Region(SkPath* path, const char* bitmap,
-                                   int w, int h, int rowBytes);
-
-};
-
-#endif
diff --git a/include/views/SkOSWindow_NaCl.h b/include/views/SkOSWindow_NaCl.h
deleted file mode 100644
index 2626337..0000000
--- a/include/views/SkOSWindow_NaCl.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2012 Skia
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkOSWindow_NaCl_DEFINED
-#define SkOSWindow_NaCl_DEFINED
-
-#include "SkWindow.h"
-
-class SkOSWindow : public SkWindow {
-public:
-    SkOSWindow(void*) {}
-    ~SkOSWindow() {}
-
-    enum SkBackEndTypes {
-        kNone_BackEndType,
-        kNativeGL_BackEndType,
-    };
-
-    bool attach(SkBackEndTypes /* attachType */, int /* msaaSampleCount */, AttachmentInfo* info) {
-        info->fSampleCount = 0;
-        info->fStencilBits = 0;
-        return true;
-    }
-    void detach() {}
-    void present() {}
-
-    virtual void onPDFSaved(const char title[], const char desc[],
-        const char path[]);
-
-protected:
-    // overrides from SkWindow
-    virtual void onHandleInval(const SkIRect&);
-    virtual void onSetTitle(const char title[]);
-
-private:
-    typedef SkWindow INHERITED;
-};
-
-#endif
diff --git a/include/views/SkOSWindow_Unix.h b/include/views/SkOSWindow_Unix.h
index 8c4d819..2811ff8 100644
--- a/include/views/SkOSWindow_Unix.h
+++ b/include/views/SkOSWindow_Unix.h
@@ -48,7 +48,7 @@
 
 protected:
     // Overridden from from SkWindow:
-    void onSetTitle(const char title[]) SK_OVERRIDE;
+    void onSetTitle(const char title[]) override;
 
 private:
     enum NextXEventResult {
diff --git a/include/views/SkWidget.h b/include/views/SkWidget.h
index 28f0e9a..3c93634 100644
--- a/include/views/SkWidget.h
+++ b/include/views/SkWidget.h
@@ -101,10 +101,10 @@
     SkPushButtonWidget(uint32_t flags = 0) : SkButtonWidget(flags) {}
 
 protected:
-    bool onEvent(const SkEvent&) SK_OVERRIDE;
-    void onDraw(SkCanvas*) SK_OVERRIDE;
-    Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE;
-    bool onClick(Click* click) SK_OVERRIDE;
+    bool onEvent(const SkEvent&) override;
+    void onDraw(SkCanvas*) override;
+    Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override;
+    bool onClick(Click* click) override;
 
 private:
     typedef SkButtonWidget INHERITED;
diff --git a/include/views/SkWindow.h b/include/views/SkWindow.h
index 76a1aa5..748e6da 100644
--- a/include/views/SkWindow.h
+++ b/include/views/SkWindow.h
@@ -122,9 +122,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-#if defined(SK_BUILD_FOR_NACL)
-    #include "SkOSWindow_NaCl.h"
-#elif defined(SK_BUILD_FOR_MAC)
+#if defined(SK_BUILD_FOR_MAC)
     #include "SkOSWindow_Mac.h"
 #elif defined(SK_BUILD_FOR_WIN)
     #include "SkOSWindow_Win.h"
diff --git a/include/xml/SkXMLWriter.h b/include/xml/SkXMLWriter.h
index 6e3c7de..45356d4 100644
--- a/include/xml/SkXMLWriter.h
+++ b/include/xml/SkXMLWriter.h
@@ -69,10 +69,10 @@
     SkDEBUGCODE(static void UnitTest();)
 
 protected:
-    void onStartElementLen(const char elem[], size_t length) SK_OVERRIDE;
-    void onEndElement() SK_OVERRIDE;
-    void onAddAttributeLen(const char name[], const char value[], size_t length) SK_OVERRIDE;
-    void onAddText(const char text[], size_t length) SK_OVERRIDE;
+    void onStartElementLen(const char elem[], size_t length) override;
+    void onEndElement() override;
+    void onAddAttributeLen(const char name[], const char value[], size_t length) override;
+    void onAddText(const char text[], size_t length) override;
 
 private:
     SkWStream&      fStream;
@@ -86,7 +86,7 @@
     virtual void onStartElementLen(const char elem[], size_t length);
     virtual void onEndElement();
     virtual void onAddAttributeLen(const char name[], const char value[], size_t length);
-    virtual void onAddText(const char text[], size_t length) SK_OVERRIDE;
+    virtual void onAddText(const char text[], size_t length) override;
 private:
     SkXMLParser&        fParser;
 };
diff --git a/platform_tools/android/app/jni/com_skia_SkiaSampleRenderer.cpp b/platform_tools/android/app/jni/com_skia_SkiaSampleRenderer.cpp
index 169670a..7eb1c09 100644
--- a/platform_tools/android/app/jni/com_skia_SkiaSampleRenderer.cpp
+++ b/platform_tools/android/app/jni/com_skia_SkiaSampleRenderer.cpp
@@ -172,7 +172,7 @@
 }
 
 JNIEXPORT void JNICALL Java_com_skia_SkiaSampleRenderer_init(JNIEnv* env,
-        jobject thiz, jobject jsampleActivity, jint msaaSampleCount)
+        jobject thiz, jobject jsampleActivity, jstring cmdLineFlags, jint msaaSampleCount)
 {
     // setup jni hooks to the java activity
     gActivityGlue.m_env = env;
@@ -194,12 +194,17 @@
     env->DeleteLocalRef(clazz);
 
     application_init();
-    SkTArray<const char*> args;
 
+    const char* flags = env->GetStringUTFChars(cmdLineFlags, JNI_FALSE);
+    SkTArray<SkString> flagEntries;
+    SkStrSplit(flags, " ", &flagEntries);
+
+    SkTArray<const char*> args;
     args.push_back("SampleApp");
-    // TODO: push ability to select skp dir into the UI
-    args.push_back("--pictureDir");
-    args.push_back("/sdcard/skiabot/skia_skp");
+    for (int i = 0; i < flagEntries.count(); i++) {
+        SkDebugf(flagEntries[i].c_str());
+        args.push_back(flagEntries[i].c_str());
+    }
 
     SkString msaaSampleCountString;
     if (msaaSampleCount > 0) {
@@ -208,7 +213,14 @@
         args.push_back(msaaSampleCountString.c_str());
     }
 
-    gWindow = new SampleWindow(NULL, args.count(), const_cast<char**>(args.begin()), NULL);
+    if (gWindow) {
+        SkDebugf("The sample window already exists.");
+    } else {
+        gWindow = new SampleWindow(NULL, args.count(), const_cast<char**>(args.begin()), NULL);
+    }
+
+    // cleanup the command line flags
+    env->ReleaseStringUTFChars(cmdLineFlags, flags);
 
     // send the list of slides up to the activity
     const int slideCount = gWindow->sampleCount();
diff --git a/platform_tools/android/app/jni/com_skia_SkiaSampleRenderer.h b/platform_tools/android/app/jni/com_skia_SkiaSampleRenderer.h
index 023f679..263353a 100644
--- a/platform_tools/android/app/jni/com_skia_SkiaSampleRenderer.h
+++ b/platform_tools/android/app/jni/com_skia_SkiaSampleRenderer.h
@@ -10,10 +10,10 @@
 /*
  * Class:     com_skia_SkiaSampleRenderer
  * Method:    init
- * Signature: (Lcom/skia/SkiaSampleActivity;I)V
+ * Signature: (Lcom/skia/SkiaSampleActivity;Ljava/lang/String;I)V
  */
 JNIEXPORT void JNICALL Java_com_skia_SkiaSampleRenderer_init
-  (JNIEnv *, jobject, jobject, jint);
+  (JNIEnv *, jobject, jobject, jstring, jint);
 
 /*
  * Class:     com_skia_SkiaSampleRenderer
diff --git a/platform_tools/android/app/src/com/skia/SkiaSampleActivity.java b/platform_tools/android/app/src/com/skia/SkiaSampleActivity.java
index 090d6ad..d9cd9f8 100644
--- a/platform_tools/android/app/src/com/skia/SkiaSampleActivity.java
+++ b/platform_tools/android/app/src/com/skia/SkiaSampleActivity.java
@@ -10,6 +10,7 @@
 import android.app.ActionBar;
 import android.app.Activity;
 import android.app.DownloadManager;
+import android.content.Intent;
 import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
@@ -65,7 +66,16 @@
             mSampleView.terminate();
         }
 
-        mSampleView = new SkiaSampleView(this, useOpenGLAPI, msaaSampleCount);
+        // intent get intent extras if triggered from the command line
+        Intent intent = this.getIntent();
+        String flags = intent.getStringExtra("cmdLineFlags");
+        
+        if (flags == null || flags.isEmpty()) {
+            flags  = "--pictureDir /data/local/tmp/skia_skp ";
+            flags += "--resourcePath /data/local/tmp/skia_resources ";
+        }
+        
+        mSampleView = new SkiaSampleView(this, flags, useOpenGLAPI, msaaSampleCount);
         LinearLayout holder = (LinearLayout) findViewById(R.id.holder);
         holder.addView(mSampleView, new LinearLayout.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT,
diff --git a/platform_tools/android/app/src/com/skia/SkiaSampleRenderer.java b/platform_tools/android/app/src/com/skia/SkiaSampleRenderer.java
index d8fb884..5525709 100644
--- a/platform_tools/android/app/src/com/skia/SkiaSampleRenderer.java
+++ b/platform_tools/android/app/src/com/skia/SkiaSampleRenderer.java
@@ -20,9 +20,11 @@
     private final SkiaSampleView mSampleView;
     private Handler mHandler = new Handler();
     private int mMSAASampleCount;
+    private String mCmdLineFlags;
 
-    SkiaSampleRenderer(SkiaSampleView view) {
+    SkiaSampleRenderer(SkiaSampleView view, String cmdLineFlags) {
         mSampleView = view;
+        mCmdLineFlags = cmdLineFlags;
     }
 
     @Override
@@ -49,7 +51,8 @@
 
         gl.glClearStencil(0);
         gl.glClear(GL10.GL_STENCIL_BUFFER_BIT);
-        init((SkiaSampleActivity)mSampleView.getContext(), mMSAASampleCount);
+
+        init((SkiaSampleActivity)mSampleView.getContext(), mCmdLineFlags, mMSAASampleCount);
     }
 
     // Called by JNI and the view.
@@ -89,7 +92,7 @@
         mSampleView.requestRender();
     }
 
-    native void init(SkiaSampleActivity activity, int msaaSampleCount);
+    native void init(SkiaSampleActivity activity, String flags, int msaaSampleCount);
     native void term();
     native void draw();
     native void updateSize(int w, int h);
diff --git a/platform_tools/android/app/src/com/skia/SkiaSampleView.java b/platform_tools/android/app/src/com/skia/SkiaSampleView.java
index cf06bea..c33f8ae 100644
--- a/platform_tools/android/app/src/com/skia/SkiaSampleView.java
+++ b/platform_tools/android/app/src/com/skia/SkiaSampleView.java
@@ -25,10 +25,10 @@
     private boolean mRequestedOpenGLAPI; // true == use (desktop) OpenGL. false == use OpenGL ES.
     private int mRequestedMSAASampleCount;
 
-    public SkiaSampleView(Context ctx, boolean useOpenGL, int msaaSampleCount) {
+    public SkiaSampleView(Context ctx, String cmdLineFlags, boolean useOpenGL, int msaaSampleCount) {
         super(ctx);
 
-        mSampleRenderer = new SkiaSampleRenderer(this);
+        mSampleRenderer = new SkiaSampleRenderer(this, cmdLineFlags);
         mRequestedMSAASampleCount = msaaSampleCount;
 
         setEGLContextClientVersion(2);
diff --git a/platform_tools/android/bin/android_kill_skia b/platform_tools/android/bin/android_kill_skia
index f1bbef6..09866b7 100755
--- a/platform_tools/android/bin/android_kill_skia
+++ b/platform_tools/android/bin/android_kill_skia
@@ -4,6 +4,7 @@
 
 SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 
+source $SCRIPT_DIR/android_setup.sh
 source $SCRIPT_DIR/utils/setup_adb.sh
 
 if [ $(uname) == "Linux" ]; then
diff --git a/platform_tools/android/bin/android_launch_app b/platform_tools/android/bin/android_launch_app
new file mode 100755
index 0000000..d31e5cb
--- /dev/null
+++ b/platform_tools/android/bin/android_launch_app
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# android_launch_app: Launches the skia sampleApp on the device.
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/android_setup.sh
+source $SCRIPT_DIR/utils/setup_adb.sh
+
+# TODO: check to ensure that the app exists on the device and prompt to install
+
+if [[ -n $RESOURCE_PATH ]]; then
+  adb_push_if_needed "${SKIA_SRC_DIR}/resources" $RESOURCE_PATH
+fi
+
+$ADB ${DEVICE_SERIAL} shell am start -S -n "com.skia/.SkiaSampleActivity" --es "cmdLineFlags" "${APP_ARGS[*]}"
+
diff --git a/platform_tools/android/bin/android_run_skia b/platform_tools/android/bin/android_run_skia
index 7c69309..016bd79 100755
--- a/platform_tools/android/bin/android_run_skia
+++ b/platform_tools/android/bin/android_run_skia
@@ -25,9 +25,15 @@
 
 if [ $LOGCAT ]; then $ADB $DEVICE_SERIAL logcat -c; fi
 STATUS_FILENAME="/data/local/tmp/.skia_tmp_$(date +%s%N)"
-$ADB ${DEVICE_SERIAL} shell \
-    "LD_LIBRARY_PATH=/data/local/tmp:$LD_LIBRARY_PATH \
-     /data/local/tmp/skia_launcher ${APP_ARGS[*]}; echo \$? > ${STATUS_FILENAME}"
+CMD_FILENAME=".skia_cmd_tmp_$(date +%s%N)"
+echo "LD_LIBRARY_PATH=/data/local/tmp:$LD_LIBRARY_PATH \
+     /data/local/tmp/skia_launcher ${APP_ARGS[*]}; \
+     echo \$? > ${STATUS_FILENAME}" > ${CMD_FILENAME}
+chmod +x ${CMD_FILENAME}
+$ADB ${DEVICE_SERIAL} push ${CMD_FILENAME} /data/local/tmp
+rm ${CMD_FILENAME}
+$ADB ${DEVICE_SERIAL} shell sh /data/local/tmp/${CMD_FILENAME}
+    
 if [ -z "$($ADB $DEVICE_SERIAL shell 'if [ -f $STATUS_FILENAME ]; then echo exists; fi')" ]; then
   if [ $LOGCAT ]; then $ADB $DEVICE_SERIAL logcat -d; fi
   echo "***********************************************************************"
@@ -37,7 +43,7 @@
 fi
 
 EXIT_CODE=`$ADB ${DEVICE_SERIAL} shell cat ${STATUS_FILENAME}`
-$ADB ${DEVICE_SERIAL} shell rm -f ${STATUS_FILENAME}
+$ADB ${DEVICE_SERIAL} shell rm -f ${STATUS_FILENAME} ${CMD_FILENAME}
 
 # check to see if the 'cat' command failed and print errors accordingly
 if [[ ${EXIT_CODE} == *${STATUS_FILENAME}* ]]; then
diff --git a/platform_tools/android/bin/android_setup.sh b/platform_tools/android/bin/android_setup.sh
index 3c6d44d..f1db3b5 100755
--- a/platform_tools/android/bin/android_setup.sh
+++ b/platform_tools/android/bin/android_setup.sh
@@ -1,4 +1,10 @@
 #!/bin/bash
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
 #
 # android_setup.sh: Sets environment variables used by other Android scripts.
 
@@ -27,6 +33,8 @@
     USE_CLANG="true"
   elif [[ "$1" == "--logcat" ]]; then
     LOGCAT=1
+  elif [[ "$1" == "--verbose" ]]; then
+    set -x
   else
     APP_ARGS=("${APP_ARGS[@]}" "${1}")
   fi
@@ -91,34 +99,27 @@
       TARGET_DEVICE=$(cat .android_config)
       verbose "no target device (-d), using ${TARGET_DEVICE} from most recent build"
     else
-      TARGET_DEVICE="arm_v7_thumb"
+      TARGET_DEVICE="arm_v7_neon"
       verbose "no target device (-d), using ${TARGET_DEVICE}"
     fi
   fi
 
   case $TARGET_DEVICE in
-    nexus_s)
-      DEFINES="${DEFINES} skia_arch_type=arm arm_neon=1 arm_version=7 arm_thumb=1"
-      DEFINES="${DEFINES} skia_resource_cache_mb_limit=24"
+    arm)
+      DEFINES="${DEFINES} skia_arch_type=arm arm_neon=0"
       ANDROID_ARCH="arm"
       ;;
-    nexus_4 | nexus_7 | nexus_10)
-      DEFINES="${DEFINES} skia_arch_type=arm arm_neon=1 arm_version=7 arm_thumb=1"
+    arm_v7 | xoom)
+      DEFINES="${DEFINES} skia_arch_type=arm arm_neon_optional=1 arm_version=7"
       ANDROID_ARCH="arm"
       ;;
-    xoom)
-      DEFINES="${DEFINES} skia_arch_type=arm arm_neon=0 arm_version=7 arm_thumb=1"
+    arm_v7_neon | nexus_4 | nexus_5 | nexus_6 | nexus_7 | nexus_10)
+      DEFINES="${DEFINES} skia_arch_type=arm arm_neon=1 arm_version=7"
       ANDROID_ARCH="arm"
       ;;
-    galaxy_nexus)
-      DEFINES="${DEFINES} skia_arch_type=arm arm_neon=1 arm_version=7 arm_thumb=1"
-      DEFINES="${DEFINES} skia_resource_cache_mb_limit=32"
-      ANDROID_ARCH="arm"
-      ;;
-    intel_rhb | razr_i)
-      DEFINES="${DEFINES} skia_arch_type=x86"
-      DEFINES="${DEFINES} skia_resource_cache_mb_limit=32"
-      ANDROID_ARCH="x86"
+    arm64 | nexus_9)
+      DEFINES="${DEFINES} skia_arch_type=arm64 skia_arch_width=64"
+      ANDROID_ARCH="arm64"
       ;;
     x86)
       DEFINES="${DEFINES} skia_arch_type=x86"
@@ -128,26 +129,6 @@
       DEFINES="${DEFINES} skia_arch_type=x86_64"
       ANDROID_ARCH="x86_64"
       ;;
-    arm_v7)
-      DEFINES="${DEFINES} skia_arch_type=arm arm_neon_optional=1 arm_version=7 arm_thumb=0"
-      ANDROID_ARCH="arm"
-      ;;
-    arm_v7_thumb | nvidia_logan | nexus_5)
-      DEFINES="${DEFINES} skia_arch_type=arm arm_neon_optional=1 arm_version=7 arm_thumb=1"
-      ANDROID_ARCH="arm"
-      ;;
-    arm)
-      DEFINES="${DEFINES} skia_arch_type=arm arm_neon=0 arm_thumb=0"
-      ANDROID_ARCH="arm"
-      ;;
-    arm_thumb)
-      DEFINES="${DEFINES} skia_arch_type=arm arm_neon=0 arm_thumb=1"
-      ANDROID_ARCH="arm"
-      ;;
-    arm64)
-      DEFINES="${DEFINES} skia_arch_type=arm64 skia_arch_width=64"
-      ANDROID_ARCH="arm64"
-      ;;
     mips)
       DEFINES="${DEFINES} skia_arch_type=mips skia_arch_width=32"
       DEFINES="${DEFINES} skia_resource_cache_mb_limit=32"
@@ -203,8 +184,13 @@
 
   if [ -f $HOST_DST ];
   then
-    #get the MD5 for dst and src
-    ANDROID_MD5=`$ADB $DEVICE_SERIAL shell md5 $ANDROID_SRC`
+    #get the MD5 for dst and src depending on OS and/or OS revision
+    ANDROID_MD5_SUPPORT=`$ADB $DEVICE_SERIAL shell ls -ld /system/bin/md5`
+    if [ "${ANDROID_MD5_SUPPORT:0:15}" != "/system/bin/md5" ]; then
+      ANDROID_MD5=`$ADB $DEVICE_SERIAL shell md5 $ANDROID_DST`
+    else
+      ANDROID_MD5=`$ADB $DEVICE_SERIAL shell md5sum $ANDROID_DST`
+    fi
     if [ $(uname) == "Darwin" ]; then
       HOST_MD5=`md5 -q $HOST_DST`
     else
@@ -241,8 +227,14 @@
 
   ANDROID_LS=`$ADB $DEVICE_SERIAL shell ls -ld $ANDROID_DST`
   if [ "${ANDROID_LS:0:1}" == "-" ]; then
-    #get the MD5 for dst and src
-    ANDROID_MD5=`$ADB $DEVICE_SERIAL shell md5 $ANDROID_DST`
+    #get the MD5 for dst and src depending on OS and/or OS revision
+    ANDROID_MD5_SUPPORT=`$ADB $DEVICE_SERIAL shell ls -ld /system/bin/md5`
+    if [ "${ANDROID_MD5_SUPPORT:0:15}" != "/system/bin/md5" ]; then
+      ANDROID_MD5=`$ADB $DEVICE_SERIAL shell md5 $ANDROID_DST`
+    else
+      ANDROID_MD5=`$ADB $DEVICE_SERIAL shell md5sum $ANDROID_DST`
+    fi
+
     if [ $(uname) == "Darwin" ]; then
       HOST_MD5=`md5 -q $HOST_SRC`
     else
diff --git a/platform_tools/android/gyp/dependencies.gypi b/platform_tools/android/gyp/dependencies.gypi
index 6507ee4..8bacd43 100644
--- a/platform_tools/android/gyp/dependencies.gypi
+++ b/platform_tools/android/gyp/dependencies.gypi
@@ -1,3 +1,8 @@
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
 # This GYP file stores the dependencies necessary to build Skia on the Android
 # platform. The OS doesn't provide many stable libraries as part of the
 # distribution so we have to build a few of them ourselves.
@@ -30,6 +35,19 @@
       ],
     },
     {
+      'target_name': 'ashmem',
+      'type': 'static_library',
+      'sources': [
+        '../third_party/ashmem/cutils/ashmem.h',
+        '../third_party/ashmem/cutils/ashmem-dev.c'
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../third_party/ashmem',
+        ]
+      },
+    },
+    {
       'target_name': 'expat',
       'type': 'static_library',
       'sources': [
@@ -55,27 +73,6 @@
       }
     },
     {
-      'target_name': 'gif',
-      'type': 'static_library',
-      'sources': [
-        '../third_party/externals/gif/dgif_lib.c',
-        '../third_party/externals/gif/gifalloc.c',
-        '../third_party/externals/gif/gif_err.c',
-      ],
-      'include_dirs': [
-        '../third_party/externals/gif',
-      ],
-      'cflags': [
-        '-w',
-        '-DHAVE_CONFIG_H',
-      ],
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '../third_party/externals/gif',
-        ],
-      }
-    },
-    {
       'target_name': 'png',
       'type': 'static_library',
       'sources': [
@@ -118,6 +115,9 @@
     {
       'target_name': 'jpeg',
       'type': 'static_library',
+      'dependencies': [
+        'ashmem'
+      ],
       'sources': [
         '../third_party/externals/jpeg/jcapimin.c',
         '../third_party/externals/jpeg/jcapistd.c',
@@ -160,11 +160,46 @@
         '../third_party/externals/jpeg/jidctfst.c',
         '../third_party/externals/jpeg/jidctint.c',
         '../third_party/externals/jpeg/jidctred.c',
+        '../third_party/externals/jpeg/jmem-ashmem.c',
+        '../third_party/externals/jpeg/jmemmgr.c',
         '../third_party/externals/jpeg/jquant1.c',
         '../third_party/externals/jpeg/jquant2.c',
         '../third_party/externals/jpeg/jutils.c',
-        '../third_party/externals/jpeg/jmemmgr.c',
-        '../third_party/externals/jpeg/jmem-android.c', # ashmem is also available
+      ],
+      'conditions': [
+        [ 'arm_neon == 1 and skia_clang_build == 0',
+          {
+            'sources' : [
+              '../third_party/externals/jpeg/armv6_idct.S',
+              '../third_party/externals/jpeg/jsimd_arm_neon.S',
+              '../third_party/externals/jpeg/jsimd_neon.c',
+            ],
+            'defines' : [
+              'NV_ARM_NEON',
+            ],
+          },
+        ],
+        [ 'skia_arch_type == "mips" and mips_dsp == 2',
+          {
+            'sources' : [
+              '../third_party/externals/jpeg/mips_jidctfst.c',
+              '../third_party/externals/jpeg/mips_idct_le.S',
+            ],
+            'defines' : [
+              'ANDROID_MIPS_IDCT',
+            ],
+          },
+        ],
+        [ '"x86" in skia_arch_type',
+          {
+            'sources' : [
+              '../third_party/externals/jpeg/jidctintelsse.c',
+            ],
+            'defines' : [
+              'ANDROID_INTELSSE2_IDCT',
+            ],
+          },
+        ],
       ],
       'include_dirs': [
         '../third_party/externals/jpeg',
@@ -173,6 +208,7 @@
         '-w',
         '-fvisibility=hidden',
         '-DAVOID_TABLES',
+        '-DUSE_ANDROID_ASHMEM',
         '-O3',
         '-fstrict-aliasing',
         '-fprefetch-loop-arrays',
diff --git a/platform_tools/android/gyp_gen/android_framework_gyp.py b/platform_tools/android/gyp_gen/android_framework_gyp.py
index 787a4d9..34a9eee 100644
--- a/platform_tools/android/gyp_gen/android_framework_gyp.py
+++ b/platform_tools/android/gyp_gen/android_framework_gyp.py
@@ -63,8 +63,8 @@
   gyp_defines = ('skia_android_framework=1 OS=android skia_arch_type=%s '
                  % skia_arch_type)
   if skia_arch_type == 'arm':
-    # Always use thumb and version 7 for arm
-    gyp_defines += 'arm_thumb=1 arm_version=7 '
+    # Always version 7 (which implies thumb) for arm
+    gyp_defines += 'arm_version=7 '
     if have_neon:
       gyp_defines += 'arm_neon=1 '
     else:
diff --git a/platform_tools/android/third_party/ashmem/LICENSE b/platform_tools/android/third_party/ashmem/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/platform_tools/android/third_party/ashmem/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/platform_tools/android/third_party/ashmem/README.skia b/platform_tools/android/third_party/ashmem/README.skia
new file mode 100644
index 0000000..10ce2b3
--- /dev/null
+++ b/platform_tools/android/third_party/ashmem/README.skia
@@ -0,0 +1,10 @@
+This is copied directly from chromium/src/third_party/ashmem.  It is used only
+for testing purposes.  The client is expected to provide its own ashmem
+implementation.
+
+Name: Android
+URL: http://source.android.com
+Description: Android shared memory implementation. Only applies to OS_ANDROID.
+Version: 7203eb2a8a29a7b721a48cd291700f38f3da1456
+Security Critical: yes
+License: Apache 2.0
diff --git a/platform_tools/android/third_party/ashmem/cutils/ashmem-dev.c b/platform_tools/android/third_party/ashmem/cutils/ashmem-dev.c
new file mode 100644
index 0000000..2303369
--- /dev/null
+++ b/platform_tools/android/third_party/ashmem/cutils/ashmem-dev.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implementation of the user-space ashmem API for devices, which have our
+ * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
+ * used by the simulator.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <linux/ashmem.h>
+#include "ashmem.h"
+
+#define ASHMEM_DEVICE	"/dev/ashmem"
+
+/*
+ * ashmem_create_region - creates a new ashmem region and returns the file
+ * descriptor, or <0 on error
+ *
+ * `name' is an optional label to give the region (visible in /proc/pid/maps)
+ * `size' is the size of the region, in page-aligned bytes
+ */
+int ashmem_create_region(const char *name, size_t size)
+{
+	int fd, ret;
+
+	fd = open(ASHMEM_DEVICE, O_RDWR);
+	if (fd < 0)
+		return fd;
+
+	if (name) {
+		char buf[ASHMEM_NAME_LEN];
+
+		strlcpy(buf, name, sizeof(buf));
+		ret = ioctl(fd, ASHMEM_SET_NAME, buf);
+		if (ret < 0)
+			goto error;
+	}
+
+	ret = ioctl(fd, ASHMEM_SET_SIZE, size);
+	if (ret < 0)
+		goto error;
+
+	return fd;
+
+error:
+	close(fd);
+	return ret;
+}
+
+int ashmem_set_prot_region(int fd, int prot)
+{
+	return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
+}
+
+int ashmem_pin_region(int fd, size_t offset, size_t len)
+{
+	struct ashmem_pin pin = { offset, len };
+	return ioctl(fd, ASHMEM_PIN, &pin);
+}
+
+int ashmem_unpin_region(int fd, size_t offset, size_t len)
+{
+	struct ashmem_pin pin = { offset, len };
+	return ioctl(fd, ASHMEM_UNPIN, &pin);
+}
+
+int ashmem_get_size_region(int fd)
+{
+  return ioctl(fd, ASHMEM_GET_SIZE, NULL);
+}
+
+int ashmem_purge_all(void)
+{
+  const int fd = open(ASHMEM_DEVICE, O_RDWR);
+  if (fd < 0)
+    return fd;
+  const int ret = ioctl(fd, ASHMEM_PURGE_ALL_CACHES, 0);
+  close(fd);
+  return ret;
+}
diff --git a/platform_tools/android/third_party/ashmem/cutils/ashmem.h b/platform_tools/android/third_party/ashmem/cutils/ashmem.h
new file mode 100644
index 0000000..7d411cc
--- /dev/null
+++ b/platform_tools/android/third_party/ashmem/cutils/ashmem.h
@@ -0,0 +1,46 @@
+/* third_party/ashmem/ashmem.h
+ **
+ ** Copyright 2008 The Android Open Source Project
+ **
+ ** This file is dual licensed.  It may be redistributed and/or modified
+ ** under the terms of the Apache 2.0 License OR version 2 of the GNU
+ ** General Public License.
+ */
+
+#ifndef _THIRD_PARTY_ASHMEM_H
+#define _THIRD_PARTY_ASHMEM_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ashmem_create_region(const char *name, size_t size);
+int ashmem_set_prot_region(int fd, int prot);
+int ashmem_pin_region(int fd, size_t offset, size_t len);
+int ashmem_unpin_region(int fd, size_t offset, size_t len);
+int ashmem_get_size_region(int fd);
+int ashmem_purge_all(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef __ASHMEMIOC	/* in case someone included <linux/ashmem.h> too */
+
+#define ASHMEM_NAME_LEN		256
+
+#define ASHMEM_NAME_DEF		"dev/ashmem"
+
+/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
+#define ASHMEM_NOT_PURGED	0
+#define ASHMEM_WAS_PURGED	1
+
+/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */
+#define ASHMEM_IS_UNPINNED	0
+#define ASHMEM_IS_PINNED	1
+
+#endif	/* ! __ASHMEMIOC */
+
+#endif	/* _THIRD_PARTY_ASHMEM_H */
diff --git a/platform_tools/android/tradefed/upload_dm_results.py b/platform_tools/android/tradefed/upload_dm_results.py
index 9aac407..a8ca01e 100755
--- a/platform_tools/android/tradefed/upload_dm_results.py
+++ b/platform_tools/android/tradefed/upload_dm_results.py
@@ -32,6 +32,9 @@
     gs_utils.GSUtils.Permission.READ
   )]
 
+  if not os.path.isfile(os.path.join(dm_dir, 'dm.json')):
+    sys.exit("no dm.json file found in output directory.")
+
   # Move dm.json to its own directory to make uploading it easier.
   tmp = tempfile.mkdtemp()
   shutil.move(os.path.join(dm_dir, 'dm.json'),
diff --git a/platform_tools/chromeos/bin/chromeos_setup.sh b/platform_tools/chromeos/bin/chromeos_setup.sh
index e2dc32b..41267ec 100755
--- a/platform_tools/chromeos/bin/chromeos_setup.sh
+++ b/platform_tools/chromeos/bin/chromeos_setup.sh
@@ -1,4 +1,10 @@
 # Set up the environment to build Skia for ChromeOS.
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
 
 function exportVar {
   NAME=$1
@@ -23,16 +29,12 @@
   DEFINES="OS=linux host_os=linux skia_os=chromeos skia_gpu=0"
 
   case $TARGET_DEVICE in
-    x86-alex)
-        DEFINES="${DEFINES} skia_arch_type=x86 skia_arch_width=32"
-        GENERIC_BOARD_TYPE="x86-generic"
-        ;;
     link)
-        DEFINES="${DEFINES} skia_arch_type=x86 skia_arch_width=64"
+        DEFINES="${DEFINES} skia_arch_type=x86_64"
         GENERIC_BOARD_TYPE="amd64-generic"
         ;;
     daisy)
-        DEFINES="${DEFINES} skia_arch_type=arm arm_neon=1 armv7=1 arm_thumb=0 skia_arch_width=32"
+        DEFINES="${DEFINES} skia_arch_type=arm arm_version=7 arm_neon=1"
         # TODO(borenet): We have to define skia_warnings_as_errors=0 for the arm
         # build, which throws lots of "mangling of va_list has changed" warnings.
         DEFINES="${DEFINES} skia_warnings_as_errors=0"
@@ -51,6 +53,7 @@
   exportVar GYP_DEFINES "$DEFINES"
   exportVar GYP_GENERATORS "ninja"
   exportVar GYP_GENERATOR_FLAGS ""
-  exportVar SKIA_OUT "out/config/chromeos-${TARGET_DEVICE}"
+  SKIA_OUT=${SKIA_OUT:-out}
+  exportVar SKIA_OUT "${SKIA_OUT}/config/chromeos-${TARGET_DEVICE}"
   exportVar builddir_name "."
 }
diff --git a/platform_tools/chromeos/gyp/dependencies.gypi b/platform_tools/chromeos/gyp/dependencies.gypi
deleted file mode 100644
index 38e3c53..0000000
--- a/platform_tools/chromeos/gyp/dependencies.gypi
+++ /dev/null
@@ -1,35 +0,0 @@
-# This GYP file stores the dependencies necessary to build Skia on the Chrome OS
-# platform. The OS doesn't provide many stable libraries as part of the
-# distribution so we have to build a few of them ourselves.
-
-{
-  'variables': {
-    'skia_warnings_as_errors': 0,
-  },
-  'targets': [
-    {
-      'target_name': 'gif',
-      'type': 'static_library',
-      'sources': [
-        '../third_party/externals/gif/dgif_lib.c',
-        '../third_party/externals/gif/gifalloc.c',
-        '../third_party/externals/gif/gif_err.c',
-      ],
-      'include_dirs': [
-        '../third_party/externals/gif',
-      ],
-      'cflags': [
-        '-Wno-format',
-        '-DHAVE_CONFIG_H',
-      ],
-      'cflags!': [
-        '-Wall',
-      ],
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '../third_party/externals/gif',
-        ],
-      }
-    },
-  ]
-}
diff --git a/platform_tools/ios/bin/ios_cat_file b/platform_tools/ios/bin/ios_cat_file
new file mode 100755
index 0000000..2e712ca
--- /dev/null
+++ b/platform_tools/ios/bin/ios_cat_file
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# Writes the content of the file identified by the first argument to standard
+# output. The path is relative to the Documents directory of the installed app.
+#
+
+# Do not set -x here, since we only want the content of the file sent to
+# standard out.
+set -e
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+ios_cat $1
diff --git a/platform_tools/ios/bin/ios_install b/platform_tools/ios/bin/ios_install
new file mode 100755
index 0000000..7dfe3b6
--- /dev/null
+++ b/platform_tools/ios/bin/ios_install
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# Installs the app on the device.
+#
+
+set -e -x
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+ios_install_app
diff --git a/platform_tools/ios/bin/ios_mkdir b/platform_tools/ios/bin/ios_mkdir
new file mode 100755
index 0000000..9bc5094
--- /dev/null
+++ b/platform_tools/ios/bin/ios_mkdir
@@ -0,0 +1,16 @@
+#!/bin/bash
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# Create a directory relative to the Documents directory.
+#
+set -e -x
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+ios_mkdir $1
diff --git a/platform_tools/ios/bin/ios_mount b/platform_tools/ios/bin/ios_mount
new file mode 100755
index 0000000..d71e32d
--- /dev/null
+++ b/platform_tools/ios/bin/ios_mount
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# Mounts the iOS device locally. See the value of IOS_MOUNT_POINT in
+# ios_setup.sh for the exact location.
+#
+set -x -e
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+ios_mount
diff --git a/platform_tools/ios/bin/ios_ninja b/platform_tools/ios/bin/ios_ninja
new file mode 100755
index 0000000..55f30bb
--- /dev/null
+++ b/platform_tools/ios/bin/ios_ninja
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# Build the skia for ios. This assumes that GYP_DEFINES has been set.
+#
+set -x -e
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+OLD_DIR=`pwd`
+cd ${SKIA_SRC_DIR}
+"${SKIA_SRC_DIR}/gyp_skia"
+xcodebuild -project "$SKIA_SRC_DIR/out/gyp/iOSShell.xcodeproj"\
+       -configuration "$BUILDTYPE"
+cd $OLD_DIR
diff --git a/platform_tools/ios/bin/ios_path_exists b/platform_tools/ios/bin/ios_path_exists
new file mode 100755
index 0000000..42a4f7e
--- /dev/null
+++ b/platform_tools/ios/bin/ios_path_exists
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# Check if ios path exists.
+#
+
+set -x -e
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+if [[ "$#" -ne "1" ]]; then
+	echo "Usage: ios_path_exists <path_in_Documents_folder_of_ios_device>"
+	exit 1
+fi
+
+RET=ios_path_exists $1
+exit $RET
diff --git a/platform_tools/ios/bin/ios_pull_if_needed b/platform_tools/ios/bin/ios_pull_if_needed
new file mode 100755
index 0000000..25edfd9
--- /dev/null
+++ b/platform_tools/ios/bin/ios_pull_if_needed
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# Pull the given file/directory off the device.
+#
+set -x -e
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+DEVICE_PATH=$1
+HOST_PATH=$2
+
+ios_pull $DEVICE_PATH $HOST_PATH
diff --git a/platform_tools/ios/bin/ios_push_file b/platform_tools/ios/bin/ios_push_file
new file mode 100755
index 0000000..504f0cc
--- /dev/null
+++ b/platform_tools/ios/bin/ios_push_file
@@ -0,0 +1,19 @@
+#!/bin/bash
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# Copies a single file from the host to the device.
+#
+set -x -e
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+HOST_PATH=$1
+DEVICE_PATH=$2
+
+ios_push $HOST_PATH $DEVICE_PATH
diff --git a/platform_tools/ios/bin/ios_push_if_needed b/platform_tools/ios/bin/ios_push_if_needed
new file mode 100755
index 0000000..11b9dfa
--- /dev/null
+++ b/platform_tools/ios/bin/ios_push_if_needed
@@ -0,0 +1,22 @@
+#!/bin/bash
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# Copies the files identified by the first argument from the host to the target
+# path on the device. The path on the devide is relative to the Documents
+# directory.
+#
+
+set -x -e
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+HOST_PATH=$1
+DEVICE_PATH=$2
+
+ios_push $HOST_PATH $DEVICE_PATH
diff --git a/platform_tools/ios/bin/ios_rm b/platform_tools/ios/bin/ios_rm
new file mode 100755
index 0000000..1d606fc
--- /dev/null
+++ b/platform_tools/ios/bin/ios_rm
@@ -0,0 +1,17 @@
+#!/bin/bash
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# Removes the path identified by the first argument from the directory.
+#
+
+set -x -e
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+ios_rm $1
diff --git a/platform_tools/ios/bin/ios_run_skia b/platform_tools/ios/bin/ios_run_skia
new file mode 100755
index 0000000..248593a
--- /dev/null
+++ b/platform_tools/ios/bin/ios_run_skia
@@ -0,0 +1,20 @@
+#!/bin/bash
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# ios_run_skia: starts the correct skia program on the device, prints the
+# output, and kills the app if interrupted.
+
+set -x -e
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+# Run the application.
+IOS_DEPLOY_ARGS="${SKIA_SRC_DIR}/xcodebuild/${BUILDTYPE}-iphoneos/iOSShell.app --args \"${*}\""
+CMD="ios-deploy -I -m -b ${IOS_DEPLOY_ARGS}"
+eval $CMD
diff --git a/platform_tools/ios/bin/ios_setup.sh b/platform_tools/ios/bin/ios_setup.sh
new file mode 100755
index 0000000..7b42e21
--- /dev/null
+++ b/platform_tools/ios/bin/ios_setup.sh
@@ -0,0 +1,187 @@
+#!/bin/bash
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# ios_setup.sh: Sets environment variables used by other iOS scripts.
+
+# File system location where we mount the ios devices.
+IOS_MOUNT_POINT="/tmp/mnt_iosdevice"
+
+# Location on the ios device where all data are stored. This is
+# relative to the mount point.
+IOS_DOCS_DIR="Documents"
+
+# Temporary location to assemble the app into an .ipa package.
+IOS_PCKG_DIR="/tmp/ios_pckg"
+
+# Bundle id of the app that runs the tests.
+TEST_RUNNER_BUNDLE_ID="com.google.iOSShell"
+
+# Directory with the Skia source.
+SKIA_SRC_DIR=$(cd "${SCRIPT_DIR}/../../.."; pwd)
+
+# Provisioning profile - this needs to be set up on the local machine.
+PROVISIONING_PROFILE="9e88090d-abed-4e89-b106-3eff3512d31f"
+
+# Code Signing identity - this needs to be set up on the local machine.
+CODE_SIGN_IDENTITY="iPhone Developer: Google Development (3F4Y5873JF)"
+
+IOS_BUNDLE_ID="com.google.iOSShell"
+
+IOS_RESULTS_DIR="results"
+
+# BUILDTYPE is 'Debug' by default.
+if [[ -z "$BUILDTYPE" ]]; then
+  BUILDTYPE="Debug"
+fi
+
+ios_uninstall_app() {
+  ideviceinstaller -U "$IOS_BUNDLE_ID"
+}
+
+ios_install_app() {
+  rm -rf $IOS_PCKG_DIR
+  mkdir -p $IOS_PCKG_DIR/Payload  # this directory must be named 'Payload'
+  cp -rf "${SKIA_SRC_DIR}/xcodebuild/${BUILDTYPE}-iphoneos/iOSShell.app" "${IOS_PCKG_DIR}/Payload/"
+  local RET_DIR=`pwd`
+  cd $IOS_PCKG_DIR
+  zip -r iOSShell.ipa Payload
+  ideviceinstaller -i ./iOSShell.ipa
+  cd $RET_DIR
+}
+
+ios_rm() {
+  local TARGET="$IOS_MOUNT_POINT/$IOS_DOCS_DIR/$1"
+
+  ios_mount
+  rm -rf "$TARGET"
+  ios_umount
+}
+
+ios_mkdir() {
+  local TARGET="$IOS_MOUNT_POINT/$IOS_DOCS_DIR/$1"
+  ios_mount
+  mkdir -p "$TARGET"
+  ios_umount
+}
+
+ios_cat() {
+  local TARGET="$IOS_MOUNT_POINT/$IOS_DOCS_DIR/$1"
+  ios_mount
+  RET="$(cat $TARGET)"
+  ios_umount
+  echo -e "$RET"
+}
+
+# ios_mount: mounts the iOS device for reading or writing.
+ios_mount() {
+  # If this is already mounted we unmount it.
+  if $(mount | grep --quiet "$IOS_MOUNT_POINT"); then
+    >&2 echo "Device already mounted at: $IOS_MOUNT_POINT - Unmounting."
+    ios_umount
+  fi
+
+  # Ensure there is a mount directory.
+  if [[ ! -d "$IOS_MOUNT_POINT" ]]; then
+    mkdir -p $IOS_MOUNT_POINT
+  fi
+  ifuse --container $TEST_RUNNER_BUNDLE_ID $IOS_MOUNT_POINT
+  sleep 1
+  >&2 echo "Successfully mounted device."
+}
+
+# ios_umount: unmounts the ios device.
+ios_umount() {
+  umount $IOS_MOUNT_POINT
+  sleep 1
+}
+
+# ios_restart: restarts the iOS device.
+ios_restart() {
+  idevicediagnostics restart
+}
+
+# ios_pull(ios_src, host_dst): Copies the content of ios_src to host_dst.
+# The path is relative to the 'Documents' folder on the device.
+ios_pull() {
+  # read input params
+  local IOS_SRC="$IOS_MOUNT_POINT/$IOS_DOCS_DIR/$1"
+  local HOST_DST="$2"
+
+  ios_mount
+  if [[ -d "${HOST_DST}" ]]; then
+    cp -r "$IOS_SRC/" "$HOST_DST"
+  else
+    cp -r "$IOS_SRC" "$HOST_DST"
+  fi
+  ios_umount
+}
+
+# ios_push(host_src, ios_dst)
+ios_push() {
+  # read input params
+  local HOST_SRC="$1"
+  local IOS_DST="$IOS_MOUNT_POINT/$IOS_DOCS_DIR/$2"
+
+  ios_mount
+  rm -rf $IOS_DST
+  mkdir -p "$(dirname $IOS_DST)"
+  cp -r "$HOST_SRC" "$IOS_DST"
+  ios_umount
+}
+
+ios_path_exists() {
+  local TARGET_PATH="$IOS_MOUNT_POINT/$IOS_DOCS_DIR/$1"
+  local RET=1
+  ios_mount
+  if [[ -e $TARGET_PATH ]]; then
+    RET=0
+  fi
+  ios_umount
+  return $RET
+}
+
+#   ANDROID_LS=`$ADB $DEVICE_SERIAL shell ls -ld $ANDROID_DST`
+#   HOST_LS=`ls -ld $HOST_SRC`
+#   if [ "${ANDROID_LS:0:1}" == "d" -a "${HOST_LS:0:1}" == "-" ];
+#   then
+#     ANDROID_DST="${ANDROID_DST}/$(basename ${HOST_SRC})"
+#   fi
+
+
+#   ANDROID_LS=`$ADB $DEVICE_SERIAL shell ls -ld $ANDROID_DST`
+#   if [ "${ANDROID_LS:0:1}" == "-" ]; then
+#     #get the MD5 for dst and src
+#     ANDROID_MD5=`$ADB $DEVICE_SERIAL shell md5 $ANDROID_DST`
+#     if [ $(uname) == "Darwin" ]; then
+#       HOST_MD5=`md5 -q $HOST_SRC`
+#     else
+#       HOST_MD5=`md5sum $HOST_SRC`
+#     fi
+
+#     if [ "${ANDROID_MD5:0:32}" != "${HOST_MD5:0:32}" ]; then
+#       echo -n "$ANDROID_DST "
+#       $ADB $DEVICE_SERIAL push $HOST_SRC $ANDROID_DST
+#     fi
+#   elif [ "${ANDROID_LS:0:1}" == "d" ]; then
+#     for FILE_ITEM in `ls $HOST_SRC`; do
+#       adb_push_if_needed "${HOST_SRC}/${FILE_ITEM}" "${ANDROID_DST}/${FILE_ITEM}"
+#     done
+#   else
+#     HOST_LS=`ls -ld $HOST_SRC`
+#     if [ "${HOST_LS:0:1}" == "d" ]; then
+#       $ADB $DEVICE_SERIAL shell mkdir -p $ANDROID_DST
+#       adb_push_if_needed $HOST_SRC $ANDROID_DST
+#     else
+#       echo -n "$ANDROID_DST "
+#       $ADB $DEVICE_SERIAL shell mkdir -p "$(dirname "$ANDROID_DST")"
+#       $ADB $DEVICE_SERIAL push $HOST_SRC $ANDROID_DST
+#     fi
+#   fi
+# }
+
+# setup_device "${DEVICE_ID}"
diff --git a/platform_tools/ios/bin/ios_umount b/platform_tools/ios/bin/ios_umount
new file mode 100755
index 0000000..ddc477c
--- /dev/null
+++ b/platform_tools/ios/bin/ios_umount
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+###############################################################################
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+###############################################################################
+#
+# Mount the ios device.
+#
+set -x -e
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source $SCRIPT_DIR/ios_setup.sh
+
+ios_umount
diff --git a/platform_tools/nacl/SampleApp/SampleApp.nmf b/platform_tools/nacl/SampleApp/SampleApp.nmf
deleted file mode 100644
index 7296c07..0000000
--- a/platform_tools/nacl/SampleApp/SampleApp.nmf
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "program": {
-    "x86-64": {"url": "../../out/nacl64/Debug/SampleApp"},
-    "x86-32": {"url": "../../out/nacl32/Debug/SampleApp"}
-  }
-}
diff --git a/platform_tools/nacl/SampleApp/index.html b/platform_tools/nacl/SampleApp/index.html
deleted file mode 100644
index cbdeefe..0000000
--- a/platform_tools/nacl/SampleApp/index.html
+++ /dev/null
@@ -1,71 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <!--
-  Copyright 2013 Google Inc.
-
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-  -->
-<head>
-
-  <title>Skia Sample App</title>
-
-  <script type="text/javascript">
-    "use strict";
-
-    var SkiaModule = null;  // Global application object.
-
-    // Force a re-draw of the given element.
-    function refresh(elem) {
-      var old_display_style = elem.style.display;
-      elem.style.display = "none";
-      elem.style.display = old_display_style;
-    }
-
-    // When the module loads, begin running the application.
-    function moduleDidLoad() {
-      SkiaModule = document.getElementById("skia_nacl");
-      run();
-    }
-
-    function handleMessage(message_event) {
-      var skdebugf_cmd = "SkDebugf:";
-      if (message_event.data.indexOf(skdebugf_cmd) == 0) {
-        var msg_contents = message_event.data.slice(skdebugf_cmd.length)
-        console.log("Skia: " + msg_contents);
-      } else {
-        alert(message_event.data);
-      }
-    }
-
-    // Run the application.
-    function run() {
-      if (SkiaModule) {
-        var cmd = "init";
-        SkiaModule.postMessage(cmd);
-      } else {
-        alert("The Skia module has not properly loaded...");
-      }
-    }
-  </script>
-</head>
-<body>
-
-<h1>Skia Sample App</h1>
-<p>
-  <div id="listener">
-    <script type="text/javascript">
-      var listener = document.getElementById('listener');
-      listener.addEventListener('load', moduleDidLoad, true);
-      listener.addEventListener('message', handleMessage, true);
-    </script>
-
-    <embed name="nacl_module"
-       id="skia_nacl"
-       width=0 height=0
-       src="SampleApp.nmf"
-       type="application/x-nacl" />
-  </div>
-</p>
-</body>
-</html>
diff --git a/platform_tools/nacl/debugger/debugger.css b/platform_tools/nacl/debugger/debugger.css
deleted file mode 100644
index a63b7e0..0000000
--- a/platform_tools/nacl/debugger/debugger.css
+++ /dev/null
@@ -1,172 +0,0 @@
-body {
-  width:100%;
-  height:100%;
-  margin:0px;
-  padding:0px;
-  background-color:#EEE;
-}
-
-div.single-line {
-  clear:both;
-}
-
-div.column-set {
-  width:100%;
-  height:100%;
-  display:table;
-  vertical-align:top;
-  border-collapse:collapse;
-  margin:0px;
-}
-
-div.column {
-  display:table-cell;
-  vertical-align:top;
-  margin:0px;
-}
-
-div.row-set {
-  width:100%;
-  height:100%;
-  display:table;
-  vertical-align:top;
-  border-collapse:collapse;
-  margin:0px;
-}
-
-div.row {
-  display:table-row;
-  vertical-align:top;
-  margin:0px;
-}
-
-#buttons {
-  height:5px;
-  overflow:auto;
-}
-
-#left_column {
-  width:230px;
-}
-
-#command_list_div {
-}
-
-#command_list_form {
-  width:100%;
-  height:100%;
-}
-
-#command_list {
-  width:100%;
-  height:100%;
-}
-
-#bottom_row {
-  height:275px;
-}
-
-#display_pane {
-}
-
-#right_panel {
-  width:230px;
-}
-
-#tabview {
-}
-
-#matrixclip {
-}
-
-#small_window {
-  width:218px;
-  height:218px;
-  background-color:#FFF;
-}
-
-div.thin_outline {
-  border:1px solid;
-  margin:6px;
-  padding:8px;
-}
-
-div.settings_block {
-}
-
-input.matrix {
-  width:50px;
-}
-
-#overviewdetails {
-  width:100%;
-  height:100%;
-  resize:none;
-  padding:10px;
-}
-
-#menu {
-  height:5px;
-  overflow:auto;
-  background-color:#999;
-}
-
-#menu-bar {
-  margin:0px;
-}
-
-ul.dropdown-menu a {
-  display:block;
-  text-decoration:none;
-  color:#000;
-}
-
-ul.dropdown-menu, ul.dropdown-menu li, ul.dropdown-menu ul {
-  list-style:none;
-  margin:0px;
-  padding:0px;
-}
-
-ul.dropdown-menu {
-  position:relative;
-  z-index:597;
-  float:left;
-}
-
-ul.dropdown-menu li {
-  float:left;
-  padding:5px;
-  cursor:pointer;
-}
-
-ul.dropdown-menu li.hover, ul.dropdown-menu li:hover {
-  position:relative;
-  z-index:599;
-  cursor:default;
-  background-color:#FFF;
-}
-
-ul.dropdown-menu ul {
-  visibility:hidden;
-  position:absolute;
-  top:100%;
-  left:0;
-  z-index:598;
-  width:195px;
-  border:1px solid;
-  border-collapse:collapse;
-  background-color:#DDD;
-}
-
-ul.dropdown-menu ul li {
-  float:none;
-}
-
-ul.dropdown-menu ul ul {
-  top:-2px;
-  left:100%;
-}
-
-ul.dropdown-menu li:hover > ul {
-  visibility:visible;
-}
\ No newline at end of file
diff --git a/platform_tools/nacl/debugger/debugger.nmf b/platform_tools/nacl/debugger/debugger.nmf
deleted file mode 100644
index 7a53e52..0000000
--- a/platform_tools/nacl/debugger/debugger.nmf
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "program": {
-    "x86-64": {"url": "../../out/nacl64/Debug/debugger"},
-    "x86-32": {"url": "../../out/nacl32/Debug/debugger"}
-  }
-}
diff --git a/platform_tools/nacl/debugger/icons/blank.png b/platform_tools/nacl/debugger/icons/blank.png
deleted file mode 100644
index 97246fb..0000000
--- a/platform_tools/nacl/debugger/icons/blank.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/breakpoint.png b/platform_tools/nacl/debugger/icons/breakpoint.png
deleted file mode 100644
index b6290b4..0000000
--- a/platform_tools/nacl/debugger/icons/breakpoint.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/breakpoint_16x16.png b/platform_tools/nacl/debugger/icons/breakpoint_16x16.png
deleted file mode 100644
index a25ece8..0000000
--- a/platform_tools/nacl/debugger/icons/breakpoint_16x16.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/delete.png b/platform_tools/nacl/debugger/icons/delete.png
deleted file mode 100644
index 50b604f..0000000
--- a/platform_tools/nacl/debugger/icons/delete.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/inspector.png b/platform_tools/nacl/debugger/icons/inspector.png
deleted file mode 100644
index 360f23e..0000000
--- a/platform_tools/nacl/debugger/icons/inspector.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/next.png b/platform_tools/nacl/debugger/icons/next.png
deleted file mode 100644
index aae8934..0000000
--- a/platform_tools/nacl/debugger/icons/next.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/pause.png b/platform_tools/nacl/debugger/icons/pause.png
deleted file mode 100644
index a026a54..0000000
--- a/platform_tools/nacl/debugger/icons/pause.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/play.png b/platform_tools/nacl/debugger/icons/play.png
deleted file mode 100644
index 5528baf..0000000
--- a/platform_tools/nacl/debugger/icons/play.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/previous.png b/platform_tools/nacl/debugger/icons/previous.png
deleted file mode 100644
index 3396a3f..0000000
--- a/platform_tools/nacl/debugger/icons/previous.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/profile.png b/platform_tools/nacl/debugger/icons/profile.png
deleted file mode 100644
index d4b8501..0000000
--- a/platform_tools/nacl/debugger/icons/profile.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/reload.png b/platform_tools/nacl/debugger/icons/reload.png
deleted file mode 100644
index 316f36c..0000000
--- a/platform_tools/nacl/debugger/icons/reload.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/rewind.png b/platform_tools/nacl/debugger/icons/rewind.png
deleted file mode 100644
index d8d4902..0000000
--- a/platform_tools/nacl/debugger/icons/rewind.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/icons/skia.png b/platform_tools/nacl/debugger/icons/skia.png
deleted file mode 100644
index 017ee4f..0000000
--- a/platform_tools/nacl/debugger/icons/skia.png
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/debugger/index.html b/platform_tools/nacl/debugger/index.html
deleted file mode 100644
index 9d4644d..0000000
--- a/platform_tools/nacl/debugger/index.html
+++ /dev/null
@@ -1,468 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <!--
-  Copyright 2013 Google Inc.
-
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-  -->
-<head>
-  <title>Skia Debugger</title>
-  <link rel="stylesheet" type="text/css" href="debugger.css"/>
-  <script type="text/javascript">
-    "use strict";
-
-    var skia_module = null;  // Global application object.
-    var display_right_panel = null;
-    var display_bottom_row = null;
-    var overview_text = "";
-    var details_text = "Default details text.";
-    var command_list = [];
-    var command_types = {};
-    var no_filter_text = "--Filter By Available Commands--";
-
-    function openFileDialog() {
-      var event = document.createEvent("MouseEvents");
-      event.initEvent("click", true, false);
-      document.getElementById("file_open").dispatchEvent(event);
-    }
-
-    function updateOverviewDetails() {
-      var radio_buttons = document.getElementsByName("overviewdetails_radio");
-      for (var i = 0; i < radio_buttons.length; ++i) {
-        if (radio_buttons[i].checked) {
-          if (radio_buttons[i].value == "details") {
-            document.getElementById("overviewdetails").innerHTML = details_text;
-          } else {
-            document.getElementById("overviewdetails").innerHTML = overview_text;
-          }
-          return;
-        }
-      }
-      // If no radio button is checked, check the "overview" button.
-      for (var i = 0; i < radio_buttons.length; ++i) {
-        if (radio_buttons[i].value == "overview") {
-          radio_buttons[i].checked = true;
-          document.getElementById("overviewdetails").innerHTML = overview_text;
-          return;
-        }
-      }
-    }
-
-    function makeIndentString(indent_amt) {
-      var indent_str = "";
-      for (var i = 0; i < indent_amt; ++i) {
-        indent_str += "--";
-      }
-      return indent_str;
-    }
-
-    function updateCommandList(filter) {
-      var command_list_display = document.getElementById("command_list");
-      command_list_display.options.length = 0;
-      var indent = 0;
-      var indent_str = "";
-      for (var i = 0; i < command_list.length; ++i) {
-        if (command_list[i] == "Restore") {
-          indent--;
-          indent_str = makeIndentString(indent);
-        }
-        if (!filter || filter == no_filter_text || command_list[i] == filter) {
-          command_list_display.options.add(new Option(indent_str + command_list[i], i));
-        }
-        if (command_list[i] == "Save" || command_list[i] == "Save Layer") {
-          indent++;
-          indent_str = makeIndentString(indent);
-        }
-      }
-      command_list_display.selectedIndex = command_list_display.length - 1;
-
-      // TODO(borenet): Should the SKP re-draw when the command list is updated?
-      //commandSelected();
-    }
-
-    function updateFilterList() {
-      var filter_list_display = document.getElementById("command_filter");
-      filter_list_display.options.length = 0;
-      filter_list_display.options.add(new Option(no_filter_text, no_filter_text));
-      for (var command_type in command_types) {
-        if (command_types.hasOwnProperty(command_type)) {
-          filter_list_display.options.add(new Option(command_type, command_type));
-        }
-      }
-    }
-
-    function openFile(event) {
-      document.getElementById("overviewdetails").innerHTML = "";
-      var files = event.target.files;
-      if (files.length != 1) {
-        return;
-      }
-      var file = files[0];
-      var reader = new FileReader();
-      reader.onload = (function(theFile) {
-        return function(e) {
-          var data_prefix = "data:;base64,";
-          skia_module.postMessage("LoadSKP" + e.target.result.slice(data_prefix.length));
-        };
-      })(file);
-      reader.readAsDataURL(file);
-    }
-
-    function toggleInspector() {
-      var right_panel = document.getElementById("right_panel");
-      var bottom_row = document.getElementById("bottom_row");
-      if (right_panel.style.display == display_right_panel) {
-        right_panel.style.display = "none";
-        bottom_row.style.display = "none";
-      } else {
-        right_panel.style.display = display_right_panel;
-        bottom_row.style.display = display_bottom_row;
-      }
-    }
-
-    function onLoad() {
-      document.getElementById("file_open").addEventListener("change", openFile, false);
-      var right_panel = document.getElementById("right_panel");
-      var bottom_row = document.getElementById("bottom_row");
-      display_right_panel = right_panel.style.display;
-      display_bottom_row = bottom_row.style.display;
-      updateOverviewDetails();
-      updateFilterList();
-    }
-
-    // When the module loads, begin running the application.
-    function moduleDidLoad() {
-      skia_module = document.getElementById("skia_nacl");
-      sendMsg("init");
-    }
-
-    function handleMessage(message_event) {
-      var cmd_skdebugf = "SkDebugf:";
-      var cmd_clear_commands = "ClearCommands";
-      var cmd_add_command = "AddCommand:";
-      var cmd_update_commands = "UpdateCommands";
-      var cmd_set_overview = "SetOverview:";
-      var cmd_add_filter_option = "AddFilterOption";
-      if (message_event.data.indexOf(cmd_skdebugf) == 0) {
-        var msg_contents = message_event.data.slice(cmd_skdebugf.length)
-        console.log("Skia: " + msg_contents);
-      } else if (message_event.data.indexOf(cmd_clear_commands) == 0) {
-        command_list = [];
-        command_types = {};
-        updateCommandList();
-        updateFilterList();
-      } else if (message_event.data.indexOf(cmd_add_command) == 0) {
-        var command = message_event.data.slice(cmd_add_command.length);
-        command_list.push(command);
-        if (command_types[command] == undefined) {
-          command_types[command] = 1;
-        } else {
-          command_types[command]++;
-        }
-      } else if (message_event.data.indexOf(cmd_update_commands) == 0) {
-        updateCommandList();
-        updateFilterList();
-      } else if (message_event.data.indexOf(cmd_set_overview) == 0) {
-        overview_text = message_event.data.slice(cmd_set_overview.length);
-        document.getElementById("overview_radio").checked = true;
-        updateOverviewDetails();
-      } else {
-        alert(message_event.data);
-      }
-    }
-
-    // Send a message to the plugin.
-    function sendMsg(msg) {
-      if (skia_module) {
-        //console.log("Sending msg:" + msg);
-        skia_module.postMessage(msg);
-      } else {
-        alert("The Skia module has not properly loaded...");
-      }
-    }
-
-    function commandSelected() {
-      var command_list = document.getElementById("command_list");
-      var selected_index = command_list.options[command_list.selectedIndex].value;
-      if (selected_index >= 0) {
-        sendMsg("CommandSelected:" + selected_index);
-      }
-    }
-
-    function rewind() {
-      command_list.selectedIndex = 0;
-      sendMsg("Rewind");
-    }
-
-    function stepBack() {
-      if (command_list.selectedIndex > 0) {
-        command_list.selectedIndex = command_list.selectedIndex - 1;
-      }
-      sendMsg("StepBack");
-    }
-
-    function pause() {
-      sendMsg("Pause");
-    }
-
-    function stepForward() {
-      if (command_list.selectedIndex < command_list.length - 1) {
-        command_list.selectedIndex = command_list.selectedIndex + 1;
-      }
-      sendMsg("StepForward");
-    }
-
-    function play() {
-      command_list.selectedIndex = command_list.length - 1;
-      sendMsg("Play");
-    }
-  </script>
-</head>
-<body onLoad="javascript:onLoad()">
-<div id="content" class="row-set">
-  <div id="menu" class="row">
-    <ul id="menu-bar" class="dropdown-menu">
-      <li><a href="#">File</a>
-        <ul>
-          <li><a href="#" onClick="javascript:openFileDialog()">Open</a></li>
-          <li><a href="#">Save</a></li>
-          <li><a href="#">Save As</a></li>
-          <li><a href="#">Exit</a></li>
-        </ul>
-      </li>
-      <li><a href="#">Edit</a>
-        <ul>
-          <li><a href="#">Delete Command</a></li>
-          <li><a href="#">Clear Deletes</a></li>
-          <li><a href="#">Set Breakpoint</a></li>
-          <li><a href="#">Clear Breakpoints</a></li>
-        </ul>
-      </li>
-      <li><a href="#">View</a>
-        <ul>
-          <li><a href="#">Breakpoints</a></li>
-          <li><a href="#">Deleted Commands</a></li>
-          <li><a href="#">Zoom In</a></li>
-          <li><a href="#">Zoom Out</a></li>
-        </ul>
-      </li>
-      <li><a href="#">Navigate</a>
-        <ul>
-          <li><a href="#" onClick="javascript:rewind()">Rewind</a></li>
-          <li><a href="#" onClick="javascript:stepBack()">Step Back</a></li>
-          <li><a href="#" onClick="javascript:stepForward()">Step Forward</a></li>
-          <li><a href="#" onClick="javascript:play()">Play</a></li>
-          <li><a href="#" onClick="javascript:pause()">Pause</a></li>
-          <li><a href="#">Go to Line...</a></li>
-        </ul>
-      </li>
-      <li><a href="#">Window</a>
-        <ul>
-          <li><a href="#">Inspector</a></li>
-          <li><a href="#">Directory</a></li>
-        </ul>
-      </li>
-    </ul>
-  </div>
-  <div id="buttons" class="row">
-    <div class="column-set">
-      <div class="column">
-        <button onClick="javascript:rewind()"><img src="icons/rewind.png"/><br/>Rewind</button>
-        <button onClick="javascript:stepBack()"><img src="icons/previous.png"/><br/>Step Back</button>
-        <button onClick="javascript:pause()"><img src="icons/pause.png"/><br/>Pause</button>
-        <button onClick="javascript:stepForward()"><img src="icons/next.png"/><br/>Step Forward</button>
-        <button onClick="javascript:play()"><img src="icons/play.png"/><br/>Play</button>
-      </div>
-      <div class="column">
-        <button onClick="javascript:toggleInspector()"><img src="icons/inspector.png"/><br/>Inspector</button>
-      </div>
-      <div class="column">
-        <button><img src="icons/profile.png"/><br/>Profile</button>
-      </div>
-      <div class="column" style="text-align:right; vertical-align:middle;">
-        <select id="command_filter" onChange="javascript:updateCommandList(this.options[this.selectedIndex].value)"></select>
-        <button onClick="javascript:updateCommandList()"><img src="icons/reload.png" /><br/>Clear Filter</button>
-      </div>
-    </div>
-  </div>
-  <div class="row">
-    <div class="column-set">
-      <div id="left_column" class="column">
-        <div class="row-set">
-          <div id="command_list_div" class="row">
-            <form id="command_list_form">
-              <select id="command_list" size="2" onChange="javascript:commandSelected()">
-                <option value="-1">Commands go here...</option>
-              </select>
-            </form>
-          </div>
-        </div>
-      </div>
-      <div id="right_column" class="row-set">
-        <div id="top_row" class="row">
-          <div id="display_pane" class="column">
-            <div id="listener" style="width:100%; height:100%;">
-              <script type="text/javascript">
-                var listener = document.getElementById('listener');
-                listener.addEventListener('load', moduleDidLoad, true);
-                listener.addEventListener('message', handleMessage, true);
-              </script>
-              <embed name="nacl_module"
-                 id="skia_nacl"
-                 src="debugger.nmf"
-                 type="application/x-nacl"
-                 width="100%"
-                 height="100%"
-                 style="width:100%, height:100%;"/>
-            </div>
-          </div>
-          <div id="right_panel" class="column">
-            <div class="thin_outline">
-              <div id="visibility_filter" class="settings_block">
-                Visibility Filter<br/>
-                <div class="thin_outline">
-                  <form id="visibility_filter_form">
-                    <input type="radio" name="visibility_filter_radio" value="on">On<br/>
-                    <input type="radio" name="visibility_filter_radio" value="off" checked>Off
-                  </form>
-                </div>
-              </div>
-              <div id="command_scrolling" class="settings_block">
-                Command Scrolling Preferences<br/>
-                <div class="thin_outline">
-                  <div class="row-set">
-                    <div class="row">
-                      <div class="column-set">
-                        <div class="column">
-                          Current Command:
-                        </div>
-                        <div class="column" style="text-align:right; width:35%;">
-                          <input type="text" style="width:100%;"/>
-                        </div>
-                      </div>
-                    </div>
-                    <div class="row">
-                      <div class="column-set">
-                        <div class="column">
-                          Command HitBox:
-                        </div>
-                        <div class="column" style="text-align:right; width:35%;">
-                          <input type="text" style="width:100%;"/>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-              </div>
-              <div id="render_targets" class="settings_block">
-                Render Targets<br/>
-                <div class="thin_outline">
-                  <form id="render_targets_form">
-                    <div class="row-set">
-                      <div class="row">
-                        <div class="column-set">
-                          <div class="column">Raster:</div>
-                          <div class="column" style="text-align:right;">
-                            <input type="checkbox" name="render_targets_checkbox" value="raster" checked/>
-                          </div>
-                        </div>
-                      </div>
-                      <div class="row">
-                        <div class="column-set">
-                          <div class="column" style="padding-left:30px;">Overdraw Viz:</div>
-                          <div class="column" style="text-align:right;">
-                            <input type="checkbox" name="render_targets_checkbox" value="overdraw"/>
-                          </div>
-                        </div>
-                      </div>
-                      <div class="row">
-                        <div class="column-set">
-                          <div class="column">OpenGL</div>
-                          <div class="column" style="text-align:right;">
-                            <input type="checkbox" name="render_targets_checkbox" value="opengl"/>
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                  </form>
-                </div>
-              </div>
-              <div id="zoom_level" class="settings_block">
-                <div class="thin_outline">
-                  <div class="row-set">
-                    <div class="row">
-                      <div class="column-set">
-                        <div class="column">
-                          Zoom Level:
-                        </div>
-                        <div class="column" style="text-align:right; width:35%;">
-                          <input type="text" style="width:100%;"/>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-              </div>
-            </div>
-            <div id="small_window_wrapper" class="settings_block">
-              <div class="thin_outline" style="padding:0px;">
-                <div id="small_window">
-                </div>
-              </div>
-            </div>
-          </div>
-        </div>
-        <div id="bottom_row" class="row">
-          <div id="tabview" class="column">
-            <div class="row-set">
-              <div class="row" style="height:5px; overflow:auto;">
-                <form id="overviewdetails_form">
-                  <input type="radio" name="overviewdetails_radio" onChange="javascript:updateOverviewDetails()" id="overview_radio" value="overview" checked>Overview
-                  <input type="radio" name="overviewdetails_radio" onChange="javascript:updateOverviewDetails()" id="details_radio" value="details">Details
-                </form>
-              </div>
-              <div class="row">
-                <div id="overviewdetails"></div>
-              </div>
-            </div>
-          </div>
-          <div id="matrixclip" class="column">
-            Current Matrix
-            <table>
-              <tr>
-                <td><input type="text" id="matrix00" class="matrix" /></td>
-                <td><input type="text" id="matrix01" class="matrix" /></td>
-                <td><input type="text" id="matrix02" class="matrix" /></td>
-              </tr>
-              <tr>
-                <td><input type="text" id="matrix10" class="matrix" /></td>
-                <td><input type="text" id="matrix11" class="matrix" /></td>
-                <td><input type="text" id="matrix12" class="matrix" /></td>
-              </tr>
-              <tr>
-                <td><input type="text" id="matrix20" class="matrix" /></td>
-                <td><input type="text" id="matrix21" class="matrix" /></td>
-                <td><input type="text" id="matrix22" class="matrix" /></td>
-              </tr>
-            </table>
-            Current Clip
-            <table>
-              <tr>
-                <td><input type="text" id="clip00" class="matrix" /></td>
-                <td><input type="text" id="clip01" class="matrix" /></td>
-              </tr>
-              <tr>
-                <td><input type="text" id="clip10" class="matrix" /></td>
-                <td><input type="text" id="clip11" class="matrix" /></td>
-              </tr>
-            </table>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</div>
-<input type="file" id="file_open" style="display:none;"/>
-</body>
-</html>
diff --git a/platform_tools/nacl/favicon.ico b/platform_tools/nacl/favicon.ico
deleted file mode 100644
index e7440c7..0000000
--- a/platform_tools/nacl/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/platform_tools/nacl/httpd.py b/platform_tools/nacl/httpd.py
deleted file mode 100755
index 2ad1d49..0000000
--- a/platform_tools/nacl/httpd.py
+++ /dev/null
@@ -1,212 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 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.
-
-"""A tiny web server.
-
-This is intended to be used for testing, and only run from within the examples
-directory.
-"""
-
-import BaseHTTPServer
-import logging
-import optparse
-import os
-import SimpleHTTPServer
-import SocketServer
-import sys
-import urlparse
-
-
-EXAMPLE_PATH=os.path.dirname(os.path.abspath(__file__))
-NACL_SDK_ROOT = os.getenv('NACL_SDK_ROOT', os.path.dirname(EXAMPLE_PATH))
-
-
-if os.path.exists(NACL_SDK_ROOT):
-  sys.path.append(os.path.join(NACL_SDK_ROOT, 'tools'))
-  import decode_dump
-  import getos
-else:
-  NACL_SDK_ROOT=None
-
-last_nexe = None
-last_nmf = None
-
-logging.getLogger().setLevel(logging.INFO)
-
-# Using 'localhost' means that we only accept connections
-# via the loop back interface.
-SERVER_PORT = 5103
-SERVER_HOST = ''
-
-# We only run from the examples directory so that not too much is exposed
-# via this HTTP server.  Everything in the directory is served, so there should
-# never be anything potentially sensitive in the serving directory, especially
-# if the machine might be a multi-user machine and not all users are trusted.
-# We only serve via the loopback interface.
-def SanityCheckDirectory():
-  httpd_path = os.path.abspath(os.path.dirname(__file__))
-  serve_path = os.path.abspath(os.getcwd())
-
-  # Verify we are serving from the directory this script came from, or bellow
-  if serve_path[:len(httpd_path)] == httpd_path:
-    return
-  logging.error('For security, httpd.py should only be run from within the')
-  logging.error('example directory tree.')
-  logging.error('We are currently in %s.' % serve_path)
-  sys.exit(1)
-
-
-# An HTTP server that will quit when |is_running| is set to False.  We also use
-# SocketServer.ThreadingMixIn in order to handle requests asynchronously for
-# faster responses.
-class QuittableHTTPServer(SocketServer.ThreadingMixIn,
-                          BaseHTTPServer.HTTPServer):
-  def serve_forever(self, timeout=0.5):
-    self.is_running = True
-    self.timeout = timeout
-    while self.is_running:
-      self.handle_request()
-
-  def shutdown(self):
-    self.is_running = False
-    return 1
-
-
-# "Safely" split a string at |sep| into a [key, value] pair.  If |sep| does not
-# exist in |str|, then the entire |str| is the key and the value is set to an
-# empty string.
-def KeyValuePair(str, sep='='):
-  if sep in str:
-    return str.split(sep)
-  else:
-    return [str, '']
-
-
-# A small handler that looks for '?quit=1' query in the path and shuts itself
-# down if it finds that parameter.
-class QuittableHTTPHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
-  def send_head(self):
-    """Common code for GET and HEAD commands.
-
-    This sends the response code and MIME headers.
-
-    Return value is either a file object (which has to be copied
-    to the outputfile by the caller unless the command was HEAD,
-    and must be closed by the caller under all circumstances), or
-    None, in which case the caller has nothing further to do.
-
-    """
-    path = self.translate_path(self.path)
-    f = None
-    if os.path.isdir(path):
-        if not self.path.endswith('/'):
-            # redirect browser - doing basically what apache does
-            self.send_response(301)
-            self.send_header("Location", self.path + "/")
-            self.end_headers()
-            return None
-        for index in "index.html", "index.htm":
-            index = os.path.join(path, index)
-            if os.path.exists(index):
-                path = index
-                break
-        else:
-            return self.list_directory(path)
-    ctype = self.guess_type(path)
-    try:
-        # Always read in binary mode. Opening files in text mode may cause
-        # newline translations, making the actual size of the content
-        # transmitted *less* than the content-length!
-        f = open(path, 'rb')
-    except IOError:
-        self.send_error(404, "File not found")
-        return None
-    self.send_response(200)
-    self.send_header("Content-type", ctype)
-    fs = os.fstat(f.fileno())
-    self.send_header("Content-Length", str(fs[6]))
-    self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
-    self.send_header('Cache-Control','no-cache, must-revalidate')
-    self.send_header('Expires','-1')
-    self.end_headers()
-    return f
-
-  def do_GET(self):
-    global last_nexe, last_nmf
-    (_, _, path, query, _) = urlparse.urlsplit(self.path)
-    url_params = dict([KeyValuePair(key_value)
-                      for key_value in query.split('&')])
-    if 'quit' in url_params and '1' in url_params['quit']:
-      self.send_response(200, 'OK')
-      self.send_header('Content-type', 'text/html')
-      self.send_header('Content-length', '0')
-      self.end_headers()
-      self.server.shutdown()
-      return
-
-    if path.endswith('.nexe'):
-      last_nexe = path
-    if path.endswith('.nmf'):
-      last_nmf = path
-
-    SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
-
-  def do_POST(self):
-    (_, _,path, query, _) = urlparse.urlsplit(self.path)
-    if 'Content-Length' in self.headers:
-      if not NACL_SDK_ROOT:
-        self.wfile('Could not find NACL_SDK_ROOT to decode trace.')
-        return
-      data = self.rfile.read(int(self.headers['Content-Length']))
-      nexe = '.' + last_nexe
-      nmf = '.' + last_nmf
-      addr = os.path.join(NACL_SDK_ROOT, 'toolchain', 
-                          getos.GetPlatform() + '_x86_newlib', 
-                          'bin', 'x86_64-nacl-addr2line')
-      decoder = decode_dump.CoreDecoder(nexe, nmf, addr, None, None)
-      info = decoder.Decode(data)
-      trace = decoder.StackTrace(info)
-      decoder.PrintTrace(trace, sys.stdout)
-      decoder.PrintTrace(trace, self.wfile)
-
-
-def Run(server_address,
-        server_class=QuittableHTTPServer,
-        handler_class=QuittableHTTPHandler):
-  httpd = server_class(server_address, handler_class)
-  logging.info("Starting local server on port %d", server_address[1])
-  logging.info("To shut down send http://localhost:%d?quit=1",
-               server_address[1])
-  try:
-    httpd.serve_forever()
-  except KeyboardInterrupt:
-    logging.info("Received keyboard interrupt.")
-    httpd.server_close()
-
-  logging.info("Shutting down local server on port %d", server_address[1])
-
-
-def main():
-  usage_str = "usage: %prog [options] [optional_portnum]"
-  parser = optparse.OptionParser(usage=usage_str)
-  parser.add_option(
-    '--no_dir_check', dest='do_safe_check',
-    action='store_false', default=True,
-    help='Do not ensure that httpd.py is being run from a safe directory.')
-  (options, args) = parser.parse_args(sys.argv)
-  if options.do_safe_check:
-    SanityCheckDirectory()
-  if len(args) > 2:
-    print 'Too many arguments specified.'
-    parser.print_help()
-  elif len(args) == 2:
-    Run((SERVER_HOST, int(args[1])))
-  else:
-    Run((SERVER_HOST, SERVER_PORT))
-  return 0
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/platform_tools/nacl/index.html b/platform_tools/nacl/index.html
deleted file mode 100644
index 4d87f3e..0000000
--- a/platform_tools/nacl/index.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <!--
-  Copyright 2013 Google Inc.
-
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-  -->
-<head>
-  <title>Skia Native Client Apps</title>
-</head>
-<body>
-  <h1>Skia Native Client Apps</h1>
-  <p>
-    <ul>
-      <li><a href="skhello">Skia Hello World</a></li>
-      <li><a href="tests">Skia Unit Tests</a></li>
-      <li><a href="debugger">Skia Debugger</a></li>
-      <li><a href="SampleApp">Skia Sample App</a></li>
-    </ul>
-  </p>
-</body>
-</html>
diff --git a/platform_tools/nacl/nacl_make b/platform_tools/nacl/nacl_make
deleted file mode 100755
index 0109a2c..0000000
--- a/platform_tools/nacl/nacl_make
+++ /dev/null
@@ -1,115 +0,0 @@
-#!/bin/bash
-
-function exportVar {
-  NAME=$1
-  VALUE=$2
-  echo export $NAME=\"$VALUE\"
-  export $NAME="$VALUE"
-}
-
-function setenv {
-  if [ -z "$1" ]; then
-    echo "ERROR: setenv() requires one argument."
-    exit 1
-  fi
-  if [ -z "${NACL_SDK_ROOT}" ]; then
-    echo "ERROR: This script requires NACL_SDK_ROOT to be set."
-    exit 1
-  fi
-
-  ARCH_WIDTH=$1
-  if [ ${ARCH_WIDTH} = "32" ]; then
-    ARCH_TYPE=x86
-    CROSS_ID=i686
-  elif [ ${ARCH_WIDTH} = "64" ]; then
-    ARCH_TYPE=x86_64
-    CROSS_ID=x86_64
-  else
-    echo "ERROR: Unknown arch width: ${ARCH_WIDTH}"
-    exit 1
-  fi
-
-  OS_NAME=$(uname -s)
-  if [ $OS_NAME = "Darwin" ]; then
-    OS_SUBDIR="mac"
-    OS_SUBDIR_SHORT="mac"
-    OS_JOBS=4
-  elif [ $OS_NAME = "Linux" ]; then
-    OS_SUBDIR="linux"
-    OS_SUBDIR_SHORT="linux"
-    OS_JOBS=4
-  else
-    OS_SUBDIR="windows"
-    OS_SUBDIR_SHORT="win"
-    OS_JOBS=1
-  fi
-
-  NACL_TOOLCHAIN_ROOT=${NACL_SDK_ROOT}/toolchain/${OS_SUBDIR_SHORT}_x86_newlib
-  NACL_BIN_PATH=${NACL_TOOLCHAIN_ROOT}/bin
-  export NACL_CROSS_PREFIX=${CROSS_ID}-nacl
-
-  if [[ -z "$NACL_MAKE_CCACHE" ]]; then
-    exportVar NACLCC ${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-gcc
-    exportVar NACLCXX ${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-g++
-  else
-    exportVar NACLCC "${NACL_MAKE_CCACHE} ${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-gcc"
-    exportVar NACLCXX "${NACL_MAKE_CCACHE} ${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-g++"
-  fi
-  exportVar NACLAR ${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-ar
-  exportVar NACLRANLIB ${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-ranlib
-  exportVar NACLLD ${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-ld
-  exportVar NACLSTRINGS ${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-strings
-  exportVar NACLSTRIP ${NACL_BIN_PATH}/${NACL_CROSS_PREFIX}-strip
-
-  exportVar CC "${NACLCC}"
-  exportVar CXX "${NACLCXX}"
-  exportVar AR "${NACLAR}"
-  exportVar RANLIB "${NACLRANLIB}"
-  exportVar PATH ${NACL_BIN_PATH}:${PATH};
-  exportVar C_INCLUDE_PATH "${NACL_SDK_ROOT}/include:${NACL_SDK_ROOT}/ports/include:${C_INCLUDE_PATH}"
-  exportVar CPLUS_INCLUDE_PATH "${NACL_SDK_ROOT}/include:${NACL_SDK_ROOT}/ports/include:${CPLUS_INCLUDE_PATH}"
-  exportVar GYP_DEFINES \
-      "skia_os=nacl skia_gpu=0 skia_arch_width=${ARCH_WIDTH} skia_arch_type=${ARCH_TYPE}"
-}
-
-function build {
-  if [ -z "$1" ]; then
-    echo "ERROR: build() requires one argument."
-    exit 1
-  fi
-  setenv $1
-
-  export SKIA_OUT=out/nacl$1
-  make ${MAKE_ARGS}
-}
-
-MAKE_ARGS=""
-
-while (( "$#" )); do
-  if [[ "$1" == "--use-ccache" ]];
-  then
-    if [[ -z "$NACL_MAKE_CCACHE" ]];
-    then
-      NACL_MAKE_CCACHE=$(which ccache)
-    fi
-  elif [ -z "${MAKE_ARGS}" ]; then
-    MAKE_ARGS="$1"
-  else
-    MAKE_ARGS="${MAKE_ARGS} $1"
-  fi
-  shift
-done
-
-if [[ -n "$NACL_MAKE_CCACHE" ]]; then
-  $NACL_MAKE_CCACHE --version &> /dev/null
-  if [[ "$?" != "0" ]]; then
-    echo "Unable to find ccache!"
-    exit 1
-  fi
-fi
-
-build 32 && \
-build 64 && \
-if ! [ -L platform_tools/nacl/out ]; then
-  ln -s ../../out platform_tools/nacl
-fi
diff --git a/platform_tools/nacl/skhello/index.html b/platform_tools/nacl/skhello/index.html
deleted file mode 100644
index 4119aed..0000000
--- a/platform_tools/nacl/skhello/index.html
+++ /dev/null
@@ -1,71 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <!--
-  Copyright 2013 Google Inc.
-
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-  -->
-<head>
-
-  <title>Skia Hello World</title>
-
-  <script type="text/javascript">
-    "use strict";
-
-    var SkiaModule = null;  // Global application object.
-
-    // Force a re-draw of the given element.
-    function refresh(elem) {
-      var old_display_style = elem.style.display;
-      elem.style.display = "none";
-      elem.style.display = old_display_style;
-    }
-
-    // When the module loads, begin running the application.
-    function moduleDidLoad() {
-      SkiaModule = document.getElementById("skia_nacl");
-      run();
-    }
-
-    function handleMessage(message_event) {
-      var skdebugf_cmd = "SkDebugf:";
-      if (message_event.data.indexOf(skdebugf_cmd) == 0) {
-        var msg_contents = message_event.data.slice(skdebugf_cmd.length)
-        console.log("Skia: " + msg_contents);
-      } else {
-        alert(message_event.data);
-      }
-    }
-
-    // Run the application.
-    function run() {
-      if (SkiaModule) {
-        var cmd = "init";
-        SkiaModule.postMessage(cmd);
-      } else {
-        alert("The Skia module has not properly loaded...");
-      }
-    }
-  </script>
-</head>
-<body>
-
-<h1>Skia Hello World</h1>
-<p>
-  <div id="listener">
-    <script type="text/javascript">
-      var listener = document.getElementById('listener');
-      listener.addEventListener('load', moduleDidLoad, true);
-      listener.addEventListener('message', handleMessage, true);
-    </script>
-
-    <embed name="nacl_module"
-       id="skia_nacl"
-       width=300 height=300
-       src="skhello.nmf"
-       type="application/x-nacl" />
-  </div>
-</p>
-</body>
-</html>
diff --git a/platform_tools/nacl/skhello/skhello.nmf b/platform_tools/nacl/skhello/skhello.nmf
deleted file mode 100644
index 3d6c02d..0000000
--- a/platform_tools/nacl/skhello/skhello.nmf
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "program": {
-    "x86-64": {"url": "../../out/nacl64/Debug/skhello"},
-    "x86-32": {"url": "../../out/nacl32/Debug/skhello"}
-  }
-}
diff --git a/platform_tools/nacl/src/nacl_debugger.cpp b/platform_tools/nacl/src/nacl_debugger.cpp
deleted file mode 100644
index e7e26b8..0000000
--- a/platform_tools/nacl/src/nacl_debugger.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-
-/*
- * 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 "ppapi/cpp/completion_callback.h"
-#include "ppapi/cpp/graphics_2d.h"
-#include "ppapi/cpp/image_data.h"
-#include "ppapi/cpp/instance.h"
-#include "ppapi/cpp/module.h"
-#include "ppapi/cpp/point.h"
-#include "ppapi/cpp/rect.h"
-#include "ppapi/cpp/var.h"
-
-#include "SkBase64.h"
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkColor.h"
-#include "SkDebugger.h"
-#include "SkGraphics.h"
-#include "SkStream.h"
-#include "SkString.h"
-
-class SkiaInstance;
-
-// Used by SkDebugf
-SkiaInstance* gPluginInstance;
-
-void FlushCallback(void* data, int32_t result);
-
-// Skia's subclass of pp::Instance, our interface with the browser.
-class SkiaInstance : public pp::Instance {
-public:
-    explicit SkiaInstance(PP_Instance instance)
-        : pp::Instance(instance)
-        , fCanvas(NULL)
-        , fFlushLoopRunning(false)
-        , fFlushPending(false)
-
-    {
-        gPluginInstance = this;
-        SkGraphics::Init();
-    }
-
-    virtual ~SkiaInstance() {
-        SkGraphics::Term();
-        gPluginInstance = NULL;
-    }
-
-    virtual void HandleMessage(const pp::Var& var_message) {
-        // Receive a message from javascript.
-        if (var_message.is_string()) {
-            SkString msg(var_message.AsString().c_str());
-            if (msg.startsWith("init")) {
-            } else if (msg.startsWith("LoadSKP")) {
-                size_t startIndex = strlen("LoadSKP");
-                size_t dataSize = msg.size()/sizeof(char) - startIndex;
-                SkBase64 decodedData;
-                decodedData.decode(msg.c_str() + startIndex, dataSize);
-                size_t decodedSize = 3 * (dataSize / 4);
-                SkDebugf("Got size: %d\n", decodedSize);
-                if (!decodedData.getData()) {
-                    SkDebugf("Failed to decode SKP\n");
-                    return;
-                }
-                SkMemoryStream pictureStream(decodedData.getData(), decodedSize);
-                SkPicture* picture = SkPicture::CreateFromStream(&pictureStream);
-                if (NULL == picture) {
-                    SkDebugf("Failed to create SKP.\n");
-                    return;
-                }
-                fDebugger.loadPicture(picture);
-                picture->unref();
-
-                // Set up the command list.
-                PostMessage("ClearCommands");
-                for (int i = 0; i < fDebugger.getSize(); ++i) {
-                    SkString addCommand("AddCommand:");
-                    addCommand.append(fDebugger.getDrawCommandAt(i)->toString());
-                    PostMessage(addCommand.c_str());
-                }
-                PostMessage("UpdateCommands");
-
-                // Set the overview text.
-                SkString overviewText;
-                fDebugger.getOverviewText(NULL, 0.0, &overviewText, 1);
-                overviewText.prepend("SetOverview:");
-                PostMessage(overviewText.c_str());
-
-                // Draw the SKP.
-                if (!fFlushLoopRunning) {
-                    Paint();
-                }
-            } else if (msg.startsWith("CommandSelected:")) {
-                size_t startIndex = strlen("CommandSelected:");
-                int index = atoi(msg.c_str() + startIndex);
-                fDebugger.setIndex(index);
-                if (!fFlushLoopRunning) {
-                    Paint();
-                }
-            } else if (msg.startsWith("Rewind")) {
-                fCanvas->clear(SK_ColorWHITE);
-                fDebugger.setIndex(0);
-                if (!fFlushLoopRunning) {
-                    Paint();
-                }
-            } else if (msg.startsWith("StepBack")) {
-                fCanvas->clear(SK_ColorWHITE);
-                int currentIndex = fDebugger.index();
-                if (currentIndex > 1) {
-                    fDebugger.setIndex(currentIndex - 1);
-                    if (!fFlushLoopRunning) {
-                        Paint();
-                    }
-                }
-            } else if (msg.startsWith("Pause")) {
-                // TODO(borenet)
-            } else if (msg.startsWith("StepForward")) {
-                int currentIndex = fDebugger.index();
-                if (currentIndex < fDebugger.getSize() -1) {
-                    fDebugger.setIndex(currentIndex + 1);
-                    if (!fFlushLoopRunning) {
-                        Paint();
-                    }
-                }
-            } else if (msg.startsWith("Play")) {
-                fDebugger.setIndex(fDebugger.getSize() - 1);
-                if (!fFlushLoopRunning) {
-                    Paint();
-                }
-            }
-        }
-    }
-
-    void Paint() {
-        if (!fImage.is_null()) {
-            fDebugger.draw(fCanvas);
-            fDeviceContext.PaintImageData(fImage, pp::Point(0, 0));
-            if (!fFlushPending) {
-                fFlushPending = true;
-                fDeviceContext.Flush(pp::CompletionCallback(&FlushCallback, this));
-            } else {
-                SkDebugf("A flush is pending... Skipping flush.\n");
-            }
-        } else {
-            SkDebugf("No pixels to write to!\n");
-        }
-    }
-
-    virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
-        if (position.size().width() == fWidth &&
-            position.size().height() == fHeight) {
-            return;  // We don't care about the position, only the size.
-        }
-        fWidth = position.size().width();
-        fHeight = position.size().height();
-
-        fDeviceContext = pp::Graphics2D(this, pp::Size(fWidth, fHeight), false);
-        if (!BindGraphics(fDeviceContext)) {
-            SkDebugf("Couldn't bind the device context\n");
-            return;
-        }
-        fImage = pp::ImageData(this,
-                               PP_IMAGEDATAFORMAT_BGRA_PREMUL,
-                               pp::Size(fWidth, fHeight), false);
-        const SkImageInfo info = SkImageInfo::MakeN32Premul(fWidth, fHeight);
-        fBitmap.installPixels(info, fImage.data(), info.minRowBytes());
-        if (fCanvas) {
-            delete fCanvas;
-        }
-        fCanvas = new SkCanvas(fBitmap);
-        fCanvas->clear(SK_ColorWHITE);
-        if (!fFlushLoopRunning) {
-            Paint();
-        }
-    }
-
-    void OnFlush() {
-        fFlushLoopRunning = true;
-        fFlushPending = false;
-        Paint();
-    }
-
-private:
-    pp::Graphics2D fDeviceContext;
-    pp::ImageData fImage;
-    int fWidth;
-    int fHeight;
-
-    SkBitmap fBitmap;
-    SkCanvas* fCanvas;
-    SkDebugger fDebugger;
-
-    bool fFlushLoopRunning;
-    bool fFlushPending;
-};
-
-void FlushCallback(void* data, int32_t result) {
-    static_cast<SkiaInstance*>(data)->OnFlush();
-}
-
-class SkiaModule : public pp::Module {
-public:
-    SkiaModule() : pp::Module() {}
-    virtual ~SkiaModule() {}
-
-    virtual pp::Instance* CreateInstance(PP_Instance instance) {
-        return new SkiaInstance(instance);
-    }
-};
-
-namespace pp {
-Module* CreateModule() {
-    return new SkiaModule();
-}
-}  // namespace pp
diff --git a/platform_tools/nacl/src/nacl_hello.cpp b/platform_tools/nacl/src/nacl_hello.cpp
deleted file mode 100644
index a091ab4..0000000
--- a/platform_tools/nacl/src/nacl_hello.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-
-/*
- * 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 "ppapi/cpp/completion_callback.h"
-#include "ppapi/cpp/graphics_2d.h"
-#include "ppapi/cpp/image_data.h"
-#include "ppapi/cpp/instance.h"
-#include "ppapi/cpp/module.h"
-#include "ppapi/cpp/point.h"
-#include "ppapi/cpp/rect.h"
-#include "ppapi/cpp/var.h"
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkColor.h"
-#include "SkGraphics.h"
-#include "SkStream.h"
-#include "SkString.h"
-
-class SkiaInstance;
-
-// Used by SkDebugf
-SkiaInstance* gPluginInstance;
-
-void FlushCallback(void* data, int32_t result);
-
-static void doDraw(SkCanvas* canvas, const SkPaint& paint, const char text[]) {
-    canvas->drawColor(SK_ColorWHITE);
-    SkPaint red;
-    red.setColor(SK_ColorRED);
-    canvas->drawCircle(150.0, 150.0, 100.0, red);
-    SkRect bounds;
-    canvas->getClipBounds(&bounds);
-    canvas->drawText(text, strlen(text),
-                     bounds.centerX(), bounds.centerY(),
-                     paint);
-}
-
-// Skia's subclass of pp::Instance, our interface with the browser.
-class SkiaInstance : public pp::Instance {
-public:
-    explicit SkiaInstance(PP_Instance instance)
-        : pp::Instance(instance)
-        , fCanvas(NULL)
-        , fFlushLoopRunning(false)
-        , fFlushPending(false)
-    {
-        gPluginInstance = this;
-        SkGraphics::Init();
-    }
-
-    virtual ~SkiaInstance() {
-        SkGraphics::Term();
-        gPluginInstance = NULL;
-    }
-
-    virtual void HandleMessage(const pp::Var& var_message) {
-        // Receive a message from javascript.
-    }
-
-    void Paint() {
-        if (!fImage.is_null()) {
-            SkPaint paint;
-            paint.setAntiAlias(true);
-            paint.setTextSize(SkIntToScalar(30));
-            paint.setTextAlign(SkPaint::kCenter_Align);
-            doDraw(fCanvas, paint, "Hello");
-
-            fDeviceContext.PaintImageData(fImage, pp::Point(0, 0));
-            if (!fFlushPending) {
-                fFlushPending = true;
-                fDeviceContext.Flush(pp::CompletionCallback(&FlushCallback, this));
-            } else {
-                SkDebugf("A flush is pending... Skipping flush.\n");
-            }
-        } else {
-            SkDebugf("No pixels to write to!\n");
-        }
-    }
-
-    virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
-        if (position.size().width() == fWidth &&
-            position.size().height() == fHeight) {
-            return;  // We don't care about the position, only the size.
-        }
-        fWidth = position.size().width();
-        fHeight = position.size().height();
-        fDeviceContext = pp::Graphics2D(this, pp::Size(fWidth, fHeight), false);
-        if (!BindGraphics(fDeviceContext)) {
-            SkDebugf("Couldn't bind the device context\n");
-            return;
-        }
-        fImage = pp::ImageData(this,
-                               PP_IMAGEDATAFORMAT_BGRA_PREMUL,
-                               pp::Size(fWidth, fHeight), false);
-        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, fWidth, fHeight);
-        fBitmap.setPixels(fImage.data());
-        if (fCanvas) {
-            delete fCanvas;
-        }
-        fCanvas = new SkCanvas(fBitmap);
-        fCanvas->clear(SK_ColorWHITE);
-        if (!fFlushLoopRunning) {
-            Paint();
-        }
-    }
-
-    void OnFlush() {
-        fFlushLoopRunning = true;
-        fFlushPending = false;
-        Paint();
-    }
-
-private:
-    pp::Graphics2D fDeviceContext;
-    pp::ImageData fImage;
-    int fWidth;
-    int fHeight;
-
-    SkBitmap fBitmap;
-    SkCanvas* fCanvas;
-
-    bool fFlushLoopRunning;
-    bool fFlushPending;
-};
-
-void FlushCallback(void* data, int32_t result) {
-    static_cast<SkiaInstance*>(data)->OnFlush();
-}
-
-class SkiaModule : public pp::Module {
-public:
-    SkiaModule() : pp::Module() {}
-    virtual ~SkiaModule() {}
-
-    virtual pp::Instance* CreateInstance(PP_Instance instance) {
-        return new SkiaInstance(instance);
-    }
-};
-
-namespace pp {
-Module* CreateModule() {
-    return new SkiaModule();
-}
-}  // namespace pp
diff --git a/platform_tools/nacl/src/nacl_interface.cpp b/platform_tools/nacl/src/nacl_interface.cpp
deleted file mode 100644
index 297458d..0000000
--- a/platform_tools/nacl/src/nacl_interface.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-
-/*
- * 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 "ppapi/cpp/completion_callback.h"
-#include "ppapi/cpp/graphics_2d.h"
-#include "ppapi/cpp/image_data.h"
-#include "ppapi/cpp/instance.h"
-#include "ppapi/cpp/module.h"
-#include "ppapi/cpp/var.h"
-
-#include "SkCanvas.h"
-#include "SkBitmap.h"
-#include "SkString.h"
-#include "SkThreadUtils.h"
-
-class SkiaInstance;
-
-// Used by SkDebugf
-SkiaInstance* gPluginInstance;
-
-// Main entry point for the app we're linked into
-extern int test_main();
-
-// Tokenize a command line and store it in argc and argv.
-void SkStringToProgramArgs(const SkString commandLine, int* argc, char*** argv) {
-    int numBreaks = 0;
-    const char* commandChars = commandLine.c_str();
-    for (size_t i = 0; i < strlen(commandChars); i++) {
-        if (isspace(commandChars[i])) {
-            numBreaks++;
-        }
-    }
-    int numArgs;
-    if (strlen(commandChars) > 0) {
-        numArgs = numBreaks + 1;
-    } else {
-        numArgs = 0;
-    }
-    *argc = numArgs;
-    *argv = new char*[numArgs + 1];
-    (*argv)[numArgs] = NULL;
-    char* start = (char*) commandChars;
-    int length = 0;
-    int argIndex = 0;
-    for (size_t i = 0; i < strlen(commandChars) + 1; i++) {
-        if (isspace(commandChars[i]) || '\0' == commandChars[i]) {
-            if (length > 0) {
-                char* argument = new char[length + 1];
-                memcpy(argument, start, length);
-                argument[length] = '\0';
-                (*argv)[argIndex++] = argument;
-            }
-            start = (char*) commandChars + i + 1;
-            length = 0;
-        } else {
-            length++;
-        }
-    }
-}
-
-// Run the program with the given command line.
-void RunProgram(const SkString& commandLine) {
-    int argc;
-    char** argv;
-    SkStringToProgramArgs(commandLine, &argc, &argv);
-    test_main();
-}
-
-
-// Skia's subclass of pp::Instance, our interface with the browser.
-class SkiaInstance : public pp::Instance {
-public:
-    explicit SkiaInstance(PP_Instance instance) : pp::Instance(instance) {
-        gPluginInstance = this;
-    }
-
-    virtual ~SkiaInstance() {
-        gPluginInstance = NULL;
-    }
-
-    virtual void HandleMessage(const pp::Var& var_message) {
-        // Receive a message from javascript.
-        if (var_message.is_string()) {
-            SkString msg(var_message.AsString().c_str());
-            if (msg.startsWith("init")) {
-                RunProgram(msg);
-            }
-        }
-    }
-};
-
-class SkiaModule : public pp::Module {
-public:
-    SkiaModule() : pp::Module() {}
-    virtual ~SkiaModule() {}
-
-    virtual pp::Instance* CreateInstance(PP_Instance instance) {
-        return new SkiaInstance(instance);
-    }
-};
-
-namespace pp {
-Module* CreateModule() {
-    return new SkiaModule();
-}
-}  // namespace pp
diff --git a/platform_tools/nacl/src/nacl_sample.cpp b/platform_tools/nacl/src/nacl_sample.cpp
deleted file mode 100644
index aca74f4..0000000
--- a/platform_tools/nacl/src/nacl_sample.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-#include <cstdio>
-#include <string>
-
-#include "ppapi/cpp/completion_callback.h"
-#include "ppapi/cpp/graphics_2d.h"
-#include "ppapi/cpp/image_data.h"
-#include "ppapi/cpp/instance.h"
-#include "ppapi/cpp/module.h"
-#include "ppapi/cpp/var.h"
-
-#include "SampleApp.h"
-#include "SkApplication.h"
-#include "SkCanvas.h"
-#include "SkBitmap.h"
-#include "SkEvent.h"
-#include "SkWindow.h"
-
-class SkiaInstance;
-
-namespace {
-void FlushCallback(void* data, int32_t result);
-}
-
-SkiaInstance* gPluginInstance;
-extern int main(int, char**);
-
-class SkiaInstance : public pp::Instance {
- public:
-    explicit SkiaInstance(PP_Instance instance) : pp::Instance(instance),
-                                                  fFlushPending(false),
-                                                  fGraphics2dContext(NULL),
-                                                  fPixelBuffer(NULL)
-    {
-        gPluginInstance = this;
-        application_init();
-        char* commandName = "SampleApp";
-        fWindow = new SampleWindow(NULL, 0, &commandName, NULL);
-    }
-
-    virtual ~SkiaInstance() {
-        gPluginInstance = NULL;
-        delete fWindow;
-        application_term();
-    }
-
-    virtual void HandleMessage(const pp::Var& var_message) {
-        // Receive a message from javascript.  Right now this just signals us to
-        // get started.
-        uint32_t width = 500;
-        uint32_t height = 500;
-        char buffer[2048];
-        sprintf(buffer, "SetSize:%d,%d", width, height);
-        PostMessage(buffer);
-    }
-
-    virtual void DidChangeView(const pp::Rect& position,
-                               const pp::Rect& clip) {
-        if (position.size().width() == width() &&
-            position.size().height() == height()) {
-            return;  // Size didn't change, no need to update anything.
-        }
-        // Create a new device context with the new size.
-        DestroyContext();
-        CreateContext(position.size());
-        // Delete the old pixel buffer and create a new one.
-        delete fPixelBuffer;
-        fPixelBuffer = NULL;
-        if (fGraphics2dContext != NULL) {
-            fPixelBuffer = new pp::ImageData(this,
-                                              PP_IMAGEDATAFORMAT_BGRA_PREMUL,
-                                              fGraphics2dContext->size(),
-                                              false);
-            fWindow->resize(position.size().width(), position.size().height());
-            fWindow->update(NULL);
-            paint();
-        }
-    }
-
-    // Indicate whether a flush is pending.  This can only be called from the
-    // main thread; it is not thread safe.
-    bool flush_pending() const {
-        return fFlushPending;
-    }
-    void set_flush_pending(bool flag) {
-        fFlushPending = flag;
-    }
-
-  void paint() {
-    if (fPixelBuffer) {
-      // Draw some stuff.  TODO(borenet): Actually have SampleApp draw into
-      // the plugin area.
-      uint32_t w = fPixelBuffer->size().width();
-      uint32_t h = fPixelBuffer->size().height();
-      uint32_t* data = (uint32_t*) fPixelBuffer->data();
-      // Create a bitmap using the fPixelBuffer pixels
-      SkBitmap bitmap;
-      bitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
-      bitmap.setPixels(data);
-      // Create a canvas with the bitmap as the backend
-      SkCanvas canvas(bitmap);
-
-      canvas.drawColor(SK_ColorBLUE);
-      SkRect rect = SkRect::MakeXYWH(10, 10, 80, 80);
-      SkPaint rect_paint;
-      rect_paint.setStyle(SkPaint::kFill_Style);
-      rect_paint.setColor(SK_ColorRED);
-      canvas.drawRect(rect, rect_paint);
-
-      FlushPixelBuffer();
-    }
-  }
-
-private:
-    int width() const {
-        return fPixelBuffer ? fPixelBuffer->size().width() : 0;
-    }
-
-    int height() const {
-        return fPixelBuffer ? fPixelBuffer->size().height() : 0;
-    }
-
-    bool IsContextValid() const {
-        return fGraphics2dContext != NULL;
-    }
-
-    void CreateContext(const pp::Size& size) {
-        if (IsContextValid())
-            return;
-        fGraphics2dContext = new pp::Graphics2D(this, size, false);
-        if (!BindGraphics(*fGraphics2dContext)) {
-            SkDebugf("Couldn't bind the device context");
-        }
-    }
-
-    void DestroyContext() {
-        if (!IsContextValid())
-            return;
-        delete fGraphics2dContext;
-        fGraphics2dContext = NULL;
-    }
-
-    void FlushPixelBuffer() {
-        if (!IsContextValid())
-            return;
-        // Note that the pixel lock is held while the buffer is copied into the
-        // device context and then flushed.
-        fGraphics2dContext->PaintImageData(*fPixelBuffer, pp::Point());
-        if (flush_pending())
-            return;
-        set_flush_pending(true);
-        fGraphics2dContext->Flush(pp::CompletionCallback(&FlushCallback, this));
-    }
-
-    bool fFlushPending;
-    pp::Graphics2D* fGraphics2dContext;
-    pp::ImageData* fPixelBuffer;
-    SampleWindow* fWindow;
-};
-
-class SkiaModule : public pp::Module {
-public:
-    SkiaModule() : pp::Module() {}
-    virtual ~SkiaModule() {}
-
-    virtual pp::Instance* CreateInstance(PP_Instance instance) {
-        gPluginInstance = new SkiaInstance(instance);
-        return gPluginInstance;
-    }
-};
-
-namespace {
-void FlushCallback(void* data, int32_t result) {
-    static_cast<SkiaInstance*>(data)->set_flush_pending(false);
-}
-}
-
-namespace pp {
-Module* CreateModule() {
-    return new SkiaModule();
-}
-}  // namespace pp
-
-
-///////////////////////////////////////////
-///////////// SkOSWindow impl /////////////
-///////////////////////////////////////////
-
-void SkOSWindow::onSetTitle(const char title[])
-{
-    char buffer[2048];
-    sprintf(buffer, "SetTitle:%s", title);
-    gPluginInstance->PostMessage(buffer);
-}
-
-void SkOSWindow::onHandleInval(const SkIRect& rect)
-{
-    gPluginInstance->paint();
-}
-
-void SkOSWindow::onPDFSaved(const char title[], const char desc[],
-                            const char path[]) {
-}
-
-///////////////////////////////////////////
-/////////////// SkEvent impl //////////////
-///////////////////////////////////////////
-
-void SkEvent::SignalQueueTimer(SkMSec ms) {
-}
-
-void SkEvent::SignalNonEmptyQueue() {
-}
diff --git a/platform_tools/nacl/tests/index.html b/platform_tools/nacl/tests/index.html
deleted file mode 100644
index 3a0bdb9..0000000
--- a/platform_tools/nacl/tests/index.html
+++ /dev/null
@@ -1,78 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <!--
-  Copyright 2013 Google Inc.
-
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-  -->
-<head>
-
-  <title>Skia Unit Tests</title>
-
-  <script type="text/javascript">
-    "use strict";
-
-    var SkiaModule = null;  // Global application object.
-
-    // Force a re-draw of the given element.
-    function refresh(elem) {
-      var old_display_style = elem.style.display;
-      elem.style.display = "none";
-      elem.style.display = old_display_style;
-    }
-
-    // When the module loads, begin running the application.
-    function moduleDidLoad() {
-      SkiaModule = document.getElementById("skia_nacl");
-      run();
-    }
-
-    function handleMessage(message_event) {
-      var skdebugf_cmd = "SkDebugf:";
-      if (message_event.data.indexOf(skdebugf_cmd) == 0) {
-        var msg_contents = message_event.data.slice(skdebugf_cmd.length)
-        //console.log("Skia: " + msg_contents);
-        var log_textarea = document.getElementById("log_textarea")
-        log_textarea.value += msg_contents;
-        log_textarea.scrollTop = log_textarea.scrollHeight;
-        refresh(log_textarea);
-      } else {
-        alert(message_event.data);
-      }
-    }
-
-    // Run the application.
-    function run() {
-      if (SkiaModule) {
-        var cmd = "init";
-        SkiaModule.postMessage(cmd);
-      } else {
-        alert("The Skia module has not properly loaded...");
-      }
-    }
-  </script>
-</head>
-<body>
-
-<h1>Skia Unit Tests</h1>
-<p>
-<textarea id="log_textarea" rows="2" cols="2" readonly style="width:100%; height:500px; resize:none;"></textarea>
-</p>
-<p>
-  <div id="listener">
-    <script type="text/javascript">
-      var listener = document.getElementById('listener');
-      listener.addEventListener('load', moduleDidLoad, true);
-      listener.addEventListener('message', handleMessage, true);
-    </script>
-
-    <embed name="nacl_module"
-       id="skia_nacl"
-       width=0 height=0
-       src="tests.nmf"
-       type="application/x-nacl" />
-  </div>
-</p>
-</body>
-</html>
diff --git a/platform_tools/nacl/tests/tests.nmf b/platform_tools/nacl/tests/tests.nmf
deleted file mode 100644
index 95db050..0000000
--- a/platform_tools/nacl/tests/tests.nmf
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "program": {
-    "x86-64": {"url": "../../out/nacl64/Debug/tests"},
-    "x86-32": {"url": "../../out/nacl32/Debug/tests"}
-  }
-}
diff --git a/resources/android_fonts/v22/fonts.xml b/resources/android_fonts/v22/fonts.xml
index 180d5f7..b9ea87f 100644
--- a/resources/android_fonts/v22/fonts.xml
+++ b/resources/android_fonts/v22/fonts.xml
@@ -2,7 +2,9 @@
 <familyset version="21">
     <!-- first font is default -->
     <family name="sans-serif">
-        <font weight="100" style="normal">Roboto-Thin.ttf</font>
+        <font weight="100" style="normal">
+            Roboto-Thin.ttf
+        </font>
         <font weight="100" style="italic">Roboto-ThinItalic.ttf</font>
         <font weight="300" style="normal">Roboto-Light.ttf</font>
         <font weight="300" style="italic">Roboto-LightItalic.ttf</font>
diff --git a/resources/color_wheel.ico b/resources/color_wheel.ico
new file mode 100644
index 0000000..fdfa381
--- /dev/null
+++ b/resources/color_wheel.ico
Binary files differ
diff --git a/resources/empty_images/zero-dims.gif b/resources/empty_images/zero-dims.gif
new file mode 100644
index 0000000..8b1b084
--- /dev/null
+++ b/resources/empty_images/zero-dims.gif
Binary files differ
diff --git a/resources/empty_images/zero-embedded.ico b/resources/empty_images/zero-embedded.ico
new file mode 100644
index 0000000..d6ba292
--- /dev/null
+++ b/resources/empty_images/zero-embedded.ico
Binary files differ
diff --git a/resources/empty_images/zero-height.bmp b/resources/empty_images/zero-height.bmp
new file mode 100644
index 0000000..b07834b
--- /dev/null
+++ b/resources/empty_images/zero-height.bmp
Binary files differ
diff --git a/resources/empty_images/zero-height.jpg b/resources/empty_images/zero-height.jpg
new file mode 100644
index 0000000..14231b4
--- /dev/null
+++ b/resources/empty_images/zero-height.jpg
Binary files differ
diff --git a/resources/empty_images/zero-height.png b/resources/empty_images/zero-height.png
new file mode 100644
index 0000000..7eae117
--- /dev/null
+++ b/resources/empty_images/zero-height.png
Binary files differ
diff --git a/resources/empty_images/zero-height.wbmp b/resources/empty_images/zero-height.wbmp
new file mode 100644
index 0000000..1748771
--- /dev/null
+++ b/resources/empty_images/zero-height.wbmp
Binary files differ
diff --git a/resources/empty_images/zero-width.bmp b/resources/empty_images/zero-width.bmp
new file mode 100644
index 0000000..2e8cb8a
--- /dev/null
+++ b/resources/empty_images/zero-width.bmp
Binary files differ
diff --git a/resources/empty_images/zero-width.jpg b/resources/empty_images/zero-width.jpg
new file mode 100644
index 0000000..d1dd997
--- /dev/null
+++ b/resources/empty_images/zero-width.jpg
Binary files differ
diff --git a/resources/empty_images/zero-width.png b/resources/empty_images/zero-width.png
new file mode 100644
index 0000000..30c12fe
--- /dev/null
+++ b/resources/empty_images/zero-width.png
Binary files differ
diff --git a/resources/empty_images/zero-width.wbmp b/resources/empty_images/zero-width.wbmp
new file mode 100644
index 0000000..0a6a675
--- /dev/null
+++ b/resources/empty_images/zero-width.wbmp
Binary files differ
diff --git a/resources/fonts/Distortable.ttf b/resources/fonts/Distortable.ttf
new file mode 100644
index 0000000..95f30c3
--- /dev/null
+++ b/resources/fonts/Distortable.ttf
Binary files differ
diff --git a/resources/Funkster.ttf b/resources/fonts/Funkster.ttf
similarity index 100%
rename from resources/Funkster.ttf
rename to resources/fonts/Funkster.ttf
Binary files differ
diff --git a/resources/fonts/ReallyBigA.ttf b/resources/fonts/ReallyBigA.ttf
new file mode 100644
index 0000000..088a560
--- /dev/null
+++ b/resources/fonts/ReallyBigA.ttf
Binary files differ
diff --git a/resources/test.ttc b/resources/fonts/test.ttc
similarity index 100%
rename from resources/test.ttc
rename to resources/fonts/test.ttc
Binary files differ
diff --git a/resources/google_chrome.ico b/resources/google_chrome.ico
new file mode 100644
index 0000000..7af91ee
--- /dev/null
+++ b/resources/google_chrome.ico
Binary files differ
diff --git a/resources/invalid_images/ico_fuzz0.ico b/resources/invalid_images/ico_fuzz0.ico
new file mode 100644
index 0000000..97efb89
--- /dev/null
+++ b/resources/invalid_images/ico_fuzz0.ico
Binary files differ
diff --git a/resources/invalid_images/ico_fuzz1.ico b/resources/invalid_images/ico_fuzz1.ico
new file mode 100644
index 0000000..5276123
--- /dev/null
+++ b/resources/invalid_images/ico_fuzz1.ico
Binary files differ
diff --git a/resources/invalid_images/ico_leak01.ico b/resources/invalid_images/ico_leak01.ico
new file mode 100644
index 0000000..2e28283
--- /dev/null
+++ b/resources/invalid_images/ico_leak01.ico
Binary files differ
diff --git a/resources/mandrill.wbmp b/resources/mandrill.wbmp
new file mode 100644
index 0000000..ac84598
--- /dev/null
+++ b/resources/mandrill.wbmp
Binary files differ
diff --git a/resources/test640x479.gif b/resources/test640x479.gif
new file mode 100644
index 0000000..a17cb08
--- /dev/null
+++ b/resources/test640x479.gif
Binary files differ
diff --git a/samplecode/ClockFaceView.cpp b/samplecode/ClockFaceView.cpp
index 73ea566..4717c30 100644
--- a/samplecode/ClockFaceView.cpp
+++ b/samplecode/ClockFaceView.cpp
@@ -32,7 +32,7 @@
 class SkGrayScaleColorFilter : public SkColorFilter {
 public:
     virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) const SK_OVERRIDE {
+                            SkPMColor result[]) const override {
         for (int i = 0; i < count; i++) {
             result[i] = rgb2gray(src[i]);
         }
@@ -46,7 +46,7 @@
     }
 
     virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) const SK_OVERRIDE {
+                            SkPMColor result[]) const override {
         SkPMColor mask = fMask;
         for (int i = 0; i < count; i++) {
             result[i] = src[i] & mask;
@@ -74,7 +74,7 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Dot2DPathEffect)
 
 protected:
-    void begin(const SkIRect& uvBounds, SkPath* dst) const SK_OVERRIDE {
+    void begin(const SkIRect& uvBounds, SkPath* dst) const override {
         if (fPts) {
             fPts->reset();
         }
@@ -82,14 +82,14 @@
     }
 
     virtual void next(const SkPoint& loc, int u, int v,
-                      SkPath* dst) const SK_OVERRIDE {
+                      SkPath* dst) const override {
         if (fPts) {
             *fPts->append() = loc;
         }
         dst->addCircle(loc.fX, loc.fY, fRadius);
     }
 
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
+    void flatten(SkWriteBuffer& buffer) const override {
         buffer.writeMatrix(this->getMatrix());
         buffer.writeScalar(fRadius);
     }
@@ -111,14 +111,14 @@
 public:
     InverseFillPE() {}
     virtual bool filterPath(SkPath* dst, const SkPath& src,
-                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE {
+                            SkStrokeRec*, const SkRect*) const override {
         *dst = src;
         dst->setFillType(SkPath::kInverseWinding_FillType);
         return true;
     }
 
 #ifndef SK_IGNORE_TO_STRING
-    void toString(SkString* str) const SK_OVERRIDE {
+    void toString(SkString* str) const override {
         str->appendf("InverseFillPE: ()");
     }
 #endif
diff --git a/samplecode/GMSampleView.h b/samplecode/GMSampleView.h
index f97ce92..4d29190 100644
--- a/samplecode/GMSampleView.h
+++ b/samplecode/GMSampleView.h
@@ -23,11 +23,11 @@
     static SkEvent* NewShowSizeEvt(bool doShowSize);
 
 protected:
-    bool onQuery(SkEvent*) SK_OVERRIDE;
-    bool onEvent(const SkEvent&) SK_OVERRIDE;
-    void onDrawContent(SkCanvas*) SK_OVERRIDE;
-    void onDrawBackground(SkCanvas*) SK_OVERRIDE;
-    bool onAnimate(const SkAnimTimer&) SK_OVERRIDE;
+    bool onQuery(SkEvent*) override;
+    bool onEvent(const SkEvent&) override;
+    void onDrawContent(SkCanvas*) override;
+    void onDrawBackground(SkCanvas*) override;
+    bool onAnimate(const SkAnimTimer&) override;
 
 private:
     GM* fGM;
diff --git a/samplecode/OverView.cpp b/samplecode/OverView.cpp
index bbf647a..bdb8ddd 100644
--- a/samplecode/OverView.cpp
+++ b/samplecode/OverView.cpp
@@ -48,8 +48,8 @@
     virtual ~OverView();
 
 protected:
-    bool onEvent(const SkEvent&) SK_OVERRIDE;
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onEvent(const SkEvent&) override;
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Overview");
             return true;
@@ -66,13 +66,13 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE;
+    void onDraw(SkCanvas* canvas) override;
 
-    bool onSendClickToChildren(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
+    bool onSendClickToChildren(SkScalar x, SkScalar y, unsigned modi) override {
         return false;
     }
 
-    Click* onFindClickHandler(SkScalar cx, SkScalar cy, unsigned modi) SK_OVERRIDE {
+    Click* onFindClickHandler(SkScalar cx, SkScalar cy, unsigned modi) override {
         const SkRect crect = SkRect::MakeXYWH(cx - 0.5f, cy - 0.5f, 1, 1);
         SkPoint loc = this->start();
         for (int i = 0; i < fCount; ++i) {
diff --git a/samplecode/Sample2PtRadial.cpp b/samplecode/Sample2PtRadial.cpp
index 073fd82..764c2b1 100644
--- a/samplecode/Sample2PtRadial.cpp
+++ b/samplecode/Sample2PtRadial.cpp
@@ -11,15 +11,15 @@
 #include "SkGradientShader.h"
 
 
-class TwoPtRadialView : public SampleView {
+class TwoPtConicalView : public SampleView {
 public:
-    TwoPtRadialView() {}
+    TwoPtConicalView() {}
 
 protected:
     // overrides from SkEventSink
     virtual bool onQuery(SkEvent* evt) {
         if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "2PtRadial");
+            SampleCode::TitleR(evt, "2PtConical");
             return true;
         }
         return this->INHERITED::onQuery(evt);
@@ -33,7 +33,7 @@
         SkScalar r0 = 100;
         SkPoint c1 = { 100, 100 };
         SkScalar r1 = 100;
-        SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
+        SkShader* s = SkGradientShader::CreateTwoPointConical(c0, r0, c1, r1, colors,
                                                              NULL, 2,
                                                              SkShader::kClamp_TileMode);
 
@@ -48,5 +48,5 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-static SkView* MyFactory() { return new TwoPtRadialView; }
+static SkView* MyFactory() { return new TwoPtConicalView; }
 static SkViewRegister reg(MyFactory);
diff --git a/samplecode/SampleAll.cpp b/samplecode/SampleAll.cpp
index 16c79c9..9a39ef9 100644
--- a/samplecode/SampleAll.cpp
+++ b/samplecode/SampleAll.cpp
@@ -53,7 +53,7 @@
 class SkGrayScaleColorFilter : public SkColorFilter {
 public:
     virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) const SK_OVERRIDE {
+                            SkPMColor result[]) const override {
         for (int i = 0; i < count; i++)
             result[i] = rgb2gray(src[i]);
     }
@@ -66,7 +66,7 @@
     }
 
     virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) const SK_OVERRIDE {
+                            SkPMColor result[]) const override {
         SkPMColor mask = fMask;
         for (int i = 0; i < count; i++) {
             result[i] = src[i] & mask;
@@ -167,11 +167,11 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Dot2DPathEffect)
 
 protected:
-    void next(const SkPoint& loc, int u, int v, SkPath* dst) const SK_OVERRIDE {
+    void next(const SkPoint& loc, int u, int v, SkPath* dst) const override {
         dst->addCircle(loc.fX, loc.fY, fRadius);
     }
 
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
+    void flatten(SkWriteBuffer& buffer) const override {
         this->INHERITED::flatten(buffer);
         buffer.writeScalar(fRadius);
     }
@@ -393,7 +393,7 @@
         canvas->save();
         canvas->translate(SkIntToScalar(0), SkIntToScalar(5));
         paint.setAntiAlias(true);
-        paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        paint.setFilterQuality(kLow_SkFilterQuality);
         // !!! draw through a clip
         paint.setColor(SK_ColorLTGRAY);
         paint.setStyle(SkPaint::kFill_Style);
diff --git a/samplecode/SampleAnimBlur.cpp b/samplecode/SampleAnimBlur.cpp
index 3c0be83..98a3560 100644
--- a/samplecode/SampleAnimBlur.cpp
+++ b/samplecode/SampleAnimBlur.cpp
@@ -28,7 +28,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "AnimBlur");
             return true;
@@ -36,7 +36,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         static const SkBlurStyle gStyles[] = {
             kNormal_SkBlurStyle,
             kInner_SkBlurStyle,
@@ -59,7 +59,7 @@
         }
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         fBlurSigma = get_anim_sin(timer.secs(), 100, 4, 5);
         fCircleRadius = 3 + get_anim_sin(timer.secs(), 150, 25, 3);
         return true;
diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp
index 2b31830..04b1cb0 100644
--- a/samplecode/SampleApp.cpp
+++ b/samplecode/SampleApp.cpp
@@ -15,6 +15,7 @@
 #include "SkCanvas.h"
 #include "SkCommandLineFlags.h"
 #include "SkData.h"
+#include "SkDeferredCanvas.h"
 #include "SkDevice.h"
 #include "SkDocument.h"
 #include "SkGPipe.h"
@@ -48,7 +49,7 @@
     SkString fFilename;
 public:
     PictFileFactory(const SkString& filename) : fFilename(filename) {}
-    SkView* operator() () const SK_OVERRIDE {
+    SkView* operator() () const override {
         return CreateSamplePictFileView(fFilename.c_str());
     }
 };
@@ -60,7 +61,7 @@
     SkString fFilename;
 public:
     PdfFileViewerFactory(const SkString& filename) : fFilename(filename) {}
-    SkView* operator() () const SK_OVERRIDE {
+    SkView* operator() () const override {
         return CreateSamplePdfFileViewer(fFilename.c_str());
     }
 };
@@ -181,10 +182,9 @@
 
 #if SK_SUPPORT_GPU
         switch (win->getDeviceType()) {
-            case kRaster_DeviceType:
-                // fallthrough
-            case kPicture_DeviceType:
-                // fallthrough
+            case kRaster_DeviceType:    // fallthrough
+            case kPicture_DeviceType:    // fallthrough
+            case kDeferred_DeviceType:    // fallthrough
             case kGPU_DeviceType:
                 // all these guys use the native backend
                 fBackend = kNativeGL_BackEndType;
@@ -210,10 +210,9 @@
         SkASSERT(NULL == fCurIntf);
         SkAutoTUnref<const GrGLInterface> glInterface;
         switch (win->getDeviceType()) {
-            case kRaster_DeviceType:
-                // fallthrough
-            case kPicture_DeviceType:
-                // fallthrough
+            case kRaster_DeviceType:    // fallthrough
+            case kPicture_DeviceType:   // fallthrough
+            case kDeferred_DeviceType:  // fallthrough
             case kGPU_DeviceType:
                 // all these guys use the native interface
                 glInterface.reset(GrGLCreateNativeInterface());
@@ -270,7 +269,7 @@
     }
 
     virtual SkSurface* createSurface(SampleWindow::DeviceType dType,
-                                     SampleWindow* win) SK_OVERRIDE {
+                                     SampleWindow* win) override {
 #if SK_SUPPORT_GPU
         if (IsGpuDeviceType(dType) && fCurContext) {
             SkSurfaceProps props(win->getSurfaceProps());
@@ -324,7 +323,7 @@
 #endif
     }
 
-    GrRenderTarget* getGrRenderTarget() SK_OVERRIDE {
+    GrRenderTarget* getGrRenderTarget() override {
 #if SK_SUPPORT_GPU
         return fCurRenderTarget;
 #else
@@ -451,30 +450,30 @@
     {SkPaint::kFull_Hinting, "Full", "Hf " },
 };
 
-struct FilterLevelState {
-    SkPaint::FilterLevel    fLevel;
-    const char*             fName;
-    const char*             fLabel;
+struct FilterQualityState {
+    SkFilterQuality fQuality;
+    const char*     fName;
+    const char*     fLabel;
 };
-static FilterLevelState gFilterLevelStates[] = {
-    { SkPaint::kNone_FilterLevel,   "Mixed",    NULL    },
-    { SkPaint::kNone_FilterLevel,   "None",     "F0 "   },
-    { SkPaint::kLow_FilterLevel,    "Low",      "F1 "   },
-    { SkPaint::kMedium_FilterLevel, "Medium",   "F2 "   },
-    { SkPaint::kHigh_FilterLevel,   "High",     "F3 "   },
+static FilterQualityState gFilterQualityStates[] = {
+    { kNone_SkFilterQuality,   "Mixed",    NULL    },
+    { kNone_SkFilterQuality,   "None",     "F0 "   },
+    { kLow_SkFilterQuality,    "Low",      "F1 "   },
+    { kMedium_SkFilterQuality, "Medium",   "F2 "   },
+    { kHigh_SkFilterQuality,   "High",     "F3 "   },
 };
 
 class FlagsDrawFilter : public SkDrawFilter {
 public:
     FlagsDrawFilter(SkOSMenu::TriState lcd, SkOSMenu::TriState aa,
-                    SkOSMenu::TriState subpixel, int hinting, int filterlevel)
+                    SkOSMenu::TriState subpixel, int hinting, int filterQuality)
         : fLCDState(lcd)
         , fAAState(aa)
         , fSubpixelState(subpixel)
         , fHintingState(hinting)
-        , fFilterLevelIndex(filterlevel)
+        , fFilterQualityIndex(filterQuality)
     {
-        SkASSERT((unsigned)filterlevel < SK_ARRAY_COUNT(gFilterLevelStates));
+        SkASSERT((unsigned)filterQuality < SK_ARRAY_COUNT(gFilterQualityStates));
     }
 
     virtual bool filter(SkPaint* paint, Type t) {
@@ -484,8 +483,8 @@
         if (SkOSMenu::kMixedState != fAAState) {
             paint->setAntiAlias(SkOSMenu::kOnState == fAAState);
         }
-        if (0 != fFilterLevelIndex) {
-            paint->setFilterLevel(gFilterLevelStates[fFilterLevelIndex].fLevel);
+        if (0 != fFilterQualityIndex) {
+            paint->setFilterQuality(gFilterQualityStates[fFilterQualityIndex].fQuality);
         }
         if (SkOSMenu::kMixedState != fSubpixelState) {
             paint->setSubpixelText(SkOSMenu::kOnState == fSubpixelState);
@@ -501,7 +500,7 @@
     SkOSMenu::TriState  fAAState;
     SkOSMenu::TriState  fSubpixelState;
     int fHintingState;
-    int fFilterLevelIndex;
+    int fFilterQualityIndex;
 };
 
 //////////////////////////////////////////////////////////////////////////////
@@ -673,7 +672,8 @@
         SampleWindow::kANGLE_DeviceType,
 #endif // SK_ANGLE
 #endif // SK_SUPPORT_GPU
-        SampleWindow::kRaster_DeviceType
+        SampleWindow::kDeferred_DeviceType,
+        SampleWindow::kRaster_DeviceType,
     };
     SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gCT) == SampleWindow::kDeviceTypeCnt, array_size_mismatch);
     return gCT[ct];
@@ -804,7 +804,7 @@
     fAAState = SkOSMenu::kMixedState;
     fSubpixelState = SkOSMenu::kMixedState;
     fHintingState = 0;
-    fFilterLevelIndex = 0;
+    fFilterQualityIndex = 0;
     fFlipAxis = 0;
     fScrollTestX = fScrollTestY = 0;
 
@@ -829,7 +829,7 @@
     int itemID;
 
     itemID =fAppMenu->appendList("Device Type", "Device Type", sinkID, 0,
-                                "Raster", "Picture", "OpenGL",
+                                "Raster", "Picture", "OpenGL", "Deferred",
 #if SK_ANGLE
                                 "ANGLE",
 #endif
@@ -839,12 +839,12 @@
     fAppMenu->assignKeyEquivalentToItem(itemID, 'b');
     itemID = fAppMenu->appendTriState("LCD", "LCD", sinkID, fLCDState);
     fAppMenu->assignKeyEquivalentToItem(itemID, 'l');
-    itemID = fAppMenu->appendList("FilterLevel", "FilterLevel", sinkID, fFilterLevelIndex,
-                                  gFilterLevelStates[0].fName,
-                                  gFilterLevelStates[1].fName,
-                                  gFilterLevelStates[2].fName,
-                                  gFilterLevelStates[3].fName,
-                                  gFilterLevelStates[4].fName,
+    itemID = fAppMenu->appendList("FilterQuality", "FilterQuality", sinkID, fFilterQualityIndex,
+                                  gFilterQualityStates[0].fName,
+                                  gFilterQualityStates[1].fName,
+                                  gFilterQualityStates[2].fName,
+                                  gFilterQualityStates[3].fName,
+                                  gFilterQualityStates[4].fName,
                                   NULL);
     fAppMenu->assignKeyEquivalentToItem(itemID, 'n');
     itemID = fAppMenu->appendTriState("Subpixel", "Subpixel", sinkID, fSubpixelState);
@@ -1196,6 +1196,12 @@
         canvas = fPDFDocument->beginPage(this->width(), this->height());
     } else if (kPicture_DeviceType == fDeviceType) {
         canvas = fRecorder.beginRecording(9999, 9999, NULL, 0);
+    } else if (kDeferred_DeviceType == fDeviceType) {
+        fDeferredSurface.reset(canvas->newSurface(canvas->imageInfo()));
+        if (fDeferredSurface.get()) {
+            fDeferredCanvas.reset(SkDeferredCanvas::Create(fDeferredSurface));
+            canvas = fDeferredCanvas;
+        }
     } else {
         canvas = this->INHERITED::beforeChildren(canvas);
     }
@@ -1284,6 +1290,13 @@
         } else {
             picture->playback(orig);
         }
+    } else if (kDeferred_DeviceType == fDeviceType) {
+        SkAutoTUnref<SkImage> image(fDeferredCanvas->newImageSnapshot());
+        if (image) {
+            orig->drawImage(image, 0, 0, NULL);
+        }
+        fDeferredCanvas.reset(NULL);
+        fDeferredSurface.reset(NULL);
     }
 
     // Do this after presentGL and other finishing, rather than in afterChild
@@ -1307,18 +1320,18 @@
         static const SkScalar gAnimPeriod = 10 * SK_Scalar1;
         static const SkScalar gAnimMag = SK_Scalar1 / 1000;
         SkScalar t = SkScalarMod(secs, gAnimPeriod);
-        if (SkScalarFloorToInt(SkScalarDiv(secs, gAnimPeriod)) & 0x1) {
+        if (SkScalarFloorToInt(secs / gAnimPeriod) & 0x1) {
             t = gAnimPeriod - t;
         }
         t = 2 * t - gAnimPeriod;
-        t = SkScalarMul(SkScalarDiv(t, gAnimPeriod), gAnimMag);
+        t *= gAnimMag / gAnimPeriod;
         SkMatrix m;
         m.reset();
 #if 1
         m.setPerspY(t);
 #else
         m.setPerspY(SK_Scalar1 / 1000);
-        m.setSkewX(SkScalarDiv(8, 25));
+        m.setSkewX(8.0f / 25);
         m.dump();
 #endif
         canvas->concat(m);
@@ -1423,7 +1436,7 @@
 
 void SampleWindow::installDrawFilter(SkCanvas* canvas) {
     canvas->setDrawFilter(new FlagsDrawFilter(fLCDState, fAAState, fSubpixelState,
-                                              fHintingState, fFilterLevelIndex))->unref();
+                                              fHintingState, fFilterQualityIndex))->unref();
 }
 
 void SampleWindow::postAnimatingEvent() {
@@ -1473,7 +1486,7 @@
     }
     if (SkOSMenu::FindTriState(evt, "AA", &fAAState) ||
         SkOSMenu::FindTriState(evt, "LCD", &fLCDState) ||
-        SkOSMenu::FindListIndex(evt, "FilterLevel", &fFilterLevelIndex) ||
+        SkOSMenu::FindListIndex(evt, "FilterQuality", &fFilterQualityIndex) ||
         SkOSMenu::FindTriState(evt, "Subpixel", &fSubpixelState) ||
         SkOSMenu::FindListIndex(evt, "Hinting", &fHintingState) ||
         SkOSMenu::FindSwitchState(evt, "Clip", &fUseClip) ||
@@ -1858,6 +1871,7 @@
     "angle: ",
 #endif // SK_ANGLE
 #endif // SK_SUPPORT_GPU
+    "deferred: ",
 };
 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gDeviceTypePrefix) == SampleWindow::kDeviceTypeCnt,
                   array_size_mismatch);
@@ -1905,7 +1919,7 @@
 
     title.prepend(trystate_str(fLCDState, "LCD ", "lcd "));
     title.prepend(trystate_str(fAAState, "AA ", "aa "));
-    title.prepend(gFilterLevelStates[fFilterLevelIndex].fLabel);
+    title.prepend(gFilterQualityStates[fFilterQualityIndex].fLabel);
     title.prepend(trystate_str(fSubpixelState, "S ", "s "));
     title.prepend(fFlipAxis & kFlipAxis_X ? "X " : NULL);
     title.prepend(fFlipAxis & kFlipAxis_Y ? "Y " : NULL);
diff --git a/samplecode/SampleApp.h b/samplecode/SampleApp.h
index 2385fc0..8fc1a90 100644
--- a/samplecode/SampleApp.h
+++ b/samplecode/SampleApp.h
@@ -22,6 +22,7 @@
 
 class SkCanvas;
 class SkData;
+class SkDeferredCanvas;
 class SkDocument;
 class SkEvent;
 class SkTypeface;
@@ -39,7 +40,7 @@
         kANGLE_DeviceType,
 #endif // SK_ANGLE
 #endif // SK_SUPPORT_GPU
-
+        kDeferred_DeviceType,
         kDeviceTypeCnt
     };
 
@@ -99,7 +100,7 @@
     SampleWindow(void* hwnd, int argc, char** argv, DeviceManager*);
     virtual ~SampleWindow();
 
-    SkSurface* createSurface() SK_OVERRIDE {
+    SkSurface* createSurface() override {
         SkSurface* surface = NULL;
         if (fDevManager) {
             surface = fDevManager->createSurface(fDeviceType, this);
@@ -110,7 +111,7 @@
         return surface;
     }
 
-    void draw(SkCanvas*) SK_OVERRIDE;
+    void draw(SkCanvas*) override;
 
     void setDeviceType(DeviceType type);
     void toggleRendering();
@@ -136,24 +137,24 @@
     DeviceType getDeviceType() const { return fDeviceType; }
 
 protected:
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE;
-    bool onHandleKey(SkKey key) SK_OVERRIDE;
-    bool onHandleChar(SkUnichar) SK_OVERRIDE;
-    void onSizeChange() SK_OVERRIDE;
+    void onDraw(SkCanvas* canvas) override;
+    bool onHandleKey(SkKey key) override;
+    bool onHandleChar(SkUnichar) override;
+    void onSizeChange() override;
 
-    SkCanvas* beforeChildren(SkCanvas*) SK_OVERRIDE;
-    void afterChildren(SkCanvas*) SK_OVERRIDE;
-    void beforeChild(SkView* child, SkCanvas* canvas) SK_OVERRIDE;
-    void afterChild(SkView* child, SkCanvas* canvas) SK_OVERRIDE;
+    SkCanvas* beforeChildren(SkCanvas*) override;
+    void afterChildren(SkCanvas*) override;
+    void beforeChild(SkView* child, SkCanvas* canvas) override;
+    void afterChild(SkView* child, SkCanvas* canvas) override;
 
-    bool onEvent(const SkEvent& evt) SK_OVERRIDE;
-    bool onQuery(SkEvent* evt) SK_OVERRIDE;
+    bool onEvent(const SkEvent& evt) override;
+    bool onQuery(SkEvent* evt) override;
 
     virtual bool onDispatchClick(int x, int y, Click::State, void* owner,
-                                 unsigned modi) SK_OVERRIDE;
-    bool onClick(Click* click) SK_OVERRIDE;
+                                 unsigned modi) override;
+    bool onClick(Click* click) override;
     virtual Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                      unsigned modi) SK_OVERRIDE;
+                                      unsigned modi) override;
 
 private:
     class DefaultDeviceManager;
@@ -161,6 +162,8 @@
     int fCurrIndex;
 
     SkPictureRecorder fRecorder;
+    SkAutoTDelete<SkSurface> fDeferredSurface;
+    SkAutoTDelete<SkDeferredCanvas> fDeferredCanvas;
     SkPath fClipPath;
 
     SkTouchGesture fGesture;
@@ -202,7 +205,7 @@
     SkOSMenu::TriState fAAState;
     SkOSMenu::TriState fSubpixelState;
     int fHintingState;
-    int fFilterLevelIndex;
+    int fFilterQualityIndex;
     unsigned   fFlipAxis;
 
     int fMSAASampleCount;
diff --git a/samplecode/SampleArc.cpp b/samplecode/SampleArc.cpp
index 710901b..842e7a0 100644
--- a/samplecode/SampleArc.cpp
+++ b/samplecode/SampleArc.cpp
@@ -52,7 +52,7 @@
             }
         }
 
-        void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        void onDraw(SkCanvas* canvas) override {
             SkPaint paint;
             paint.setAntiAlias(true);
             paint.setStrokeWidth(SkIntToScalar(2));
@@ -73,7 +73,7 @@
             canvas->drawArc(fR, 0, fSweep, false, paint);
         }
 
-        SkRect onGetBounds() SK_OVERRIDE {
+        SkRect onGetBounds() override {
             SkRect r(fR);
             r.outset(2, 2);
             return r;
@@ -99,14 +99,14 @@
         fRootDrawable = recorder.endRecordingAsDrawable();
     }
 
-    ~ArcsView() SK_OVERRIDE {
+    ~ArcsView() override {
         fAnimatingDrawable->unref();
         fRootDrawable->unref();
     }
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Arcs");
             return true;
@@ -192,17 +192,17 @@
         DrawArcs(canvas);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         canvas->drawDrawable(fRootDrawable);
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         SkScalar angle = SkDoubleToScalar(fmod(timer.secs() * 360 / 24, 360));
         fAnimatingDrawable->setSweep(angle);
         return true;
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
      //   fSweep += SK_Scalar1;
         this->inval(NULL);
         return this->INHERITED::onFindClickHandler(x, y, modi);
diff --git a/samplecode/SampleBitmapRect.cpp b/samplecode/SampleBitmapRect.cpp
index 82d1098..123bfb8 100644
--- a/samplecode/SampleBitmapRect.cpp
+++ b/samplecode/SampleBitmapRect.cpp
@@ -103,7 +103,7 @@
     }
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "BitmapRect");
             return true;
@@ -111,7 +111,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkRect srcR;
         srcR.set(fSrcPts[0], fSrcPts[1]);
         srcR = SkRect::MakeXYWH(fSrcPts[0].fX, fSrcPts[0].fY, 32, 32);
@@ -130,13 +130,13 @@
         canvas->drawRect(srcR, paint);
 
         for (int i = 0; i < 2; ++i) {
-            paint.setFilterLevel(1 == i ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel);
+            paint.setFilterQuality(1 == i ? kLow_SkFilterQuality : kNone_SkFilterQuality);
             canvas->drawBitmapRectToRect(bitmap, &srcR, fDstR[i], &paint);
             canvas->drawRect(fDstR[i], paint);
         }
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         if (timer.isStopped()) {
             this->resetBounce();
         } else if (timer.isRunning()) {
@@ -210,7 +210,7 @@
     }
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "BigBitmapRect");
             return true;
@@ -218,19 +218,19 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setStyle(SkPaint::kStroke_Style);
         paint.setColor(SK_ColorYELLOW);
 
         for (int i = 0; i < 2; ++i) {
-            paint.setFilterLevel(1 == i ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel);
+            paint.setFilterQuality(1 == i ? kLow_SkFilterQuality : kNone_SkFilterQuality);
             canvas->drawBitmapRectToRect(fBitmap, &fSrcR, fDstR[i], &paint);
             canvas->drawRect(fDstR[i], paint);
         }
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         if (timer.isStopped()) {
             this->resetBounce();
         } else if (timer.isRunning()) {
diff --git a/samplecode/SampleCamera.cpp b/samplecode/SampleCamera.cpp
index af9dbe8..1f3b8c3 100644
--- a/samplecode/SampleCamera.cpp
+++ b/samplecode/SampleCamera.cpp
@@ -83,13 +83,13 @@
 
             paint.setAntiAlias(true);
             paint.setShader(fShaders[fShaderIndex]);
-            paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+            paint.setFilterQuality(kLow_SkFilterQuality);
             SkRect r = { -150, -150, 150, 150 };
             canvas->drawRoundRect(r, 30, 30, paint);
         }
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         if (timer.isStopped()) {
             fRY = 0;
         } else {
diff --git a/samplecode/SampleChart.cpp b/samplecode/SampleChart.cpp
index ec5448c..4c128a9 100644
--- a/samplecode/SampleChart.cpp
+++ b/samplecode/SampleChart.cpp
@@ -88,7 +88,7 @@
     }
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Chart");
             return true;
@@ -96,7 +96,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         bool sizeChanged = false;
         if (canvas->getDeviceSize() != fSize) {
             fSize = canvas->getDeviceSize();
diff --git a/samplecode/SampleClipDrawMatch.cpp b/samplecode/SampleClipDrawMatch.cpp
index 0dbe2b1..6a2a37b 100644
--- a/samplecode/SampleClipDrawMatch.cpp
+++ b/samplecode/SampleClipDrawMatch.cpp
@@ -131,7 +131,7 @@
     }
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "ClipDrawMatch");
             return true;
@@ -227,7 +227,7 @@
         }
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkScalar trans[2];
         fTrans.timeToValues(SkTime::GetMSecs(), trans);
 
diff --git a/samplecode/SampleClock.cpp b/samplecode/SampleClock.cpp
index 651a995..ec71f0f 100644
--- a/samplecode/SampleClock.cpp
+++ b/samplecode/SampleClock.cpp
@@ -22,7 +22,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Clock");
             return true;
@@ -30,7 +30,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkPaint paintFill;
         SkPaint paintStroke;
         SkPath  path;
diff --git a/samplecode/SampleCode.h b/samplecode/SampleCode.h
index 4152f54..6818c44 100644
--- a/samplecode/SampleCode.h
+++ b/samplecode/SampleCode.h
@@ -53,7 +53,7 @@
 class SkFuncViewFactory : public SkViewFactory {
 public:
     SkFuncViewFactory(SkViewCreateFunc func);
-    SkView* operator() () const SK_OVERRIDE;
+    SkView* operator() () const override;
 
 private:
     SkViewCreateFunc fCreateFunc;
@@ -72,7 +72,7 @@
 class SkGMSampleViewFactory : public SkViewFactory {
 public:
     SkGMSampleViewFactory(GMFactoryFunc func);
-    SkView* operator() () const SK_OVERRIDE;
+    SkView* operator() () const override;
 private:
     GMFactoryFunc fFunc;
 };
diff --git a/samplecode/SampleDegenerateTwoPtRadials.cpp b/samplecode/SampleDegenerateTwoPtRadials.cpp
index dec9850..32b4537 100644
--- a/samplecode/SampleDegenerateTwoPtRadials.cpp
+++ b/samplecode/SampleDegenerateTwoPtRadials.cpp
@@ -45,7 +45,7 @@
     }
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "DegenerateTwoPtRadials");
             return true;
@@ -53,7 +53,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkScalar delta = fTime / 15.f;
         int intPart = SkScalarFloorToInt(delta);
         delta = delta - SK_Scalar1 * intPart;
@@ -77,7 +77,7 @@
         canvas->drawText(txt.c_str(), txt.size(), l + w/2 + w*DELTA_SCALE*delta, t + h + SK_Scalar1 * 10, paint);
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         fTime = SkDoubleToScalar(timer.secs() / 15);
         return true;
     }
diff --git a/samplecode/SampleFatBits.cpp b/samplecode/SampleFatBits.cpp
index dfe5ba0..62d4a3d 100644
--- a/samplecode/SampleFatBits.cpp
+++ b/samplecode/SampleFatBits.cpp
@@ -394,7 +394,7 @@
     }
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "FatBits");
             return true;
@@ -474,7 +474,7 @@
     }
 
     virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              unsigned modi) SK_OVERRIDE {
+                                              unsigned modi) override {
         SkPoint pt = { x, y };
         int index = -1;
         int count = fFB.getTriangle() ? 3 : 2;
@@ -489,7 +489,7 @@
         return new IndexClick(this, index);
     }
 
-    bool onClick(Click* click) SK_OVERRIDE {
+    bool onClick(Click* click) override {
         int index = IndexClick::GetIndex(click);
         if (index >= 0 && index <= 2) {
             fPts[index] = click->fCurr;
diff --git a/samplecode/SampleFilter.cpp b/samplecode/SampleFilter.cpp
index ae66a0c..3713a72 100644
--- a/samplecode/SampleFilter.cpp
+++ b/samplecode/SampleFilter.cpp
@@ -47,7 +47,7 @@
 
 static SkScalar draw_set(SkCanvas* c, const SkBitmap& bm, SkScalar x, SkPaint* p) {
     x += draw_bm(c, bm, x, 0, p);
-    p->setFilterLevel(SkPaint::kLow_FilterLevel);
+    p->setFilterQuality(kLow_SkFilterQuality);
     x += draw_bm(c, bm, x, 0, p);
     p->setDither(true);
     return x + draw_bm(c, bm, x, 0, p);
diff --git a/samplecode/SampleFilter2.cpp b/samplecode/SampleFilter2.cpp
index 0c81196..3541e15 100644
--- a/samplecode/SampleFilter2.cpp
+++ b/samplecode/SampleFilter2.cpp
@@ -74,7 +74,7 @@
         canvas->scale(SK_Scalar1, scale);
 
         for (int k = 0; k < 2; k++) {
-            paint.setFilterLevel(k == 1 ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel);
+            paint.setFilterQuality(k == 1 ? kLow_SkFilterQuality : kNone_SkFilterQuality);
             for (int j = 0; j < 2; j++) {
                 paint.setDither(j == 1);
                 for (int i = 0; i < fBitmapCount; i++) {
@@ -91,7 +91,7 @@
                         SkString s("dither=");
                         s.appendS32(paint.isDither());
                         s.append(" filter=");
-                        s.appendS32(paint.getFilterLevel() != SkPaint::kNone_FilterLevel);
+                        s.appendS32(paint.getFilterQuality() != kNone_SkFilterQuality);
                         canvas->drawText(s.c_str(), s.size(), x + W/2,
                                          y - p.getTextSize(), p);
                     }
diff --git a/samplecode/SampleFilterFuzz.cpp b/samplecode/SampleFilterFuzz.cpp
index 894e4fa..30389b6 100644
--- a/samplecode/SampleFilterFuzz.cpp
+++ b/samplecode/SampleFilterFuzz.cpp
@@ -19,7 +19,6 @@
 #include "SkFlattenableSerialization.h"
 #include "SkLightingImageFilter.h"
 #include "SkMagnifierImageFilter.h"
-#include "SkMatrixImageFilter.h"
 #include "SkMatrixConvolutionImageFilter.h"
 #include "SkMergeImageFilter.h"
 #include "SkMorphologyImageFilter.h"
@@ -310,9 +309,9 @@
         filter = SkOffsetImageFilter::Create(make_scalar(), make_scalar(), make_image_filter());
         break;
     case MATRIX:
-        filter = SkMatrixImageFilter::Create(make_matrix(),
-                                             (SkPaint::FilterLevel)R(4),
-                                             make_image_filter());
+        filter = SkImageFilter::CreateMatrixFilter(make_matrix(),
+                                                   (SkFilterQuality)R(4),
+                                                   make_image_filter());
         break;
     case MATRIX_CONVOLUTION:
     {
@@ -380,7 +379,7 @@
     case DROP_SHADOW:
         filter = SkDropShadowImageFilter::Create(make_scalar(), make_scalar(), make_scalar(true),
                     make_scalar(true), make_color(), make_shadow_mode(), make_image_filter(),
-                    NULL, 0);
+                    NULL);
         break;
     case MORPHOLOGY:
         if (R(2) == 1) {
diff --git a/samplecode/SampleFilterQuality.cpp b/samplecode/SampleFilterQuality.cpp
index fe94864..64b7f55 100644
--- a/samplecode/SampleFilterQuality.cpp
+++ b/samplecode/SampleFilterQuality.cpp
@@ -156,7 +156,7 @@
     }
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "FilterQuality");
             return true;
@@ -175,11 +175,11 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void drawTheImage(SkCanvas* canvas, const SkISize& size, SkPaint::FilterLevel filter,
+    void drawTheImage(SkCanvas* canvas, const SkISize& size, SkFilterQuality filter,
                       SkScalar dx, SkScalar dy) {
         SkPaint paint;
         paint.setAntiAlias(true);
-        paint.setFilterLevel(filter);
+        paint.setFilterQuality(filter);
 
         SkAutoCanvasRestore acr(canvas, true);
 
@@ -197,7 +197,7 @@
         }
     }
 
-    void drawHere(SkCanvas* canvas, SkPaint::FilterLevel filter, SkScalar dx, SkScalar dy) {
+    void drawHere(SkCanvas* canvas, SkFilterQuality filter, SkScalar dx, SkScalar dy) {
         SkCanvas* origCanvas = canvas;
         SkAutoCanvasRestore acr(canvas, true);
 
@@ -239,7 +239,7 @@
         canvas->drawLine(r.centerX(), r.top(), r.centerX(), r.bottom(), p);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         fCell.set(this->height() / 2, this->height() / 2);
 
         SkScalar trans[2];
@@ -253,7 +253,7 @@
                 SkRect r = SkRect::MakeWH(fCell.width(), fCell.height());
                 r.inset(4, 4);
                 canvas->clipRect(r);
-                this->drawHere(canvas, SkPaint::FilterLevel(index), trans[0], trans[1]);
+                this->drawHere(canvas, SkFilterQuality(index), trans[0], trans[1]);
             }
         }
 
@@ -276,7 +276,7 @@
         canvas->drawText(str.c_str(), str.size(), textX, 250, paint);
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         fCurrTime = timer.msec();
         return true;
     }
diff --git a/samplecode/SampleGradients.cpp b/samplecode/SampleGradients.cpp
index f328d76..12d24d2 100644
--- a/samplecode/SampleGradients.cpp
+++ b/samplecode/SampleGradients.cpp
@@ -82,24 +82,24 @@
     return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount);
 }
 
-static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) {
+static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) {
     SkPoint center0, center1;
     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
-    return SkGradientShader::CreateTwoPointRadial(
+    return SkGradientShader::CreateTwoPointConical(
                             center1, (pts[1].fX - pts[0].fX) / 7,
                             center0, (pts[1].fX - pts[0].fX) / 2,
                             data.fColors, data.fPos, data.fCount, tm);
 }
 
-static SkShader* Make2RadialConcentric(const SkPoint pts[2], const GradData& data,
+static SkShader* Make2ConicalConcentric(const SkPoint pts[2], const GradData& data,
                                        SkShader::TileMode tm) {
     SkPoint center;
     center.set(SkScalarAve(pts[0].fX, pts[1].fX),
                SkScalarAve(pts[0].fY, pts[1].fY));
-    return SkGradientShader::CreateTwoPointRadial(
+    return SkGradientShader::CreateTwoPointConical(
                             center, (pts[1].fX - pts[0].fX) / 7,
                             center, (pts[1].fX - pts[0].fX) / 2,
                             data.fColors, data.fPos, data.fCount, tm);
@@ -108,7 +108,7 @@
 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm);
 
 static const GradMaker gGradMakers[] = {
-    MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2RadialConcentric
+    MakeLinear, MakeRadial, MakeSweep, Make2Conical, Make2ConicalConcentric
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/samplecode/SampleHT.cpp b/samplecode/SampleHT.cpp
index 33fb771..20648cc 100644
--- a/samplecode/SampleHT.cpp
+++ b/samplecode/SampleHT.cpp
@@ -83,7 +83,7 @@
 
     void setTime(SkMSec time) { fTime = time; }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkAutoCanvasRestore acr(canvas, false);
 
         SkPaint paint;
@@ -112,7 +112,7 @@
         canvas->drawRect(fR, paint);
     }
 
-    SkRect onGetBounds() SK_OVERRIDE { return fR; }
+    SkRect onGetBounds() override { return fR; }
 };
 
 class HTView : public SampleView {
@@ -144,7 +144,7 @@
     }
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "HT");
             return true;
@@ -152,11 +152,11 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         canvas->drawDrawable(fRoot);
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         fTime = timer.msec();
         for (int i = 0; i < N; ++i) {
             fArray[i].fDrawable->setTime(fTime);
@@ -164,7 +164,7 @@
         return true;
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
         // search backwards to find the top-most
         for (int i = N - 1; i >= 0; --i) {
             if (fArray[i].fDrawable->hitTest(x, y)) {
diff --git a/samplecode/SampleHairline.cpp b/samplecode/SampleHairline.cpp
index 7687633..bb2e2f1 100644
--- a/samplecode/SampleHairline.cpp
+++ b/samplecode/SampleHairline.cpp
@@ -185,7 +185,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SkString str;
             str.printf("Hair-%s", gProcs[fProcIndex].fName);
@@ -201,7 +201,7 @@
         canvas->drawBitmap(b1, SkIntToScalar(b0.width()), 0, NULL);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         gRand.setSeed(fNow);
 
         SkBitmap bm, bm2;
@@ -222,7 +222,7 @@
         canvas->drawBitmap(bm2, SkIntToScalar(10), SkIntToScalar(10), NULL);
     }
 
-    bool onAnimate(const SkAnimTimer&) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer&) override {
         if (fDoAA) {
             fProcIndex = cycle_hairproc_index(fProcIndex);
             // todo: signal that we want to rebuild our TITLE
@@ -231,7 +231,7 @@
         return true;
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
         fDoAA = !fDoAA;
         this->inval(NULL);
         return this->INHERITED::onFindClickHandler(x, y, modi);
diff --git a/samplecode/SampleIdentityScale.cpp b/samplecode/SampleIdentityScale.cpp
index 073bc7c..4450d61 100644
--- a/samplecode/SampleIdentityScale.cpp
+++ b/samplecode/SampleIdentityScale.cpp
@@ -43,7 +43,7 @@
     SkBitmap fBM;
 
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "IdentityScale");
             return true;
@@ -51,13 +51,13 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
 
         SkPaint paint;
 
         paint.setAntiAlias(true);
         paint.setTextSize(48);
-        paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
+        paint.setFilterQuality(kHigh_SkFilterQuality);
 
         SkTime::DateTime time;
         SkTime::GetDateTime(&time);
diff --git a/samplecode/SampleLayers.cpp b/samplecode/SampleLayers.cpp
index 9b61fcc..62a8c3b 100644
--- a/samplecode/SampleLayers.cpp
+++ b/samplecode/SampleLayers.cpp
@@ -123,7 +123,7 @@
 
 class RedFilter : public SkDrawFilter {
 public:
-    bool filter(SkPaint* p, SkDrawFilter::Type) SK_OVERRIDE {
+    bool filter(SkPaint* p, SkDrawFilter::Type) override {
         fColor = p->getColor();
         if (fColor == SK_ColorRED) {
             p->setColor(SK_ColorGREEN);
@@ -215,7 +215,7 @@
     }
 
     virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              unsigned modi) SK_OVERRIDE {
+                                              unsigned modi) override {
         this->inval(NULL);
 
         return this->INHERITED::onFindClickHandler(x, y, modi);
diff --git a/samplecode/SampleLines.cpp b/samplecode/SampleLines.cpp
index c2509bc..4b491a5 100644
--- a/samplecode/SampleLines.cpp
+++ b/samplecode/SampleLines.cpp
@@ -96,7 +96,7 @@
         canvas->drawLine(x, y, x + SkIntToScalar(90), y + SkIntToScalar(90), paint);
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
         fAlpha = SkScalarRoundToInt(y);
         this->inval(NULL);
         return NULL;
diff --git a/samplecode/SampleLua.cpp b/samplecode/SampleLua.cpp
index f21c7de..8865f1a 100644
--- a/samplecode/SampleLua.cpp
+++ b/samplecode/SampleLua.cpp
@@ -75,7 +75,7 @@
     }
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Lua");
             return true;
@@ -101,7 +101,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         lua_State* L = this->ensureLua();
 
         lua_getglobal(L, gDrawName);
@@ -126,7 +126,7 @@
     }
 
     virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              unsigned modi) SK_OVERRIDE {
+                                              unsigned modi) override {
         lua_State* L = this->ensureLua();
         lua_getglobal(L, gClickName);
         if (lua_isfunction(L, -1)) {
@@ -147,7 +147,7 @@
         return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
-    bool onClick(Click* click) SK_OVERRIDE {
+    bool onClick(Click* click) override {
         if (click->getType() != gLuaClickHandlerName) {
             return this->INHERITED::onClick(click);
         }
diff --git a/samplecode/SampleManyRects.cpp b/samplecode/SampleManyRects.cpp
index c563394..5032498 100644
--- a/samplecode/SampleManyRects.cpp
+++ b/samplecode/SampleManyRects.cpp
@@ -26,7 +26,7 @@
     ManyRectsView() {}
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "ManyRects");
             return true;
diff --git a/samplecode/SamplePatch.cpp b/samplecode/SamplePatch.cpp
index 4153682..ac500bf 100644
--- a/samplecode/SamplePatch.cpp
+++ b/samplecode/SamplePatch.cpp
@@ -276,7 +276,7 @@
 
         SkPaint paint;
         paint.setDither(true);
-        paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        paint.setFilterQuality(kLow_SkFilterQuality);
 
         canvas->translate(DX, DY);
 
@@ -319,7 +319,7 @@
         drawpatches(canvas, paint, nu, nv, &patch);
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         fAngle = timer.scaled(60, 360);
         return true;
     }
@@ -335,7 +335,7 @@
     }
 
     virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              unsigned modi) SK_OVERRIDE {
+                                              unsigned modi) override {
         x -= DX;
         y -= DY;
         for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
diff --git a/samplecode/SamplePath.cpp b/samplecode/SamplePath.cpp
index b5edeb3..4493d39 100644
--- a/samplecode/SamplePath.cpp
+++ b/samplecode/SamplePath.cpp
@@ -195,7 +195,7 @@
         }
     }
     
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         SkScalar currSecs = timer.scaled(100);
         SkScalar delta = currSecs - fPrevSecs;
         fPrevSecs = currSecs;
@@ -207,7 +207,7 @@
         return true;
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
         fShowHairline = !fShowHairline;
         this->inval(NULL);
         return this->INHERITED::onFindClickHandler(x, y, modi);
@@ -272,7 +272,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "ArcTo");
             return true;
@@ -300,7 +300,7 @@
         }
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         canvas->drawPoints(SkCanvas::kPoints_PointMode, N, fPts, fPtsPaint);
 
         SkPath path;
@@ -316,7 +316,7 @@
         canvas->drawPath(path, fSkeletonPaint);
     }
 
-    bool onClick(Click* click) SK_OVERRIDE {
+    bool onClick(Click* click) override {
         int32_t index;
         if (click->fMeta.findS32("index", &index)) {
             SkASSERT((unsigned)index < N);
@@ -327,7 +327,7 @@
         return false;
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
         const SkScalar tol = 4;
         const SkRect r = SkRect::MakeXYWH(x - tol, y - tol, tol * 2, tol * 2);
         for (int i = 0; i < N; ++i) {
diff --git a/samplecode/SamplePathClip.cpp b/samplecode/SamplePathClip.cpp
index a85038e..ab7ed5d 100644
--- a/samplecode/SamplePathClip.cpp
+++ b/samplecode/SamplePathClip.cpp
@@ -70,7 +70,7 @@
         canvas->drawOval(oval, p);
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
         return new Click(this);
     }
 
diff --git a/samplecode/SamplePathEffects.cpp b/samplecode/SamplePathEffects.cpp
index f1574fb..e194793 100644
--- a/samplecode/SamplePathEffects.cpp
+++ b/samplecode/SamplePathEffects.cpp
@@ -99,7 +99,7 @@
         }
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         SkRandom    rand;
         int         steps = 20;
         SkScalar    dist = SkIntToScalar(400);
@@ -130,7 +130,7 @@
         this->setBGColor(0xFFDDDDDD);
     }
 
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "PathEffects");
             return true;
@@ -138,7 +138,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkPaint paint;
 
         canvas->translate(0, 50);
@@ -162,7 +162,7 @@
         canvas->drawPath(fPath, paint);
     }
 
-    bool onAnimate(const SkAnimTimer& timer) SK_OVERRIDE {
+    bool onAnimate(const SkAnimTimer& timer) override {
         fPhase = timer.scaled(40);
         return true;
     }
diff --git a/samplecode/SamplePathFuzz.cpp b/samplecode/SamplePathFuzz.cpp
index d14d918..f2595c5 100644
--- a/samplecode/SamplePathFuzz.cpp
+++ b/samplecode/SamplePathFuzz.cpp
@@ -671,7 +671,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         fIndex = 0;
         SkImageInfo info(SkImageInfo::MakeN32Premul(SkScalarRoundToInt(width()), 
                 SkScalarRoundToInt(height())));
diff --git a/samplecode/SamplePathUtils.cpp b/samplecode/SamplePathUtils.cpp
deleted file mode 100644
index 503a6ea..0000000
--- a/samplecode/SamplePathUtils.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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 "SkCornerPathEffect.h"
-#include "SkDashPathEffect.h"
-#include "SkPathUtils.h"
-#include "SkRandom.h"
-#include "SkView.h"
-
-typedef void (*BitsToPath)(SkPath*, const char*, int, int, int);
-
-static const BitsToPath gBitsToPath_fns[] = {
-    SkPathUtils::BitsToPath_Path,
-    SkPathUtils::BitsToPath_Region,
-};
-
-// hardcoded bitmap patterns
-static const uint8_t gBits[][16] = {
-    { 0x18, 0x00, 0x3c, 0x00, 0x7e, 0x00, 0xdb, 0x00,
-      0xff, 0x00, 0x24, 0x00, 0x5a, 0x00, 0xa5, 0x00 },
-
-    { 0x20, 0x80, 0x91, 0x20, 0xbf, 0xa0, 0xee, 0xe0,
-      0xff, 0xe0, 0x7f, 0xc0, 0x20, 0x80, 0x40, 0x40 },
-
-    { 0x0f, 0x00, 0x7f, 0xe0, 0xff, 0xf0, 0xe6, 0x70,
-      0xff, 0xf0, 0x19, 0x80, 0x36, 0xc0, 0xc0, 0x30 }
-};
-
-
-class SamplePathUtils : public SampleView {
-public:
-    static const int fNumBits = 3;
-    static const int fH = 8, fW = 12;
-    static const size_t fRowBytes = 2;
-    static const int fNumChars = fH * fRowBytes;
-
-    SkPaint fBmpPaint;
-    SkScalar fPhase;
-
-    SamplePathUtils() {
-        fBmpPaint.setAntiAlias(true);  // Black paint for bitmap
-        fBmpPaint.setStyle(SkPaint::kFill_Style);
-
-        fPhase = 0.0f; // to animate the dashed path
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "PathUtils");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    /////////////////////////////////////////////////////////////
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkScalar intervals[8] = { .5f, .3f, .5f, .3f, .5f, .3f, .5f, .3f };
-        SkAutoTUnref<SkDashPathEffect> dash(SkDashPathEffect::Create(intervals, 2, fPhase));
-        SkAutoTUnref<SkCornerPathEffect> corner(SkCornerPathEffect::Create(.25f));
-        SkAutoTUnref<SkComposePathEffect> compose(SkComposePathEffect::Create(dash, corner));
-
-        SkPaint outlinePaint;
-        outlinePaint.setAntiAlias(true);  // dashed paint for bitmap
-        outlinePaint.setStyle(SkPaint::kStroke_Style);
-        outlinePaint.setPathEffect(compose);
-
-        canvas->scale(10.0f, 10.0f);  // scales up
-
-        for (int i = 0; i < fNumBits; ++i) {
-            canvas->save();
-            for (size_t j = 0; j < SK_ARRAY_COUNT(gBitsToPath_fns); ++j) {
-                SkPath path;
-                gBitsToPath_fns[j](&path, (char*) &gBits[i], fW, fH, fRowBytes);
-
-                //draw skPath and outline
-                canvas->drawPath(path, fBmpPaint);
-                canvas->translate(1.5f * fW, 0); // translates past previous bitmap
-                canvas->drawPath(path, outlinePaint);
-                canvas->translate(1.5f * fW, 0); // translates past previous bitmap
-            }
-            canvas->restore();
-            canvas->translate(0, 1.5f * fH); //translate to next row
-        }
-
-        // for animated pathEffect
-        fPhase += .01f;
-        this->inval(NULL);
-    }
-
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new SamplePathUtils; }
-static SkViewRegister reg(MyFactory)
-;
diff --git a/samplecode/SamplePictFile.cpp b/samplecode/SamplePictFile.cpp
index 8f827e1..a0bab7d 100644
--- a/samplecode/SamplePictFile.cpp
+++ b/samplecode/SamplePictFile.cpp
@@ -49,7 +49,7 @@
         }
     }
 
-    void onTileSizeChanged(const SkSize &tileSize) SK_OVERRIDE {
+    void onTileSizeChanged(const SkSize &tileSize) override {
         if (tileSize != fTileSize) {
             fTileSize = tileSize;
         }
@@ -57,7 +57,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SkString name("P:");
             const char* basename = strrchr(fFilename.c_str(), SkPATH_SEPARATOR);
@@ -79,7 +79,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    bool onEvent(const SkEvent& evt) SK_OVERRIDE {
+    bool onEvent(const SkEvent& evt) override {
         if (evt.isType("PictFileView::toggleBBox")) {
             fBBox = (BBoxType)((fBBox + 1) % kBBoxTypeCount);
             return true;
@@ -87,7 +87,7 @@
         return this->INHERITED::onEvent(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkASSERT(static_cast<int>(fBBox) < kBBoxTypeCount);
         SkPicture** picture = fPictures + fBBox;
 
diff --git a/samplecode/SamplePicture.cpp b/samplecode/SamplePicture.cpp
index 44c4940..638af05 100644
--- a/samplecode/SamplePicture.cpp
+++ b/samplecode/SamplePicture.cpp
@@ -95,7 +95,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Picture");
             return true;
@@ -123,7 +123,7 @@
 
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         this->drawSomething(canvas);
 
         SkPictureRecorder recorder;
@@ -159,7 +159,7 @@
         (new SkEvent(INVAL_ALL_TYPE, this->getSinkID()))->postDelay(delay);
     }
 
-    bool onEvent(const SkEvent& evt) SK_OVERRIDE {
+    bool onEvent(const SkEvent& evt) override {
         if (evt.isType(INVAL_ALL_TYPE)) {
             this->inval(NULL);
             return true;
diff --git a/samplecode/SampleQuadStroker.cpp b/samplecode/SampleQuadStroker.cpp
index 37663d2..73373a2 100644
--- a/samplecode/SampleQuadStroker.cpp
+++ b/samplecode/SampleQuadStroker.cpp
@@ -175,7 +175,7 @@
     }
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "QuadStroker");
             return true;
@@ -202,7 +202,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onSizeChange() SK_OVERRIDE {
+    void onSizeChange() override {
         fWeightControl.setXYWH(this->width() - 150, 30, 30, 400);
         fErrorControl.setXYWH(this->width() - 100, 30, 30, 400);
         fWidthControl.setXYWH(this->width() -  50, 30, 30, 400);
@@ -445,7 +445,7 @@
         }
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkPath path;
         SkScalar width = fWidth;
 
@@ -551,7 +551,7 @@
     };
 
     virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              unsigned modi) SK_OVERRIDE {
+                                              unsigned modi) override {
         for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) {
             if (hittest(fPts[i], x, y)) {
                 return new MyClick(this, (int)i);
@@ -603,7 +603,7 @@
         return (SkIntToScalar(y) - control.fTop) / control.height() * (max - min) + min;
     }
 
-    bool onClick(Click* click) SK_OVERRIDE {
+    bool onClick(Click* click) override {
         int index = ((MyClick*)click)->fIndex;
         if (index < (int) SK_ARRAY_COUNT(fPts)) {
             fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX),
diff --git a/samplecode/SampleRectanizer.cpp b/samplecode/SampleRectanizer.cpp
index 9d2f6ff..7bb712c 100644
--- a/samplecode/SampleRectanizer.cpp
+++ b/samplecode/SampleRectanizer.cpp
@@ -51,7 +51,7 @@
     }
 
 protected:
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Rectanizer");
             return true;
@@ -77,7 +77,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         if (fCurRandRect < kNumRandRects) {
             if (fCurRectanizer->addRect((*fCurRects)[fCurRandRect].fWidth,
                                         (*fCurRects)[fCurRandRect].fHeight,
diff --git a/samplecode/SampleRegion.cpp b/samplecode/SampleRegion.cpp
index c25b073..3c5fc2e 100644
--- a/samplecode/SampleRegion.cpp
+++ b/samplecode/SampleRegion.cpp
@@ -219,7 +219,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Regions");
             return true;
@@ -321,7 +321,7 @@
         canvas->drawPath(path, paint);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         if (false) { // avoid bit rot, suppress warning
             test_strokerect(canvas);
             return;
@@ -394,12 +394,12 @@
     }
 
     virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              unsigned modi) SK_OVERRIDE {
+                                              unsigned modi) override {
         return fRect.contains(SkScalarRoundToInt(x),
                               SkScalarRoundToInt(y)) ? new Click(this) : NULL;
     }
 
-    bool onClick(Click* click) SK_OVERRIDE {
+    bool onClick(Click* click) override {
         fRect.offset(click->fICurr.fX - click->fIPrev.fX,
                      click->fICurr.fY - click->fIPrev.fY);
         this->inval(NULL);
diff --git a/samplecode/SampleRepeatTile.cpp b/samplecode/SampleRepeatTile.cpp
index bfdfab8..61bc8cd 100644
--- a/samplecode/SampleRepeatTile.cpp
+++ b/samplecode/SampleRepeatTile.cpp
@@ -49,7 +49,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "RepeatTile");
             return true;
@@ -57,7 +57,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkPaint paint;
         make_paint(&paint, SkShader::kRepeat_TileMode);
 
@@ -66,13 +66,13 @@
         canvas->drawPaint(paint);
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
         this->inval(NULL);
 
         return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
-    bool onClick(Click* click) SK_OVERRIDE {
+    bool onClick(Click* click) override {
         return this->INHERITED::onClick(click);
     }
 
diff --git a/samplecode/SampleShaderText.cpp b/samplecode/SampleShaderText.cpp
index 89abfc2..9fdae84 100644
--- a/samplecode/SampleShaderText.cpp
+++ b/samplecode/SampleShaderText.cpp
@@ -72,13 +72,13 @@
     return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount);
 }
 
-static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) {
+static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) {
     SkPoint center0, center1;
     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
-    return SkGradientShader::CreateTwoPointRadial(
+    return SkGradientShader::CreateTwoPointConical(
                             center1, (pts[1].fX - pts[0].fX) / 7,
                             center0, (pts[1].fX - pts[0].fX) / 2,
                             data.fColors, data.fPos, data.fCount, tm);
@@ -87,7 +87,7 @@
 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm);
 
 static const GradMaker gGradMakers[] = {
-    MakeLinear, MakeRadial, MakeSweep, Make2Radial
+    MakeLinear, MakeRadial, MakeSweep, Make2Conical
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/samplecode/SampleShaders.cpp b/samplecode/SampleShaders.cpp
index 5a358c6..093e14f 100644
--- a/samplecode/SampleShaders.cpp
+++ b/samplecode/SampleShaders.cpp
@@ -83,7 +83,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Shaders");
             return true;
@@ -91,7 +91,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         canvas->drawBitmap(fBitmap, 0, 0);
 
         canvas->translate(SkIntToScalar(20), SkIntToScalar(120));
@@ -122,12 +122,12 @@
     }
 
     virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              unsigned modi) SK_OVERRIDE {
+                                              unsigned modi) override {
         this->inval(NULL);
         return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
-    bool onClick(Click* click)  SK_OVERRIDE {
+    bool onClick(Click* click)  override {
         return this->INHERITED::onClick(click);
     }
 
diff --git a/samplecode/SampleSlides.cpp b/samplecode/SampleSlides.cpp
index e115235..83e91bb 100644
--- a/samplecode/SampleSlides.cpp
+++ b/samplecode/SampleSlides.cpp
@@ -197,13 +197,13 @@
     return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount);
 }
 
-static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) {
+static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) {
     SkPoint center0, center1;
     center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
                 SkScalarAve(pts[0].fY, pts[1].fY));
     center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
                 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
-    return SkGradientShader::CreateTwoPointRadial(
+    return SkGradientShader::CreateTwoPointConical(
                                                   center1, (pts[1].fX - pts[0].fX) / 7,
                                                   center0, (pts[1].fX - pts[0].fX) / 2,
                                                   data.fColors, data.fPos, data.fCount, tm);
@@ -211,7 +211,7 @@
 
 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm);
 static const GradMaker gGradMakers[] = {
-    MakeLinear, MakeRadial, MakeSweep, Make2Radial
+    MakeLinear, MakeRadial, MakeSweep, Make2Conical
 };
 
 static void gradient_slide(SkCanvas* canvas) {
@@ -438,7 +438,7 @@
 
     SkPaint paint;
     paint.setDither(true);
-    paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+    paint.setFilterQuality(kLow_SkFilterQuality);
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
         canvas->save();
@@ -697,7 +697,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Slides");
             return true;
@@ -705,12 +705,12 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         this->init();
         gProc[fIndex](canvas);
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
         this->init();
         fIndex = (fIndex + 1) % SK_ARRAY_COUNT(gProc);
         this->inval(NULL);
diff --git a/samplecode/SampleStringArt.cpp b/samplecode/SampleStringArt.cpp
index 8522478..0e711b0 100644
--- a/samplecode/SampleStringArt.cpp
+++ b/samplecode/SampleStringArt.cpp
@@ -19,7 +19,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "StringArt");
             return true;
@@ -27,7 +27,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkScalar angle = fAngle*SK_ScalarPI + SkScalarHalf(SK_ScalarPI);
 
         SkPoint center = SkPoint::Make(SkScalarHalf(this->width()), SkScalarHalf(this->height()));
@@ -42,7 +42,7 @@
             SkPoint rp = SkPoint::Make(length*SkScalarCos(step) + center.fX,
                                        length*SkScalarSin(step) + center.fY);
             path.lineTo(rp);
-            length += SkScalarDiv(angle, SkScalarHalf(SK_ScalarPI));
+            length += angle / SkScalarHalf(SK_ScalarPI);
             step += angle;
         }
         path.close();
@@ -55,7 +55,7 @@
         canvas->drawPath(path, paint);
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
         fAngle = x/width();
         this->inval(NULL);
         return NULL;
diff --git a/samplecode/SampleStrokePath.cpp b/samplecode/SampleStrokePath.cpp
index 00e5821..63809ce 100644
--- a/samplecode/SampleStrokePath.cpp
+++ b/samplecode/SampleStrokePath.cpp
@@ -99,7 +99,7 @@
     SkScalar    fWidth;
     SkPath      fPath;
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
 //        test_blur();
         fWidth = SkIntToScalar(120);
 
@@ -123,7 +123,7 @@
     }
 
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "StrokePath");
             return true;
@@ -145,7 +145,7 @@
         }
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         test_huge_stroke(canvas); return;
         canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
 
@@ -209,7 +209,7 @@
     }
 
     virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              unsigned modi) SK_OVERRIDE {
+                                              unsigned modi) override {
         this->inval(NULL);
         return this->INHERITED::onFindClickHandler(x, y, modi);
     }
diff --git a/samplecode/SampleSubpixelTranslate.cpp b/samplecode/SampleSubpixelTranslate.cpp
index 58ad072..0299363 100644
--- a/samplecode/SampleSubpixelTranslate.cpp
+++ b/samplecode/SampleSubpixelTranslate.cpp
@@ -52,7 +52,7 @@
     SkPoint fCurPos;
 
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "SubpixelTranslate");
             return true;
@@ -60,13 +60,13 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
 
-        static const SkPaint::FilterLevel gLevels[] = {
-            SkPaint::kNone_FilterLevel,
-            SkPaint::kLow_FilterLevel,
-            SkPaint::kMedium_FilterLevel,
-            SkPaint::kHigh_FilterLevel
+        static const SkFilterQuality gQualitys[] = {
+            kNone_SkFilterQuality,
+            kLow_SkFilterQuality,
+            kMedium_SkFilterQuality,
+            kHigh_SkFilterQuality
         };
 
         SkPaint paint;
@@ -74,37 +74,37 @@
         paint.setSubpixelText(true);
 
         paint.setAntiAlias(true);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gLevels); ++i) {
-            paint.setFilterLevel(gLevels[i]);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
+            paint.setFilterQuality(gQualitys[i]);
             SkRect r = SkRect::MakeXYWH( fCurPos.fX + i * (fSize + 10), fCurPos.fY, fSize, fSize );
             canvas->drawBitmapRect( fBM, r, &paint );
         }
 
-        canvas->drawText( "AA Scaled", strlen("AA Scaled"), fCurPos.fX + SK_ARRAY_COUNT(gLevels) * (fSize + 10), fCurPos.fY + fSize/2, paint );
+        canvas->drawText( "AA Scaled", strlen("AA Scaled"), fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fSize + 10), fCurPos.fY + fSize/2, paint );
 
         paint.setAntiAlias(false);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gLevels); ++i) {
-            paint.setFilterLevel(gLevels[i]);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
+            paint.setFilterQuality(gQualitys[i]);
             SkRect r = SkRect::MakeXYWH( fCurPos.fX + i * (fSize + 10), fCurPos.fY + fSize + 10, fSize, fSize );
             canvas->drawBitmapRect( fBM, r, &paint );
         }
-        canvas->drawText( "Scaled", strlen("Scaled"), fCurPos.fX + SK_ARRAY_COUNT(gLevels) * (fSize + 10), fCurPos.fY + fSize + 10 + fSize/2, paint );
+        canvas->drawText( "Scaled", strlen("Scaled"), fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fSize + 10), fCurPos.fY + fSize + 10 + fSize/2, paint );
 
         paint.setAntiAlias(true);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gLevels); ++i) {
-            paint.setFilterLevel(gLevels[i]);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
+            paint.setFilterQuality(gQualitys[i]);
             canvas->drawBitmap( fBM, fCurPos.fX + i * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10), &paint );
         }
 
-        canvas->drawText( "AA No Scale", strlen("AA No Scale"), fCurPos.fX + SK_ARRAY_COUNT(gLevels) * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10) + fSize/2, paint );
+        canvas->drawText( "AA No Scale", strlen("AA No Scale"), fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10) + fSize/2, paint );
 
         paint.setAntiAlias(false);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gLevels); ++i) {
-            paint.setFilterLevel(gLevels[i]);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
+            paint.setFilterQuality(gQualitys[i]);
             canvas->drawBitmap( fBM, fCurPos.fX + i * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10) + fBM.height() + 10, &paint );
         }
 
-        canvas->drawText( "No Scale", strlen("No Scale"), fCurPos.fX + SK_ARRAY_COUNT(gLevels) * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10) + fBM.height() + 10 + fSize/2, paint );
+        canvas->drawText( "No Scale", strlen("No Scale"), fCurPos.fX + SK_ARRAY_COUNT(gQualitys) * (fBM.width() + 10), fCurPos.fY + 2*(fSize + 10) + fBM.height() + 10 + fSize/2, paint );
 
 
         fCurPos.fX += fHorizontalVelocity;
diff --git a/samplecode/SampleText.cpp b/samplecode/SampleText.cpp
index 112573e..aae42c1 100644
--- a/samplecode/SampleText.cpp
+++ b/samplecode/SampleText.cpp
@@ -115,7 +115,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Text");
             return true;
@@ -143,7 +143,7 @@
             pts[i].set(rand->nextUScalar1() * 640, rand->nextUScalar1() * 480);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkAutoCanvasRestore restore(canvas, false);
         {
             SkRect r;
@@ -186,13 +186,13 @@
     }
 
     virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
-                                              unsigned modi) SK_OVERRIDE {
+                                              unsigned modi) override {
         fClickX = x;
         this->inval(NULL);
         return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
-    bool onClick(Click* click) SK_OVERRIDE {
+    bool onClick(Click* click) override {
         return this->INHERITED::onClick(click);
     }
 
diff --git a/samplecode/SampleTextAlpha.cpp b/samplecode/SampleTextAlpha.cpp
index 58e1db2..f344021 100644
--- a/samplecode/SampleTextAlpha.cpp
+++ b/samplecode/SampleTextAlpha.cpp
@@ -36,7 +36,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "TextAlpha");
             return true;
@@ -44,7 +44,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
         SkPaint paint;
         SkScalar    x = SkIntToScalar(10);
@@ -69,11 +69,11 @@
         }
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
         return new Click(this);
     }
 
-    bool onClick(Click* click) SK_OVERRIDE {
+    bool onClick(Click* click) override {
         int y = click->fICurr.fY;
         if (y < 0) {
             y = 0;
diff --git a/samplecode/SampleTextOnPath.cpp b/samplecode/SampleTextOnPath.cpp
index 95ae548..7ea4694 100644
--- a/samplecode/SampleTextOnPath.cpp
+++ b/samplecode/SampleTextOnPath.cpp
@@ -94,7 +94,7 @@
     SkScalar    fHOffset;
 
 protected:
-    void onOnceBeforeDraw() SK_OVERRIDE {
+    void onOnceBeforeDraw() override {
         SkRect r;
         r.set(SkIntToScalar(100), SkIntToScalar(100),
               SkIntToScalar(300), SkIntToScalar(300));
@@ -105,7 +105,7 @@
     }
 
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Text On Path");
             return true;
@@ -113,7 +113,7 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setTextSize(SkIntToScalar(48));
@@ -151,13 +151,13 @@
             this->inval(NULL);
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
         fHints += 1;
         this->inval(NULL);
         return this->INHERITED::onFindClickHandler(x, y, modi);
     }
 
-    bool onClick(Click* click) SK_OVERRIDE {
+    bool onClick(Click* click) override {
         return this->INHERITED::onClick(click);
     }
 
diff --git a/samplecode/SampleTextureDomain.cpp b/samplecode/SampleTextureDomain.cpp
index af511c7..2794a06 100644
--- a/samplecode/SampleTextureDomain.cpp
+++ b/samplecode/SampleTextureDomain.cpp
@@ -47,7 +47,7 @@
         SkRect srcRect;
         SkRect dstRect;
         SkPaint paint;
-        paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        paint.setFilterQuality(kLow_SkFilterQuality);
 
         // Test that bitmap draws from malloc-backed bitmaps respect
         // the constrained texture domain.
diff --git a/samplecode/SampleTiling.cpp b/samplecode/SampleTiling.cpp
index 4f073bd..5f995e2 100644
--- a/samplecode/SampleTiling.cpp
+++ b/samplecode/SampleTiling.cpp
@@ -44,7 +44,7 @@
                   SkShader::TileMode tmx, SkShader::TileMode tmy) {
     SkShader* shader = SkShader::CreateBitmapShader(bm, tmx, tmy);
     paint->setShader(shader)->unref();
-    paint->setFilterLevel(filter ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel);
+    paint->setFilterQuality(filter ? kLow_SkFilterQuality : kNone_SkFilterQuality);
 }
 
 static const SkColorType gColorTypes[] = {
diff --git a/samplecode/SampleUnpremul.cpp b/samplecode/SampleUnpremul.cpp
index 7e5c950..ee3ec82 100644
--- a/samplecode/SampleUnpremul.cpp
+++ b/samplecode/SampleUnpremul.cpp
@@ -49,7 +49,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "unpremul");
             return true;
@@ -75,11 +75,11 @@
         return this->INHERITED::onQuery(evt);
     }
 
-    void onDrawBackground(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawBackground(SkCanvas* canvas) override {
         sk_tool_utils::draw_checkerboard(canvas, 0xFFCCCCCC, 0xFFFFFFFF, 12);
     }
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setTextSize(SkIntToScalar(24));
diff --git a/samplecode/SampleVertices.cpp b/samplecode/SampleVertices.cpp
index 6dac0b5..138ab2e 100644
--- a/samplecode/SampleVertices.cpp
+++ b/samplecode/SampleVertices.cpp
@@ -77,7 +77,7 @@
 
 protected:
     // overrides from SkEventSink
-    bool onQuery(SkEvent* evt) SK_OVERRIDE {
+    bool onQuery(SkEvent* evt) override {
         if (SampleCode::TitleQ(*evt)) {
             SampleCode::TitleR(evt, "Vertices");
             return true;
@@ -87,10 +87,10 @@
 
     SkScalar fScale;
 
-    void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
+    void onDrawContent(SkCanvas* canvas) override {
         SkPaint paint;
         paint.setDither(true);
-        paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        paint.setFilterQuality(kLow_SkFilterQuality);
 
         for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
             canvas->save();
@@ -119,11 +119,11 @@
         }
     }
 
-    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) SK_OVERRIDE {
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override {
         return new Click(this);
     }
 
-    bool onClick(Click* click) SK_OVERRIDE {
+    bool onClick(Click* click) override {
     //    fCurrX = click->fICurr.fX;
     //    fCurrY = click->fICurr.fY;
         this->inval(NULL);
diff --git a/site/dev/METADATA b/site/dev/METADATA
new file mode 100644
index 0000000..8c2e83f
--- /dev/null
+++ b/site/dev/METADATA
@@ -0,0 +1,4 @@
+{
+  "dirOrder": ["chrome", "contrib", "design", "present", "runtime", "testing", "tools", "sheriffing"],
+  "fileOrder": []
+}
diff --git a/site/dev/contrib/c++11.md b/site/dev/contrib/c++11.md
index 4f175b3..0df1506 100644
--- a/site/dev/contrib/c++11.md
+++ b/site/dev/contrib/c++11.md
@@ -61,12 +61,8 @@
 
 A few miscellaneous compile-only bots are actually our current overall weak link:
 
--   Our NaCl builds use an old non-PNaCl toolchain, which is based on GCC
-    4.4.  GCC 4.4 has some support for C++11, but it's not nearly complete.
-    There is no upgrade path except PNaCl; even the very latest NaCl toolchain
-    is GCC 4.4, while PNaCl is based on Clang 3.4 (with complete C++11 support).
 -   Our iOS builds are driven from a Mac 10.7 machine using some unknown old Clang.
     Who knows how old that is or what it supports?  It's probably due for an update.
 
-If we were to eliminate the problems of the NaCl and iOS bots, our ability to
+If we were to eliminate the problems of iOS bots, our ability to
 use C++11 would match Mozilla's list nearly identically.
diff --git a/site/dev/contrib/cqkeywords.md b/site/dev/contrib/cqkeywords.md
index 6c834e2..80faf25 100644
--- a/site/dev/contrib/cqkeywords.md
+++ b/site/dev/contrib/cqkeywords.md
@@ -4,8 +4,10 @@
 COMMIT
 ------
 
-If you want to test your CL through the commit queue but are not ready to commit 
-the changes yet, you can add the following line to the CL description:
+If you are working on experimental code and do not want to risk accidentally
+submitting the change via the CQ, then you can mark it with "COMMIT=false".
+The CQ will immediately abandon the change if it contains this option.
+To do a dry run through the CQ please use Rietveld's [dry run](https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/G5-X0_tfmok) feature.
 
     COMMIT=false
 
@@ -25,7 +27,7 @@
 
     CQ_INCLUDE_TRYBOTS=tryserver.chromium:linux_layout_rel
 
-    CQ_INCLUDE_TRYBOTS=tryserver.skia:Build-Ubuntu13.10-GCC4.8-NaCl-Release-Trybot
+    CQ_INCLUDE_TRYBOTS=tryserver.skia:Build-Mac10.9-Clang-x86_64-Debug
 
     CQ_EXCLUDE_TRYBOTS
 
@@ -94,3 +96,12 @@
     NOTRY=true
 
 The CQ will then not run any try jobs for your change and will commit the CL as soon as the tree is open, assuming the presubmit check passes.
+
+NO_MERGE_BUILDS
+---------------
+
+This keyword prevents the Skia build masters from building this commit with others. Use it when your
+commit may have effects that you don't want mis-attributed to other commits. Just include the keyword
+somewhere in the commit message:
+
+    NO_MERGE_BUILDS
diff --git a/site/dev/contrib/flatten.md b/site/dev/contrib/flatten.md
index c06a14b..192571e 100644
--- a/site/dev/contrib/flatten.md
+++ b/site/dev/contrib/flatten.md
@@ -11,7 +11,7 @@
 
 <!--?prettify?-->
 ~~~~
-virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
+virtual void flatten(SkFlattenableWriteBuffer& buffer) const override {
     this->INHERITED::flatten(buffer);
     // Write any private data that needs to be stored to recreate this object
 }
diff --git a/site/dev/contrib/style.md b/site/dev/contrib/style.md
index 455eed6..177a58b 100644
--- a/site/dev/contrib/style.md
+++ b/site/dev/contrib/style.md
@@ -78,10 +78,19 @@
 }
 ~~~~
 
-Enum values are prefixed with k and have post fix that consists of an underscore
-and singular name of the enum name. The enum itself should be singular for
-exclusive values or plural for a bitfield. If a count is needed it is 
-k<singular enum name>Count and not be a member of the enum (see example):
+Enum values are prefixed with k. Unscoped enum values are post fixed with
+an underscore and singular name of the enum name. The enum itself should be
+singular for exclusive values or plural for a bitfield. If a count is needed it
+is  k&lt;singular enum name&gt;Count and not be a member of the enum (see example):
+
+<!--?prettify?-->
+~~~~
+enum class SkPancakeType {
+     kBlueberry,
+     kPlain,
+     kChocolateChip,
+};
+~~~~
 
 <!--?prettify?-->
 ~~~~
@@ -321,12 +330,12 @@
 };
 ~~~~
 
-Virtual functions that are overridden in derived classes should use SK_OVERRIDE
+Virtual functions that are overridden in derived classes should use override
 (and not the override keyword). The virtual keyword can be omitted.
 
 <!--?prettify?-->
 ~~~~
-void myVirtual() SK_OVERRIDE {
+void myVirtual() override {
 }
 ~~~~
 
@@ -335,7 +344,7 @@
 
 <!--?prettify?-->
 ~~~~
-void myVirtual() SK_OVERRIDE {
+void myVirtual() override {
     ...
     this->INHERITED::myVirtual();
     ...
@@ -343,7 +352,7 @@
 ~~~~
 
 As in the above example, derived classes that redefine virtual functions should
-use SK_OVERRIDE to note that explicitly.
+use override to note that explicitly.
 
 Constructor initializers should be one per line, indented, with punctuation
 placed before the initializer. This is a fairly new rule so much of the existing
@@ -376,7 +385,7 @@
 <!--?prettify?-->
 ~~~~
 this->method();
-Memory Managemt
+Memory Management
 ~~~~
 
 All memory allocation should be routed through SkNEW and its variants. These are
@@ -456,7 +465,7 @@
 
 <!--?prettify?-->
 ~~~~
-if (NULL == x) {  // slightly preferred over if (x)
+if (NULL == x) {  // slightly preferred over if (!x)
    ...
 }
 ~~~~
diff --git a/site/dev/contrib/submit.md b/site/dev/contrib/submit.md
index a66e727..724201d 100644
--- a/site/dev/contrib/submit.md
+++ b/site/dev/contrib/submit.md
@@ -8,7 +8,7 @@
 First create a branch for your changes:
 
 ~~~~
-$ git checkout --track origin/master -b my_feature master
+$ git checkout -b my_feature origin/master
 ~~~~
 
 After making your changes, create a commit
diff --git a/site/dev/present/index.md b/site/dev/present/index.md
new file mode 100644
index 0000000..7452cb6
--- /dev/null
+++ b/site/dev/present/index.md
@@ -0,0 +1,4 @@
+Presentations
+================
+
+Resources providing technical overview of various aspects of the Skia library
diff --git a/site/dev/present/pathops.md b/site/dev/present/pathops.md
new file mode 100644
index 0000000..105f20d
--- /dev/null
+++ b/site/dev/present/pathops.md
@@ -0,0 +1,13 @@
+Path Ops
+========
+
+View the PathOps presentations with speaker notes enabled for full content. 
+
+2014 Updates
+------------
+<iframe
+src="https://docs.google.com/presentation/d/1NbmG5W6VW9h5HtjpCVLx4h6SXW0qW7HIwmSfiwzFbrI/embed?start=false&loop=false&delayms=3000"
+frameborder="0" width="480" height="299" allowfullscreen="true"
+mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>
+
+
diff --git a/site/dev/sheriffing/index.md b/site/dev/sheriffing/index.md
new file mode 100644
index 0000000..052388c
--- /dev/null
+++ b/site/dev/sheriffing/index.md
@@ -0,0 +1,161 @@
+Tree Sheriffs Documentation
+===========================
+
+### Contents ###
+
+*   [What does a sheriff do?](#what_is_a_sheriff)
+    +   [Skia tree](#skia_tree)
+    +   [DEPS rolls](#deps_rolls)
+    +   [Gold and Perf](#gold_and_perf)
+    +   [Documentation](#sheriff_doc)
+*   [View current and upcoming sheriffs](#view_current_upcoming_sheriffs)
+*   [How to swap sheriff shifts](#how_to_swap)
+*   [Tips for sheriffs](#tips)
+    +   [When to file bugs](#when_to_file_bugs)
+    +   [How to close or re-open the tree](#how_close_tree)
+    +   [Compile bot failures automatically close the tree](#tree_closers)
+    +   [How to revert a CL](#how_to_revert)
+    +   [What to do if DEPS roll fails to land](#deps_roll_failures)
+    +   [How to rebaseline](#how_to_rebaseline)
+
+
+<a name="what_is_a_sheriff"></a>
+What does a sheriff do?
+-----------------------
+
+A sheriff keeps an eye on the tree, DEPS rolls, Gold tool and the Perf tool.
+
+Below is a brief summary of what the sheriff does for each task:
+
+<a name="skia_tree"></a>
+### Skia tree
+* Understand the [buildbots infrastructure](https://skia.org/dev/testing/buildbot).
+* Start watching the [status page](https://status.skia.org) for bot breakages.
+* Track down people responsible for breakages and revert broken changes if there is no easy fix.
+* Close and open the [tree](http://skia-tree-status.appspot.com).
+* Keep the builder comments on the [status page](https://status.skia.org) up to date.
+* File or follow up with [BreakingTheBuildbots bugs](https://code.google.com/p/skia/issues/list?q=label:BreakingTheBuildbots). See the tip on [when to file bugs](#when_to_file_bugs).
+
+<a name="deps_rolls"></a>
+### DEPS rolls
+* Ensure that [AutoRoll Bot](https://skia-tree-status.appspot.com/set_arb_action)'s DEPS rolls land successfully.
+
+<a name="gold_and_perf"></a>
+### Gold and Perf
+* Pay attention for new [Perf](https://perf.skia.org/) and [Gold](https://gold.skia.org/) alerts (by clicking on the bell at the top right of the [status page](https://status.skia.org)).
+* The sheriff's duty here is to make sure that when developers introduce new images or new perf regressions, that they are aware of what happened, and they use these tools to take appropriate action.
+ 
+<a name="sheriff_doc"></a>
+### Documentation
+* Improve/update this documentation page for future sheriffs, especially the [Tips section](#tips).
+
+In general, sheriffs should have a strong bias towards actions that keep the tree green and then open; if a simple revert can fix the problem, the sheriff <b>should revert first and ask questions later</b>.
+
+
+<a name="view_current_upcoming_sheriffs"></a>
+View current and upcoming sheriffs
+----------------------------------
+
+The list of sheriffs is specified in the [skia-tree-status web app](https://skia-tree-status.appspot.com/sheriff). The current sheriff is highlighted in green.
+The banner on the top of the [status page](https://status.skia.org) also displays the current sheriff.
+
+
+<a name="how_to_swap"></a>
+How to swap sheriff shifts
+--------------------------
+
+If you need to swap shifts with someone (because you are out sick or on vacation), please get approval from the person you want to swap with. Then send an email to skiabot@google.com to have someone make the database change (or directly ping rmistry@).
+
+
+<a name="tips"></a>
+Tips for sheriffs
+-----------------
+
+<a name="when_to_file_bugs"></a>
+### When to file bugs
+
+Pay close attention to the "Failures" view in the [status page](https://status.skia.org).
+Look at all existing [BreakingTheBuildbots bugs](https://code.google.com/p/skia/issues/list?q=label:BreakingTheBuildbots). If the list is kept up to date then it should accurately represent everything that is causing failures. If it does not, then please file/update bugs accordingly.
+
+
+<a name="how_close_tree"></a>
+### How to close or re-open the tree
+
+1. Go to [skia-tree-status.appspot.com](skia-tree-status.appspot.com).
+2. Change the status.
+ *  To close the tree, include the word "closed" in the status.
+ * To open the tree, include the word "open" in the status.
+ * To caution the tree, include the word "caution" in the status.
+
+
+<a name="how_to_submit_when_tree_closed"></a>
+### How to submit when the tree is closed
+
+* Submit manually using the "git cl land" with the --bypass-hooks flag.
+* Add "NOTREECHECKS=true" to your CL description and use the CQ as usual.
+
+
+<a name="tree_closers"></a>
+### Compile bot failures automatically close the tree
+
+A failure of the build steps in all compile bots automatically closes the tree. Sheriffs will have to manually reopen the tree manually when they deem the problem fixed.
+
+Note: The tree is not closed automatically if the last run of the failed compile builder had the same failing step. The tree is also not closed if the tree was automatically closed less than 10 mins ago. If the tree is already closed then no action is taken.
+
+
+<a name="how_to_revert"></a>
+### How to revert a CL
+
+See the revert documentation [here](https://skia.org/dev/contrib/revert).
+
+
+<a name="deps_roll_failures"></a>
+### What to do if DEPS roll fails to land
+
+A common cause of DEPS roll failures are layout tests. Find the offending Skia CL by examining the commit hash range in the DEPS roll and revert (or talk to the commit author if they are available). If you do revert then keep an eye on the next DEPS roll to make sure it succeeds.
+
+If a Skia CL changes layout tests, but the new images look good, the tests need to be rebaselined. See [Rebaseline Layout Tests](#how_to_rebaseline).
+
+<a name="how_to_rebaseline"></a>
+### Rebaseline Layout Tests (i.e., add suppressions)
+
+* First create a Chromium bug:
+  * goto [crbug.com](https://crbug.com)
+  * Make sure you’re logged in with your Chromium credentials
+  * Click “New Issue”
+  * Summary: “Skia image rebaseline”
+  * Description:
+      * DEPS roll #,
+      * Helpful message about what went wrong (e.g., “Changes to how lighting is scaled in Skia r#### changed the following images:”)
+      * Layout tests effected
+      * You should copy the list of affected from stdio of the failing bot
+  * Status: Assigned
+  * Owner: yourself
+  * cc: reed@, bsalomon@, robertphillips@ & developer responsible for changes
+  * Labels: OS-All & Cr-Blink-LayoutTests
+  * If it is filter related, cc senorblanco@
+
+* (Dispreferred but faster) Edit [skia/skia_test_expectations.txt](https://chromium.googlesource.com/chromium/src/+/master/skia/skia_test_expectations.txt)
+  * Add # comment about what has changed (I usually paraphrase the crbug text)
+  * Add line(s) like the following after the comment:
+      * crbug.com/<bug#youjustcreated> foo/bar/test-name.html [ ImageOnlyFailure ]
+  * Note: this change is usually done in the DEPS roll patch itself
+
+* (Preferred but slower) Make a separate Blink patch by editing LayoutTests/TestExpectations
+  * Add # comment about what has changed (I usually paraphrase the crbug text)
+  * Add line(s) like the following after the comment:
+      * crbug.com/<bug#youjustcreated> foo/bar/test-name.html [ NeedsManualRebaseline ]
+  * Commit the patch you created and wait until it lands and rolls into Chrome
+
+* Retry the DEPS roll (for the 1st/dispreferred option this usually means just retrying the layout bots)
+* Make a Blink patch by editing LayoutTests/TestExpectations
+  * Add # comment about what has changed 
+  * Add line(s) like the following after the comment:
+      * crbug.com/<bug#youjustcreated> foo/bar/test-name.html [ NeedsRebaseline ]
+        * (if you took the second option above you can just edit the existing line(s))
+
+* If you took the first/dispreferred option above:
+  * Wait for the Blink patch to roll into Chrome
+  * Create a Chrome patch that removes your suppressions from skia/skia_test_expectations.txt
+
+
diff --git a/site/dev/testing/ct-system.png b/site/dev/testing/ct-system.png
new file mode 100644
index 0000000..299630f
--- /dev/null
+++ b/site/dev/testing/ct-system.png
Binary files differ
diff --git a/site/dev/testing/ct.md b/site/dev/testing/ct.md
new file mode 100644
index 0000000..6c3c9e8
--- /dev/null
+++ b/site/dev/testing/ct.md
@@ -0,0 +1,102 @@
+Cluster Telemetry
+=================
+
+### Contents ###
+
+*   [Overview](#overview)
+*   [Framework Usage](#framework_usage)
+*   [System Architecture](#system_architecture)
+    +   [System Diagram](#system_diagram)
+    +   [Detailed explanation of steps](#detailed_explanation)
+*   [Code](#code)
+*   [Contact Us](#contact_us)
+
+<a name="overview"></a>
+Overview
+--------
+
+Cluster Telemetry allows you to run [telemetry](https://www.chromium.org/developers/telemetry)'s benchmarks, lua scripts and other tasks using multiple repository patches through Alexa's [top 1 million](http://s3.amazonaws.com/alexa-static/top-1m.csv.zip) web pages.
+Developers can use the framework to measure the performance of their patch against the top subset of the internet on both Desktop and Android.
+
+SKP files are a binary format for the draw commands Chromium sends to Skia for rasterization. The goal of the project started off with wanting to collect a large repository of 10k SKP files. This repository, after incremental changes in approaches, has since grown to ~900k and now supports running all telemetry benchmarks. The top level feature request of this project was [skia:1268](https://code.google.com/p/skia/issues/detail?id=1268).
+
+A web application has been created on App Engine that automates the process of capturing new archives and running telemetry benchmarks at a click of a button; results are emailed to the requester and the web application contains complete history of runs with links to results. You can run telemetry benchmarks at http://ct.skia.org.
+
+The framework also contains the ability to run lua scripts on the SKP repository to scrape web pages. It only takes a few minutes to run a lua scrapping script on ~900k SKP files.
+
+These are the different parts of the framework:
+
+* Chromium Perf Tryserver. Documentation [here](https://www.chromium.org/developers/cluster-telemetry). Webpage [here](http://skia-tree-status.appspot.com/skia-telemetry/chromium_try).
+* Skia Correctness Tryserver. Documentation [here](http://goto/skiatryclustertelemetry). Webpage [here](http://skia-tree-status.appspot.com/skia-telemetry/skia_try).
+* Run Lua Scripts. Documentation about lua bindings is [here](https://skia.org/user/special/lua). Webpage [here](http://skia-tree-status.appspot.com/skia-telemetry/lua_script).
+
+
+<a name="framework_usage"></a>
+Framework Usage
+---------------
+
+The Chromium Perf tryserver in CT has been used to gather perf data over the top 10k web pages for the following Chromium projects:
+
+* Slimming paint
+* Performance data for layer squashing and compositing overlap map
+* SkPaint in Graphics Context
+* Culling
+* New paint dictionary
+
+blink-dev threads discussing how to make Chrome faster using the results gathered from CT:
+
+* [Main thread attribution for top million sites](https://groups.google.com/a/chromium.org/d/msg/blink-dev/-R47hzmkdig/xILVgczlKgQJ)
+* [Layout time for top 10k sites](https://groups.google.com/a/chromium.org/d/msg/blink-dev/fkRYGcIQN1g/_uYcAt6G8XsJ)
+* [Perf profile for top million sites](https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/8qd5SmLF5n0)
+
+Documents detailing data generated by the framework:
+
+* [Loading measurement: alexa top 1,000](https://docs.google.com/a/chromium.org/document/d/1ca_Q7xePmCRqaYnHe7vkpCmKNFNLdDXvzgtUPt9iG8w/edit)
+* [Loading measurement: alexa top million](https://docs.google.com/a/google.com/document/d/1hDDUUNE5OUV8eCjtOj7Ow6EZ2DSBCTjQirnA3Rp5pOg/edit)
+* [Loading measurement: alexa top million netsim](https://docs.google.com/a/google.com/document/d/1cpLSSYpqi4SprkJcVxbS7af6avKM0qc-imxvkexmCZs/edit)
+* [Perf profile - alexa top million sites](https://docs.google.com/a/google.com/document/d/1di__87watociuZj_dm22Cn72UM2xsZBXixbl8TCFQmw/edit)
+
+The framework has also been used to run multiple lua scripts to scrape the SKP repositories for the the following:
+chars-vs-glyphs, bitmap transform types, gradient color counter, 3 color gradient checks, etc.
+This has been very useful for the Skia team to help determine which parts of the library to optimize and focus on.
+
+All runs are recorded [here](http://skia-tree-status.appspot.com/skia-telemetry/all_tasks).
+
+
+<a name="system_architecture"></a>
+System Architecture
+------------------
+
+<a name="system_diagram"></a>
+### System Diagram
+
+![CT System Diagram](ct-system.png)
+
+
+<a name="detailed_explanation"></a>
+### Detailed explanation of steps
+
+1. User submits an Admin task (rebuild chrome, recreate pagesets, recreate webpage archives), Lua script task, or Telemetry benchmark task using the App Engine web application [here](http://ct.skia.org).
+
+2. Each task is exposed by the web application in JSON. The CT master polls the web application and picks up new tasks.
+
+3. The master pushes new tasks to all the workers using the master scripts [here](https://skia.googlesource.com/buildbot/+/master/ct/go/master_scripts/) (in a new process so that the poller is not blocked). The master scripts then check to see when the workers are done with the requested task.
+
+4. The workers execute the task using the worker scripts [here](https://skia.googlesource.com/buildbot/+/master/ct/go/worker_scripts/). All generated artifacts (CSV files, logs, SKP files, archives, etc) are then stored locally and copied to Google Storage.
+
+5. The master scripts periodically check the workers to see when they are done with the requested task. Once the workers are done the generated artifacts are then read from Google Storage and consolidated (if required).
+
+6. The master scripts then email results of the task to the user who requested it. The master scripts also update the status of the task to completed on App Engine.
+
+
+<a name="code"></a>
+Code
+----
+
+Cluster Telemetry is primarily written in Go with a few python scripts. The framework lives in [master/ct](https://skia.googlesource.com/buildbot/+/master/ct) and the appengine code lives in [master/appengine_scripts](https://skia.googlesource.com/buildbot/+/master/appengine_scripts/skia-tree-status).
+
+<a name="contact_us"></a>
+Contact Us
+----------
+
+If you have questions, please email <cluster-telemetry@chromium.org> or contact rmistry@ directly.
diff --git a/site/dev/testing/tests.md b/site/dev/testing/tests.md
index fbe84df..11dea34 100644
--- a/site/dev/testing/tests.md
+++ b/site/dev/testing/tests.md
@@ -63,3 +63,13 @@
         ./gyp_skia
         ninja -C out/Debug dm
         out/Debug/dm --match newgmtest
+
+4.  Run the GM inside SampleApp:
+
+        ./gyp_skia
+        ninja -C out/Debug SampleApp
+        out/Debug/SampleApp --slide GM:newgmtest
+
+    On MacOS, try this:
+
+        out/Debug/SampleApp.app/Contents/MacOS/SampleApp --slide GM:newgmtest
diff --git a/site/roles.md b/site/roles.md
index 97d48cb..9cdec69 100644
--- a/site/roles.md
+++ b/site/roles.md
@@ -9,4 +9,59 @@
 For more information on ways to get involved in Skia development, see the
 [Contributing to Skia page](/dev/contrib/).
 
-  ![roles.png](roles.png)
+<div>
+<style scoped><!--
+#rolestable {border-collapse:collapse;}
+#rolestable tr th, #rolestable tr td {border-right:white 2px solid;padding:0 5px;}
+#rolestable tr td {height:10ex;}
+#rolestable tr td p {margin:5px 0; padding:0;}
+--></style>
+<table id="rolestable">
+ <tr>
+  <th></th>
+  <th>Source Code &amp; Documentation</th>
+  <th>Code Reviews</th>
+  <th>Bug Tracker</th>
+  <th></th>
+ </tr>
+ <tr style="background-color:#e6b8af;color:black;">
+  <th>Committer</th>
+  <td>
+    <p>force-commit</p>
+  </td>
+  <td>
+    <p>approve changes</p>
+  </td>
+  <td></td>
+ </tr>
+ <tr style="background-color:#ffe599;color:black;">
+  <th>Contributor<br>(and above)</th>
+  <td></td>
+  <td>
+    <p>launch try jobs</p>
+  </td>
+  <td>
+    <p>change bug status</p>
+    <p>edit bug labels</p>
+    <p>own bugs</p>
+  </td>
+ </tr>
+ <tr style="background-color:#d0e0e3;color:black;">
+  <th>Developer<br>(and above)</th>
+  <td>
+    <p>download</p>
+    <p>view history</p>
+  </td>
+  <td>
+    <p>commit changes (once approved)</p>
+    <p>upload changes for approval</p>
+    <p>view</p>
+  </td>
+  <td>
+    <p>add comments to existing bugs</p>
+    <p>file new bugs</p>
+    <p>view bugs</p>
+  </td>
+ </tr>
+</table>
+</div>
diff --git a/site/roles.png b/site/roles.png
deleted file mode 100644
index 9c11068..0000000
--- a/site/roles.png
+++ /dev/null
Binary files differ
diff --git a/site/user/quick/android.md b/site/user/quick/android.md
index 833ec12..9df11a8 100644
--- a/site/user/quick/android.md
+++ b/site/user/quick/android.md
@@ -139,8 +139,19 @@
 
     ./platform_tools/android/bin/android_install_app
 
-Finally to run the application you must navigate to the Skia Samples
-application using the application launcher on your device.
+Finally to run the application you can either navigate to the Skia Samples
+application using the application launcher on your device or from the command
+line.  The command line option allows you to pass additional details to the
+application (similiar to other operating system) that specify where to find
+skp files and other resources.
+
+    ./platform_tools/android/bin/android_launch_app --resourcePath /data/local/tmp/resources
+
+By default if no additional parameters are specified the app will use the default
+params...
+
+    --resourcePath /data/local/tmp/skia_resoures 
+    --pictureDir /data/local/tmp/skia_skp
 
 Build tools
 -----------
diff --git a/site/user/quick/linux.md b/site/user/quick/linux.md
index ef4f95b..7a172a5 100644
--- a/site/user/quick/linux.md
+++ b/site/user/quick/linux.md
@@ -1,6 +1,42 @@
 Linux
 =====
 
+Quickstart
+----------
+
+1.  Install depot tools.
+
+    <!--?prettify lang=sh?-->
+
+        git clone 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'
+        export PATH="${PWD}/depot_tools:${PATH}"
+
+2.  Get Skia.
+
+    <!--?prettify lang=sh?-->
+
+        git clone 'https://skia.googlesource.com/skia'
+        cd skia
+
+3.  Install Dependencies (may require sudo).
+
+    <!--?prettify lang=sh?-->
+
+        tools/install_dependencies.sh
+
+4.  Build.
+
+    <!--?prettify lang=sh?-->
+
+        bin/sync-and-gyp && ninja -C out/Debug
+
+5.  Run DM (the Skia test app) and SampleApp.
+
+    <!--?prettify lang=sh?-->
+
+        out/Debug/dm
+        out/Debug/SampleApp
+
 Prerequisites
 -------------
 
diff --git a/site/user/quick/macos.md b/site/user/quick/macos.md
index ed74551..c9d09a3 100644
--- a/site/user/quick/macos.md
+++ b/site/user/quick/macos.md
@@ -1,6 +1,38 @@
 Mac OS X
 ========
 
+Quickstart
+----------
+
+1.  Install [XCode](http://developer.apple.com/xcode/).
+
+2.  Install depot tools.
+
+    <!--?prettify lang=sh?-->
+
+        git clone 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'
+        export PATH="${PWD}/depot_tools:${PATH}"
+
+3.  Get Skia.
+
+    <!--?prettify lang=sh?-->
+
+        git clone 'https://skia.googlesource.com/skia'
+        cd skia
+
+4.  Build.
+
+    <!--?prettify lang=sh?-->
+
+        bin/sync-and-gyp && ninja -C out/Debug
+
+5.  Run DM (the Skia test app) and SampleApp.
+
+    <!--?prettify lang=sh?-->
+
+        out/Debug/dm
+        open out/Debug/SampleApp.app
+
 Prerequisites
 -------------
 
diff --git a/site/user/quick/nacl.md b/site/user/quick/nacl.md
deleted file mode 100644
index 2d88d88..0000000
--- a/site/user/quick/nacl.md
+++ /dev/null
@@ -1,93 +0,0 @@
-NaCl (Experimental)
-===================
-
-Important Notes
----------------
-
-  * This process has only been verified to work on Linux
-  * Skia for NaCl is new and currently under development.  Therefore, some features are not (yet) supported:
-    * GPU backend
-    * Fonts - Currently, NaCl has no way to access system fonts.  This means
-      that text drawn in Skia will not display.  A Pepper font API is in the
-      works and should be available in the near future, but for now your best
-      bet is to either package font data with your nexe or to send font data
-      from javascript to your plugin at runtime.  Note that this will be the
-      case with any graphics library in NaCl until the font API is finished.
-
-Prerequisites
--------------
-
-Execute the following commands in whatever directory you want to hold the NaCl SDK directory:
-
-    wget http://storage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/nacl_sdk.zip
-    unzip nacl_sdk.zip
-    rm nacl_sdk.zip
-    nacl_sdk/naclsdk update pepper_32
-    export NACL_SDK_ROOT=/path/to/nacl_sdk
-
-Check out the Skia source
--------------------------
-
-We use the "gclient" script (part of the depot_tools toolkit) to manage the
-Skia source code. Follow the instructions at
-http://www.chromium.org/developers/how-tos/depottools to get the gclient
-script from depot_tools.
-
-Instead of checking out trunk directly you will use gclient to checkout the
-nacl directory, which will automatically pull the trunk directory for you.
-Execute the following commands in whatever directory you want to be the root
-for your Skia on NaCl development:
-
-    gclient config https://skia.googlesource.com/skia.git
-    gclient sync
-
-Building the Skia libraries for NaCl
-------------------------------------
-
-The nacl_make script is used to compile Skia targets for NaCl.  It sets the
-appropriate environment variables, calls GYP to generate makefiles, and runs
-Make to build both 32 and 64-bit targets as required by NaCl.  To build the
-Skia libraries, run the following from the trunk directory:
-
-    platform_tools/nacl/nacl_make skia_lib
-
-This will result in a set of static libraries being built into the out/nacl32
-and out/nacl64 directories.  You can use these libraries in external NaCl
-apps.
-
-Building and running Skia's Apps in NaCl (Experimental)
--------------------------------------------------------
-
-It is possible to run some of Skia's console apps in the browser.
-
-### Skia Unit Tests
-
-Build Skia tests from the trunk directory:
-
-    platform_tools/nacl/bin/nacl_make tests
-
-This will build the tests executable.  We include a tiny HTTP server (borrowed
-from the NaCl SDK) in order to run the apps:
-
-    cd platform_tools/nacl
-    ./httpd.py
-
-The HTTP server runs on port 5103 by default.  In Chrome, navigate to
-`http://localhost:5103/` and click on the link for "unit tests."  After the
-module downloads, you should see the tests begin to run.
-
-### Sample App
-
-The sample app relies on the GPU backend.  Therefore, it will compile but will not yet run.
-
-    platform_tools/nacl/bin/nacl_make SampleApp
-
-You can access the sample app at http://localhost:5103/SampleApp.
-
-### Debugger
-
-The debugger is currently in a partially-working state:
-
-    platform_tools/nacl/bin/nacl_make debugger
-
-You can access it at http://localhost:5103/debugger.
diff --git a/site/user/sample/hello.md b/site/user/sample/hello.md
deleted file mode 100644
index d917cf7..0000000
--- a/site/user/sample/hello.md
+++ /dev/null
@@ -1,129 +0,0 @@
-Creating a Skia "Hello World!"
-==============================
-
-This tutorial will guide you through the steps to create a Hello World Desktop
-application in Skia.
-
-Who this tutorial is for:
--------------------------
-
-This will be useful to you if you want to create a window that can receive
-events and to which you can draw with Skia.
-
-Step 1: Check out and build Skia
---------------------------------
-
-Follow the instructions for: Linux, Mac OS X or Windows. The framework that we
-will be using does not currently support other platforms.
-
-Once you have a working development environment, we can move on to the next step.
-
-Step 2: Build the included HelloSkia Example
---------------------------------------------
-
-We will be using the "SkiaExamples" framework. You can find it in the
-experimental/SkiaExamples directory. There is an included HelloWorld example,
-and we will start by building it before we go ahead and create our own.
-
-### On Mac OS X
-
-Run `GYP_GENERATORS="ninja" ./gyp_skia`
-This will generate a ninja target, and `ninja -C out/Debug SkiaExamples` will create `SkiaExamples.app`
-
-### On Linux:
-Run `GYP_GENERATORS="ninja" ./gyp_skia`
-
-Build the SkiaExamples target:
-
-    ninja -C out/Release SkiaExamples
-
-The SkiaExamples binary should be in `out/Release/SkiaExamples`
-
-### On Windows
-
-Run `./gyp_skia`
-
-There should be a Visual Studio project `out/gyp/SkiaExamples.vcproj`  with
-which you can build the SkiaExamples binary.
-
-### Run the SkiaExamples.
-
-You should see a window open displaying rotating text and some geometry.
-
-Step 3: Create your own Sample
-------------------------------
-
-Create a file `experimental/SkiaExamples/Tutorial.cpp` within the Skia tree.  Copy the following code:
-
-<!--?prettify lang=cc?-->
-
-~~~~
-#include "SkExample.h"
-#include "SkDevice.h"
-
-class HelloTutorial : public SkExample {
-    public:
-        HelloTutorial(SkExampleWindow* window)
-            : SkExample(window)
-        {
-            fName = "Tutorial";  // This is how Skia will find your example.
-
-            fWindow->setupBackend(SkExampleWindow::kGPU_DeviceType);
-           // Another option is the CPU backend:  fWindow->setupBackend(kRaster_DeviceType);
-        }
-
-    protected:
-        void draw(SkCanvas* canvas) SK_OVERRIDE {
-            // Clear background
-            canvas->drawColor(SK_ColorWHITE);
-
-            SkPaint paint;
-            // Draw a message with a nice black paint.
-            paint.setFlags(SkPaint::kAntiAlias_Flag);
-            paint.setColor(SK_ColorBLACK);
-            paint.setTextSize(SkIntToScalar(20));
-
-            static const char message[] = "Hello World!";
-
-            // Translate and draw the text:
-            canvas->save();
-            canvas->translate(SkIntToScalar(50), SkIntToScalar(100));
-            canvas->drawText(message, strlen(message), SkIntToScalar(0), SkIntToScalar(0), paint);
-            canvas->restore();
-
-            // If you ever want to do animation. Use the inval method to trigger a redraw.
-            this->fWindow->inval(NULL);
-        }
-};
-
-static SkExample* MyFactory(SkExampleWindow* window) {
-    return new HelloTutorial(window);
-}
-static SkExample::Registry registry(MyFactory);
-~~~~
-
-
-Step 4: Compile and run SkiaExamples with your Sample
------------------------------------------------------
-
-Here is what you have to do to compile your example. There will be
-functionality to make this easier, but for now, this is what you have to do:
-
-*   Open `gyp/experimental.gyp` and look for the `SkiaExamples` target.
-
-*   In the 'sources' section of the SkiaExampels target, add
-    `../experimental/SkiaExamples/Tutorial.cpp` to the list of sources.
-
-*   Repeat Step 2 to update our gyp targets and build our example.
-
-*   Run the SkiaExamples, specifying the name of our new example:
-
-        $> out/Release/SkiaExamples --match Tutorial
-
-Step 5: How to iterate through multiple examples
-------------------------------------------------
-
-If you did not specify an example with the `--match` flag, or if your match
-string matches more than one example, you can use the *n* key to iterate
-through all of the examples registered.
-
diff --git a/src/animator/SkAnimate.h b/src/animator/SkAnimate.h
index 225d27d..ee391fc 100644
--- a/src/animator/SkAnimate.h
+++ b/src/animator/SkAnimate.h
@@ -19,11 +19,11 @@
     DECLARE_MEMBER_INFO(Animate);
     SkAnimate();
     virtual ~SkAnimate();
-    int components() SK_OVERRIDE;
+    int components() override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    void onEndElement(SkAnimateMaker& maker) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& maker) override;
 protected:
     bool resolveCommon(SkAnimateMaker& );
     int fComponents;
diff --git a/src/animator/SkAnimateBase.cpp b/src/animator/SkAnimateBase.cpp
index 3515ed5..82f777e 100644
--- a/src/animator/SkAnimateBase.cpp
+++ b/src/animator/SkAnimateBase.cpp
@@ -91,7 +91,7 @@
         SkDebugf("to=\"%s\" ", to.c_str());
     }
     if (begin != 0) {
-        SkDebugf("begin=\"%g\" ", SkScalarToFloat(SkScalarDiv(begin,1000)));
+        SkDebugf("begin=\"%g\" ", begin * 0.001);
     }
 }
 #endif
diff --git a/src/animator/SkAnimateBase.h b/src/animator/SkAnimateBase.h
index 6927297..0da4af1 100644
--- a/src/animator/SkAnimateBase.h
+++ b/src/animator/SkAnimateBase.h
@@ -24,26 +24,26 @@
     SkAnimateBase();
     virtual ~SkAnimateBase();
     virtual int components();
-    SkDisplayable* deepCopy(SkAnimateMaker* ) SK_OVERRIDE;
-    void dirty() SK_OVERRIDE;
+    SkDisplayable* deepCopy(SkAnimateMaker* ) override;
+    void dirty() override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
     int entries() { return fValues.count() / components(); }
     virtual bool hasExecute() const;
     bool isDynamic() const { return SkToBool(fDynamic); }
-    SkDisplayable* getParent() const SK_OVERRIDE;
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
+    SkDisplayable* getParent() const override;
+    bool getProperty(int index, SkScriptValue* value) const override;
     SkMSec getStart() const { return fStart; }
     SkOperand* getValues() { return fValues.begin(); }
     SkDisplayTypes getValuesType() { return fValues.getType(); }
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& ) override;
     void packARGB(SkScalar [], int count, SkTDOperandArray* );
     virtual void refresh(SkAnimateMaker& );
     void setChanged(bool changed) { fChanged = changed; }
     void setHasEndEvent() { fHasEndEvent = true; }
-    bool setParent(SkDisplayable* ) SK_OVERRIDE;
-    bool setProperty(int index, SkScriptValue& value) SK_OVERRIDE;
+    bool setParent(SkDisplayable* ) override;
+    bool setProperty(int index, SkScriptValue& value) override;
     void setTarget(SkAnimateMaker& );
     virtual bool targetNeedsInitialization() const;
 protected:
diff --git a/src/animator/SkAnimateField.cpp b/src/animator/SkAnimateField.cpp
index 7aebb78..c6676e5 100644
--- a/src/animator/SkAnimateField.cpp
+++ b/src/animator/SkAnimateField.cpp
@@ -41,7 +41,7 @@
             SkDebugf("mirror=\"true\" ");
         if (fReset)
             SkDebugf("reset=\"true\" ");
-        SkDebugf("dur=\"%g\" ", SkScalarToFloat(SkScalarDiv(dur,1000)));
+        SkDebugf("dur=\"%g\" ", dur * 0.001);
         if (repeat != SK_Scalar1)
             SkDebugf("repeat=\"%g\" ", SkScalarToFloat(repeat));
         //if (fHasValues)
diff --git a/src/animator/SkAnimateSet.cpp b/src/animator/SkAnimateSet.cpp
index d146118..ec06a9e 100644
--- a/src/animator/SkAnimateSet.cpp
+++ b/src/animator/SkAnimateSet.cpp
@@ -39,7 +39,7 @@
 void SkSet::dump(SkAnimateMaker* maker) {
     INHERITED::dump(maker);
     if (dur != 1) {
-        SkDebugf("dur=\"%g\" ", SkScalarToFloat(SkScalarDiv(dur,1000)));
+        SkDebugf("dur=\"%g\" ", dur * 0.001);
     }
     //don't want double />\n's
     SkDebugf("/>\n");
diff --git a/src/animator/SkAnimateSet.h b/src/animator/SkAnimateSet.h
index c1ccde3..32a9197 100644
--- a/src/animator/SkAnimateSet.h
+++ b/src/animator/SkAnimateSet.h
@@ -16,10 +16,10 @@
     DECLARE_MEMBER_INFO(Set);
     SkSet();
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
-    void refresh(SkAnimateMaker& ) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& ) override;
+    void refresh(SkAnimateMaker& ) override;
 private:
     typedef SkAnimate INHERITED;
 };
diff --git a/src/animator/SkAnimatorScript.cpp b/src/animator/SkAnimatorScript.cpp
index df2e563..f4ea518 100644
--- a/src/animator/SkAnimatorScript.cpp
+++ b/src/animator/SkAnimatorScript.cpp
@@ -235,7 +235,7 @@
             if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction)
                 value->fOperand.fS32 = *(int32_t*) info->memberData(displayable);   // OK for SkScalar too
             if (type == SkType_MSec) {
-                value->fOperand.fScalar = SkScalarDiv((SkScalar) value->fOperand.fS32, 1000); // dividing two ints is the same as dividing two scalars
+                value->fOperand.fScalar = value->fOperand.fS32 * 0.001f;
                 type = SkType_Float;
             }
             break;
diff --git a/src/animator/SkAnimatorScript2.cpp b/src/animator/SkAnimatorScript2.cpp
index 80ae0c6..28912fb 100644
--- a/src/animator/SkAnimatorScript2.cpp
+++ b/src/animator/SkAnimatorScript2.cpp
@@ -412,7 +412,7 @@
             if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction)
                 value->fS32 = *(int32_t*) info->memberData(displayable);    // OK for SkScalar too
             if (type == SkType_MSec) {
-                value->fScalar = SkScalarDiv((SkScalar) value->fS32, 1000); // dividing two ints is the same as dividing two scalars
+                value->fScalar = value->fS32 * 0.001f;
                 type = SkType_Float;
             }
             break;
diff --git a/src/animator/SkDisplayAdd.h b/src/animator/SkDisplayAdd.h
index 9e0ff30..c106d86 100644
--- a/src/animator/SkDisplayAdd.h
+++ b/src/animator/SkDisplayAdd.h
@@ -22,15 +22,15 @@
         kMode_immediate
     };
 
-    SkDisplayable* deepCopy(SkAnimateMaker* ) SK_OVERRIDE;
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    SkDisplayable* deepCopy(SkAnimateMaker* ) override;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    bool enable(SkAnimateMaker& ) SK_OVERRIDE;
-    bool hasEnable() const SK_OVERRIDE;
-    void initialize() SK_OVERRIDE;
-    bool isDrawable() const SK_OVERRIDE;
+    bool enable(SkAnimateMaker& ) override;
+    bool hasEnable() const override;
+    void initialize() override;
+    bool isDrawable() const override;
 protected:
 //  struct _A {
         Mode mode;
diff --git a/src/animator/SkDisplayApply.h b/src/animator/SkDisplayApply.h
index 9e7e12f..12cf6ce 100644
--- a/src/animator/SkDisplayApply.h
+++ b/src/animator/SkDisplayApply.h
@@ -38,41 +38,41 @@
     void appendActive(SkActive* );
     void applyValues(int animatorIndex, SkOperand* values, int count,
         SkDisplayTypes , SkMSec time);
-    bool contains(SkDisplayable*) SK_OVERRIDE;
+    bool contains(SkDisplayable*) override;
 //  void createActive(SkAnimateMaker& );
-    SkDisplayable* deepCopy(SkAnimateMaker* ) SK_OVERRIDE;
+    SkDisplayable* deepCopy(SkAnimateMaker* ) override;
     void disable();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    bool enable(SkAnimateMaker& ) SK_OVERRIDE;
+    bool enable(SkAnimateMaker& ) override;
     void enableCreate(SkAnimateMaker& );
     void enableDynamic(SkAnimateMaker& );
     void endSave(int index);
     Mode getMode() { return mode; }
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
+    bool getProperty(int index, SkScriptValue* value) const override;
     SkADrawable* getScope() { return scope; }
     void getStep(SkScriptValue* );
     SkADrawable* getTarget(SkAnimateBase* );
     bool hasDelayedAnimator() const;
-    bool hasEnable() const SK_OVERRIDE;
+    bool hasEnable() const override;
     bool inactivate(SkAnimateMaker& maker);
-    void initialize() SK_OVERRIDE;
+    void initialize() override;
     bool interpolate(SkAnimateMaker& , SkMSec time);
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
-    const SkMemberInfo* preferredChild(SkDisplayTypes type) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& ) override;
+    const SkMemberInfo* preferredChild(SkDisplayTypes type) override;
     void refresh(SkAnimateMaker& );
     void reset();
-    bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* ) SK_OVERRIDE;
+    bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* ) override;
     bool resolveField(SkAnimateMaker& , SkDisplayable* parent, SkString* str);
     void save(int index);
     void setEmbedded() { fEmbedded = true; }
-    bool setProperty(int index, SkScriptValue& ) SK_OVERRIDE;
-    void setSteps(int _steps) SK_OVERRIDE;
+    bool setProperty(int index, SkScriptValue& ) override;
+    void setSteps(int _steps) override;
 //  virtual void setTime(SkMSec time);
 #ifdef SK_DEBUG
-    void validate() SK_OVERRIDE;
+    void validate() override;
 #endif
 private:
     SkMSec begin;
diff --git a/src/animator/SkDisplayBounds.h b/src/animator/SkDisplayBounds.h
index 4c21871..547a29e 100644
--- a/src/animator/SkDisplayBounds.h
+++ b/src/animator/SkDisplayBounds.h
@@ -15,7 +15,7 @@
 class SkDisplayBounds : public SkDrawRect {
     DECLARE_DISPLAY_MEMBER_INFO(Bounds);
     SkDisplayBounds();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 private:
     SkBool inval;
     typedef SkDrawRect INHERITED;
diff --git a/src/animator/SkDisplayEvent.h b/src/animator/SkDisplayEvent.h
index 4da49b9..d223771 100644
--- a/src/animator/SkDisplayEvent.h
+++ b/src/animator/SkDisplayEvent.h
@@ -34,17 +34,17 @@
     };
     SkDisplayEvent();
     virtual ~SkDisplayEvent();
-    bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
-    bool contains(SkDisplayable*) SK_OVERRIDE;
-    SkDisplayable* contains(const SkString& ) SK_OVERRIDE;
+    bool addChild(SkAnimateMaker& , SkDisplayable* child) override;
+    bool contains(SkDisplayable*) override;
+    SkDisplayable* contains(const SkString& ) override;
 #ifdef SK_DEBUG
     void dumpEvent(SkAnimateMaker* );
 #endif
     bool enableEvent(SkAnimateMaker& );
-    bool getProperty(int index, SkScriptValue* ) const SK_OVERRIDE;
-    void onEndElement(SkAnimateMaker& maker) SK_OVERRIDE;
+    bool getProperty(int index, SkScriptValue* ) const override;
+    void onEndElement(SkAnimateMaker& maker) override;
     void populateInput(SkAnimateMaker& , const SkEvent& fEvent);
-    bool setProperty(int index, SkScriptValue& ) SK_OVERRIDE;
+    bool setProperty(int index, SkScriptValue& ) override;
 protected:
     SkKey code;
     SkBool disable;
diff --git a/src/animator/SkDisplayInclude.h b/src/animator/SkDisplayInclude.h
index 1b6580a..cc87dfa 100644
--- a/src/animator/SkDisplayInclude.h
+++ b/src/animator/SkDisplayInclude.h
@@ -15,9 +15,9 @@
 
 class SkInclude : public SkDisplayable {
     DECLARE_MEMBER_INFO(Include);
-    void onEndElement(SkAnimateMaker & ) SK_OVERRIDE;
-    bool enable(SkAnimateMaker & ) SK_OVERRIDE;
-    bool hasEnable() const SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker & ) override;
+    bool enable(SkAnimateMaker & ) override;
+    bool hasEnable() const override;
 protected:
     SkString src;
 };
diff --git a/src/animator/SkDisplayInput.h b/src/animator/SkDisplayInput.h
index 8eba709..b9a1bac 100644
--- a/src/animator/SkDisplayInput.h
+++ b/src/animator/SkDisplayInput.h
@@ -16,10 +16,10 @@
 class SkInput : public SkDisplayable {
     DECLARE_MEMBER_INFO(Input);
     SkInput();
-    SkDisplayable* contains(const SkString& ) SK_OVERRIDE;
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
-    bool enable(SkAnimateMaker & ) SK_OVERRIDE;
-    bool hasEnable() const SK_OVERRIDE;
+    SkDisplayable* contains(const SkString& ) override;
+    bool getProperty(int index, SkScriptValue* value) const override;
+    bool enable(SkAnimateMaker & ) override;
+    bool hasEnable() const override;
 protected:
     SkString name;
     int32_t  fInt;
diff --git a/src/animator/SkDisplayMath.h b/src/animator/SkDisplayMath.h
index 05c1fe4..0311a7c 100644
--- a/src/animator/SkDisplayMath.h
+++ b/src/animator/SkDisplayMath.h
@@ -16,11 +16,11 @@
 
 class SkDisplayMath : public SkDisplayable {
     DECLARE_DISPLAY_MEMBER_INFO(Math);
-    virtual void executeFunction(SkDisplayable* , int index,
+    void executeFunction(SkDisplayable* , int index,
         SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
-        SkScriptValue* ) SK_OVERRIDE;
-    const SkFunctionParamType* getFunctionsParameters() SK_OVERRIDE;
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
+        SkScriptValue* ) override;
+    const SkFunctionParamType* getFunctionsParameters() override;
+    bool getProperty(int index, SkScriptValue* value) const override;
 private:
     mutable SkRandom fRandom;
     static const SkScalar gConstants[];
diff --git a/src/animator/SkDisplayMovie.h b/src/animator/SkDisplayMovie.h
index c1f184d..76e2d9c 100644
--- a/src/animator/SkDisplayMovie.h
+++ b/src/animator/SkDisplayMovie.h
@@ -21,21 +21,21 @@
     SkDisplayMovie();
     virtual ~SkDisplayMovie();
     void buildMovie();
-    SkDisplayable* deepCopy(SkAnimateMaker* ) SK_OVERRIDE;
-    void dirty() SK_OVERRIDE;
+    SkDisplayable* deepCopy(SkAnimateMaker* ) override;
+    void dirty() override;
     bool doEvent(const SkEvent& evt) {
         return fLoaded && fMovie.doEvent(evt);
     }
-    bool doEvent(SkDisplayEvent::Kind , SkEventState* state ) SK_OVERRIDE;
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool doEvent(SkDisplayEvent::Kind , SkEventState* state ) override;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
-    void dumpEvents() SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
+    void dumpEvents() override;
 #endif
-    bool enable(SkAnimateMaker& ) SK_OVERRIDE;
+    bool enable(SkAnimateMaker& ) override;
     const SkAnimator* getAnimator() const { return &fMovie; }
-    bool hasEnable() const SK_OVERRIDE;
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
+    bool hasEnable() const override;
+    void onEndElement(SkAnimateMaker& ) override;
 protected:
     SkString src;
     SkAnimator fMovie;
diff --git a/src/animator/SkDisplayNumber.h b/src/animator/SkDisplayNumber.h
index 92f0fa3..b92c311 100644
--- a/src/animator/SkDisplayNumber.h
+++ b/src/animator/SkDisplayNumber.h
@@ -15,7 +15,7 @@
 
 class SkDisplayNumber : public SkDisplayable {
     DECLARE_DISPLAY_MEMBER_INFO(Number);
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
+    bool getProperty(int index, SkScriptValue* value) const override;
 private:
 };
 
diff --git a/src/animator/SkDisplayPost.cpp b/src/animator/SkDisplayPost.cpp
index cc45b21..0fd4a06 100644
--- a/src/animator/SkDisplayPost.cpp
+++ b/src/animator/SkDisplayPost.cpp
@@ -76,7 +76,7 @@
     delete eventType;
 
     if (delay > 0) {
-        SkDebugf("delay=\"%g\" ", SkScalarToFloat(SkScalarDiv(delay, 1000)));
+        SkDebugf("delay=\"%g\" ", delay * 0.001);
     }
 //  if (initialized == false)
 //      SkDebugf("(uninitialized) ");
diff --git a/src/animator/SkDisplayPost.h b/src/animator/SkDisplayPost.h
index c6c4b51..80fdcfc 100644
--- a/src/animator/SkDisplayPost.h
+++ b/src/animator/SkDisplayPost.h
@@ -27,17 +27,17 @@
     };
     SkPost();
     virtual ~SkPost();
-    bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
-    bool childrenNeedDisposing() const SK_OVERRIDE;
-    void dirty() SK_OVERRIDE;
+    bool addChild(SkAnimateMaker& , SkDisplayable* child) override;
+    bool childrenNeedDisposing() const override;
+    void dirty() override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    bool enable(SkAnimateMaker& ) SK_OVERRIDE;
-    bool hasEnable() const SK_OVERRIDE;
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
-    void setChildHasID() SK_OVERRIDE;
-    bool setProperty(int index, SkScriptValue& ) SK_OVERRIDE;
+    bool enable(SkAnimateMaker& ) override;
+    bool hasEnable() const override;
+    void onEndElement(SkAnimateMaker& ) override;
+    void setChildHasID() override;
+    bool setProperty(int index, SkScriptValue& ) override;
 protected:
     SkMSec delay;
     SkString sink;
diff --git a/src/animator/SkDisplayRandom.h b/src/animator/SkDisplayRandom.h
index 6a04f59..2999156 100644
--- a/src/animator/SkDisplayRandom.h
+++ b/src/animator/SkDisplayRandom.h
@@ -26,10 +26,10 @@
     DECLARE_DISPLAY_MEMBER_INFO(Random);
     SkDisplayRandom();
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
-    bool setProperty(int index, SkScriptValue& ) SK_OVERRIDE;
+    bool getProperty(int index, SkScriptValue* value) const override;
+    bool setProperty(int index, SkScriptValue& ) override;
 private:
     SkScalar blend;
     SkScalar min;
diff --git a/src/animator/SkDisplayTypes.h b/src/animator/SkDisplayTypes.h
index a9432da..07e8448 100644
--- a/src/animator/SkDisplayTypes.h
+++ b/src/animator/SkDisplayTypes.h
@@ -34,7 +34,7 @@
     DECLARE_DISPLAY_MEMBER_INFO(Boolean);
     SkDisplayBoolean();
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
     SkBool value;
     friend class SkAnimatorScript;
@@ -47,7 +47,7 @@
     DECLARE_DISPLAY_MEMBER_INFO(Int);
     SkDisplayInt();
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
 private:
     int32_t value;
@@ -61,7 +61,7 @@
     DECLARE_DISPLAY_MEMBER_INFO(Float);
     SkDisplayFloat();
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
 private:
     SkScalar value;
@@ -75,11 +75,11 @@
     DECLARE_DISPLAY_MEMBER_INFO(String);
     SkDisplayString();
     SkDisplayString(SkString& );
-    virtual void executeFunction(SkDisplayable* , int index,
+    void executeFunction(SkDisplayable* , int index,
         SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
-        SkScriptValue* ) SK_OVERRIDE;
-    const SkFunctionParamType* getFunctionsParameters() SK_OVERRIDE;
-    bool getProperty(int index, SkScriptValue* ) const SK_OVERRIDE;
+        SkScriptValue* ) override;
+    const SkFunctionParamType* getFunctionsParameters() override;
+    bool getProperty(int index, SkScriptValue* ) const override;
     SkString value;
 private:
     static const SkFunctionParamType fFunctionParameters[];
@@ -91,7 +91,7 @@
     SkDisplayArray(SkTypedArray& );
     SkDisplayArray(SkOpArray& ); // compiled script experiment
     virtual ~SkDisplayArray();
-    bool getProperty(int index, SkScriptValue* ) const SK_OVERRIDE;
+    bool getProperty(int index, SkScriptValue* ) const override;
 private:
     SkTypedArray values;
     friend class SkAnimator;
diff --git a/src/animator/SkDisplayable.cpp b/src/animator/SkDisplayable.cpp
index aff3aae..52532fe 100644
--- a/src/animator/SkDisplayable.cpp
+++ b/src/animator/SkDisplayable.cpp
@@ -377,7 +377,7 @@
         break;
     case SkType_MSec:
         if (op.fS32 != blankOp.fS32) {
-            SkDebugf(" %s=\"%g\"  ", info->fName, SkScalarToFloat(SkScalarDiv(op.fS32, 1000)));
+            SkDebugf(" %s=\"%g\"  ", info->fName, op.fS32 * 0.001);
         }
     default:
         SkDebugf("");
diff --git a/src/animator/SkDraw3D.h b/src/animator/SkDraw3D.h
index 5f066ba..8c79b5e 100644
--- a/src/animator/SkDraw3D.h
+++ b/src/animator/SkDraw3D.h
@@ -27,7 +27,7 @@
     DECLARE_MEMBER_INFO(3D_Camera);
     Sk3D_Camera();
     virtual ~Sk3D_Camera();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 private:
     SkScalar hackWidth;
     SkScalar hackHeight;
@@ -38,10 +38,10 @@
 class Sk3D_Patch : public SkDisplayable {
     DECLARE_MEMBER_INFO(3D_Patch);
 private:
-    virtual void executeFunction(SkDisplayable* , int index,
+    void executeFunction(SkDisplayable* , int index,
         SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
-        SkScriptValue* ) SK_OVERRIDE;
-    const SkFunctionParamType* getFunctionsParameters() SK_OVERRIDE;
+        SkScriptValue* ) override;
+    const SkFunctionParamType* getFunctionsParameters() override;
     SkPatch3D  fPatch;
     static const SkFunctionParamType fFunctionParameters[];
     friend class Sk3D_Camera;
diff --git a/src/animator/SkDrawBitmap.h b/src/animator/SkDrawBitmap.h
index a1b74b6..270545d 100644
--- a/src/animator/SkDrawBitmap.h
+++ b/src/animator/SkDrawBitmap.h
@@ -20,7 +20,7 @@
     DECLARE_MEMBER_INFO(BaseBitmap);
     SkBaseBitmap();
     virtual ~SkBaseBitmap();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 protected:
     SkBitmap fBitmap;
     SkScalar x;
@@ -36,10 +36,10 @@
     SkDrawBitmap();
     virtual ~SkDrawBitmap();
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
-    bool setProperty(int index, SkScriptValue& value) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& ) override;
+    bool setProperty(int index, SkScriptValue& value) override;
 protected:
     int /*SkBitmap::Config*/ format;
     int32_t height;
@@ -54,11 +54,11 @@
     DECLARE_MEMBER_INFO(ImageBaseBitmap);
     SkImageBaseBitmap();
     virtual ~SkImageBaseBitmap();
-    SkDisplayable* deepCopy(SkAnimateMaker* ) SK_OVERRIDE;
-    void dirty() SK_OVERRIDE;
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
-    void onEndElement(SkAnimateMaker& maker) SK_OVERRIDE;
+    SkDisplayable* deepCopy(SkAnimateMaker* ) override;
+    void dirty() override;
+    bool draw(SkAnimateMaker& ) override;
+    bool getProperty(int index, SkScriptValue* value) const override;
+    void onEndElement(SkAnimateMaker& maker) override;
 private:
     void resolve() const { (const_cast<SkImageBaseBitmap*>(this))->resolve(); }
     void resolve();
diff --git a/src/animator/SkDrawBlur.h b/src/animator/SkDrawBlur.h
index 56f29db..d3a528c 100644
--- a/src/animator/SkDrawBlur.h
+++ b/src/animator/SkDrawBlur.h
@@ -14,7 +14,7 @@
 class SkDrawBlur : public SkDrawMaskFilter {
     DECLARE_DRAW_MEMBER_INFO(Blur);
     SkDrawBlur();
-    SkMaskFilter* getMaskFilter() SK_OVERRIDE;
+    SkMaskFilter* getMaskFilter() override;
 protected:
     SkScalar fSigma;
     int /*SkBlurStyle*/ fBlurStyle;
diff --git a/src/animator/SkDrawClip.h b/src/animator/SkDrawClip.h
index 54c8651..6c64f93 100644
--- a/src/animator/SkDrawClip.h
+++ b/src/animator/SkDrawClip.h
@@ -20,7 +20,7 @@
 class SkDrawClip : public SkADrawable {
     DECLARE_DRAW_MEMBER_INFO(Clip);
     SkDrawClip();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 private:
     SkDrawRect* rect;
     SkDrawPath* path;
diff --git a/src/animator/SkDrawColor.cpp b/src/animator/SkDrawColor.cpp
index eb57d9d..d86a2e9 100644
--- a/src/animator/SkDrawColor.cpp
+++ b/src/animator/SkDrawColor.cpp
@@ -30,14 +30,14 @@
     if (choice == kGetValue)
         return value/255;
     SkScalar delta = value - min;
-    SkScalar saturation = value == 0 ? 0 : SkScalarDiv(delta, value);
+    SkScalar saturation = value == 0 ? 0 : delta / value;
     if (choice == kGetSaturation)
         return saturation;
     SkScalar hue;
     if (saturation == 0)
         hue = 0;
     else {
-        SkScalar part60 = SkScalarDiv(60 * SK_Scalar1, delta);
+        SkScalar part60 = 60 / delta;
         if (red == value) {
             hue = SkScalarMul(green - blue, part60);
             if (hue < 0)
diff --git a/src/animator/SkDrawColor.h b/src/animator/SkDrawColor.h
index b5b4f2d..14788e4 100644
--- a/src/animator/SkDrawColor.h
+++ b/src/animator/SkDrawColor.h
@@ -14,18 +14,18 @@
 class SkDrawColor : public SkPaintPart {
     DECLARE_DRAW_MEMBER_INFO(Color);
     SkDrawColor();
-    bool add() SK_OVERRIDE;
-    void dirty() SK_OVERRIDE;
+    bool add() override;
+    void dirty() override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
     SkColor getColor();
-    SkDisplayable* deepCopy(SkAnimateMaker* ) SK_OVERRIDE;
-    SkDisplayable* getParent() const SK_OVERRIDE;
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
-    bool setParent(SkDisplayable* parent) SK_OVERRIDE;
-    bool setProperty(int index, SkScriptValue&) SK_OVERRIDE;
+    SkDisplayable* deepCopy(SkAnimateMaker* ) override;
+    SkDisplayable* getParent() const override;
+    bool getProperty(int index, SkScriptValue* value) const override;
+    void onEndElement(SkAnimateMaker& ) override;
+    bool setParent(SkDisplayable* parent) override;
+    bool setProperty(int index, SkScriptValue&) override;
 protected:
     SkColor color;
     SkScalar fHue;
diff --git a/src/animator/SkDrawDash.h b/src/animator/SkDrawDash.h
index fa020c2..3083fe8 100644
--- a/src/animator/SkDrawDash.h
+++ b/src/animator/SkDrawDash.h
@@ -15,7 +15,7 @@
     DECLARE_MEMBER_INFO(Dash);
     SkDash();
     virtual ~SkDash();
-    SkPathEffect* getPathEffect() SK_OVERRIDE;
+    SkPathEffect* getPathEffect() override;
 private:
     SkTDScalarArray intervals;
     SkScalar phase;
diff --git a/src/animator/SkDrawDiscrete.h b/src/animator/SkDrawDiscrete.h
index ea44199..fe13f26 100644
--- a/src/animator/SkDrawDiscrete.h
+++ b/src/animator/SkDrawDiscrete.h
@@ -13,7 +13,7 @@
 class SkDiscrete : public SkDrawPathEffect {
     DECLARE_MEMBER_INFO(Discrete);
     SkDiscrete();
-    SkPathEffect* getPathEffect() SK_OVERRIDE;
+    SkPathEffect* getPathEffect() override;
 private:
     SkScalar deviation;
     SkScalar segLength;
diff --git a/src/animator/SkDrawEmboss.h b/src/animator/SkDrawEmboss.h
index 6ffe207..941be61 100644
--- a/src/animator/SkDrawEmboss.h
+++ b/src/animator/SkDrawEmboss.h
@@ -15,7 +15,7 @@
 class SkDrawEmboss : public SkDrawMaskFilter {
     DECLARE_DRAW_MEMBER_INFO(Emboss);
     SkDrawEmboss();
-    SkMaskFilter* getMaskFilter() SK_OVERRIDE;
+    SkMaskFilter* getMaskFilter() override;
 protected:
     SkTDScalarArray fDirection;
     SkScalar        fSigma;
diff --git a/src/animator/SkDrawExtraPathEffect.cpp b/src/animator/SkDrawExtraPathEffect.cpp
index b13429e..68b5006 100644
--- a/src/animator/SkDrawExtraPathEffect.cpp
+++ b/src/animator/SkDrawExtraPathEffect.cpp
@@ -20,8 +20,8 @@
     DECLARE_PRIVATE_MEMBER_INFO(DrawShapePathEffect);
     SkDrawShapePathEffect();
     virtual ~SkDrawShapePathEffect();
-    bool addChild(SkAnimateMaker& , SkDisplayable* ) SK_OVERRIDE;
-    SkPathEffect* getPathEffect() SK_OVERRIDE;
+    bool addChild(SkAnimateMaker& , SkDisplayable* ) override;
+    SkPathEffect* getPathEffect() override;
 protected:
     SkADrawable* addPath;
     SkADrawable* addMatrix;
@@ -35,7 +35,7 @@
     DECLARE_EXTRAS_MEMBER_INFO(SkDrawShape1DPathEffect);
     SkDrawShape1DPathEffect(SkDisplayTypes );
     virtual ~SkDrawShape1DPathEffect();
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& ) override;
 private:
     SkString phase;
     SkString spacing;
@@ -47,7 +47,7 @@
     DECLARE_EXTRAS_MEMBER_INFO(SkDrawShape2DPathEffect);
     SkDrawShape2DPathEffect(SkDisplayTypes );
     virtual ~SkDrawShape2DPathEffect();
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& ) override;
 private:
     SkDrawMatrix* matrix;
     friend class SkShape2DPathEffect;
@@ -58,9 +58,9 @@
     DECLARE_EXTRAS_MEMBER_INFO(SkDrawComposePathEffect);
     SkDrawComposePathEffect(SkDisplayTypes );
     virtual ~SkDrawComposePathEffect();
-    bool addChild(SkAnimateMaker& , SkDisplayable* ) SK_OVERRIDE;
-    SkPathEffect* getPathEffect() SK_OVERRIDE;
-    bool isPaint() const SK_OVERRIDE;
+    bool addChild(SkAnimateMaker& , SkDisplayable* ) override;
+    SkPathEffect* getPathEffect() override;
+    bool isPaint() const override;
 private:
     SkDrawPathEffect* effect1;
     SkDrawPathEffect* effect2;
@@ -70,7 +70,7 @@
     DECLARE_EXTRAS_MEMBER_INFO(SkDrawCornerPathEffect);
     SkDrawCornerPathEffect(SkDisplayTypes );
     virtual ~SkDrawCornerPathEffect();
-    SkPathEffect* getPathEffect() SK_OVERRIDE;
+    SkPathEffect* getPathEffect() override;
 private:
     SkScalar radius;
 };
@@ -90,10 +90,10 @@
     }
 
     // For serialization.  This will never be called.
-    Factory getFactory() const SK_OVERRIDE { sk_throw(); return NULL; }
+    Factory getFactory() const override { sk_throw(); return NULL; }
 
 protected:
-    SkScalar begin(SkScalar contourLength) const SK_OVERRIDE {
+    SkScalar begin(SkScalar contourLength) const override {
         SkScriptValue value;
         SkAnimatorScript engine(*fMaker, NULL, SkType_Float);
         engine.propertyCallBack(GetContourLength, &contourLength);
@@ -102,7 +102,7 @@
         return value.fOperand.fScalar;
     }
 
-    SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure&) const SK_OVERRIDE {
+    SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure&) const override {
         fMaker->setExtraPropertyCallBack(fDraw->fType, GetDistance, &distance);
         SkDrawPath* drawPath = NULL;
         if (fDraw->addPath->isPath()) {
@@ -140,7 +140,7 @@
     }
 
 #ifndef SK_IGNORE_TO_STRING
-    void toString(SkString* str) const SK_OVERRIDE {
+    void toString(SkString* str) const override {
         str->appendf("SkShape1DPathEffect: (");
         // TODO: fill in
         str->appendf(")");
@@ -238,14 +238,14 @@
     }
 
     // For serialization.  This will never be called.
-    Factory getFactory() const SK_OVERRIDE { sk_throw(); return NULL; }
+    Factory getFactory() const override { sk_throw(); return NULL; }
 
 protected:
-    void begin(const SkIRect& uvBounds, SkPath*) const SK_OVERRIDE {
+    void begin(const SkIRect& uvBounds, SkPath*) const override {
         const_cast<SkShape2DPathEffect*>(this)->setUVBounds(uvBounds);
     }
 
-    void next(const SkPoint& loc, int u, int v, SkPath* dst) const SK_OVERRIDE {
+    void next(const SkPoint& loc, int u, int v, SkPath* dst) const override {
         const_cast<SkShape2DPathEffect*>(this)->addPath(loc, u, v, dst);
     }
 
diff --git a/src/animator/SkDrawFull.h b/src/animator/SkDrawFull.h
index 029fb33..8a79c4d 100644
--- a/src/animator/SkDrawFull.h
+++ b/src/animator/SkDrawFull.h
@@ -14,7 +14,7 @@
 
 class SkFull : public SkBoundable {
     DECLARE_EMPTY_MEMBER_INFO(Full);
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 private:
     typedef SkBoundable INHERITED;
 };
diff --git a/src/animator/SkDrawGradient.h b/src/animator/SkDrawGradient.h
index d6dd144..87df376 100644
--- a/src/animator/SkDrawGradient.h
+++ b/src/animator/SkDrawGradient.h
@@ -18,11 +18,11 @@
     DECLARE_PRIVATE_MEMBER_INFO(DrawGradient);
     SkDrawGradient();
     virtual ~SkDrawGradient();
-    bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
+    bool addChild(SkAnimateMaker& , SkDisplayable* child) override;
 #ifdef SK_DUMP_ENABLED
     virtual void dumpRest(SkAnimateMaker*);
 #endif
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& ) override;
 protected:
     SkTDScalarArray offsets;
     SkString unitMapper;
@@ -36,11 +36,11 @@
 class SkDrawLinearGradient : public SkDrawGradient {
     DECLARE_MEMBER_INFO(DrawLinearGradient);
     SkDrawLinearGradient();
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker*) SK_OVERRIDE;
+    void dump(SkAnimateMaker*) override;
 #endif
-    SkShader* getShader() SK_OVERRIDE;
+    SkShader* getShader() override;
 protected:
     SkTDScalarArray points;
 private:
@@ -51,9 +51,9 @@
     DECLARE_MEMBER_INFO(DrawRadialGradient);
     SkDrawRadialGradient();
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker*) SK_OVERRIDE;
+    void dump(SkAnimateMaker*) override;
 #endif
-    SkShader* getShader() SK_OVERRIDE;
+    SkShader* getShader() override;
 protected:
     SkPoint center;
     SkScalar radius;
diff --git a/src/animator/SkDrawGroup.h b/src/animator/SkDrawGroup.h
index 5d33dfe..83e6771 100644
--- a/src/animator/SkDrawGroup.h
+++ b/src/animator/SkDrawGroup.h
@@ -19,35 +19,35 @@
     DECLARE_MEMBER_INFO(Group);
     SkGroup();
     virtual ~SkGroup();
-    bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
-    bool contains(SkDisplayable* ) SK_OVERRIDE;
+    bool addChild(SkAnimateMaker& , SkDisplayable* child) override;
+    bool contains(SkDisplayable* ) override;
     SkGroup* copy();
     SkBool copySet(int index);
-    SkDisplayable* deepCopy(SkAnimateMaker* ) SK_OVERRIDE;
-    bool doEvent(SkDisplayEvent::Kind , SkEventState* state ) SK_OVERRIDE;
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    SkDisplayable* deepCopy(SkAnimateMaker* ) override;
+    bool doEvent(SkDisplayEvent::Kind , SkEventState* state ) override;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
     virtual void dumpDrawables(SkAnimateMaker* );
-    void dumpEvents() SK_OVERRIDE;
+    void dumpEvents() override;
 #endif
     int findGroup(SkADrawable* drawable,  SkTDDrawableArray** list,
         SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList);
-    bool enable(SkAnimateMaker& ) SK_OVERRIDE;
+    bool enable(SkAnimateMaker& ) override;
     SkTDDrawableArray* getChildren() { return &fChildren; }
     SkGroup* getOriginal() { return fOriginal; }
-    bool hasEnable() const SK_OVERRIDE;
-    void initialize() SK_OVERRIDE;
+    bool hasEnable() const override;
+    void initialize() override;
     SkBool isACopy() { return fOriginal != NULL; }
     void markCopyClear(int index);
     void markCopySet(int index);
     void markCopySize(int index);
     bool markedForDelete(int index) const { return (fCopies[index >> 5] & 1 << (index & 0x1f)) == 0; }
     void reset();
-    bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* ) SK_OVERRIDE;
-    void setSteps(int steps) SK_OVERRIDE;
+    bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* ) override;
+    void setSteps(int steps) override;
 #ifdef SK_DEBUG
-    void validate() SK_OVERRIDE;
+    void validate() override;
 #endif
 protected:
     bool ifCondition(SkAnimateMaker& maker, SkADrawable* drawable,
@@ -64,7 +64,7 @@
 
 class SkSave: public SkGroup {
     DECLARE_MEMBER_INFO(Save);
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 private:
     typedef SkGroup INHERITED;
 };
diff --git a/src/animator/SkDrawLine.h b/src/animator/SkDrawLine.h
index 7367aab..996964a 100644
--- a/src/animator/SkDrawLine.h
+++ b/src/animator/SkDrawLine.h
@@ -16,7 +16,7 @@
 class SkLine : public SkBoundable {
     DECLARE_MEMBER_INFO(Line);
     SkLine();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 private:
     SkScalar x1;
     SkScalar x2;
diff --git a/src/animator/SkDrawMatrix.h b/src/animator/SkDrawMatrix.h
index d5e19a5..df17a9b 100644
--- a/src/animator/SkDrawMatrix.h
+++ b/src/animator/SkDrawMatrix.h
@@ -21,25 +21,25 @@
     DECLARE_DRAW_MEMBER_INFO(Matrix);
     SkDrawMatrix();
     virtual ~SkDrawMatrix();
-    bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
-    bool childrenNeedDisposing() const SK_OVERRIDE;
-    void dirty() SK_OVERRIDE;
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool addChild(SkAnimateMaker& , SkDisplayable* child) override;
+    bool childrenNeedDisposing() const override;
+    void dirty() override;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
     SkMatrix& getMatrix();
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
-    void initialize() SK_OVERRIDE;
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
-    void setChildHasID() SK_OVERRIDE;
-    bool setProperty(int index, SkScriptValue& ) SK_OVERRIDE;
+    bool getProperty(int index, SkScriptValue* value) const override;
+    void initialize() override;
+    void onEndElement(SkAnimateMaker& ) override;
+    void setChildHasID() override;
+    bool setProperty(int index, SkScriptValue& ) override;
 
     void concat(SkMatrix& inMatrix) {
         fConcat.preConcat(inMatrix);
     }
 
-    SkDisplayable* deepCopy(SkAnimateMaker* ) SK_OVERRIDE;
+    SkDisplayable* deepCopy(SkAnimateMaker* ) override;
 
 
     void rotate(SkScalar degrees, SkPoint& center) {
diff --git a/src/animator/SkDrawOval.h b/src/animator/SkDrawOval.h
index 03b0270..e4d4712 100644
--- a/src/animator/SkDrawOval.h
+++ b/src/animator/SkDrawOval.h
@@ -14,7 +14,7 @@
 
 class SkOval : public SkDrawRect {
     DECLARE_MEMBER_INFO(Oval);
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 private:
     typedef SkDrawRect INHERITED;
 };
diff --git a/src/animator/SkDrawPaint.cpp b/src/animator/SkDrawPaint.cpp
index c882427..f5d3388 100644
--- a/src/animator/SkDrawPaint.cpp
+++ b/src/animator/SkDrawPaint.cpp
@@ -218,7 +218,7 @@
     if (fakeBold != -1)
         paint->setFakeBoldText(SkToBool(fakeBold));
     if (filterBitmap != -1)
-        paint->setFilterLevel(filterBitmap ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel);
+        paint->setFilterQuality(filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
     //  stroke is legacy; style setting if present overrides stroke
     if (stroke != -1)
         paint->setStyle(SkToBool(stroke) ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
diff --git a/src/animator/SkDrawPaint.h b/src/animator/SkDrawPaint.h
index e2be340..bef5252 100644
--- a/src/animator/SkDrawPaint.h
+++ b/src/animator/SkDrawPaint.h
@@ -27,17 +27,17 @@
     SkDrawPaint();
     virtual ~SkDrawPaint();
     virtual bool add(SkAnimateMaker* , SkDisplayable* child);
-    SkDisplayable* deepCopy(SkAnimateMaker* ) SK_OVERRIDE;
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    SkDisplayable* deepCopy(SkAnimateMaker* ) override;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    virtual void executeFunction(SkDisplayable* target, int index,
+    void executeFunction(SkDisplayable* target, int index,
         SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
-        SkScriptValue* ) SK_OVERRIDE;
-    const SkFunctionParamType* getFunctionsParameters() SK_OVERRIDE;
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
-    bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply) SK_OVERRIDE;
+        SkScriptValue* ) override;
+    const SkFunctionParamType* getFunctionsParameters() override;
+    bool getProperty(int index, SkScriptValue* value) const override;
+    bool resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply) override;
 protected:
     static const SkFunctionParamType fFunctionParameters[];
     void setupPaint(SkPaint* paint) const;
diff --git a/src/animator/SkDrawPath.h b/src/animator/SkDrawPath.h
index d64195b..81978fb 100644
--- a/src/animator/SkDrawPath.h
+++ b/src/animator/SkDrawPath.h
@@ -19,22 +19,22 @@
     DECLARE_DRAW_MEMBER_INFO(Path);
     SkDrawPath();
     virtual ~SkDrawPath();
-    bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
+    bool addChild(SkAnimateMaker& , SkDisplayable* child) override;
     bool childHasID() { return SkToBool(fChildHasID); }
-    bool childrenNeedDisposing() const SK_OVERRIDE;
-    void dirty() SK_OVERRIDE;
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
-    SkDisplayable* getParent() const SK_OVERRIDE;
+    bool childrenNeedDisposing() const override;
+    void dirty() override;
+    bool draw(SkAnimateMaker& ) override;
+    SkDisplayable* getParent() const override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
     SkPath& getPath();
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
-    bool setProperty(int index, SkScriptValue& value) SK_OVERRIDE;
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
-    void setChildHasID() SK_OVERRIDE;
-    bool setParent(SkDisplayable* parent) SK_OVERRIDE;
-    bool isPath() const SK_OVERRIDE { return true; }
+    bool getProperty(int index, SkScriptValue* value) const override;
+    bool setProperty(int index, SkScriptValue& value) override;
+    void onEndElement(SkAnimateMaker& ) override;
+    void setChildHasID() override;
+    bool setParent(SkDisplayable* parent) override;
+    bool isPath() const override { return true; }
 public:
     SkPath fPath;
 protected:
@@ -51,8 +51,8 @@
 
 class SkPolyline : public SkDrawPath {
     DECLARE_MEMBER_INFO(Polyline);
-    bool addChild(SkAnimateMaker& , SkDisplayable*) SK_OVERRIDE;
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
+    bool addChild(SkAnimateMaker& , SkDisplayable*) override;
+    void onEndElement(SkAnimateMaker& ) override;
 protected:
     SkTDScalarArray points;
 private:
@@ -61,7 +61,7 @@
 
 class SkPolygon : public SkPolyline {
     DECLARE_MEMBER_INFO(Polygon);
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& ) override;
 private:
     typedef SkPolyline INHERITED;
 };
diff --git a/src/animator/SkDrawPoint.h b/src/animator/SkDrawPoint.h
index f2077f8..03c8521 100644
--- a/src/animator/SkDrawPoint.h
+++ b/src/animator/SkDrawPoint.h
@@ -24,7 +24,7 @@
 class SkDrawPoint : public SkDisplayable {
     DECLARE_MEMBER_INFO(DrawPoint);
     SkDrawPoint();
-    void getBounds(SkRect* ) SK_OVERRIDE;
+    void getBounds(SkRect* ) override;
 private:
     SkPoint fPoint;
     typedef SkDisplayable INHERITED;
diff --git a/src/animator/SkDrawRectangle.h b/src/animator/SkDrawRectangle.h
index 44ed7c4..036d52e 100644
--- a/src/animator/SkDrawRectangle.h
+++ b/src/animator/SkDrawRectangle.h
@@ -19,15 +19,15 @@
 class SkDrawRect : public SkBoundable {
     DECLARE_DRAW_MEMBER_INFO(Rect);
     SkDrawRect();
-    void dirty() SK_OVERRIDE;
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    void dirty() override;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    SkDisplayable* getParent() const SK_OVERRIDE;
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
-    bool setParent(SkDisplayable* parent) SK_OVERRIDE;
-    bool setProperty(int index, SkScriptValue& ) SK_OVERRIDE;
+    SkDisplayable* getParent() const override;
+    bool getProperty(int index, SkScriptValue* value) const override;
+    bool setParent(SkDisplayable* parent) override;
+    bool setProperty(int index, SkScriptValue& ) override;
 protected:
     SkRect fRect;
     SkDisplayable* fParent;
@@ -41,9 +41,9 @@
 class SkRoundRect : public SkDrawRect {
     DECLARE_MEMBER_INFO(RoundRect);
     SkRoundRect();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
 protected:
     SkScalar rx;
diff --git a/src/animator/SkDrawSaveLayer.h b/src/animator/SkDrawSaveLayer.h
index 1080e81..cb9c9a9 100644
--- a/src/animator/SkDrawSaveLayer.h
+++ b/src/animator/SkDrawSaveLayer.h
@@ -20,11 +20,11 @@
     DECLARE_MEMBER_INFO(SaveLayer);
     SkSaveLayer();
     virtual ~SkSaveLayer();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& ) override;
 protected:
     SkDrawPaint* paint;
     SkDrawRect* bounds;
diff --git a/src/animator/SkDrawShader.h b/src/animator/SkDrawShader.h
index 32c8fd5..f7ef29d 100644
--- a/src/animator/SkDrawShader.h
+++ b/src/animator/SkDrawShader.h
@@ -16,8 +16,8 @@
 class SkDrawBitmapShader : public SkDrawShader {
     DECLARE_DRAW_MEMBER_INFO(BitmapShader);
     SkDrawBitmapShader();
-    bool add() SK_OVERRIDE;
-    SkShader* getShader() SK_OVERRIDE;
+    bool add() override;
+    SkShader* getShader() override;
 protected:
     SkBool filterBitmap;
     SkBaseBitmap* image;
diff --git a/src/animator/SkDrawText.h b/src/animator/SkDrawText.h
index 2630007..e7632d0 100644
--- a/src/animator/SkDrawText.h
+++ b/src/animator/SkDrawText.h
@@ -17,11 +17,11 @@
     DECLARE_MEMBER_INFO(Text);
     SkText();
     virtual ~SkText();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
+    bool getProperty(int index, SkScriptValue* value) const override;
     const char* getText() { return text.c_str(); }
     size_t getSize() { return text.size(); }
 protected:
diff --git a/src/animator/SkDrawTextBox.h b/src/animator/SkDrawTextBox.h
index 4bb3bf2..8f99c73 100644
--- a/src/animator/SkDrawTextBox.h
+++ b/src/animator/SkDrawTextBox.h
@@ -18,12 +18,12 @@
     SkDrawTextBox();
 
     // overrides
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    bool getProperty(int index, SkScriptValue* value) const SK_OVERRIDE;
-    bool setProperty(int index, SkScriptValue& ) SK_OVERRIDE;
+    bool getProperty(int index, SkScriptValue* value) const override;
+    bool setProperty(int index, SkScriptValue& ) override;
 
 private:
     SkString fText;
diff --git a/src/animator/SkDrawTo.h b/src/animator/SkDrawTo.h
index 0c089d6..29f6b63 100644
--- a/src/animator/SkDrawTo.h
+++ b/src/animator/SkDrawTo.h
@@ -19,9 +19,9 @@
     DECLARE_MEMBER_INFO(DrawTo);
     SkDrawTo();
 //  virtual ~SkDrawTo();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
 protected:
     SkBool drawOnce;
diff --git a/src/animator/SkDrawTransparentShader.h b/src/animator/SkDrawTransparentShader.h
index b6ab7c7..5b5b156 100644
--- a/src/animator/SkDrawTransparentShader.h
+++ b/src/animator/SkDrawTransparentShader.h
@@ -12,7 +12,7 @@
 
 class SkDrawTransparentShader : public SkDrawShader {
     DECLARE_EMPTY_MEMBER_INFO(TransparentShader);
-    SkShader* getShader() SK_OVERRIDE;
+    SkShader* getShader() override;
 };
 
 #endif // SkDrawTransparentShader_DEFINED
diff --git a/src/animator/SkDump.h b/src/animator/SkDump.h
index 86e9e08..bedd533 100644
--- a/src/animator/SkDump.h
+++ b/src/animator/SkDump.h
@@ -20,9 +20,9 @@
     DECLARE_MEMBER_INFO(Dump);
 #ifdef SK_DUMP_ENABLED
     SkDump();
-    bool enable(SkAnimateMaker & ) SK_OVERRIDE;
+    bool enable(SkAnimateMaker & ) override;
     bool evaluate(SkAnimateMaker &);
-    bool hasEnable() const SK_OVERRIDE;
+    bool hasEnable() const override;
     static void GetEnumString(SkDisplayTypes , int index, SkString* result);
     SkBool displayList;
     SkBool eventList;
diff --git a/src/animator/SkHitClear.h b/src/animator/SkHitClear.h
index 3e09700..042c181 100644
--- a/src/animator/SkHitClear.h
+++ b/src/animator/SkHitClear.h
@@ -16,8 +16,8 @@
 
 class SkHitClear : public SkDisplayable {
     DECLARE_MEMBER_INFO(HitClear);
-    bool enable(SkAnimateMaker& ) SK_OVERRIDE;
-    bool hasEnable() const SK_OVERRIDE;
+    bool enable(SkAnimateMaker& ) override;
+    bool hasEnable() const override;
 private:
     SkTDDisplayableArray targets;
 };
diff --git a/src/animator/SkHitTest.h b/src/animator/SkHitTest.h
index f79dab0..bd1cbe2 100644
--- a/src/animator/SkHitTest.h
+++ b/src/animator/SkHitTest.h
@@ -16,10 +16,10 @@
 class SkHitTest : public SkADrawable {
     DECLARE_MEMBER_INFO(HitTest);
     SkHitTest();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
-    bool enable(SkAnimateMaker& ) SK_OVERRIDE;
-    bool hasEnable() const SK_OVERRIDE;
-    const SkMemberInfo* preferredChild(SkDisplayTypes type) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
+    bool enable(SkAnimateMaker& ) override;
+    bool hasEnable() const override;
+    const SkMemberInfo* preferredChild(SkDisplayTypes type) override;
 private:
     SkTDDisplayableArray bullets;
     SkTDIntArray hits;
diff --git a/src/animator/SkMatrixParts.h b/src/animator/SkMatrixParts.h
index e6a8850..3276d02 100644
--- a/src/animator/SkMatrixParts.h
+++ b/src/animator/SkMatrixParts.h
@@ -39,7 +39,7 @@
     DECLARE_MEMBER_INFO(Rotate);
     SkRotate();
 protected:
-    bool add() SK_OVERRIDE;
+    bool add() override;
     SkScalar degrees;
     SkPoint center;
 };
@@ -48,7 +48,7 @@
     DECLARE_MEMBER_INFO(Scale);
     SkScale();
 protected:
-    bool add() SK_OVERRIDE;
+    bool add() override;
     SkScalar x;
     SkScalar y;
     SkPoint center;
@@ -58,7 +58,7 @@
     DECLARE_MEMBER_INFO(Skew);
     SkSkew();
 protected:
-    bool add() SK_OVERRIDE;
+    bool add() override;
     SkScalar x;
     SkScalar y;
     SkPoint center;
@@ -68,7 +68,7 @@
     DECLARE_MEMBER_INFO(Translate);
     SkTranslate();
 protected:
-    bool add() SK_OVERRIDE;
+    bool add() override;
     SkScalar x;
     SkScalar y;
 };
@@ -78,7 +78,7 @@
     SkFromPath();
     virtual ~SkFromPath();
 protected:
-    bool add() SK_OVERRIDE;
+    bool add() override;
     int32_t mode;
     SkScalar offset;
     SkDrawPath* path;
@@ -90,11 +90,11 @@
     SkRectToRect();
     virtual ~SkRectToRect();
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    const SkMemberInfo* preferredChild(SkDisplayTypes type) SK_OVERRIDE;
+    const SkMemberInfo* preferredChild(SkDisplayTypes type) override;
 protected:
-    bool add() SK_OVERRIDE;
+    bool add() override;
     SkDrawRect* source;
     SkDrawRect* destination;
 };
@@ -104,12 +104,12 @@
     SkPolyToPoly();
     virtual ~SkPolyToPoly();
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker* ) SK_OVERRIDE;
+    void dump(SkAnimateMaker* ) override;
 #endif
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
-    const SkMemberInfo* preferredChild(SkDisplayTypes type) SK_OVERRIDE;
+    void onEndElement(SkAnimateMaker& ) override;
+    const SkMemberInfo* preferredChild(SkDisplayTypes type) override;
 protected:
-    bool add() SK_OVERRIDE;
+    bool add() override;
     SkPolygon* source;
     SkPolygon* destination;
 };
diff --git a/src/animator/SkMemberInfo.h b/src/animator/SkMemberInfo.h
index c0d9e20..e07c322 100644
--- a/src/animator/SkMemberInfo.h
+++ b/src/animator/SkMemberInfo.h
@@ -150,49 +150,49 @@
 public: \
     static const SkMemberInfo fInfo[]; \
     static const int fInfoCount; \
-    const SkMemberInfo* getMember(int index) SK_OVERRIDE; \
-    const SkMemberInfo* getMember(const char name[]) SK_OVERRIDE; \
+    const SkMemberInfo* getMember(int index) override; \
+    const SkMemberInfo* getMember(const char name[]) override; \
     typedef Sk##_type BASE_CLASS
 
 #define DECLARE_MEMBER_INFO(_type) \
 public: \
     static const SkMemberInfo fInfo[]; \
     static const int fInfoCount; \
-    const SkMemberInfo* getMember(int index) SK_OVERRIDE; \
-    const SkMemberInfo* getMember(const char name[]) SK_OVERRIDE; \
-    SkDisplayTypes getType() const SK_OVERRIDE { return SkType_##_type; } \
+    const SkMemberInfo* getMember(int index) override; \
+    const SkMemberInfo* getMember(const char name[]) override; \
+    SkDisplayTypes getType() const override { return SkType_##_type; } \
     typedef Sk##_type BASE_CLASS
 
 #define DECLARE_DRAW_MEMBER_INFO(_type) \
 public: \
     static const SkMemberInfo fInfo[]; \
     static const int fInfoCount; \
-    const SkMemberInfo* getMember(int index) SK_OVERRIDE; \
-    const SkMemberInfo* getMember(const char name[]) SK_OVERRIDE; \
-    SkDisplayTypes getType() const SK_OVERRIDE { return SkType_##_type; } \
+    const SkMemberInfo* getMember(int index) override; \
+    const SkMemberInfo* getMember(const char name[]) override; \
+    SkDisplayTypes getType() const override { return SkType_##_type; } \
     typedef SkDraw##_type BASE_CLASS
 
 #define DECLARE_DISPLAY_MEMBER_INFO(_type) \
 public: \
     static const SkMemberInfo fInfo[]; \
     static const int fInfoCount; \
-    const SkMemberInfo* getMember(int index) SK_OVERRIDE; \
-    const SkMemberInfo* getMember(const char name[]) SK_OVERRIDE; \
-    SkDisplayTypes getType() const SK_OVERRIDE { return SkType_##_type; } \
+    const SkMemberInfo* getMember(int index) override; \
+    const SkMemberInfo* getMember(const char name[]) override; \
+    SkDisplayTypes getType() const override { return SkType_##_type; } \
     typedef SkDisplay##_type BASE_CLASS
 
 #define DECLARE_EMPTY_MEMBER_INFO(_type) \
 public: \
-    SkDisplayTypes getType() const SK_OVERRIDE { return SkType_##_type; }
+    SkDisplayTypes getType() const override { return SkType_##_type; }
 
 #define DECLARE_EXTRAS_MEMBER_INFO(_type) \
 public: \
     static const SkMemberInfo fInfo[]; \
     static const int fInfoCount; \
-    const SkMemberInfo* getMember(int index) SK_OVERRIDE; \
-    const SkMemberInfo* getMember(const char name[]) SK_OVERRIDE; \
+    const SkMemberInfo* getMember(int index) override; \
+    const SkMemberInfo* getMember(const char name[]) override; \
     SkDisplayTypes fType; \
-    SkDisplayTypes getType() const SK_OVERRIDE { return fType; } \
+    SkDisplayTypes getType() const override { return fType; } \
     typedef _type BASE_CLASS
 
 #define DECLARE_NO_VIRTUALS_MEMBER_INFO(_type) \
diff --git a/src/animator/SkPaintPart.h b/src/animator/SkPaintPart.h
index a285a1c..2907d81 100644
--- a/src/animator/SkPaintPart.h
+++ b/src/animator/SkPaintPart.h
@@ -35,14 +35,14 @@
     DECLARE_EMPTY_MEMBER_INFO(MaskFilter);
     virtual SkMaskFilter* getMaskFilter();
 protected:
-    bool add() SK_OVERRIDE;
+    bool add() override;
 };
 
 class SkDrawPathEffect : public SkPaintPart {
     DECLARE_EMPTY_MEMBER_INFO(PathEffect);
     virtual SkPathEffect* getPathEffect();
 protected:
-    bool add() SK_OVERRIDE;
+    bool add() override;
 };
 
 class SkDrawShader : public SkPaintPart {
@@ -50,7 +50,7 @@
     SkDrawShader();
     virtual SkShader* getShader();
 protected:
-    bool add() SK_OVERRIDE;
+    bool add() override;
     SkMatrix* getMatrix(); // returns NULL if matrix is NULL
     SkDrawMatrix* matrix;
     int /*SkShader::TileMode*/ tileMode;
@@ -60,12 +60,12 @@
     DECLARE_DRAW_MEMBER_INFO(Typeface);
     SkDrawTypeface();
 #ifdef SK_DUMP_ENABLED
-    void dump(SkAnimateMaker *) SK_OVERRIDE;
+    void dump(SkAnimateMaker *) override;
 #endif
     SkTypeface* getTypeface() {
         return SkTypeface::CreateFromName(fontName.c_str(), style); }
 protected:
-    bool add() SK_OVERRIDE;
+    bool add() override;
     SkString fontName;
     SkTypeface::Style style;
 };
diff --git a/src/animator/SkPathParts.h b/src/animator/SkPathParts.h
index 6aad1ba..afa7b66 100644
--- a/src/animator/SkPathParts.h
+++ b/src/animator/SkPathParts.h
@@ -34,7 +34,7 @@
 class SkMoveTo : public SkPathPart {
     DECLARE_MEMBER_INFO(MoveTo);
     SkMoveTo();
-    bool add() SK_OVERRIDE;
+    bool add() override;
 protected:
     SkScalar x;
     SkScalar y;
@@ -42,7 +42,7 @@
 
 class SkRMoveTo : public SkMoveTo {
     DECLARE_MEMBER_INFO(RMoveTo);
-    bool add() SK_OVERRIDE;
+    bool add() override;
 private:
     typedef SkMoveTo INHERITED;
 };
@@ -50,7 +50,7 @@
 class SkLineTo : public SkPathPart {
     DECLARE_MEMBER_INFO(LineTo);
     SkLineTo();
-    bool add() SK_OVERRIDE;
+    bool add() override;
 protected:
     SkScalar x;
     SkScalar y;
@@ -58,7 +58,7 @@
 
 class SkRLineTo : public SkLineTo {
     DECLARE_MEMBER_INFO(RLineTo);
-    bool add() SK_OVERRIDE;
+    bool add() override;
 private:
     typedef SkLineTo INHERITED;
 };
@@ -66,7 +66,7 @@
 class SkQuadTo : public SkPathPart {
     DECLARE_MEMBER_INFO(QuadTo);
     SkQuadTo();
-    bool add() SK_OVERRIDE;
+    bool add() override;
 protected:
     SkScalar x1;
     SkScalar y1;
@@ -76,7 +76,7 @@
 
 class SkRQuadTo : public SkQuadTo {
     DECLARE_MEMBER_INFO(RQuadTo);
-    bool add() SK_OVERRIDE;
+    bool add() override;
 private:
     typedef SkQuadTo INHERITED;
 };
@@ -84,7 +84,7 @@
 class SkCubicTo : public SkPathPart {
     DECLARE_MEMBER_INFO(CubicTo);
     SkCubicTo();
-    bool add() SK_OVERRIDE;
+    bool add() override;
 protected:
     SkScalar x1;
     SkScalar y1;
@@ -96,14 +96,14 @@
 
 class SkRCubicTo : public SkCubicTo {
     DECLARE_MEMBER_INFO(RCubicTo);
-    bool add() SK_OVERRIDE;
+    bool add() override;
 private:
     typedef SkCubicTo INHERITED;
 };
 
 class SkClose : public SkPathPart {
     DECLARE_EMPTY_MEMBER_INFO(Close);
-    bool add() SK_OVERRIDE;
+    bool add() override;
 };
 
 class SkAddGeom : public SkPathPart {
@@ -116,7 +116,7 @@
 class SkAddRect : public SkAddGeom {
     DECLARE_MEMBER_INFO(AddRect);
     SkAddRect();
-    bool add() SK_OVERRIDE;
+    bool add() override;
 protected:
     SkRect fRect;
 private:
@@ -125,7 +125,7 @@
 
 class SkAddOval : public SkAddRect {
     DECLARE_MEMBER_INFO(AddOval);
-    bool add() SK_OVERRIDE;
+    bool add() override;
 private:
     typedef SkAddRect INHERITED;
 };
@@ -133,7 +133,7 @@
 class SkAddCircle : public SkAddGeom {
     DECLARE_MEMBER_INFO(AddCircle);
     SkAddCircle();
-    bool add() SK_OVERRIDE;
+    bool add() override;
 private:
     SkScalar radius;
     SkScalar x;
@@ -144,7 +144,7 @@
 class SkAddRoundRect : public SkAddRect {
     DECLARE_MEMBER_INFO(AddRoundRect);
     SkAddRoundRect();
-    bool add() SK_OVERRIDE;
+    bool add() override;
 private:
     SkScalar rx;
     SkScalar ry;
@@ -154,7 +154,7 @@
 class SkAddPath : public SkPathPart {
     DECLARE_MEMBER_INFO(AddPath);
     SkAddPath();
-    bool add() SK_OVERRIDE;
+    bool add() override;
 private:
     typedef SkPathPart INHERITED;
     SkDrawMatrix* matrix;
diff --git a/src/animator/SkPostParts.h b/src/animator/SkPostParts.h
index f48c33f..fb2845b 100644
--- a/src/animator/SkPostParts.h
+++ b/src/animator/SkPostParts.h
@@ -18,10 +18,10 @@
     DECLARE_MEMBER_INFO(DataInput);
     SkDataInput();
     bool add();
-    void dirty() SK_OVERRIDE;
-    SkDisplayable* getParent() const SK_OVERRIDE;
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
-    bool setParent(SkDisplayable* ) SK_OVERRIDE;
+    void dirty() override;
+    SkDisplayable* getParent() const override;
+    void onEndElement(SkAnimateMaker& ) override;
+    bool setParent(SkDisplayable* ) override;
 protected:
     SkPost* fParent;
     typedef SkInput INHERITED;
diff --git a/src/animator/SkScript.cpp b/src/animator/SkScript.cpp
index 1d04d1b..c430695 100644
--- a/src/animator/SkScript.cpp
+++ b/src/animator/SkScript.cpp
@@ -1387,7 +1387,7 @@
             if (operand2.fScalar == 0)
                 operand2.fScalar = operand1.fScalar == 0 ? SK_ScalarNaN : operand1.fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax;
             else
-                operand2.fScalar = SkScalarDiv(operand1.fScalar, operand2.fScalar);
+                operand2.fScalar = operand1.fScalar / operand2.fScalar;
             break;
         case kEqualInt:
             operand2.fS32 = operand1.fS32 == operand2.fS32;
diff --git a/src/animator/SkScriptRuntime.cpp b/src/animator/SkScriptRuntime.cpp
index 78d9d5c..07bd4c9 100644
--- a/src/animator/SkScriptRuntime.cpp
+++ b/src/animator/SkScriptRuntime.cpp
@@ -249,7 +249,7 @@
                 operand[0].fScalar = operand[0].fScalar == 0 ? SK_ScalarNaN :
                     operand[0].fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax;
             else
-                operand[0].fScalar = SkScalarDiv(operand[0].fScalar, operand[1].fScalar);
+                operand[0].fScalar = operand[0].fScalar / operand[1].fScalar;
             break;
         case SkScriptEngine2::kEqualInt:
             operand[0].fS32 = operand[0].fS32 == operand[1].fS32;
diff --git a/src/animator/SkSnapshot.h b/src/animator/SkSnapshot.h
index e3b36d1..a4eb175 100644
--- a/src/animator/SkSnapshot.h
+++ b/src/animator/SkSnapshot.h
@@ -18,7 +18,7 @@
 class SkSnapshot: public SkADrawable {
     DECLARE_MEMBER_INFO(Snapshot);
     SkSnapshot();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
     private:
     SkString filename;
     SkScalar quality;
diff --git a/src/animator/SkTextOnPath.h b/src/animator/SkTextOnPath.h
index fb1f6f2..36adfd5 100644
--- a/src/animator/SkTextOnPath.h
+++ b/src/animator/SkTextOnPath.h
@@ -19,7 +19,7 @@
 class SkTextOnPath : public SkBoundable {
     DECLARE_MEMBER_INFO(TextOnPath);
     SkTextOnPath();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
 private:
     SkScalar offset;
     SkDrawPath* path;
diff --git a/src/animator/SkTextToPath.h b/src/animator/SkTextToPath.h
index 2be67ad..23b6bfe 100644
--- a/src/animator/SkTextToPath.h
+++ b/src/animator/SkTextToPath.h
@@ -20,8 +20,8 @@
 class SkTextToPath : public SkADrawable {
     DECLARE_MEMBER_INFO(TextToPath);
     SkTextToPath();
-    bool draw(SkAnimateMaker& ) SK_OVERRIDE;
-    void onEndElement(SkAnimateMaker& ) SK_OVERRIDE;
+    bool draw(SkAnimateMaker& ) override;
+    void onEndElement(SkAnimateMaker& ) override;
 private:
     SkDrawPaint* paint;
     SkDrawPath* path;
diff --git a/src/animator/SkXMLAnimatorWriter.h b/src/animator/SkXMLAnimatorWriter.h
index 1aedada..2f6721d 100644
--- a/src/animator/SkXMLAnimatorWriter.h
+++ b/src/animator/SkXMLAnimatorWriter.h
@@ -23,10 +23,10 @@
     SkDEBUGCODE(static void UnitTest(class SkCanvas* canvas);)
 
 protected:
-    void onAddAttributeLen(const char name[], const char value[], size_t length) SK_OVERRIDE;
-    void onEndElement() SK_OVERRIDE;
-    void onStartElementLen(const char elem[], size_t length) SK_OVERRIDE;
-    void onAddText(const char text[], size_t length) SK_OVERRIDE;
+    void onAddAttributeLen(const char name[], const char value[], size_t length) override;
+    void onEndElement() override;
+    void onStartElementLen(const char elem[], size_t length) override;
+    void onAddText(const char text[], size_t length) override;
 
 private:
     SkAnimator* fAnimator;
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index ec36bc7..c4adf72 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -7,25 +7,59 @@
 
 #include "SkCodec.h"
 #include "SkData.h"
+#include "SkCodec_libbmp.h"
+#include "SkCodec_libgif.h"
+#include "SkCodec_libico.h"
 #include "SkCodec_libpng.h"
+#include "SkCodec_wbmp.h"
+#include "SkCodecPriv.h"
+#include "SkJpegCodec.h"
 #include "SkStream.h"
 
+struct DecoderProc {
+    bool (*IsFormat)(SkStream*);
+    SkCodec* (*NewFromStream)(SkStream*);
+};
+
+static const DecoderProc gDecoderProcs[] = {
+    { SkPngCodec::IsPng, SkPngCodec::NewFromStream },
+    { SkJpegCodec::IsJpeg, SkJpegCodec::NewFromStream },
+    { SkGifCodec::IsGif, SkGifCodec::NewFromStream },
+    { SkIcoCodec::IsIco, SkIcoCodec::NewFromStream },
+    { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream },
+    { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream }
+};
+
 SkCodec* SkCodec::NewFromStream(SkStream* stream) {
     if (!stream) {
         return NULL;
     }
+
     SkAutoTDelete<SkStream> streamDeleter(stream);
-    const bool isPng = SkPngCodec::IsPng(stream);
-    // TODO: Avoid rewinding.
-    if (!stream->rewind()) {
+    
+    SkAutoTDelete<SkCodec> codec(NULL);
+    for (uint32_t i = 0; i < SK_ARRAY_COUNT(gDecoderProcs); i++) {
+        DecoderProc proc = gDecoderProcs[i];
+        const bool correctFormat = proc.IsFormat(stream);
+        if (!stream->rewind()) {
+            return NULL;
+        }
+        if (correctFormat) {
+            codec.reset(proc.NewFromStream(streamDeleter.detach()));
+            break;
+        }
+    }
+
+    // Set the max size at 128 megapixels (512 MB for kN32).
+    // This is about 4x smaller than a test image that takes a few minutes for
+    // dm to decode and draw.
+    const int32_t maxSize = 1 << 27;
+    if (codec && codec->getInfo().width() * codec->getInfo().height() > maxSize) {
+        SkCodecPrintf("Error: Image size too large, cannot decode.\n");
         return NULL;
+    } else {
+        return codec.detach();
     }
-    if (isPng) {
-        streamDeleter.detach();
-        return SkPngCodec::NewFromStream(stream);
-    }
-    // TODO: Check other image types.
-    return NULL;
 }
 
 SkCodec* SkCodec::NewFromData(SkData* data) {
@@ -36,15 +70,40 @@
 }
 
 SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream)
-    : fInfo(info)
+    : INHERITED(info)
     , fStream(stream)
     , fNeedsRewind(false)
 {}
 
-bool SkCodec::rewindIfNeeded() {
+SkCodec::RewindState SkCodec::rewindIfNeeded() {
     // Store the value of fNeedsRewind so we can update it. Next read will
     // require a rewind.
-    const bool neededRewind = fNeedsRewind;
+    const bool needsRewind = fNeedsRewind;
     fNeedsRewind = true;
-    return !neededRewind || fStream->rewind();
+    if (!needsRewind) {
+        return kNoRewindNecessary_RewindState;
+    }
+    return fStream->rewind() ? kRewound_RewindState
+                             : kCouldNotRewind_RewindState;
+}
+
+SkScanlineDecoder* SkCodec::getScanlineDecoder(const SkImageInfo& dstInfo, const Options* options,
+        SkPMColor ctable[], int* ctableCount) {
+
+    // Set options.
+    Options optsStorage;
+    if (NULL == options) {
+        options = &optsStorage;
+    }
+
+    fScanlineDecoder.reset(this->onGetScanlineDecoder(dstInfo, *options, ctable, ctableCount));
+    return fScanlineDecoder.get();
+}
+
+SkScanlineDecoder* SkCodec::getScanlineDecoder(const SkImageInfo& dstInfo) {
+    SkASSERT(kIndex_8_SkColorType != dstInfo.colorType());
+    if (kIndex_8_SkColorType == dstInfo.colorType()) {
+        return NULL;
+    }
+    return this->getScanlineDecoder(dstInfo, NULL, NULL, NULL);
 }
diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h
new file mode 100644
index 0000000..98b2d3a
--- /dev/null
+++ b/src/codec/SkCodecPriv.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2015 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 SkCodecPriv_DEFINED
+#define SkCodecPriv_DEFINED
+
+#include "SkColorTable.h"
+#include "SkImageInfo.h"
+#include "SkSwizzler.h"
+#include "SkTypes.h"
+#include "SkUtils.h"
+
+/*
+ *
+ * Helper routine for alpha result codes
+ *
+ */
+#define INIT_RESULT_ALPHA                       \
+    uint8_t zeroAlpha = 0;                      \
+    uint8_t maxAlpha = 0xFF;
+
+#define UPDATE_RESULT_ALPHA(alpha)              \
+    zeroAlpha |= (alpha);                       \
+    maxAlpha  &= (alpha);
+
+#define COMPUTE_RESULT_ALPHA                    \
+    SkSwizzler::GetResult(zeroAlpha, maxAlpha);
+
+/*
+ *
+ * Copy the codec color table back to the client when kIndex8 color type is requested
+ *
+ */
+static inline void copy_color_table(const SkImageInfo& dstInfo, SkColorTable* colorTable,
+        SkPMColor* inputColorPtr, int* inputColorCount) {
+    if (kIndex_8_SkColorType == dstInfo.colorType()) {
+        SkASSERT(NULL != inputColorPtr);
+        SkASSERT(NULL != inputColorCount);
+        SkASSERT(NULL != colorTable);
+        sk_memcpy32(inputColorPtr, colorTable->readColors(), *inputColorCount);
+    }
+}
+
+/*
+ *
+ * Compute row bytes for an image using pixels per byte
+ *
+ */
+static inline size_t compute_row_bytes_ppb(int width, uint32_t pixelsPerByte) {
+    return (width + pixelsPerByte - 1) / pixelsPerByte;
+}
+
+/*
+ *
+ * Compute row bytes for an image using bytes per pixel
+ *
+ */
+static inline size_t compute_row_bytes_bpp(int width, uint32_t bytesPerPixel) {
+    return width * bytesPerPixel;
+}
+
+/*
+ *
+ * Compute row bytes for an image
+ *
+ */
+static inline size_t compute_row_bytes(int width, uint32_t bitsPerPixel) {
+    if (bitsPerPixel < 16) {
+        SkASSERT(0 == 8 % bitsPerPixel);
+        const uint32_t pixelsPerByte = 8 / bitsPerPixel;
+        return compute_row_bytes_ppb(width, pixelsPerByte);
+    } else {
+        SkASSERT(0 == bitsPerPixel % 8);
+        const uint32_t bytesPerPixel = bitsPerPixel / 8;
+        return compute_row_bytes_bpp(width, bytesPerPixel);
+    }
+}
+
+/*
+ *
+ * Get a byte from a buffer
+ * This method is unsafe, the caller is responsible for performing a check
+ *
+ */
+static inline uint8_t get_byte(uint8_t* buffer, uint32_t i) {
+    return buffer[i];
+}
+
+/*
+ *
+ * Get a short from a buffer
+ * This method is unsafe, the caller is responsible for performing a check
+ *
+ */
+static inline uint16_t get_short(uint8_t* buffer, uint32_t i) {
+    uint16_t result;
+    memcpy(&result, &(buffer[i]), 2);
+#ifdef SK_CPU_BENDIAN
+    return SkEndianSwap16(result);
+#else
+    return result;
+#endif
+}
+
+/*
+ *
+ * Get an int from a buffer
+ * This method is unsafe, the caller is responsible for performing a check
+ *
+ */
+static inline uint32_t get_int(uint8_t* buffer, uint32_t i) {
+    uint32_t result;
+    memcpy(&result, &(buffer[i]), 4);
+#ifdef SK_CPU_BENDIAN
+    return SkEndianSwap32(result);
+#else
+    return result;
+#endif
+}
+
+#ifdef SK_PRINT_CODEC_MESSAGES
+    #define SkCodecPrintf SkDebugf
+#else
+    #define SkCodecPrintf(...)
+#endif
+
+#endif // SkCodecPriv_DEFINED
diff --git a/src/codec/SkCodec_libbmp.cpp b/src/codec/SkCodec_libbmp.cpp
new file mode 100644
index 0000000..fdb0bd6
--- /dev/null
+++ b/src/codec/SkCodec_libbmp.cpp
@@ -0,0 +1,1231 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec_libbmp.h"
+#include "SkCodecPriv.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+
+/*
+ *
+ * Checks if the conversion between the input image and the requested output
+ * image has been implemented
+ *
+ */
+static bool conversion_possible(const SkImageInfo& dst,
+                                const SkImageInfo& src) {
+    // Ensure that the profile type is unchanged
+    if (dst.profileType() != src.profileType()) {
+        return false;
+    }
+
+    // Check for supported alpha types
+    if (src.alphaType() != dst.alphaType()) {
+        if (kOpaque_SkAlphaType == src.alphaType()) {
+            // If the source is opaque, we must decode to opaque
+            return false;
+        }
+
+        // The source is not opaque
+        switch (dst.alphaType()) {
+            case kPremul_SkAlphaType:
+            case kUnpremul_SkAlphaType:
+                // The source is not opaque, so either of these is okay
+                break;
+            default:
+                // We cannot decode a non-opaque image to opaque (or unknown)
+                return false;
+        }
+    }
+
+    // Check for supported color types
+    switch (dst.colorType()) {
+        // Allow output to kN32 from any type of input
+        case kN32_SkColorType:
+            return true;
+        // Allow output to kIndex_8 from compatible inputs
+        case kIndex_8_SkColorType:
+            return kIndex_8_SkColorType == src.colorType();
+        default:
+            return false;
+    }
+}
+
+/*
+ *
+ * Defines the version and type of the second bitmap header
+ *
+ */
+enum BitmapHeaderType {
+    kInfoV1_BitmapHeaderType,
+    kInfoV2_BitmapHeaderType,
+    kInfoV3_BitmapHeaderType,
+    kInfoV4_BitmapHeaderType,
+    kInfoV5_BitmapHeaderType,
+    kOS2V1_BitmapHeaderType,
+    kOS2VX_BitmapHeaderType,
+    kUnknown_BitmapHeaderType
+};
+
+/*
+ *
+ * Possible bitmap compression types
+ *
+ */
+enum BitmapCompressionMethod {
+    kNone_BitmapCompressionMethod =          0,
+    k8BitRLE_BitmapCompressionMethod =       1,
+    k4BitRLE_BitmapCompressionMethod =       2,
+    kBitMasks_BitmapCompressionMethod =      3,
+    kJpeg_BitmapCompressionMethod =          4,
+    kPng_BitmapCompressionMethod =           5,
+    kAlphaBitMasks_BitmapCompressionMethod = 6,
+    kCMYK_BitmapCompressionMethod =          11,
+    kCMYK8BitRLE_BitmapCompressionMethod =   12,
+    kCMYK4BitRLE_BitmapCompressionMethod =   13
+};
+
+/*
+ *
+ * Checks the start of the stream to see if the image is a bitmap
+ *
+ */
+bool SkBmpCodec::IsBmp(SkStream* stream) {
+    // TODO: Support "IC", "PT", "CI", "CP", "BA"
+    const char bmpSig[] = { 'B', 'M' };
+    char buffer[sizeof(bmpSig)];
+    return stream->read(buffer, sizeof(bmpSig)) == sizeof(bmpSig) &&
+            !memcmp(buffer, bmpSig, sizeof(bmpSig));
+}
+
+/*
+ *
+ * Assumes IsBmp was called and returned true
+ * Creates a bmp decoder
+ * Reads enough of the stream to determine the image format
+ *
+ */
+SkCodec* SkBmpCodec::NewFromStream(SkStream* stream) {
+    return SkBmpCodec::NewFromStream(stream, false);
+}
+
+/*
+ *
+ * Creates a bmp decoder for a bmp embedded in ico
+ * Reads enough of the stream to determine the image format
+ *
+ */
+SkCodec* SkBmpCodec::NewFromIco(SkStream* stream) {
+    return SkBmpCodec::NewFromStream(stream, true);
+}
+
+/*
+ *
+ * Read enough of the stream to initialize the SkBmpCodec. Returns a bool
+ * representing success or failure. If it returned true, and codecOut was
+ * not NULL, it will be set to a new SkBmpCodec.
+ * Does *not* take ownership of the passed in SkStream.
+ *
+ */
+bool SkBmpCodec::ReadHeader(SkStream* stream, bool isIco, SkCodec** codecOut) {
+    // Header size constants
+    static const uint32_t kBmpHeaderBytes = 14;
+    static const uint32_t kBmpHeaderBytesPlusFour = kBmpHeaderBytes + 4;
+    static const uint32_t kBmpOS2V1Bytes = 12;
+    static const uint32_t kBmpOS2V2Bytes = 64;
+    static const uint32_t kBmpInfoBaseBytes = 16;
+    static const uint32_t kBmpInfoV1Bytes = 40;
+    static const uint32_t kBmpInfoV2Bytes = 52;
+    static const uint32_t kBmpInfoV3Bytes = 56;
+    static const uint32_t kBmpInfoV4Bytes = 108;
+    static const uint32_t kBmpInfoV5Bytes = 124;
+    static const uint32_t kBmpMaskBytes = 12;
+
+    // The total bytes in the bmp file
+    // We only need to use this value for RLE decoding, so we will only
+    // check that it is valid in the RLE case.
+    uint32_t totalBytes;
+    // The offset from the start of the file where the pixel data begins
+    uint32_t offset;
+    // The size of the second (info) header in bytes
+    uint32_t infoBytes;
+
+    // Bmps embedded in Icos skip the first Bmp header
+    if (!isIco) {
+        // Read the first header and the size of the second header
+        SkAutoTDeleteArray<uint8_t> hBuffer(
+                SkNEW_ARRAY(uint8_t, kBmpHeaderBytesPlusFour));
+        if (stream->read(hBuffer.get(), kBmpHeaderBytesPlusFour) !=
+                kBmpHeaderBytesPlusFour) {
+            SkCodecPrintf("Error: unable to read first bitmap header.\n");
+            return false;
+        }
+
+        totalBytes = get_int(hBuffer.get(), 2);
+        offset = get_int(hBuffer.get(), 10);
+        if (offset < kBmpHeaderBytes + kBmpOS2V1Bytes) {
+            SkCodecPrintf("Error: invalid starting location for pixel data\n");
+            return false;
+        }
+
+        // The size of the second (info) header in bytes
+        // The size is the first field of the second header, so we have already
+        // read the first four infoBytes.
+        infoBytes = get_int(hBuffer.get(), 14);
+        if (infoBytes < kBmpOS2V1Bytes) {
+            SkCodecPrintf("Error: invalid second header size.\n");
+            return false;
+        }
+    } else {
+        // This value is only used by RLE compression.  Bmp in Ico files do not
+        // use RLE.  If the compression field is incorrectly signaled as RLE,
+        // we will catch this and signal an error below.
+        totalBytes = 0;
+
+        // Bmps in Ico cannot specify an offset.  We will always assume that
+        // pixel data begins immediately after the color table.  This value
+        // will be corrected below.
+        offset = 0;
+
+        // Read the size of the second header
+        SkAutoTDeleteArray<uint8_t> hBuffer(
+                SkNEW_ARRAY(uint8_t, 4));
+        if (stream->read(hBuffer.get(), 4) != 4) {
+            SkCodecPrintf("Error: unable to read size of second bitmap header.\n");
+            return false;
+        }
+        infoBytes = get_int(hBuffer.get(), 0);
+        if (infoBytes < kBmpOS2V1Bytes) {
+            SkCodecPrintf("Error: invalid second header size.\n");
+            return false;
+        }
+    }
+
+    // We already read the first four bytes of the info header to get the size
+    const uint32_t infoBytesRemaining = infoBytes - 4;
+
+    // Read the second header
+    SkAutoTDeleteArray<uint8_t> iBuffer(
+            SkNEW_ARRAY(uint8_t, infoBytesRemaining));
+    if (stream->read(iBuffer.get(), infoBytesRemaining) != infoBytesRemaining) {
+        SkCodecPrintf("Error: unable to read second bitmap header.\n");
+        return false;
+    }
+
+    // The number of bits used per pixel in the pixel data
+    uint16_t bitsPerPixel;
+
+    // The compression method for the pixel data
+    uint32_t compression = kNone_BitmapCompressionMethod;
+
+    // Number of colors in the color table, defaults to 0 or max (see below)
+    uint32_t numColors = 0;
+
+    // Bytes per color in the color table, early versions use 3, most use 4
+    uint32_t bytesPerColor;
+
+    // The image width and height
+    int width, height;
+
+    // Determine image information depending on second header format
+    BitmapHeaderType headerType;
+    if (infoBytes >= kBmpInfoBaseBytes) {
+        // Check the version of the header
+        switch (infoBytes) {
+            case kBmpInfoV1Bytes:
+                headerType = kInfoV1_BitmapHeaderType;
+                break;
+            case kBmpInfoV2Bytes:
+                headerType = kInfoV2_BitmapHeaderType;
+                break;
+            case kBmpInfoV3Bytes:
+                headerType = kInfoV3_BitmapHeaderType;
+                break;
+            case kBmpInfoV4Bytes:
+                headerType = kInfoV4_BitmapHeaderType;
+                break;
+            case kBmpInfoV5Bytes:
+                headerType = kInfoV5_BitmapHeaderType;
+                break;
+            case 16:
+            case 20:
+            case 24:
+            case 28:
+            case 32:
+            case 36:
+            case 42:
+            case 46:
+            case 48:
+            case 60:
+            case kBmpOS2V2Bytes:
+                headerType = kOS2VX_BitmapHeaderType;
+                break;
+            default:
+                // We do not signal an error here because there is the
+                // possibility of new or undocumented bmp header types.  Most
+                // of the newer versions of bmp headers are similar to and
+                // build off of the older versions, so we may still be able to
+                // decode the bmp.
+                SkCodecPrintf("Warning: unknown bmp header format.\n");
+                headerType = kUnknown_BitmapHeaderType;
+                break;
+        }
+        // We check the size of the header before entering the if statement.
+        // We should not reach this point unless the size is large enough for
+        // these required fields.
+        SkASSERT(infoBytesRemaining >= 12);
+        width = get_int(iBuffer.get(), 0);
+        height = get_int(iBuffer.get(), 4);
+        bitsPerPixel = get_short(iBuffer.get(), 10);
+
+        // Some versions do not have these fields, so we check before
+        // overwriting the default value.
+        if (infoBytesRemaining >= 16) {
+            compression = get_int(iBuffer.get(), 12);
+            if (infoBytesRemaining >= 32) {
+                numColors = get_int(iBuffer.get(), 28);
+            }
+        }
+
+        // All of the headers that reach this point, store color table entries
+        // using 4 bytes per pixel.
+        bytesPerColor = 4;
+    } else if (infoBytes >= kBmpOS2V1Bytes) {
+        // The OS2V1 is treated separately because it has a unique format
+        headerType = kOS2V1_BitmapHeaderType;
+        width = (int) get_short(iBuffer.get(), 0);
+        height = (int) get_short(iBuffer.get(), 2);
+        bitsPerPixel = get_short(iBuffer.get(), 6);
+        bytesPerColor = 3;
+    } else {
+        // There are no valid bmp headers
+        SkCodecPrintf("Error: second bitmap header size is invalid.\n");
+        return false;
+    }
+
+    // Check for valid dimensions from header
+    RowOrder rowOrder = kBottomUp_RowOrder;
+    if (height < 0) {
+        height = -height;
+        rowOrder = kTopDown_RowOrder;
+    }
+    // The height field for bmp in ico is double the actual height because they
+    // contain an XOR mask followed by an AND mask
+    if (isIco) {
+        height /= 2;
+    }
+    if (width <= 0 || height <= 0) {
+        // TODO: Decide if we want to disable really large bmps as well.
+        // https://code.google.com/p/skia/issues/detail?id=3617
+        SkCodecPrintf("Error: invalid bitmap dimensions.\n");
+        return false;
+    }
+
+    // Create mask struct
+    SkMasks::InputMasks inputMasks;
+    memset(&inputMasks, 0, sizeof(SkMasks::InputMasks));
+
+    // Determine the input compression format and set bit masks if necessary
+    uint32_t maskBytes = 0;
+    BitmapInputFormat inputFormat = kUnknown_BitmapInputFormat;
+    switch (compression) {
+        case kNone_BitmapCompressionMethod:
+            inputFormat = kStandard_BitmapInputFormat;
+            break;
+        case k8BitRLE_BitmapCompressionMethod:
+            if (bitsPerPixel != 8) {
+                SkCodecPrintf("Warning: correcting invalid bitmap format.\n");
+                bitsPerPixel = 8;
+            }
+            inputFormat = kRLE_BitmapInputFormat;
+            break;
+        case k4BitRLE_BitmapCompressionMethod:
+            if (bitsPerPixel != 4) {
+                SkCodecPrintf("Warning: correcting invalid bitmap format.\n");
+                bitsPerPixel = 4;
+            }
+            inputFormat = kRLE_BitmapInputFormat;
+            break;
+        case kAlphaBitMasks_BitmapCompressionMethod:
+        case kBitMasks_BitmapCompressionMethod:
+            // Load the masks
+            inputFormat = kBitMask_BitmapInputFormat;
+            switch (headerType) {
+                case kInfoV1_BitmapHeaderType: {
+                    // The V1 header stores the bit masks after the header
+                    SkAutoTDeleteArray<uint8_t> mBuffer(
+                            SkNEW_ARRAY(uint8_t, kBmpMaskBytes));
+                    if (stream->read(mBuffer.get(), kBmpMaskBytes) !=
+                            kBmpMaskBytes) {
+                        SkCodecPrintf("Error: unable to read bit inputMasks.\n");
+                        return false;
+                    }
+                    maskBytes = kBmpMaskBytes;
+                    inputMasks.red = get_int(mBuffer.get(), 0);
+                    inputMasks.green = get_int(mBuffer.get(), 4);
+                    inputMasks.blue = get_int(mBuffer.get(), 8);
+                    break;
+                }
+                case kInfoV2_BitmapHeaderType:
+                case kInfoV3_BitmapHeaderType:
+                case kInfoV4_BitmapHeaderType:
+                case kInfoV5_BitmapHeaderType:
+                    // Header types are matched based on size.  If the header
+                    // is V2+, we are guaranteed to be able to read at least
+                    // this size.
+                    SkASSERT(infoBytesRemaining >= 48);
+                    inputMasks.red = get_int(iBuffer.get(), 36);
+                    inputMasks.green = get_int(iBuffer.get(), 40);
+                    inputMasks.blue = get_int(iBuffer.get(), 44);
+                    break;
+                case kOS2VX_BitmapHeaderType:
+                    // TODO: Decide if we intend to support this.
+                    //       It is unsupported in the previous version and
+                    //       in chromium.  I have not come across a test case
+                    //       that uses this format.
+                    SkCodecPrintf("Error: huffman format unsupported.\n");
+                    return false;
+                default:
+                   SkCodecPrintf("Error: invalid bmp bit masks header.\n");
+                   return false;
+            }
+            break;
+        case kJpeg_BitmapCompressionMethod:
+            if (24 == bitsPerPixel) {
+                inputFormat = kRLE_BitmapInputFormat;
+                break;
+            }
+            // Fall through
+        case kPng_BitmapCompressionMethod:
+            // TODO: Decide if we intend to support this.
+            //       It is unsupported in the previous version and
+            //       in chromium.  I think it is used mostly for printers.
+            SkCodecPrintf("Error: compression format not supported.\n");
+            return false;
+        case kCMYK_BitmapCompressionMethod:
+        case kCMYK8BitRLE_BitmapCompressionMethod:
+        case kCMYK4BitRLE_BitmapCompressionMethod:
+            // TODO: Same as above.
+            SkCodecPrintf("Error: CMYK not supported for bitmap decoding.\n");
+            return false;
+        default:
+            SkCodecPrintf("Error: invalid format for bitmap decoding.\n");
+            return false;
+    }
+
+    // Most versions of bmps should be rendered as opaque.  Either they do
+    // not have an alpha channel, or they expect the alpha channel to be
+    // ignored.  V3+ bmp files introduce an alpha mask and allow the creator
+    // of the image to use the alpha channels.  However, many of these images
+    // leave the alpha channel blank and expect to be rendered as opaque.  This
+    // is the case for almost all V3 images, so we render these as opaque.  For
+    // V4+, we will use the alpha channel, and fix the image later if it turns
+    // out to be fully transparent.
+    // As an exception, V3 bmp-in-ico may use an alpha mask.
+    SkAlphaType alphaType = kOpaque_SkAlphaType;
+    if ((kInfoV3_BitmapHeaderType == headerType && isIco) ||
+            kInfoV4_BitmapHeaderType == headerType ||
+            kInfoV5_BitmapHeaderType == headerType) {
+        // Header types are matched based on size.  If the header is
+        // V3+, we are guaranteed to be able to read at least this size.
+        SkASSERT(infoBytesRemaining > 52);
+        inputMasks.alpha = get_int(iBuffer.get(), 48);
+        if (inputMasks.alpha != 0) {
+            alphaType = kUnpremul_SkAlphaType;
+        }
+    }
+    iBuffer.free();
+
+    // Additionally, 32 bit bmp-in-icos use the alpha channel.
+    // And, RLE inputs may skip pixels, leaving them as transparent.  This
+    // is uncommon, but we cannot be certain that an RLE bmp will be opaque.
+    if ((isIco && 32 == bitsPerPixel) || (kRLE_BitmapInputFormat == inputFormat)) {
+        alphaType = kUnpremul_SkAlphaType;
+    }
+
+    // Check for valid bits per pixel.
+    // At the same time, use this information to choose a suggested color type
+    // and to set default masks.
+    SkColorType colorType = kN32_SkColorType;
+    switch (bitsPerPixel) {
+        // In addition to more standard pixel compression formats, bmp supports
+        // the use of bit masks to determine pixel components.  The standard
+        // format for representing 16-bit colors is 555 (XRRRRRGGGGGBBBBB),
+        // which does not map well to any Skia color formats.  For this reason,
+        // we will always enable mask mode with 16 bits per pixel.
+        case 16:
+            if (kBitMask_BitmapInputFormat != inputFormat) {
+                inputMasks.red = 0x7C00;
+                inputMasks.green = 0x03E0;
+                inputMasks.blue = 0x001F;
+                inputFormat = kBitMask_BitmapInputFormat;
+            }
+            break;
+        // We want to decode to kIndex_8 for input formats that are already
+        // designed in index format.
+        case 1:
+        case 2:
+        case 4:
+        case 8:
+            // However, we cannot in RLE format since we may need to leave some
+            // pixels as transparent.  Similarly, we also cannot for ICO images
+            // since we may need to apply a transparent mask.
+            if (kRLE_BitmapInputFormat != inputFormat && !isIco) {
+                colorType = kIndex_8_SkColorType;
+            }
+        case 24:
+        case 32:
+            break;
+        default:
+            SkCodecPrintf("Error: invalid input value for bits per pixel.\n");
+            return false;
+    }
+
+    // Check that input bit masks are valid and create the masks object
+    SkAutoTDelete<SkMasks>
+            masks(SkMasks::CreateMasks(inputMasks, bitsPerPixel));
+    if (NULL == masks) {
+        SkCodecPrintf("Error: invalid input masks.\n");
+        return false;
+    }
+
+    // Check for a valid number of total bytes when in RLE mode
+    if (totalBytes <= offset && kRLE_BitmapInputFormat == inputFormat) {
+        SkCodecPrintf("Error: RLE requires valid input size.\n");
+        return false;
+    }
+    const size_t RLEBytes = totalBytes - offset;
+
+    // Calculate the number of bytes read so far
+    const uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes;
+    if (!isIco && offset < bytesRead) {
+        SkCodecPrintf("Error: pixel data offset less than header size.\n");
+        return false;
+    }
+
+    if (codecOut) {
+        // Return the codec
+        // We will use ImageInfo to store width, height, suggested color type, and
+        // suggested alpha type.
+        const SkImageInfo& imageInfo = SkImageInfo::Make(width, height,
+                colorType, alphaType);
+        *codecOut = SkNEW_ARGS(SkBmpCodec, (imageInfo, stream, bitsPerPixel,
+                                            inputFormat, masks.detach(),
+                                            numColors, bytesPerColor,
+                                            offset - bytesRead, rowOrder,
+                                            RLEBytes, isIco));
+    }
+    return true;
+}
+
+/*
+ *
+ * Creates a bmp decoder
+ * Reads enough of the stream to determine the image format
+ *
+ */
+SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, bool isIco) {
+    SkAutoTDelete<SkStream> streamDeleter(stream);
+    SkCodec* codec = NULL;
+    if (ReadHeader(stream, isIco, &codec)) {
+        // codec has taken ownership of stream, so we do not need to
+        // delete it.
+        SkASSERT(codec);
+        streamDeleter.detach();
+        return codec;
+    }
+    return NULL;
+}
+
+/*
+ *
+ * Creates an instance of the decoder
+ * Called only by NewFromStream
+ *
+ */
+SkBmpCodec::SkBmpCodec(const SkImageInfo& info, SkStream* stream,
+                       uint16_t bitsPerPixel, BitmapInputFormat inputFormat,
+                       SkMasks* masks, uint32_t numColors,
+                       uint32_t bytesPerColor, uint32_t offset,
+                       RowOrder rowOrder, size_t RLEBytes, bool isIco)
+    : INHERITED(info, stream)
+    , fBitsPerPixel(bitsPerPixel)
+    , fInputFormat(inputFormat)
+    , fMasks(masks)
+    , fColorTable(NULL)
+    , fNumColors(numColors)
+    , fBytesPerColor(bytesPerColor)
+    , fOffset(offset)
+    , fRowOrder(rowOrder)
+    , fRLEBytes(RLEBytes)
+    , fIsIco(isIco)
+
+{}
+
+/*
+ *
+ * Initiates the bitmap decode
+ *
+ */
+SkCodec::Result SkBmpCodec::onGetPixels(const SkImageInfo& dstInfo,
+                                        void* dst, size_t dstRowBytes,
+                                        const Options& opts,
+                                        SkPMColor* inputColorPtr,
+                                        int* inputColorCount) {
+    // Check for proper input and output formats
+    SkCodec::RewindState rewindState = this->rewindIfNeeded();
+    if (rewindState == kCouldNotRewind_RewindState) {
+        return kCouldNotRewind;
+    } else if (rewindState == kRewound_RewindState) {
+        if (!ReadHeader(this->stream(), fIsIco, NULL)) {
+            return kCouldNotRewind;
+        }
+    }
+    if (dstInfo.dimensions() != this->getInfo().dimensions()) {
+        SkCodecPrintf("Error: scaling not supported.\n");
+        return kInvalidScale;
+    }
+    if (!conversion_possible(dstInfo, this->getInfo())) {
+        SkCodecPrintf("Error: cannot convert input type to output type.\n");
+        return kInvalidConversion;
+    }
+
+    // Create the color table if necessary and prepare the stream for decode
+    // Note that if it is non-NULL, inputColorCount will be modified
+    if (!createColorTable(dstInfo.alphaType(), inputColorCount)) {
+        SkCodecPrintf("Error: could not create color table.\n");
+        return kInvalidInput;
+    }
+
+    // Copy the color table to the client if necessary
+    copy_color_table(dstInfo, fColorTable, inputColorPtr, inputColorCount);
+
+    // Perform the decode
+    switch (fInputFormat) {
+        case kBitMask_BitmapInputFormat:
+            return decodeMask(dstInfo, dst, dstRowBytes, opts);
+        case kRLE_BitmapInputFormat:
+            return decodeRLE(dstInfo, dst, dstRowBytes, opts);
+        case kStandard_BitmapInputFormat:
+            return decode(dstInfo, dst, dstRowBytes, opts);
+        default:
+            SkASSERT(false);
+            return kInvalidInput;
+    }
+}
+
+/*
+ *
+ * Process the color table for the bmp input
+ *
+ */
+ bool SkBmpCodec::createColorTable(SkAlphaType alphaType, int* numColors) {
+    // Allocate memory for color table
+    uint32_t colorBytes = 0;
+    uint32_t maxColors = 0;
+    SkPMColor colorTable[256];
+    if (fBitsPerPixel <= 8) {
+        // Zero is a default for maxColors
+        // Also set fNumColors to maxColors when it is too large
+        maxColors = 1 << fBitsPerPixel;
+        if (fNumColors == 0 || fNumColors >= maxColors) {
+            fNumColors = maxColors;
+        }
+
+        // Inform the caller of the number of colors
+        if (NULL != numColors) {
+            // We set the number of colors to maxColors in order to ensure
+            // safe memory accesses.  Otherwise, an invalid pixel could
+            // access memory outside of our color table array.
+            *numColors = maxColors;
+        }
+
+        // Read the color table from the stream
+        colorBytes = fNumColors * fBytesPerColor;
+        SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes));
+        if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
+            SkCodecPrintf("Error: unable to read color table.\n");
+            return false;
+        }
+
+        // Choose the proper packing function
+        SkPMColor (*packARGB) (uint32_t, uint32_t, uint32_t, uint32_t);
+        switch (alphaType) {
+            case kOpaque_SkAlphaType:
+            case kUnpremul_SkAlphaType:
+                packARGB = &SkPackARGB32NoCheck;
+                break;
+            case kPremul_SkAlphaType:
+                packARGB = &SkPreMultiplyARGB;
+                break;
+            default:
+                // This should not be reached because conversion possible
+                // should fail if the alpha type is not one of the above
+                // values.
+                SkASSERT(false);
+                packARGB = NULL;
+                break;
+        }
+
+        // Fill in the color table
+        uint32_t i = 0;
+        for (; i < fNumColors; i++) {
+            uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
+            uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
+            uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
+            uint8_t alpha;
+            if (kOpaque_SkAlphaType == alphaType || kRLE_BitmapInputFormat == fInputFormat) {
+                alpha = 0xFF;
+            } else {
+                alpha = (fMasks->getAlphaMask() >> 24) &
+                        get_byte(cBuffer.get(), i*fBytesPerColor + 3);
+            }
+            colorTable[i] = packARGB(alpha, red, green, blue);
+        }
+
+        // To avoid segmentation faults on bad pixel data, fill the end of the
+        // color table with black.  This is the same the behavior as the
+        // chromium decoder.
+        for (; i < maxColors; i++) {
+            colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
+        }
+
+        // Set the color table
+        fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorTable, maxColors)));
+    }
+
+    // Bmp-in-Ico files do not use an offset to indicate where the pixel data
+    // begins.  Pixel data always begins immediately after the color table.
+    if (!fIsIco) {
+        // Check that we have not read past the pixel array offset
+        if(fOffset < colorBytes) {
+            // This may occur on OS 2.1 and other old versions where the color
+            // table defaults to max size, and the bmp tries to use a smaller
+            // color table.  This is invalid, and our decision is to indicate
+            // an error, rather than try to guess the intended size of the
+            // color table.
+            SkCodecPrintf("Error: pixel data offset less than color table size.\n");
+            return false;
+        }
+
+        // After reading the color table, skip to the start of the pixel array
+        if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
+            SkCodecPrintf("Error: unable to skip to image data.\n");
+            return false;
+        }
+    }
+
+    // Return true on success
+    return true;
+}
+
+/*
+ *
+ * Get the destination row to start filling from
+ * Used to fill the remainder of the image on incomplete input
+ *
+ */
+static inline void* get_dst_start_row(void* dst, size_t dstRowBytes, int32_t y,
+            SkBmpCodec::RowOrder rowOrder) {
+    return (SkBmpCodec::kTopDown_RowOrder == rowOrder) ?
+            SkTAddOffset<void*>(dst, y * dstRowBytes) : dst;
+}
+
+/*
+ *
+ * Performs the bitmap decoding for bit masks input format
+ *
+ */
+SkCodec::Result SkBmpCodec::decodeMask(const SkImageInfo& dstInfo,
+                                       void* dst, size_t dstRowBytes,
+                                       const Options& opts) {
+    // Set constant values
+    const int width = dstInfo.width();
+    const int height = dstInfo.height();
+    const size_t rowBytes = SkAlign4(compute_row_bytes(width, fBitsPerPixel));
+
+    // Allocate a buffer large enough to hold the full image
+    SkAutoTDeleteArray<uint8_t>
+        srcBuffer(SkNEW_ARRAY(uint8_t, height*rowBytes));
+    uint8_t* srcRow = srcBuffer.get();
+
+    // Create the swizzler
+    SkAutoTDelete<SkMaskSwizzler> maskSwizzler(
+            SkMaskSwizzler::CreateMaskSwizzler(dstInfo, dst, dstRowBytes,
+            fMasks, fBitsPerPixel));
+
+    // Iterate over rows of the image
+    bool transparent = true;
+    for (int y = 0; y < height; y++) {
+        // Read a row of the input
+        if (stream()->read(srcRow, rowBytes) != rowBytes) {
+            SkCodecPrintf("Warning: incomplete input stream.\n");
+            // Fill the destination image on failure
+            SkPMColor fillColor = dstInfo.alphaType() == kOpaque_SkAlphaType ?
+                    SK_ColorBLACK : SK_ColorTRANSPARENT;
+            if (kNo_ZeroInitialized == opts.fZeroInitialized || 0 != fillColor) {
+                void* dstStart = get_dst_start_row(dst, dstRowBytes, y, fRowOrder);
+                SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height() - y, fillColor,
+                        NULL);
+            }
+            return kIncompleteInput;
+        }
+
+        // Decode the row in destination format
+        int row = kBottomUp_RowOrder == fRowOrder ? height - 1 - y : y;
+        SkSwizzler::ResultAlpha r = maskSwizzler->next(srcRow, row);
+        transparent &= SkSwizzler::IsTransparent(r);
+
+        // Move to the next row
+        srcRow = SkTAddOffset<uint8_t>(srcRow, rowBytes);
+    }
+
+    // Some fully transparent bmp images are intended to be opaque.  Here, we
+    // correct for this possibility.
+    if (transparent) {
+        const SkImageInfo& opaqueInfo =
+                dstInfo.makeAlphaType(kOpaque_SkAlphaType);
+        SkAutoTDelete<SkMaskSwizzler> opaqueSwizzler(
+                SkMaskSwizzler::CreateMaskSwizzler(opaqueInfo, dst, dstRowBytes,
+                                                   fMasks, fBitsPerPixel));
+        srcRow = srcBuffer.get();
+        for (int y = 0; y < height; y++) {
+            // Decode the row in opaque format
+            int row = kBottomUp_RowOrder == fRowOrder ? height - 1 - y : y;
+            opaqueSwizzler->next(srcRow, row);
+
+            // Move to the next row
+            srcRow = SkTAddOffset<uint8_t>(srcRow, rowBytes);
+        }
+    }
+
+    // Finished decoding the entire image
+    return kSuccess;
+}
+
+/*
+ *
+ * Set an RLE pixel using the color table
+ *
+ */
+void SkBmpCodec::setRLEPixel(void* dst, size_t dstRowBytes,
+                             const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
+                             uint8_t index) {
+    // Set the row
+    int height = dstInfo.height();
+    int row;
+    if (kBottomUp_RowOrder == fRowOrder) {
+        row = height - y - 1;
+    } else {
+        row = y;
+    }
+
+    // Set the pixel based on destination color type
+    switch (dstInfo.colorType()) {
+        case kN32_SkColorType: {
+            SkPMColor* dstRow = SkTAddOffset<SkPMColor>((SkPMColor*) dst,
+                    row * (int) dstRowBytes);
+            dstRow[x] = fColorTable->operator[](index);
+            break;
+        }
+        default:
+            // This case should not be reached.  We should catch an invalid
+            // color type when we check that the conversion is possible.
+            SkASSERT(false);
+            break;
+    }
+}
+
+/*
+ *
+ * Set an RLE pixel from R, G, B values
+ *
+ */
+void SkBmpCodec::setRLE24Pixel(void* dst, size_t dstRowBytes,
+                               const SkImageInfo& dstInfo, uint32_t x,
+                               uint32_t y, uint8_t red, uint8_t green,
+                               uint8_t blue) {
+    // Set the row
+    int height = dstInfo.height();
+    int row;
+    if (kBottomUp_RowOrder == fRowOrder) {
+        row = height - y - 1;
+    } else {
+        row = y;
+    }
+
+    // Set the pixel based on destination color type
+    switch (dstInfo.colorType()) {
+        case kN32_SkColorType: {
+            SkPMColor* dstRow = SkTAddOffset<SkPMColor>((SkPMColor*) dst,
+                    row * (int) dstRowBytes);
+            dstRow[x] = SkPackARGB32NoCheck(0xFF, red, green, blue);
+            break;
+        }
+        default:
+            // This case should not be reached.  We should catch an invalid
+            // color type when we check that the conversion is possible.
+            SkASSERT(false);
+            break;
+    }
+}
+
+/*
+ *
+ * Performs the bitmap decoding for RLE input format
+ * RLE decoding is performed all at once, rather than a one row at a time
+ *
+ */
+SkCodec::Result SkBmpCodec::decodeRLE(const SkImageInfo& dstInfo,
+                                      void* dst, size_t dstRowBytes,
+                                      const Options& opts) {
+    // Set RLE flags
+    static const uint8_t RLE_ESCAPE = 0;
+    static const uint8_t RLE_EOL = 0;
+    static const uint8_t RLE_EOF = 1;
+    static const uint8_t RLE_DELTA = 2;
+
+    // Set constant values
+    const int width = dstInfo.width();
+    const int height = dstInfo.height();
+
+    // Input buffer parameters
+    uint32_t currByte = 0;
+    SkAutoTDeleteArray<uint8_t> buffer(SkNEW_ARRAY(uint8_t, fRLEBytes));
+    size_t totalBytes = stream()->read(buffer.get(), fRLEBytes);
+    if (totalBytes < fRLEBytes) {
+        SkCodecPrintf("Warning: incomplete RLE file.\n");
+    } else if (totalBytes <= 0) {
+        SkCodecPrintf("Error: could not read RLE image data.\n");
+        return kInvalidInput;
+    }
+
+    // Destination parameters
+    int x = 0;
+    int y = 0;
+
+    // Set the background as transparent.  Then, if the RLE code skips pixels,
+    // the skipped pixels will be transparent.
+    // Because of the need for transparent pixels, kN32 is the only color
+    // type that makes sense for the destination format.
+    SkASSERT(kN32_SkColorType == dstInfo.colorType());
+    if (kNo_ZeroInitialized == opts.fZeroInitialized) {
+        SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, SK_ColorTRANSPARENT, NULL);
+    }
+
+    while (true) {
+        // Every entry takes at least two bytes
+        if ((int) totalBytes - currByte < 2) {
+            SkCodecPrintf("Warning: incomplete RLE input.\n");
+            return kIncompleteInput;
+        }
+
+        // Read the next two bytes.  These bytes have different meanings
+        // depending on their values.  In the first interpretation, the first
+        // byte is an escape flag and the second byte indicates what special
+        // task to perform.
+        const uint8_t flag = buffer.get()[currByte++];
+        const uint8_t task = buffer.get()[currByte++];
+
+        // If we have reached a row that is beyond the image size, and the RLE
+        // code does not indicate end of file, abort and signal a warning.
+        if (y >= height && (flag != RLE_ESCAPE || (task != RLE_EOF))) {
+            SkCodecPrintf("Warning: invalid RLE input.\n");
+            return kIncompleteInput;
+        }
+
+        // Perform decoding
+        if (RLE_ESCAPE == flag) {
+            switch (task) {
+                case RLE_EOL:
+                    x = 0;
+                    y++;
+                    break;
+                case RLE_EOF:
+                    return kSuccess;
+                case RLE_DELTA: {
+                    // Two bytes are needed to specify delta
+                    if ((int) totalBytes - currByte < 2) {
+                        SkCodecPrintf("Warning: incomplete RLE input\n");
+                        return kIncompleteInput;
+                    }
+                    // Modify x and y
+                    const uint8_t dx = buffer.get()[currByte++];
+                    const uint8_t dy = buffer.get()[currByte++];
+                    x += dx;
+                    y += dy;
+                    if (x > width || y > height) {
+                        SkCodecPrintf("Warning: invalid RLE input.\n");
+                        return kIncompleteInput;
+                    }
+                    break;
+                }
+                default: {
+                    // If task does not match any of the above signals, it
+                    // indicates that we have a sequence of non-RLE pixels.
+                    // Furthermore, the value of task is equal to the number
+                    // of pixels to interpret.
+                    uint8_t numPixels = task;
+                    const size_t rowBytes = compute_row_bytes(numPixels,
+                            fBitsPerPixel);
+                    // Abort if setting numPixels moves us off the edge of the
+                    // image.  Also abort if there are not enough bytes
+                    // remaining in the stream to set numPixels.
+                    if (x + numPixels > width ||
+                            (int) totalBytes - currByte < SkAlign2(rowBytes)) {
+                        SkCodecPrintf("Warning: invalid RLE input.\n");
+                        return kIncompleteInput;
+                    }
+                    // Set numPixels number of pixels
+                    while (numPixels > 0) {
+                        switch(fBitsPerPixel) {
+                            case 4: {
+                                SkASSERT(currByte < totalBytes);
+                                uint8_t val = buffer.get()[currByte++];
+                                setRLEPixel(dst, dstRowBytes, dstInfo, x++,
+                                        y, val >> 4);
+                                numPixels--;
+                                if (numPixels != 0) {
+                                    setRLEPixel(dst, dstRowBytes, dstInfo,
+                                            x++, y, val & 0xF);
+                                    numPixels--;
+                                }
+                                break;
+                            }
+                            case 8:
+                                SkASSERT(currByte < totalBytes);
+                                setRLEPixel(dst, dstRowBytes, dstInfo, x++,
+                                        y, buffer.get()[currByte++]);
+                                numPixels--;
+                                break;
+                            case 24: {
+                                SkASSERT(currByte + 2 < totalBytes);
+                                uint8_t blue = buffer.get()[currByte++];
+                                uint8_t green = buffer.get()[currByte++];
+                                uint8_t red = buffer.get()[currByte++];
+                                setRLE24Pixel(dst, dstRowBytes, dstInfo,
+                                            x++, y, red, green, blue);
+                                numPixels--;
+                            }
+                            default:
+                                SkASSERT(false);
+                                return kInvalidInput;
+                        }
+                    }
+                    // Skip a byte if necessary to maintain alignment
+                    if (!SkIsAlign2(rowBytes)) {
+                        currByte++;
+                    }
+                    break;
+                }
+            }
+        } else {
+            // If the first byte read is not a flag, it indicates the number of
+            // pixels to set in RLE mode.
+            const uint8_t numPixels = flag;
+            const int endX = SkTMin<int>(x + numPixels, width);
+
+            if (24 == fBitsPerPixel) {
+                // In RLE24, the second byte read is part of the pixel color.
+                // There are two more required bytes to finish encoding the
+                // color.
+                if ((int) totalBytes - currByte < 2) {
+                    SkCodecPrintf("Warning: incomplete RLE input\n");
+                    return kIncompleteInput;
+                }
+
+                // Fill the pixels up to endX with the specified color
+                uint8_t blue = task;
+                uint8_t green = buffer.get()[currByte++];
+                uint8_t red = buffer.get()[currByte++];
+                while (x < endX) {
+                    setRLE24Pixel(dst, dstRowBytes, dstInfo, x++, y, red,
+                            green, blue);
+                }
+            } else {
+                // In RLE8 or RLE4, the second byte read gives the index in the
+                // color table to look up the pixel color.
+                // RLE8 has one color index that gets repeated
+                // RLE4 has two color indexes in the upper and lower 4 bits of
+                // the bytes, which are alternated
+                uint8_t indices[2] = { task, task };
+                if (4 == fBitsPerPixel) {
+                    indices[0] >>= 4;
+                    indices[1] &= 0xf;
+                }
+
+                // Set the indicated number of pixels
+                for (int which = 0; x < endX; x++) {
+                    setRLEPixel(dst, dstRowBytes, dstInfo, x, y,
+                            indices[which]);
+                    which = !which;
+                }
+            }
+        }
+    }
+}
+
+/*
+ *
+ * Performs the bitmap decoding for standard input format
+ *
+ */
+SkCodec::Result SkBmpCodec::decode(const SkImageInfo& dstInfo,
+                                   void* dst, size_t dstRowBytes,
+                                   const Options& opts) {
+    // Set constant values
+    const int width = dstInfo.width();
+    const int height = dstInfo.height();
+    const size_t rowBytes = SkAlign4(compute_row_bytes(width, fBitsPerPixel));
+
+    // Get swizzler configuration and choose the fill value for failures.  We will use
+    // zero as the default palette index, black for opaque images, and transparent for
+    // non-opaque images.
+    SkSwizzler::SrcConfig config;
+    uint32_t fillColorOrIndex;
+    bool zeroFill = true;
+    switch (fBitsPerPixel) {
+        case 1:
+            config = SkSwizzler::kIndex1;
+            fillColorOrIndex = 0;
+            break;
+        case 2:
+            config = SkSwizzler::kIndex2;
+            fillColorOrIndex = 0;
+            break;
+        case 4:
+            config = SkSwizzler::kIndex4;
+            fillColorOrIndex = 0;
+            break;
+        case 8:
+            config = SkSwizzler::kIndex;
+            fillColorOrIndex = 0;
+            break;
+        case 24:
+            config = SkSwizzler::kBGR;
+            fillColorOrIndex = SK_ColorBLACK;
+            zeroFill = false;
+            break;
+        case 32:
+            if (kOpaque_SkAlphaType == dstInfo.alphaType()) {
+                config = SkSwizzler::kBGRX;
+                fillColorOrIndex = SK_ColorBLACK;
+                zeroFill = false;
+            } else {
+                config = SkSwizzler::kBGRA;
+                fillColorOrIndex = SK_ColorTRANSPARENT;
+            }
+            break;
+        default:
+            SkASSERT(false);
+            return kInvalidInput;
+    }
+
+    // Get a pointer to the color table if it exists
+    const SkPMColor* colorPtr = NULL != fColorTable.get() ? fColorTable->readColors() : NULL;
+
+    // Create swizzler
+    SkAutoTDelete<SkSwizzler> swizzler(SkSwizzler::CreateSwizzler(config,
+            colorPtr, dstInfo, dst, dstRowBytes,
+            SkImageGenerator::kNo_ZeroInitialized));
+
+    // Allocate space for a row buffer and a source for the swizzler
+    SkAutoTDeleteArray<uint8_t> srcBuffer(SkNEW_ARRAY(uint8_t, rowBytes));
+
+    // Iterate over rows of the image
+    // FIXME: bool transparent = true;
+    for (int y = 0; y < height; y++) {
+        // Read a row of the input
+        if (stream()->read(srcBuffer.get(), rowBytes) != rowBytes) {
+            SkCodecPrintf("Warning: incomplete input stream.\n");
+            // Fill the destination image on failure
+            if (kNo_ZeroInitialized == opts.fZeroInitialized || !zeroFill) {
+                void* dstStart = get_dst_start_row(dst, dstRowBytes, y, fRowOrder);
+                SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height() - y,
+                        fillColorOrIndex, colorPtr);
+            }
+            return kIncompleteInput;
+        }
+
+        // Decode the row in destination format
+        uint32_t row;
+        if (kTopDown_RowOrder == fRowOrder) {
+            row = y;
+        } else {
+            row = height - 1 - y;
+        }
+
+        swizzler->next(srcBuffer.get(), row);
+        // FIXME: SkSwizzler::ResultAlpha r =
+        //        swizzler->next(srcBuffer.get(), row);
+        // FIXME: transparent &= SkSwizzler::IsTransparent(r);
+    }
+
+    // FIXME: This code exists to match the behavior in the chromium decoder
+    // and to follow the bmp specification as it relates to alpha masks.  It is
+    // commented out because we have yet to discover a test image that provides
+    // an alpha mask and uses this decode mode.
+
+    // Now we adjust the output image with some additional behavior that
+    // SkSwizzler does not support.  Firstly, all bmp images that contain
+    // alpha are masked by the alpha mask.  Secondly, many fully transparent
+    // bmp images are intended to be opaque.  Here, we make those corrections
+    // in the kN32 case.
+    /*
+    SkPMColor* dstRow = (SkPMColor*) dst;
+    if (SkSwizzler::kBGRA == config) {
+        for (int y = 0; y < height; y++) {
+            for (int x = 0; x < width; x++) {
+                if (transparent) {
+                    dstRow[x] |= 0xFF000000;
+                } else {
+                    dstRow[x] &= alphaMask;
+                }
+                dstRow = SkTAddOffset<SkPMColor>(dstRow, dstRowBytes);
+            }
+        }
+    }
+    */
+
+    // Finally, apply the AND mask for bmp-in-ico images
+    if (fIsIco) {
+        // The AND mask is always 1 bit per pixel
+        const size_t rowBytes = SkAlign4(compute_row_bytes(width, 1));
+
+        SkPMColor* dstPtr = (SkPMColor*) dst;
+        for (int y = 0; y < height; y++) {
+            // The srcBuffer will at least be large enough
+            if (stream()->read(srcBuffer.get(), rowBytes) != rowBytes) {
+                SkCodecPrintf("Warning: incomplete AND mask for bmp-in-ico.\n");
+                return kIncompleteInput;
+            }
+
+            int row;
+            if (kBottomUp_RowOrder == fRowOrder) {
+                row = height - y - 1;
+            } else {
+                row = y;
+            }
+
+            SkPMColor* dstRow =
+                    SkTAddOffset<SkPMColor>(dstPtr, row * dstRowBytes);
+
+            for (int x = 0; x < width; x++) {
+                int quotient;
+                int modulus;
+                SkTDivMod(x, 8, &quotient, &modulus);
+                uint32_t shift = 7 - modulus;
+                uint32_t alphaBit =
+                        (srcBuffer.get()[quotient] >> shift) & 0x1;
+                dstRow[x] &= alphaBit - 1;
+            }
+        }
+    }
+
+    // Finished decoding the entire image
+    return kSuccess;
+}
diff --git a/src/codec/SkCodec_libbmp.h b/src/codec/SkCodec_libbmp.h
new file mode 100644
index 0000000..9909945
--- /dev/null
+++ b/src/codec/SkCodec_libbmp.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec.h"
+#include "SkColorTable.h"
+#include "SkImageInfo.h"
+#include "SkMaskSwizzler.h"
+#include "SkStream.h"
+#include "SkSwizzler.h"
+#include "SkTypes.h"
+
+// TODO: rename SkCodec_libbmp files to SkBmpCodec
+/*
+ *
+ * This class implements the decoding for bmp images
+ *
+ */
+class SkBmpCodec : public SkCodec {
+public:
+
+    /*
+     *
+     * Describes if rows of the input start at the top or bottom of the image
+     *
+     */
+    enum RowOrder {
+        kTopDown_RowOrder,
+        kBottomUp_RowOrder
+    };
+
+    /*
+     *
+     * Checks the start of the stream to see if the image is a bmp
+     *
+     */
+    static bool IsBmp(SkStream*);
+
+    /*
+     *
+     * Assumes IsBmp was called and returned true
+     * Creates a bmp decoder
+     * Reads enough of the stream to determine the image format
+     *
+     */
+    static SkCodec* NewFromStream(SkStream*);
+
+    /*
+     *
+     * Creates a bmp decoder for a bmp embedded in ico
+     * Reads enough of the stream to determine the image format
+     *
+     */
+    static SkCodec* NewFromIco(SkStream*);
+
+protected:
+
+    /*
+     *
+     * Initiates the bmp decode
+     *
+     */
+    Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
+                       size_t dstRowBytes, const Options&, SkPMColor*,
+                       int*) override;
+
+    SkEncodedFormat onGetEncodedFormat() const override { return kBMP_SkEncodedFormat; }
+
+private:
+
+    /*
+     *
+     * Used to define the input format of the bmp
+     *
+     */
+    enum BitmapInputFormat {
+        kStandard_BitmapInputFormat,
+        kRLE_BitmapInputFormat,
+        kBitMask_BitmapInputFormat,
+        kUnknown_BitmapInputFormat
+    };
+
+    /*
+     *
+     * Creates the color table
+     * Sets colorCount to the new color count if it is non-NULL
+     */
+     bool createColorTable(SkAlphaType alphaType, int* colorCount);
+
+    /*
+     *
+     * Creates a bmp decoder
+     * Reads enough of the stream to determine the image format
+     *
+     */
+    static SkCodec* NewFromStream(SkStream*, bool isIco);
+
+    /*
+     *
+     * Read enough of the stream to initialize the SkBmpCodec. Returns a bool
+     * representing success or failure. If it returned true, and codecOut was
+     * not NULL, it will be set to a new SkBmpCodec.
+     * Does *not* take ownership of the passed in SkStream.
+     *
+     */
+    static bool ReadHeader(SkStream*, bool isIco, SkCodec** codecOut);
+
+    /*
+     *
+     * Performs the bitmap decoding for bit masks input format
+     *
+     */
+    Result decodeMask(const SkImageInfo& dstInfo, void* dst,
+                      size_t dstRowBytes, const Options& opts);
+
+    /*
+     *
+     * Set an RLE pixel using the color table
+     *
+     */
+    void setRLEPixel(void* dst, size_t dstRowBytes,
+                     const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
+                     uint8_t index);
+    /*
+     *
+     * Set an RLE24 pixel from R, G, B values
+     *
+     */
+    void setRLE24Pixel(void* dst, size_t dstRowBytes,
+                       const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
+                       uint8_t red, uint8_t green, uint8_t blue);
+
+    /*
+     *
+     * Performs the bitmap decoding for RLE input format
+     *
+     */
+    Result decodeRLE(const SkImageInfo& dstInfo, void* dst,
+                     size_t dstRowBytes, const Options& opts);
+
+    /*
+     *
+     * Performs the bitmap decoding for standard input format
+     *
+     */
+    Result decode(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts);
+
+    /*
+     *
+     * Creates an instance of the decoder
+     * Called only by NewFromStream
+     *
+     * @param srcInfo contains the source width and height
+     * @param stream the stream of image data
+     * @param bitsPerPixel the number of bits used to store each pixel
+     * @param format the format of the bmp file
+     * @param masks optional color masks for certain bmp formats, passes
+                    ownership to SkBmpCodec
+     * @param numColors the number of colors in the color table
+     * @param bytesPerColor the number of bytes in the stream used to represent
+                            each color in the color table
+     * @param offset the offset of the image pixel data from the end of the
+     *               headers
+     * @param rowOrder indicates whether rows are ordered top-down or bottom-up
+     * @param RLEBytes used only for RLE decodes, as we must decode all
+     *                  of the data at once rather than row by row
+     *                  it indicates the amount of data left in the stream
+     *                  after decoding the headers
+     *
+     */
+    SkBmpCodec(const SkImageInfo& srcInfo, SkStream* stream,
+               uint16_t bitsPerPixel, BitmapInputFormat format,
+               SkMasks* masks, uint32_t numColors, uint32_t bytesPerColor,
+               uint32_t offset, RowOrder rowOrder, size_t RLEBytes,
+               bool isIco);
+
+    // Fields
+    const uint16_t                      fBitsPerPixel;
+    const BitmapInputFormat             fInputFormat;
+    SkAutoTDelete<SkMasks>              fMasks;          // owned
+    SkAutoTUnref<SkColorTable>          fColorTable;     // owned
+    uint32_t                            fNumColors;
+    const uint32_t                      fBytesPerColor;
+    const uint32_t                      fOffset;
+    const RowOrder                      fRowOrder;
+    const size_t                        fRLEBytes;
+    const bool                          fIsIco;
+
+    typedef SkCodec INHERITED;
+};
diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp
new file mode 100644
index 0000000..fb578f2
--- /dev/null
+++ b/src/codec/SkCodec_libgif.cpp
@@ -0,0 +1,570 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec_libgif.h"
+#include "SkCodecPriv.h"
+#include "SkColorPriv.h"
+#include "SkColorTable.h"
+#include "SkGifInterlaceIter.h"
+#include "SkStream.h"
+#include "SkSwizzler.h"
+#include "SkUtils.h"
+
+/*
+ * Checks the start of the stream to see if the image is a gif
+ */
+bool SkGifCodec::IsGif(SkStream* stream) {
+    char buf[GIF_STAMP_LEN];
+    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
+        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
+                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Warning reporting function
+ */
+static void gif_warning(const char* msg) {
+    SkCodecPrintf("Gif Warning: %s\n", msg);
+}
+
+/*
+ * Error function
+ */
+static SkCodec::Result gif_error(const char* msg,
+        SkCodec::Result result = SkCodec::kInvalidInput) {
+    SkCodecPrintf("Gif Error: %s\n", msg);
+    return result;
+}
+
+
+/*
+ * Read function that will be passed to gif_lib
+ */
+static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out,
+        int32_t size) {
+    SkStream* stream = (SkStream*) fileType->UserData;
+    return (int32_t) stream->read(out, size);
+}
+
+/*
+ * Open the gif file
+ */
+static GifFileType* open_gif(SkStream* stream) {
+#if GIFLIB_MAJOR < 5
+    return DGifOpen(stream, read_bytes_callback);
+#else
+    return DGifOpen(stream, read_bytes_callback, NULL);
+#endif
+}
+
+ /*
+ * This function cleans up the gif object after the decode completes
+ * It is used in a SkAutoTCallIProc template
+ */
+void SkGifCodec::CloseGif(GifFileType* gif) {
+#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
+    DGifCloseFile(gif);
+#else
+    DGifCloseFile(gif, NULL);
+#endif
+}
+
+/*
+ * This function free extension data that has been saved to assist the image
+ * decoder
+ */
+void SkGifCodec::FreeExtension(SavedImage* image) {
+    if (NULL != image->ExtensionBlocks) {
+#if GIFLIB_MAJOR < 5
+        FreeExtension(image);
+#else
+        GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks);
+#endif
+    }
+}
+
+/*
+ * Check if a there is an index of the color table for a transparent pixel
+ */
+static uint32_t find_trans_index(const SavedImage& image) {
+    // If there is a transparent index specified, it will be contained in an
+    // extension block.  We will loop through extension blocks in reverse order
+    // to check the most recent extension blocks first.
+    for (int32_t i = image.ExtensionBlockCount - 1; i >= 0; i--) {
+        // Get an extension block
+        const ExtensionBlock& extBlock = image.ExtensionBlocks[i];
+
+        // Specifically, we need to check for a graphics control extension,
+        // which may contain transparency information.  Also, note that a valid
+        // graphics control extension is always four bytes.  The fourth byte
+        // is the transparent index (if it exists), so we need at least four
+        // bytes.
+        if (GRAPHICS_EXT_FUNC_CODE == extBlock.Function &&
+                extBlock.ByteCount >= 4) {
+
+            // Check the transparent color flag which indicates whether a
+            // transparent index exists.  It is the least significant bit of
+            // the first byte of the extension block.
+            if (1 == (extBlock.Bytes[0] & 1)) {
+
+                // Use uint32_t to prevent sign extending
+                return extBlock.Bytes[3];
+            }
+
+            // There should only be one graphics control extension for the image frame
+            break;
+        }
+    }
+
+    // Use maximum unsigned int (surely an invalid index) to indicate that a valid
+    // index was not found.
+    return SK_MaxU32;
+}
+
+/*
+ * Read enough of the stream to initialize the SkGifCodec.
+ * Returns a bool representing success or failure.
+ *
+ * @param codecOut
+ * If it returned true, and codecOut was not NULL,
+ * codecOut will be set to a new SkGifCodec.
+ *
+ * @param gifOut
+ * If it returned true, and codecOut was NULL,
+ * gifOut must be non-NULL and gifOut will be set to a new
+ * GifFileType pointer.
+ *
+ * @param stream
+ * Deleted on failure.
+ * codecOut will take ownership of it in the case where we created a codec.
+ * Ownership is unchanged when we returned a gifOut.
+ *
+ */
+bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut) {
+    SkAutoTDelete<SkStream> streamDeleter(stream);
+
+    // Read gif header, logical screen descriptor, and global color table
+    SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream));
+
+    if (NULL == gif) {
+        gif_error("DGifOpen failed.\n");
+        return false;
+    }
+
+    if (NULL != codecOut) {
+        // Get fields from header
+        const int32_t width = gif->SWidth;
+        const int32_t height = gif->SHeight;
+        if (width <= 0 || height <= 0) {
+            gif_error("Invalid dimensions.\n");
+            return false;
+        }
+
+        // Return the codec
+        // kIndex is the most natural color type for gifs, so we set this as
+        // the default.
+        // Many gifs specify a color table index for transparent pixels.  Every
+        // other pixel is guaranteed to be opaque.  Despite this, because of the
+        // possiblity of transparent pixels, we cannot assume that the image is
+        // opaque.  We have the option to set the alpha type as kPremul or
+        // kUnpremul.  Both are valid since the alpha component will always be
+        // 0xFF or the entire 32-bit pixel will be set to zero.  We prefer
+        // kPremul because we support kPremul, and it is more efficient to
+        // use kPremul directly even when kUnpremul is supported.
+        const SkImageInfo& imageInfo = SkImageInfo::Make(width, height,
+                kIndex_8_SkColorType, kPremul_SkAlphaType);
+        *codecOut = SkNEW_ARGS(SkGifCodec, (imageInfo, streamDeleter.detach(), gif.detach()));
+    } else {
+        SkASSERT(NULL != gifOut);
+        streamDeleter.detach();
+        *gifOut = gif.detach();
+    }
+    return true;
+}
+
+/*
+ * Assumes IsGif was called and returned true
+ * Creates a gif decoder
+ * Reads enough of the stream to determine the image format
+ */
+SkCodec* SkGifCodec::NewFromStream(SkStream* stream) {
+    SkCodec* codec = NULL;
+    if (ReadHeader(stream, &codec, NULL)) {
+        return codec;
+    }
+    return NULL;
+}
+
+SkGifCodec::SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream,
+                       GifFileType* gif)
+    : INHERITED(srcInfo, stream)
+    , fGif(gif)
+{}
+
+/*
+ * Checks if the conversion between the input image and the requested output
+ * image has been implemented
+ */
+static bool conversion_possible(const SkImageInfo& dst,
+                                const SkImageInfo& src) {
+    // Ensure that the profile type is unchanged
+    if (dst.profileType() != src.profileType()) {
+        return false;
+    }
+
+    // Check for supported color and alpha types
+    switch (dst.colorType()) {
+        case kN32_SkColorType:
+            return kPremul_SkAlphaType == dst.alphaType() ||
+                    kUnpremul_SkAlphaType == dst.alphaType();
+        case kIndex_8_SkColorType:
+            return kPremul_SkAlphaType == dst.alphaType() ||
+                    kUnpremul_SkAlphaType == dst.alphaType();
+        default:
+            return false;
+    }
+}
+
+/*
+ * Initiates the gif decode
+ */
+SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
+                                        void* dst, size_t dstRowBytes,
+                                        const Options& opts,
+                                        SkPMColor* inputColorPtr,
+                                        int* inputColorCount) {
+    // Rewind if necessary
+    SkCodec::RewindState rewindState = this->rewindIfNeeded();
+    if (rewindState == kCouldNotRewind_RewindState) {
+        return kCouldNotRewind;
+    } else if (rewindState == kRewound_RewindState) {
+        GifFileType* gifOut = NULL;
+        if (!ReadHeader(this->stream(), NULL, &gifOut)) {
+            return kCouldNotRewind;
+        } else {
+            SkASSERT(NULL != gifOut);
+            fGif.reset(gifOut);
+        }
+    }
+
+    // Check for valid input parameters
+    if (dstInfo.dimensions() != this->getInfo().dimensions()) {
+        return gif_error("Scaling not supported.\n", kInvalidScale);
+    }
+    if (!conversion_possible(dstInfo, this->getInfo())) {
+        return gif_error("Cannot convert input type to output type.\n",
+                kInvalidConversion);
+    }
+
+    // Use this as a container to hold information about any gif extension
+    // blocks.  This generally stores transparency and animation instructions.
+    SavedImage saveExt;
+    SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt);
+    saveExt.ExtensionBlocks = NULL;
+    saveExt.ExtensionBlockCount = 0;
+    GifByteType* extData;
+#if GIFLIB_MAJOR >= 5
+    int32_t extFunction;
+#endif
+
+    // We will loop over components of gif images until we find an image.  Once
+    // we find an image, we will decode and return it.  While many gif files
+    // contain more than one image, we will simply decode the first image.
+    const int32_t width = dstInfo.width();
+    const int32_t height = dstInfo.height();
+    GifRecordType recordType;
+    do {
+        // Get the current record type
+        if (GIF_ERROR == DGifGetRecordType(fGif, &recordType)) {
+            return gif_error("DGifGetRecordType failed.\n", kInvalidInput);
+        }
+
+        switch (recordType) {
+            case IMAGE_DESC_RECORD_TYPE: {
+                // Read the image descriptor
+                if (GIF_ERROR == DGifGetImageDesc(fGif)) {
+                    return gif_error("DGifGetImageDesc failed.\n",
+                            kInvalidInput);
+                }
+
+                // If reading the image descriptor is successful, the image
+                // count will be incremented
+                SkASSERT(fGif->ImageCount >= 1);
+                SavedImage* image = &fGif->SavedImages[fGif->ImageCount - 1];
+
+                // Process the descriptor
+                const GifImageDesc& desc = image->ImageDesc;
+                int32_t imageLeft = desc.Left;
+                int32_t imageTop = desc.Top;
+                int32_t innerWidth = desc.Width;
+                int32_t innerHeight = desc.Height;
+                // Fail on non-positive dimensions
+                if (innerWidth <= 0 || innerHeight <= 0) {
+                    return gif_error("Invalid dimensions for inner image.\n",
+                            kInvalidInput);
+                }
+                // Treat the following cases as warnings and try to fix
+                if (innerWidth > width) {
+                    gif_warning("Inner image too wide, shrinking.\n");
+                    innerWidth = width;
+                    imageLeft = 0;
+                } else if (imageLeft + innerWidth > width) {
+                    gif_warning("Shifting inner image to left to fit.\n");
+                    imageLeft = width - innerWidth;
+                } else if (imageLeft < 0) {
+                    gif_warning("Shifting image to right to fit\n");
+                    imageLeft = 0;
+                }
+                if (innerHeight > height) {
+                    gif_warning("Inner image too tall, shrinking.\n");
+                    innerHeight = height;
+                    imageTop = 0;
+                } else if (imageTop + innerHeight > height) {
+                    gif_warning("Shifting inner image up to fit.\n");
+                    imageTop = height - innerHeight;
+                } else if (imageTop < 0) {
+                    gif_warning("Shifting image down to fit\n");
+                    imageTop = 0;
+                }
+
+                // Create a color table to store colors the giflib colorMap
+                SkPMColor alternateColorPtr[256];
+                SkPMColor* colorTable;
+                SkColorType dstColorType = dstInfo.colorType();
+                if (kIndex_8_SkColorType == dstColorType) {
+                    SkASSERT(NULL != inputColorPtr);
+                    SkASSERT(NULL != inputColorCount);
+                    colorTable = inputColorPtr;
+                } else {
+                    colorTable = alternateColorPtr;
+                }
+
+                // Set up the color table
+                uint32_t colorCount = 0;
+                // Allocate maximum storage to deal with invalid indices safely
+                const uint32_t maxColors = 256;
+                ColorMapObject* colorMap = fGif->Image.ColorMap;
+                // If there is no local color table, use the global color table
+                if (NULL == colorMap) {
+                    colorMap = fGif->SColorMap;
+                }
+                if (NULL != colorMap) {
+                    colorCount = colorMap->ColorCount;
+                    SkASSERT(colorCount ==
+                            (unsigned) (1 << (colorMap->BitsPerPixel)));
+                    SkASSERT(colorCount <= 256);
+                    for (uint32_t i = 0; i < colorCount; i++) {
+                        colorTable[i] = SkPackARGB32(0xFF,
+                                                     colorMap->Colors[i].Red,
+                                                     colorMap->Colors[i].Green,
+                                                     colorMap->Colors[i].Blue);
+                    }
+                }
+
+                // This is used to fill unspecified pixels in the image data.
+                uint32_t fillIndex = fGif->SBackGroundColor;
+                ZeroInitialized zeroInit = opts.fZeroInitialized;
+
+                // Gifs have the option to specify the color at a single
+                // index of the color table as transparent.
+                {
+                    // Get the transparent index.  If the return value of this
+                    // function is greater than the colorCount, we know that
+                    // there is no valid transparent color in the color table.
+                    // This occurs if there is no graphics control extension or
+                    // if the index specified by the graphics control extension
+                    // is out of range.
+                    uint32_t transIndex = find_trans_index(saveExt);
+
+                    if (transIndex < colorCount) {
+                        colorTable[transIndex] = SK_ColorTRANSPARENT;
+                        // If there is a transparent index, we also use this as
+                        // the fill index.
+                        fillIndex = transIndex;
+                    } else if (fillIndex >= colorCount) {
+                        // If the fill index is invalid, we default to 0.  This
+                        // behavior is unspecified but matches SkImageDecoder.
+                        fillIndex = 0;
+                    }
+                }
+
+                // Check if we can skip filling the background of the image.  We
+                // may be able to if the memory is zero initialized.
+                bool skipBackground =
+                        ((kN32_SkColorType == dstColorType && colorTable[fillIndex] == 0) ||
+                        (kIndex_8_SkColorType == dstColorType && fillIndex == 0)) &&
+                        kYes_ZeroInitialized == zeroInit;
+
+
+                // Fill in the color table for indices greater than color count.
+                // This allows for predictable, safe behavior.
+                for (uint32_t i = colorCount; i < maxColors; i++) {
+                    colorTable[i] = colorTable[fillIndex];
+                } 
+
+                // Check if image is only a subset of the image frame
+                SkAutoTDelete<SkSwizzler> swizzler(NULL);
+                if (innerWidth < width || innerHeight < height) {
+
+                    // Modify the destination info
+                    const SkImageInfo subsetDstInfo =
+                            dstInfo.makeWH(innerWidth, innerHeight);
+
+                    // Fill the destination with the fill color
+                    // FIXME: This may not be the behavior that we want for
+                    //        animated gifs where we draw on top of the
+                    //        previous frame.
+                    if (!skipBackground) {
+                        SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, fillIndex, colorTable);
+                    }
+
+                    // Modify the dst pointer
+                    const int32_t dstBytesPerPixel =
+                            SkColorTypeBytesPerPixel(dstColorType);
+                    void* subsetDst = SkTAddOffset<void*>(dst,
+                            dstRowBytes * imageTop +
+                            dstBytesPerPixel * imageLeft);
+
+                    // Create the subset swizzler
+                    swizzler.reset(SkSwizzler::CreateSwizzler(
+                            SkSwizzler::kIndex, colorTable, subsetDstInfo,
+                            subsetDst, dstRowBytes, zeroInit));
+                } else {
+                    // Create the fully dimensional swizzler
+                    swizzler.reset(SkSwizzler::CreateSwizzler(
+                            SkSwizzler::kIndex, colorTable, dstInfo, dst,
+                            dstRowBytes, zeroInit));
+                }
+
+                // Stores output from dgiflib and input to the swizzler
+                SkAutoTDeleteArray<uint8_t>
+                        buffer(SkNEW_ARRAY(uint8_t, innerWidth));
+
+                // Check the interlace flag and iterate over rows of the input
+                if (fGif->Image.Interlace) {
+                    // In interlace mode, the rows of input are rearranged in
+                    // the output image.  We use an iterator to take care of
+                    // the rearranging.
+                    SkGifInterlaceIter iter(innerHeight);
+                    for (int32_t y = 0; y < innerHeight; y++) {
+                        if (GIF_ERROR == DGifGetLine(fGif, buffer.get(),
+                                innerWidth)) {
+                            // Recover from error by filling remainder of image
+                            if (!skipBackground) {
+                                memset(buffer.get(), fillIndex, innerWidth);
+                                for (; y < innerHeight; y++) {
+                                    swizzler->next(buffer.get(), iter.nextY());
+                                }
+                            }
+                            return gif_error(SkStringPrintf(
+                                    "Could not decode line %d of %d.\n",
+                                    y, height - 1).c_str(), kIncompleteInput);
+                        }
+                        swizzler->next(buffer.get(), iter.nextY());
+                    }
+                } else {
+                    // Standard mode
+                    for (int32_t y = 0; y < innerHeight; y++) {
+                        if (GIF_ERROR == DGifGetLine(fGif, buffer.get(),
+                                innerWidth)) {
+                            if (!skipBackground) {
+                                SkSwizzler::Fill(swizzler->getDstRow(), dstInfo, dstRowBytes,
+                                        innerHeight - y, fillIndex, colorTable);
+                            }
+                            return gif_error(SkStringPrintf(
+                                    "Could not decode line %d of %d.\n",
+                                    y, height - 1).c_str(), kIncompleteInput);
+                        }
+                        swizzler->next(buffer.get());
+                    }
+                }
+
+                // FIXME: Gif files may have multiple images stored in a single
+                //        file.  This is most commonly used to enable
+                //        animations.  Since we are leaving animated gifs as a
+                //        TODO, we will return kSuccess after decoding the
+                //        first image in the file.  This is the same behavior
+                //        as SkImageDecoder_libgif.
+                //
+                //        Most times this works pretty well, but sometimes it
+                //        doesn't.  For example, I have an animated test image
+                //        where the first image in the file is 1x1, but the
+                //        subsequent images are meaningful.  This currently
+                //        displays the 1x1 image, which is not ideal.  Right
+                //        now I am leaving this as an issue that will be
+                //        addressed when we implement animated gifs.
+                //
+                //        It is also possible (not explicitly disallowed in the
+                //        specification) that gif files provide multiple
+                //        images in a single file that are all meant to be
+                //        displayed in the same frame together.  I will
+                //        currently leave this unimplemented until I find a
+                //        test case that expects this behavior.
+                return kSuccess;
+            }
+
+            // Extensions are used to specify special properties of the image
+            // such as transparency or animation.
+            case EXTENSION_RECORD_TYPE:
+                // Read extension data
+#if GIFLIB_MAJOR < 5
+                if (GIF_ERROR ==
+                        DGifGetExtension(fGif, &saveExt.Function, &extData)) {
+#else
+                if (GIF_ERROR ==
+                        DGifGetExtension(fGif, &extFunction, &extData)) {
+#endif
+                    return gif_error("Could not get extension.\n",
+                            kIncompleteInput);
+                }
+
+                // Create an extension block with our data
+                while (NULL != extData) {
+                    // Add a single block
+#if GIFLIB_MAJOR < 5
+                    if (GIF_ERROR == AddExtensionBlock(&saveExt, extData[0],
+                            &extData[1])) {
+#else
+                    if (GIF_ERROR ==
+                            GifAddExtensionBlock(&saveExt.ExtensionBlockCount,
+                            &saveExt.ExtensionBlocks, extFunction, extData[0],
+                            &extData[1])) {
+#endif
+                        return gif_error("Could not add extension block.\n",
+                                kIncompleteInput);
+                    }
+                    // Move to the next block
+                    if (GIF_ERROR == DGifGetExtensionNext(fGif, &extData)) {
+                        return gif_error("Could not get next extension.\n",
+                                kIncompleteInput);
+                    }
+#if GIFLIB_MAJOR < 5
+                    saveExt.Function = 0;
+#endif
+                }
+                break;
+
+            // Signals the end of the gif file
+            case TERMINATE_RECORD_TYPE:
+                break;
+
+            default:
+                // giflib returns an error code if the record type is not known.
+                // We should catch this error immediately.
+                SkASSERT(false);
+                break;
+        }
+    } while (TERMINATE_RECORD_TYPE != recordType);
+
+    return gif_error("Could not find any images to decode in gif file.\n",
+            kInvalidInput);
+}
diff --git a/src/codec/SkCodec_libgif.h b/src/codec/SkCodec_libgif.h
new file mode 100644
index 0000000..d805acc
--- /dev/null
+++ b/src/codec/SkCodec_libgif.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec.h"
+#include "SkImageInfo.h"
+
+#include "gif_lib.h"
+
+/*
+ *
+ * This class implements the decoding for gif images
+ *
+ */
+class SkGifCodec : public SkCodec {
+public:
+
+    /*
+     * Checks the start of the stream to see if the image is a gif
+     */
+    static bool IsGif(SkStream*);
+
+    /*
+     * Assumes IsGif was called and returned true
+     * Creates a gif decoder
+     * Reads enough of the stream to determine the image format
+     */
+    static SkCodec* NewFromStream(SkStream*);
+    
+
+protected:
+
+    /*
+     * Read enough of the stream to initialize the SkGifCodec.
+     * Returns a bool representing success or failure.
+     *
+     * @param codecOut
+     * If it returned true, and codecOut was not NULL,
+     * codecOut will be set to a new SkGifCodec.
+     *
+     * @param gifOut
+     * If it returned true, and codecOut was NULL,
+     * gifOut must be non-NULL and gifOut will be set to a new
+     * GifFileType pointer.
+     *
+     * @param stream
+     * Deleted on failure.
+     * codecOut will take ownership of it in the case where we created a codec.
+     * Ownership is unchanged when we returned a gifOut.
+     *
+     */
+    static bool ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut);
+
+    /*
+     * Initiates the gif decode
+     */
+    Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&,
+            SkPMColor*, int32_t*) override;
+
+    SkEncodedFormat onGetEncodedFormat() const override {
+        return kGIF_SkEncodedFormat;
+    }
+
+private:
+
+    /*
+     * This function cleans up the gif object after the decode completes
+     * It is used in a SkAutoTCallIProc template
+     */
+    static void CloseGif(GifFileType* gif);
+
+    /*
+     * Frees any extension data used in the decode
+     * Used in a SkAutoTCallVProc
+     */
+    static void FreeExtension(SavedImage* image);
+
+    /*
+     * Creates an instance of the decoder
+     * Called only by NewFromStream
+     *
+     * @param srcInfo contains the source width and height
+     * @param stream the stream of image data
+     * @param gif pointer to library type that manages gif decode
+     *            takes ownership
+     */
+    SkGifCodec(const SkImageInfo& srcInfo, SkStream* stream, GifFileType* gif);
+
+    SkAutoTCallVProc<GifFileType, CloseGif> fGif; // owned
+
+    typedef SkCodec INHERITED;
+};
diff --git a/src/codec/SkCodec_libico.cpp b/src/codec/SkCodec_libico.cpp
new file mode 100644
index 0000000..97404af
--- /dev/null
+++ b/src/codec/SkCodec_libico.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec_libbmp.h"
+#include "SkCodec_libico.h"
+#include "SkCodec_libpng.h"
+#include "SkCodecPriv.h"
+#include "SkColorPriv.h"
+#include "SkData.h"
+#include "SkStream.h"
+#include "SkTDArray.h"
+#include "SkTSort.h"
+
+/*
+ * Checks the start of the stream to see if the image is an Ico or Cur
+ */
+bool SkIcoCodec::IsIco(SkStream* stream) {
+    const char icoSig[] = { '\x00', '\x00', '\x01', '\x00' };
+    const char curSig[] = { '\x00', '\x00', '\x02', '\x00' };
+    char buffer[sizeof(icoSig)];
+    return stream->read(buffer, sizeof(icoSig)) == sizeof(icoSig) &&
+            (!memcmp(buffer, icoSig, sizeof(icoSig)) ||
+            !memcmp(buffer, curSig, sizeof(curSig)));
+}
+
+/*
+ * Assumes IsIco was called and returned true
+ * Creates an Ico decoder
+ * Reads enough of the stream to determine the image format
+ */
+SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) {
+    // Ensure that we do not leak the input stream
+    SkAutoTDelete<SkStream> inputStream(stream);
+
+    // Header size constants
+    static const uint32_t kIcoDirectoryBytes = 6;
+    static const uint32_t kIcoDirEntryBytes = 16;
+
+    // Read the directory header
+    SkAutoTDeleteArray<uint8_t> dirBuffer(
+            SkNEW_ARRAY(uint8_t, kIcoDirectoryBytes));
+    if (inputStream.get()->read(dirBuffer.get(), kIcoDirectoryBytes) !=
+            kIcoDirectoryBytes) {
+        SkCodecPrintf("Error: unable to read ico directory header.\n");
+        return NULL;
+    }
+
+    // Process the directory header
+    const uint16_t numImages = get_short(dirBuffer.get(), 4);
+    if (0 == numImages) {
+        SkCodecPrintf("Error: No images embedded in ico.\n");
+        return NULL;
+    }
+
+    // Ensure that we can read all of indicated directory entries
+    SkAutoTDeleteArray<uint8_t> entryBuffer(
+            SkNEW_ARRAY(uint8_t, numImages*kIcoDirEntryBytes));
+    if (inputStream.get()->read(entryBuffer.get(), numImages*kIcoDirEntryBytes) !=
+            numImages*kIcoDirEntryBytes) {
+        SkCodecPrintf("Error: unable to read ico directory entries.\n");
+        return NULL;
+    }
+
+    // This structure is used to represent the vital information about entries
+    // in the directory header.  We will obtain this information for each
+    // directory entry.
+    struct Entry {
+        uint32_t offset;
+        uint32_t size;
+    };
+    SkAutoTDeleteArray<Entry> directoryEntries(SkNEW_ARRAY(Entry, numImages));
+
+    // Iterate over directory entries
+    for (uint32_t i = 0; i < numImages; i++) {
+        // The directory entry contains information such as width, height,
+        // bits per pixel, and number of colors in the color palette.  We will
+        // ignore these fields since they are repeated in the header of the
+        // embedded image.  In the event of an inconsistency, we would always
+        // defer to the value in the embedded header anyway.
+
+        // Specifies the size of the embedded image, including the header
+        uint32_t size = get_int(entryBuffer.get(), 8 + i*kIcoDirEntryBytes);
+
+        // Specifies the offset of the embedded image from the start of file.
+        // It does not indicate the start of the pixel data, but rather the
+        // start of the embedded image header.
+        uint32_t offset = get_int(entryBuffer.get(), 12 + i*kIcoDirEntryBytes);
+
+        // Save the vital fields
+        directoryEntries.get()[i].offset = offset;
+        directoryEntries.get()[i].size = size;
+    }
+
+    // It is "customary" that the embedded images will be stored in order of
+    // increasing offset.  However, the specification does not indicate that
+    // they must be stored in this order, so we will not trust that this is the
+    // case.  Here we sort the embedded images by increasing offset.
+    struct EntryLessThan {
+        bool operator() (Entry a, Entry b) const {
+            return a.offset < b.offset;
+        }
+    };
+    EntryLessThan lessThan;
+    SkTQSort(directoryEntries.get(), directoryEntries.get() + numImages - 1,
+            lessThan);
+
+    // Now will construct a candidate codec for each of the embedded images
+    uint32_t bytesRead = kIcoDirectoryBytes + numImages * kIcoDirEntryBytes;
+    SkAutoTDelete<SkTArray<SkAutoTDelete<SkCodec>, true>> codecs(
+            SkNEW_ARGS((SkTArray<SkAutoTDelete<SkCodec>, true>), (numImages)));
+    for (uint32_t i = 0; i < numImages; i++) {
+        uint32_t offset = directoryEntries.get()[i].offset;
+        uint32_t size = directoryEntries.get()[i].size;
+        
+        // Ensure that the offset is valid
+        if (offset < bytesRead) {
+            SkCodecPrintf("Warning: invalid ico offset.\n");
+            continue;
+        }
+
+        // If we cannot skip, assume we have reached the end of the stream and
+        // stop trying to make codecs
+        if (inputStream.get()->skip(offset - bytesRead) != offset - bytesRead) {
+            SkCodecPrintf("Warning: could not skip to ico offset.\n");
+            break;
+        }
+        bytesRead = offset;
+
+        // Create a new stream for the embedded codec
+        SkAutoTUnref<SkData> data(
+                SkData::NewFromStream(inputStream.get(), size));
+        if (NULL == data.get()) {
+            SkCodecPrintf("Warning: could not create embedded stream.\n");
+            break;
+        }
+        SkAutoTDelete<SkMemoryStream>
+                embeddedStream(SkNEW_ARGS(SkMemoryStream, (data.get())));
+        bytesRead += size;
+
+        // Check if the embedded codec is bmp or png and create the codec
+        const bool isPng = SkPngCodec::IsPng(embeddedStream);
+        SkAssertResult(embeddedStream->rewind());
+        SkCodec* codec = NULL;
+        if (isPng) {
+            codec = SkPngCodec::NewFromStream(embeddedStream.detach());
+        } else {
+            codec = SkBmpCodec::NewFromIco(embeddedStream.detach());
+        }
+
+        // Save a valid codec
+        if (NULL != codec) {
+            codecs->push_back().reset(codec);
+        }
+    }
+
+    // Recognize if there are no valid codecs
+    if (0 == codecs->count()) {
+        SkCodecPrintf("Error: could not find any valid embedded ico codecs.\n");
+        return NULL;
+    }
+
+    // Use the largest codec as a "suggestion" for image info
+    uint32_t maxSize = 0;
+    uint32_t maxIndex = 0;
+    for (int32_t i = 0; i < codecs->count(); i++) {
+        SkImageInfo info = codecs->operator[](i)->getInfo();
+        uint32_t size = info.width() * info.height();
+        if (size > maxSize) {
+            maxSize = size;
+            maxIndex = i;
+        }
+    }
+    SkImageInfo info = codecs->operator[](maxIndex)->getInfo();
+
+    // Note that stream is owned by the embedded codec, the ico does not need
+    // direct access to the stream.
+    return SkNEW_ARGS(SkIcoCodec, (info, codecs.detach()));
+}
+
+/*
+ * Creates an instance of the decoder
+ * Called only by NewFromStream
+ */
+SkIcoCodec::SkIcoCodec(const SkImageInfo& info,
+                       SkTArray<SkAutoTDelete<SkCodec>, true>* codecs)
+    : INHERITED(info, NULL)
+    , fEmbeddedCodecs(codecs)
+{}
+
+/*
+ * Chooses the best dimensions given the desired scale
+ */
+SkISize SkIcoCodec::onGetScaledDimensions(float desiredScale) const { 
+    // We set the dimensions to the largest candidate image by default.
+    // Regardless of the scale request, this is the largest image that we
+    // will decode.
+    if (desiredScale >= 1.0) {
+        return this->getInfo().dimensions();
+    }
+
+    int origWidth = this->getInfo().width();
+    int origHeight = this->getInfo().height();
+    float desiredSize = desiredScale * origWidth * origHeight;
+    // At least one image will have smaller error than this initial value
+    float minError = ((float) (origWidth * origHeight)) - desiredSize + 1.0f;
+    int32_t minIndex = -1;
+    for (int32_t i = 0; i < fEmbeddedCodecs->count(); i++) {
+        int width = fEmbeddedCodecs->operator[](i)->getInfo().width();
+        int height = fEmbeddedCodecs->operator[](i)->getInfo().height();
+        float error = SkTAbs(((float) (width * height)) - desiredSize);
+        if (error < minError) {
+            minError = error;
+            minIndex = i;
+        }
+    }
+    SkASSERT(minIndex >= 0);
+
+    return fEmbeddedCodecs->operator[](minIndex)->getInfo().dimensions();
+}
+
+/*
+ * Initiates the Ico decode
+ */
+SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo,
+                                        void* dst, size_t dstRowBytes,
+                                        const Options& opts, SkPMColor* ct,
+                                        int* ptr) {
+    // We return invalid scale if there is no candidate image with matching
+    // dimensions.
+    Result result = kInvalidScale;
+    for (int32_t i = 0; i < fEmbeddedCodecs->count(); i++) {
+        // If the dimensions match, try to decode
+        if (dstInfo.dimensions() ==
+                fEmbeddedCodecs->operator[](i)->getInfo().dimensions()) {
+
+            // Perform the decode
+            result = fEmbeddedCodecs->operator[](i)->getPixels(dstInfo,
+                    dst, dstRowBytes, &opts, ct, ptr);
+
+            // On a fatal error, keep trying to find an image to decode
+            if (kInvalidConversion == result || kInvalidInput == result ||
+                    kInvalidScale == result) {
+                SkCodecPrintf("Warning: Attempt to decode candidate ico failed.\n");
+                continue;
+            }
+
+            // On success or partial success, return the result
+            return result;
+        }
+    }
+
+    SkCodecPrintf("Error: No matching candidate image in ico.\n");
+    return result;
+}
diff --git a/src/codec/SkCodec_libico.h b/src/codec/SkCodec_libico.h
new file mode 100644
index 0000000..a690cdd
--- /dev/null
+++ b/src/codec/SkCodec_libico.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec.h"
+#include "SkImageInfo.h"
+#include "SkStream.h"
+#include "SkTypes.h"
+
+/*
+ * This class implements the decoding for bmp images
+ */
+class SkIcoCodec : public SkCodec {
+public:
+
+    /*
+     * Checks the start of the stream to see if the image is a Ico or Cur
+     */
+    static bool IsIco(SkStream*);
+
+    /*
+     * Assumes IsIco was called and returned true
+     * Creates an Ico decoder
+     * Reads enough of the stream to determine the image format
+     */
+    static SkCodec* NewFromStream(SkStream*);
+
+protected:
+
+    /*
+     * Chooses the best dimensions given the desired scale
+     */
+    SkISize onGetScaledDimensions(float desiredScale) const override;
+
+    /*
+     * Initiates the Ico decode
+     */
+    Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
+                       size_t dstRowBytes, const Options&, SkPMColor*, int*)
+                       override;
+
+    SkEncodedFormat onGetEncodedFormat() const override {
+        return kICO_SkEncodedFormat;
+    }
+
+private:
+
+    /*
+     * Constructor called by NewFromStream
+     * @param embeddedCodecs codecs for the embedded images, takes ownership
+     */
+    SkIcoCodec(const SkImageInfo& srcInfo,
+               SkTArray<SkAutoTDelete<SkCodec>, true>* embeddedCodecs);
+
+    SkAutoTDelete<SkTArray<SkAutoTDelete<SkCodec>, true>>
+            fEmbeddedCodecs; // owned
+
+    typedef SkCodec INHERITED;
+};
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
index 8e7ee33..9cd3820 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -6,10 +6,12 @@
  */
 
 #include "SkCodec_libpng.h"
+#include "SkCodecPriv.h"
 #include "SkColorPriv.h"
 #include "SkColorTable.h"
 #include "SkBitmap.h"
 #include "SkMath.h"
+#include "SkScanlineDecoder.h"
 #include "SkSize.h"
 #include "SkStream.h"
 #include "SkSwizzler.h"
@@ -44,10 +46,14 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
-    SkDebugf("------ png error %s\n", msg);
+    SkCodecPrintf("------ png error %s\n", msg);
     longjmp(png_jmpbuf(png_ptr), 1);
 }
 
+void sk_warning_fn(png_structp, png_const_charp msg) {
+    SkCodecPrintf("----- png warning %s\n", msg);
+}
+
 static void sk_read_fn(png_structp png_ptr, png_bytep data,
                        png_size_t length) {
     SkStream* stream = static_cast<SkStream*>(png_get_io_ptr(png_ptr));
@@ -113,31 +119,22 @@
 
 // Note: SkColorTable claims to store SkPMColors, which is not necessarily
 // the case here.
-SkColorTable* decode_palette(png_structp png_ptr, png_infop info_ptr,
-                             bool premultiply, SkAlphaType* outAlphaType) {
-    SkASSERT(outAlphaType != NULL);
+bool SkPngCodec::decodePalette(bool premultiply, int bitDepth, int* ctableCount) {
     int numPalette;
     png_colorp palette;
     png_bytep trans;
 
-    if (!png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette)) {
-        return NULL;
+    if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) {
+        return false;
     }
 
-    /*  BUGGY IMAGE WORKAROUND
-
-        We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
-        which is a problem since we use the byte as an index. To work around this we grow
-        the colortable by 1 (if its < 256) and duplicate the last color into that slot.
-    */
-    const int colorCount = numPalette + (numPalette < 256);
-    // Note: These are not necessarily SkPMColors.
+    // Note: These are not necessarily SkPMColors
     SkPMColor colorStorage[256];    // worst-case storage
     SkPMColor* colorPtr = colorStorage;
 
     int numTrans;
-    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
-        png_get_tRNS(png_ptr, info_ptr, &trans, &numTrans, NULL);
+    if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
+        png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, NULL);
     } else {
         numTrans = 0;
     }
@@ -164,23 +161,34 @@
         palette++;
     }
 
-    if (transLessThanFF < 0) {
-        *outAlphaType = premultiply ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
-    } else {
-        *outAlphaType = kOpaque_SkAlphaType;
-    }
+    fReallyHasAlpha = transLessThanFF < 0;
 
     for (; index < numPalette; index++) {
         *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
         palette++;
     }
 
-    // see BUGGY IMAGE WORKAROUND comment above
-    if (numPalette < 256) {
-        *colorPtr = colorPtr[-1];
+    /*  BUGGY IMAGE WORKAROUND
+        Invalid images could contain pixel values that are greater than the number of palette
+        entries. Since we use pixel values as indices into the palette this could result in reading
+        beyond the end of the palette which could leak the contents of uninitialized memory. To
+        ensure this doesn't happen, we grow the colortable to the maximum size that can be
+        addressed by the bitdepth of the image and fill it with the last palette color or black if
+        the palette is empty (really broken image).
+    */
+    int colorCount = SkTMax(numPalette, 1 << SkTMin(bitDepth, 8));
+    SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0);
+    for (; index < colorCount; index++) {
+        *colorPtr++ = lastColor;
     }
 
-    return SkNEW_ARGS(SkColorTable, (colorStorage, colorCount));
+    // Set the new color count
+    if (ctableCount != NULL) {
+        *ctableCount = colorCount;
+    }
+
+    fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorStorage, colorCount)));
+    return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -200,20 +208,25 @@
     return true;
 }
 
-SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
+// Reads the header, and initializes the passed in fields, if not NULL (except
+// stream, which is passed to the read function).
+// Returns true on success, in which case the caller is responsible for calling
+// png_destroy_read_struct. If it returns false, the passed in fields (except
+// stream) are unchanged.
+static bool read_header(SkStream* stream, png_structp* png_ptrp,
+                        png_infop* info_ptrp, SkImageInfo* imageInfo) {
     // The image is known to be a PNG. Decode enough to know the SkImageInfo.
-    // FIXME: Allow silencing warnings.
     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
-                                                 sk_error_fn, NULL);
+                                                 sk_error_fn, sk_warning_fn);
     if (!png_ptr) {
-        return NULL;
+        return false;
     }
 
     AutoCleanPng autoClean(png_ptr);
 
     png_infop info_ptr = png_create_info_struct(png_ptr);
     if (info_ptr == NULL) {
-        return NULL;
+        return false;
     }
 
     autoClean.setInfoPtr(info_ptr);
@@ -221,7 +234,7 @@
     // FIXME: Could we use the return value of setjmp to specify the type of
     // error?
     if (setjmp(png_jmpbuf(png_ptr))) {
-        return NULL;
+        return false;
     }
 
     png_set_read_fn(png_ptr, static_cast<void*>(stream), sk_read_fn);
@@ -244,7 +257,7 @@
         int64_t size = sk_64_mul(origWidth, origHeight);
         // now check that if we are 4-bytes per pixel, we also don't overflow
         if (size < 0 || size > (0x7FFFFFFF >> 2)) {
-            return NULL;
+            return false;
         }
     }
 
@@ -270,10 +283,7 @@
     SkAlphaType skAlphaType;
     switch (colorType) {
         case PNG_COLOR_TYPE_PALETTE:
-            // Technically, this is true of the data, but I don't think we want
-            // to support it.
-            // skColorType = kIndex8_SkColorType;
-            skColorType = kN32_SkColorType;
+            skColorType = kIndex_8_SkColorType;
             skAlphaType = has_transparency_in_palette(png_ptr, info_ptr) ?
                     kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
             break;
@@ -325,73 +335,104 @@
 
     // FIXME: Also need to check for sRGB (skbug.com/3471).
 
-    SkImageInfo info = SkImageInfo::Make(origWidth, origHeight, skColorType,
-                                         skAlphaType);
-    SkCodec* codec = SkNEW_ARGS(SkPngCodec, (info, stream, png_ptr, info_ptr));
+    if (imageInfo) {
+        *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType,
+                                       skAlphaType);
+    }
     autoClean.detach();
-    return codec;
+    if (png_ptrp) {
+        *png_ptrp = png_ptr;
+    }
+    if (info_ptrp) {
+        *info_ptrp = info_ptr;
+    }
+    return true;
 }
 
+SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
+    SkAutoTDelete<SkStream> streamDeleter(stream);
+    png_structp png_ptr;
+    png_infop info_ptr;
+    SkImageInfo imageInfo;
+    if (read_header(stream, &png_ptr, &info_ptr, &imageInfo)) {
+        return SkNEW_ARGS(SkPngCodec, (imageInfo, streamDeleter.detach(), png_ptr, info_ptr));
+    }
+    return NULL;
+}
+
+#define INVALID_NUMBER_PASSES -1
 SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
                        png_structp png_ptr, png_infop info_ptr)
     : INHERITED(info, stream)
     , fPng_ptr(png_ptr)
-    , fInfo_ptr(info_ptr) {}
+    , fInfo_ptr(info_ptr)
+    , fSrcConfig(SkSwizzler::kUnknown)
+    , fNumberPasses(INVALID_NUMBER_PASSES)
+    , fReallyHasAlpha(false)
+{}
 
 SkPngCodec::~SkPngCodec() {
-    png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
+    this->destroyReadStruct();
+}
+
+void SkPngCodec::destroyReadStruct() {
+    if (fPng_ptr) {
+        // We will never have a NULL fInfo_ptr with a non-NULL fPng_ptr
+        SkASSERT(fInfo_ptr);
+        png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL);
+        fPng_ptr = NULL;
+        fInfo_ptr = NULL;
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Getting the pixels
 ///////////////////////////////////////////////////////////////////////////////
 
-static bool premul_and_unpremul(SkAlphaType A, SkAlphaType B) {
-    return kPremul_SkAlphaType == A && kUnpremul_SkAlphaType == B;
-}
-
-static bool conversion_possible(const SkImageInfo& A, const SkImageInfo& B) {
+static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
     // TODO: Support other conversions
-    if (A.colorType() != B.colorType()) {
+    if (dst.profileType() != src.profileType()) {
         return false;
     }
-    if (A.profileType() != B.profileType()) {
-        return false;
+
+    // Check for supported alpha types
+    if (src.alphaType() != dst.alphaType()) {
+        if (kOpaque_SkAlphaType == src.alphaType()) {
+            // If the source is opaque, we must decode to opaque
+            return false;
+        }
+
+        // The source is not opaque
+        switch (dst.alphaType()) {
+            case kPremul_SkAlphaType:
+            case kUnpremul_SkAlphaType:
+                // The source is not opaque, so either of these is okay
+                break;
+            default:
+                // We cannot decode a non-opaque image to opaque (or unknown)
+                return false;
+        }
     }
-    if (A.alphaType() == B.alphaType()) {
-        return true;
+
+    // Check for supported color types
+    switch (dst.colorType()) {
+        // Allow output to kN32 from any type of input
+        case kN32_SkColorType:
+            return true;
+        default:
+            return dst.colorType() == src.colorType();
     }
-    return premul_and_unpremul(A.alphaType(), B.alphaType())
-            || premul_and_unpremul(B.alphaType(), A.alphaType());
 }
 
-SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
-                                        size_t rowBytes, SkPMColor ctable[],
-                                        int* ctableCount) {
-    if (!this->rewindIfNeeded()) {
-        return kCouldNotRewind;
-    }
-    if (requestedInfo.dimensions() != this->getOriginalInfo().dimensions()) {
-        return kInvalidScale;
-    }
-    if (!conversion_possible(requestedInfo, this->getOriginalInfo())) {
-        return kInvalidConversion;
-    }
-
-    SkBitmap decodedBitmap;
-    // If installPixels would have failed, getPixels should have failed before
-    // calling onGetPixels.
-    SkAssertResult(decodedBitmap.installPixels(requestedInfo, dst, rowBytes));
-
-    // Initialize all non-trivial objects before setjmp.
-    SkAutoTUnref<SkColorTable> colorTable;
-    SkAutoTDelete<SkSwizzler> swizzler;
-    SkAutoMalloc storage;                   // Scratch memory for pre-swizzled rows.
-
+SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
+                                               void* dst, size_t rowBytes,
+                                               const Options& options,
+                                               SkPMColor ctable[],
+                                               int* ctableCount) {
     // FIXME: Could we use the return value of setjmp to specify the type of
     // error?
     if (setjmp(png_jmpbuf(fPng_ptr))) {
-        SkDebugf("setjmp long jump!\n");
+        SkCodecPrintf("setjmp long jump!\n");
         return kInvalidInput;
     }
 
@@ -401,42 +442,36 @@
     png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth,
                  &pngColorType, &interlaceType, int_p_NULL, int_p_NULL);
 
-    const int numberPasses = (interlaceType != PNG_INTERLACE_NONE) ?
+    fNumberPasses = (interlaceType != PNG_INTERLACE_NONE) ?
             png_set_interlace_handling(fPng_ptr) : 1;
 
-    SkSwizzler::SrcConfig sc;
-    bool reallyHasAlpha = false;
+    // Set to the default before calling decodePalette, which may change it.
+    fReallyHasAlpha = false;
     if (PNG_COLOR_TYPE_PALETTE == pngColorType) {
-        sc = SkSwizzler::kIndex;
-        SkAlphaType at = requestedInfo.alphaType();
-        colorTable.reset(decode_palette(fPng_ptr, fInfo_ptr,
-                                        kPremul_SkAlphaType == at,
-                                        &at));
-        if (!colorTable) {
+        fSrcConfig = SkSwizzler::kIndex;
+        if (!this->decodePalette(kPremul_SkAlphaType == requestedInfo.alphaType(), bitDepth,
+                ctableCount)) {
             return kInvalidInput;
         }
-
-        reallyHasAlpha = (at != kOpaque_SkAlphaType);
-
-        if (at != requestedInfo.alphaType()) {
-            // It turns out the image is opaque.
-            SkASSERT(kOpaque_SkAlphaType == at);
-        }
     } else if (kAlpha_8_SkColorType == requestedInfo.colorType()) {
         // Note: we check the destination, since otherwise we would have
         // told png to upscale.
         SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
-        sc = SkSwizzler::kGray;
-    } else if (this->getOriginalInfo().alphaType() == kOpaque_SkAlphaType) {
-        sc = SkSwizzler::kRGBX;
+        fSrcConfig = SkSwizzler::kGray;
+    } else if (this->getInfo().alphaType() == kOpaque_SkAlphaType) {
+        fSrcConfig = SkSwizzler::kRGBX;
     } else {
-        sc = SkSwizzler::kRGBA;
+        fSrcConfig = SkSwizzler::kRGBA;
     }
-    const SkPMColor* colors = colorTable ? colorTable->readColors() : NULL;
-    // TODO: Support skipZeroes.
-    swizzler.reset(SkSwizzler::CreateSwizzler(sc, colors, requestedInfo,
-                                              dst, rowBytes, false));
-    if (!swizzler) {
+
+    // Copy the color table to the client if they request kIndex8 mode
+    copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
+
+    // Create the swizzler.  SkPngCodec retains ownership of the color table.
+    const SkPMColor* colors = fColorTable ? fColorTable->readColors() : NULL;
+    fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
+            dst, rowBytes, options.fZeroInitialized));
+    if (!fSwizzler) {
         // FIXME: CreateSwizzler could fail for another reason.
         return kUnimplemented;
     }
@@ -445,16 +480,77 @@
     // made in the factory.
     png_read_update_info(fPng_ptr, fInfo_ptr);
 
-    if (numberPasses > 1) {
+    return kSuccess;
+}
+
+bool SkPngCodec::handleRewind() {
+    switch (this->rewindIfNeeded()) {
+        case kNoRewindNecessary_RewindState:
+            return true;
+        case kCouldNotRewind_RewindState:
+            return false;
+        case kRewound_RewindState: {
+            // This sets fPng_ptr and fInfo_ptr to NULL. If read_header
+            // succeeds, they will be repopulated, and if it fails, they will
+            // remain NULL. Any future accesses to fPng_ptr and fInfo_ptr will
+            // come through this function which will rewind and again attempt
+            // to reinitialize them.
+            this->destroyReadStruct();
+            png_structp png_ptr;
+            png_infop info_ptr;
+            if (read_header(this->stream(), &png_ptr, &info_ptr, NULL)) {
+                fPng_ptr = png_ptr;
+                fInfo_ptr = info_ptr;
+                return true;
+            }
+            return false;
+        }
+        default:
+            SkASSERT(false);
+            return false;
+    }
+}
+
+SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst,
+                                        size_t rowBytes, const Options& options,
+                                        SkPMColor ctable[], int* ctableCount) {
+    if (!this->handleRewind()) {
+        return kCouldNotRewind;
+    }
+    if (requestedInfo.dimensions() != this->getInfo().dimensions()) {
+        return kInvalidScale;
+    }
+    if (!conversion_possible(requestedInfo, this->getInfo())) {
+        return kInvalidConversion;
+    }
+
+    // Note that ctable and ctableCount may be modified if there is a color table
+    const Result result = this->initializeSwizzler(requestedInfo, dst, rowBytes,
+                                                   options, ctable, ctableCount);
+
+    if (result != kSuccess) {
+        return result;
+    }
+
+    // FIXME: Could we use the return value of setjmp to specify the type of
+    // error?
+    if (setjmp(png_jmpbuf(fPng_ptr))) {
+        SkCodecPrintf("setjmp long jump!\n");
+        return kInvalidInput;
+    }
+
+    SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
+    SkAutoMalloc storage;
+    if (fNumberPasses > 1) {
         const int width = requestedInfo.width();
         const int height = requestedInfo.height();
-        const int bpp = SkSwizzler::BytesPerPixel(sc);
+        const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig);
         const size_t rowBytes = width * bpp;
 
         storage.reset(width * height * bpp);
         uint8_t* const base = static_cast<uint8_t*>(storage.get());
 
-        for (int i = 0; i < numberPasses; i++) {
+        for (int i = 0; i < fNumberPasses; i++) {
             uint8_t* row = base;
             for (int y = 0; y < height; y++) {
                 uint8_t* bmRow = row;
@@ -466,27 +562,119 @@
         // Now swizzle it.
         uint8_t* row = base;
         for (int y = 0; y < height; y++) {
-            reallyHasAlpha |= swizzler->next(row);
+            fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(row));
             row += rowBytes;
         }
     } else {
-        storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(sc));
+        storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig));
         uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
         for (int y = 0; y < requestedInfo.height(); y++) {
             png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
-            reallyHasAlpha |= swizzler->next(srcRow);
+            fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->next(srcRow));
         }
     }
 
-    /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
-    png_read_end(fPng_ptr, fInfo_ptr);
+    // FIXME: do we need substituteTranspColor? Note that we cannot do it for
+    // scanline decoding, but we could do it here. Alternatively, we could do
+    // it as we go, instead of in post-processing like SkPNGImageDecoder.
 
-    // FIXME: do we need substituteTranspColor?
-
-    if (reallyHasAlpha && requestedInfo.alphaType() != kOpaque_SkAlphaType) {
-        // FIXME: We want to alert the caller. Is this the right way?
-        SkImageInfo* modInfo = const_cast<SkImageInfo*>(&requestedInfo);
-        *modInfo = requestedInfo.makeAlphaType(kOpaque_SkAlphaType);
-    }
+    this->finish();
     return kSuccess;
 }
+
+void SkPngCodec::finish() {
+    if (setjmp(png_jmpbuf(fPng_ptr))) {
+        // We've already read all the scanlines. This is a success.
+        return;
+    }
+    /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
+    png_read_end(fPng_ptr, fInfo_ptr);
+}
+
+class SkPngScanlineDecoder : public SkScanlineDecoder {
+public:
+    SkPngScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
+        : INHERITED(dstInfo)
+        , fCodec(codec)
+        , fHasAlpha(false)
+    {
+        fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig));
+        fSrcRow = static_cast<uint8_t*>(fStorage.get());
+    }
+
+    SkImageGenerator::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
+        if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
+            SkCodecPrintf("setjmp long jump!\n");
+            return SkImageGenerator::kInvalidInput;
+        }
+
+        for (int i = 0; i < count; i++) {
+            png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
+            fCodec->fSwizzler->setDstRow(dst);
+            fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(fSrcRow));
+            dst = SkTAddOffset<void>(dst, rowBytes);
+        }
+        return SkImageGenerator::kSuccess;
+    }
+
+    SkImageGenerator::Result onSkipScanlines(int count) override {
+        // FIXME: Could we use the return value of setjmp to specify the type of
+        // error?
+        if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
+            SkCodecPrintf("setjmp long jump!\n");
+            return SkImageGenerator::kInvalidInput;
+        }
+
+        png_read_rows(fCodec->fPng_ptr, png_bytepp_NULL, png_bytepp_NULL, count);
+        return SkImageGenerator::kSuccess;
+    }
+
+    void onFinish() override {
+        fCodec->finish();
+    }
+
+    bool onReallyHasAlpha() const override { return fHasAlpha; }
+
+private:
+    SkPngCodec*         fCodec;     // Unowned.
+    bool                fHasAlpha;
+    SkAutoMalloc        fStorage;
+    uint8_t*            fSrcRow;
+
+    typedef SkScanlineDecoder INHERITED;
+};
+
+SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo,
+        const Options& options, SkPMColor ctable[], int* ctableCount) {
+    if (!this->handleRewind()) {
+        return NULL;
+    }
+
+    // Check to see if scaling was requested.
+    if (dstInfo.dimensions() != this->getInfo().dimensions()) {
+        return NULL;
+    }
+
+    if (!conversion_possible(dstInfo, this->getInfo())) {
+        SkCodecPrintf("no conversion possible\n");
+        return NULL;
+    }
+
+    // Note: We set dst to NULL since we do not know it yet. rowBytes is not needed,
+    // since we'll be manually updating the dstRow, but the SkSwizzler requires it to
+    // be at least dstInfo.minRowBytes.
+    if (this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options, ctable,
+            ctableCount) != kSuccess) {
+        SkCodecPrintf("failed to initialize the swizzler.\n");
+        return NULL;
+    }
+
+    SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
+    if (fNumberPasses > 1) {
+        // We cannot efficiently do scanline decoding.
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, this));
+}
+
diff --git a/src/codec/SkCodec_libpng.h b/src/codec/SkCodec_libpng.h
index a5327dd..3146331 100644
--- a/src/codec/SkCodec_libpng.h
+++ b/src/codec/SkCodec_libpng.h
@@ -6,14 +6,19 @@
  */
 
 #include "SkCodec.h"
+#include "SkColorTable.h"
+#include "SkEncodedFormat.h"
 #include "SkImageInfo.h"
+#include "SkRefCnt.h"
+#include "SkSwizzler.h"
 
-extern "C" {
-    // FIXME: I'd like to force all platforms to use the same decoder, but this
-    // means an extra dependency on Mac/Win.
-    #include "png.h"
-}
+#ifdef SKIA_PNG_PREFIXED
+    // this must proceed png.h
+    #include "pngprefix.h"
+#endif
+#include "png.h"
 
+class SkScanlineDecoder;
 class SkStream;
 
 class SkPngCodec : public SkCodec {
@@ -22,13 +27,37 @@
     static SkCodec* NewFromStream(SkStream*);
     static bool IsPng(SkStream*);
 protected:
-    Result onGetPixels(const SkImageInfo&, void*, size_t, SkPMColor*, int*) SK_OVERRIDE;
+    Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*)
+            override;
+    SkEncodedFormat onGetEncodedFormat() const override { return kPNG_SkEncodedFormat; }
+    SkScanlineDecoder* onGetScanlineDecoder(const SkImageInfo& dstInfo, const Options& options,
+                                            SkPMColor ctable[], int* ctableCount) override;
+    bool onReallyHasAlpha() const override { return fReallyHasAlpha; }
 private:
-    png_structp             fPng_ptr;
-    png_infop               fInfo_ptr;
+    png_structp                 fPng_ptr;
+    png_infop                   fInfo_ptr;
+
+    // These are stored here so they can be used both by normal decoding and scanline decoding.
+    SkAutoTUnref<SkColorTable>  fColorTable;    // May be unpremul.
+    SkAutoTDelete<SkSwizzler>   fSwizzler;
+
+    SkSwizzler::SrcConfig       fSrcConfig;
+    int                         fNumberPasses;
+    bool                        fReallyHasAlpha;
 
     SkPngCodec(const SkImageInfo&, SkStream*, png_structp, png_infop);
     ~SkPngCodec();
 
+    // Helper to set up swizzler and color table. Also calls png_read_update_info.
+    Result initializeSwizzler(const SkImageInfo& requestedInfo, void* dst,
+                              size_t rowBytes, const Options&, SkPMColor*, int* ctableCount);
+    // Calls rewindIfNeeded, and returns true if the decoder can continue.
+    bool handleRewind();
+    bool decodePalette(bool premultiply, int bitDepth, int* ctableCount);
+    void finish();
+    void destroyReadStruct();
+
+    friend class SkPngScanlineDecoder;
+
     typedef SkCodec INHERITED;
 };
diff --git a/src/codec/SkCodec_wbmp.cpp b/src/codec/SkCodec_wbmp.cpp
new file mode 100644
index 0000000..073165d
--- /dev/null
+++ b/src/codec/SkCodec_wbmp.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkCodec_wbmp.h"
+
+// http://en.wikipedia.org/wiki/Variable-length_quantity
+static bool read_mbf(SkStream* stream, uint64_t* value) {
+    uint64_t n = 0;
+    uint8_t data;
+    const uint64_t kLimit = 0xFE00000000000000;
+    SkASSERT(kLimit == ~((~static_cast<uint64_t>(0)) >> 7));
+    do {
+        if (n & kLimit) { // Will overflow on shift by 7.
+            return false;
+        }
+        if (stream->read(&data, 1) != 1) {
+            return false;
+        }
+        n = (n << 7) | (data & 0x7F);
+    } while (data & 0x80);
+    *value = n;
+    return true;
+}
+
+static bool read_header(SkStream* stream, SkISize* size) {
+    uint64_t width, height;
+    uint16_t data;
+    if (stream->read(&data, 2) != 2 || data != 0) {
+        return false;
+    }
+    if (!read_mbf(stream, &width) || width > 0xFFFF || !width) {
+        return false;
+    }
+    if (!read_mbf(stream, &height) || width > 0xFFFF || !height) {
+        return false;
+    }
+    if (size) {
+        *size = SkISize::Make(SkToS32(width), SkToS32(height));
+    }
+    return true;
+}
+
+#define BLACK SkPackARGB32NoCheck(0xFF, 0, 0, 0)
+#define WHITE SkPackARGB32NoCheck(0xFF, 0xFF, 0xFF, 0xFF)
+
+#define GRAYSCALE_BLACK 0
+#define GRAYSCALE_WHITE 0xFF
+
+#define RGB565_BLACK 0
+#define RGB565_WHITE 0xFFFF
+
+static SkPMColor bit_to_pmcolor(U8CPU bit) { return bit ? WHITE : BLACK; }
+
+static uint8_t bit_to_bit(U8CPU bit) { return bit; }
+
+static uint8_t bit_to_grayscale(U8CPU bit) {
+    return bit ? GRAYSCALE_WHITE : GRAYSCALE_BLACK;
+}
+
+static uint16_t bit_to_rgb565(U8CPU bit) {
+    return bit ? RGB565_WHITE : RGB565_BLACK;
+}
+
+typedef void (*ExpandProc)(uint8_t*, const uint8_t*, int);
+
+// TODO(halcanary): Add this functionality (grayscale and indexed output) to
+//                  SkSwizzler and use it here.
+template <typename T, T (*TRANSFORM)(U8CPU)>
+static void expand_bits_to_T(uint8_t* dstptr, const uint8_t* src, int bits) {
+    T* dst = reinterpret_cast<T*>(dstptr);
+    int bytes = bits >> 3;
+    for (int i = 0; i < bytes; i++) {
+        U8CPU mask = *src++;
+        for (int j = 0; j < 8; j++) {
+            dst[j] = TRANSFORM((mask >> (7 - j)) & 1);
+        }
+        dst += 8;
+    }
+    bits &= 7;
+    if (bits > 0) {
+        U8CPU mask = *src;
+        do {
+            *dst++ = TRANSFORM((mask >> 7) & 1);
+            mask <<= 1;
+        } while (--bits != 0);
+    }
+}
+
+SkWbmpCodec::SkWbmpCodec(const SkImageInfo& info, SkStream* stream)
+    : INHERITED(info, stream) {}
+
+SkEncodedFormat SkWbmpCodec::onGetEncodedFormat() const {
+    return kWBMP_SkEncodedFormat;
+}
+
+SkImageGenerator::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
+                                                  void* pixels,
+                                                  size_t rowBytes,
+                                                  const Options&,
+                                                  SkPMColor ctable[],
+                                                  int* ctableCount) {
+    SkCodec::RewindState rewindState = this->rewindIfNeeded();
+    if (rewindState == kCouldNotRewind_RewindState) {
+        return SkImageGenerator::kCouldNotRewind;
+    } else if (rewindState == kRewound_RewindState) {
+        (void)read_header(this->stream(), NULL);
+    }
+    if (info.dimensions() != this->getInfo().dimensions()) {
+        return SkImageGenerator::kInvalidScale;
+    }
+    ExpandProc proc = NULL;
+    switch (info.colorType()) {
+        case kGray_8_SkColorType:
+            proc = expand_bits_to_T<uint8_t, bit_to_grayscale>;
+            break;
+        case kN32_SkColorType:
+            proc = expand_bits_to_T<SkPMColor, bit_to_pmcolor>;
+            break;
+        case kIndex_8_SkColorType:
+            ctable[0] = BLACK;
+            ctable[1] = WHITE;
+            *ctableCount = 2;
+            proc = expand_bits_to_T<uint8_t, bit_to_bit>;
+            break;
+        case kRGB_565_SkColorType:
+            proc = expand_bits_to_T<uint16_t, bit_to_rgb565>;
+            break;
+        default:
+            return SkImageGenerator::kInvalidConversion;
+    }
+    SkISize size = info.dimensions();
+    uint8_t* dst = static_cast<uint8_t*>(pixels);
+    size_t srcRowBytes = SkAlign8(size.width()) >> 3;
+    SkAutoTMalloc<uint8_t> src(srcRowBytes);
+    for (int y = 0; y < size.height(); ++y) {
+        if (this->stream()->read(src.get(), srcRowBytes) != srcRowBytes) {
+            return SkImageGenerator::kIncompleteInput;
+        }
+        proc(dst, src.get(), size.width());
+        dst += rowBytes;
+    }
+    return SkImageGenerator::kSuccess;
+}
+
+bool SkWbmpCodec::IsWbmp(SkStream* stream) {
+    return read_header(stream, NULL);
+}
+
+SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream) {
+    SkAutoTDelete<SkStream> streamDeleter(stream);
+    SkISize size;
+    if (!read_header(stream, &size)) {
+        return NULL;
+    }
+    SkImageInfo info =
+            SkImageInfo::Make(size.width(), size.height(), kGray_8_SkColorType,
+                              kOpaque_SkAlphaType);
+    return SkNEW_ARGS(SkWbmpCodec, (info, streamDeleter.detach()));
+}
diff --git a/src/codec/SkCodec_wbmp.h b/src/codec/SkCodec_wbmp.h
new file mode 100644
index 0000000..fece249
--- /dev/null
+++ b/src/codec/SkCodec_wbmp.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCodec_wbmp_DEFINED
+#define SkCodec_wbmp_DEFINED
+
+#include "SkCodec.h"
+
+class SkWbmpCodec final : public SkCodec {
+public:
+    static bool IsWbmp(SkStream*);
+    static SkCodec* NewFromStream(SkStream*);
+protected:
+    SkEncodedFormat onGetEncodedFormat() const override;
+    Result onGetPixels(const SkImageInfo&, void*, size_t,
+                       const Options&, SkPMColor[], int*) override;
+private:
+    SkWbmpCodec(const SkImageInfo&, SkStream*);
+    typedef SkCodec INHERITED;
+};
+
+#endif  // SkCodec_wbmp_DEFINED
diff --git a/src/codec/SkGifInterlaceIter.cpp b/src/codec/SkGifInterlaceIter.cpp
new file mode 100644
index 0000000..268a142e
--- /dev/null
+++ b/src/codec/SkGifInterlaceIter.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 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 "SkGifInterlaceIter.h"
+
+static const uint8_t kStartingInterlaceYValues[] = { 0, 4, 2, 1 };
+static const uint8_t kDeltaInterlaceYValues[] = { 8, 8, 4, 2 };
+
+SkGifInterlaceIter::SkGifInterlaceIter(int height) : fHeight(height) {
+    fStartYPtr = kStartingInterlaceYValues;
+    fDeltaYPtr = kDeltaInterlaceYValues;
+
+    fCurrY = *fStartYPtr++;
+    fDeltaY = *fDeltaYPtr++;
+}
+
+void SkGifInterlaceIter::prepareY() {
+    int32_t y = fCurrY + fDeltaY;
+
+    // Iterate through fStartYPtr until a valid row is found.
+    // This ensures that we do not move past the height of the small images.
+    while (y >= fHeight) {
+        if (kStartingInterlaceYValues +
+                SK_ARRAY_COUNT(kStartingInterlaceYValues) == fStartYPtr) {
+            // Now we have iterated over the entire image.  Forbid any
+            // subsequent calls to nextY().
+            SkDEBUGCODE(fStartYPtr = NULL;)
+            SkDEBUGCODE(fDeltaYPtr = NULL;)
+            y = 0;
+        } else {
+            y = *fStartYPtr++;
+            fDeltaY = *fDeltaYPtr++;
+        }
+    }
+    fCurrY = y;
+}
+
+int32_t SkGifInterlaceIter::nextY() {
+    SkASSERT(fStartYPtr);
+    SkASSERT(fDeltaYPtr);
+    int32_t y = fCurrY;
+    prepareY();
+    return y;
+}
diff --git a/src/codec/SkGifInterlaceIter.h b/src/codec/SkGifInterlaceIter.h
new file mode 100644
index 0000000..970cd49
--- /dev/null
+++ b/src/codec/SkGifInterlaceIter.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 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 "SkTypes.h"
+
+/*
+ * Helper class to determine the destination y-values for interlaced gifs
+ */
+class SkGifInterlaceIter : SkNoncopyable {
+public:
+
+    explicit SkGifInterlaceIter(int32_t height);
+
+    /*
+     * Get the next destination y-value
+     */
+    int32_t nextY();
+
+private:
+
+    /*
+     * Updates the iterator to prepare the next y-value
+     */
+    void prepareY();
+
+    const int32_t fHeight;
+    int32_t        fCurrY;
+    int32_t        fDeltaY;
+    const uint8_t* fStartYPtr;
+    const uint8_t* fDeltaYPtr;
+};
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
new file mode 100644
index 0000000..c125874
--- /dev/null
+++ b/src/codec/SkJpegCodec.cpp
@@ -0,0 +1,505 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodec.h"
+#include "SkJpegCodec.h"
+#include "SkJpegDecoderMgr.h"
+#include "SkJpegUtility.h"
+#include "SkCodecPriv.h"
+#include "SkColorPriv.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkTypes.h"
+
+// stdio is needed for jpeglib
+#include <stdio.h>
+
+extern "C" {
+    #include "jerror.h"
+    #include "jmorecfg.h"
+    #include "jpegint.h"
+    #include "jpeglib.h"
+}
+
+// ANDROID_RGB
+// If this is defined in the jpeg headers it indicates that jpeg offers
+// support for two additional formats: JCS_RGBA_8888 and JCS_RGB_565.
+
+/*
+ * Get the source configuarion for the swizzler
+ */
+SkSwizzler::SrcConfig get_src_config(const jpeg_decompress_struct& dinfo) {
+    if (JCS_CMYK == dinfo.out_color_space) {
+        // We will need to perform a manual conversion
+        return  SkSwizzler::kRGBX;
+    }
+    if (3 == dinfo.out_color_components && JCS_RGB == dinfo.out_color_space) {
+        return SkSwizzler::kRGB;
+    }
+#ifdef ANDROID_RGB
+    if (JCS_RGBA_8888 == dinfo.out_color_space) {
+        return SkSwizzler::kRGBX;
+    }
+
+    if (JCS_RGB_565 == dinfo.out_color_space) {
+        return SkSwizzler::kRGB_565;
+    }
+#endif
+    if (1 == dinfo.out_color_components && JCS_GRAYSCALE == dinfo.out_color_space) {
+        return SkSwizzler::kGray;
+    }
+    return SkSwizzler::kUnknown;
+}
+
+/*
+ * Convert a row of CMYK samples to RGBX in place.
+ * Note that this method moves the row pointer.
+ * @param width the number of pixels in the row that is being converted
+ *              CMYK is stored as four bytes per pixel
+ */
+static void convert_CMYK_to_RGB(uint8_t* row, uint32_t width) {
+    // We will implement a crude conversion from CMYK -> RGB using formulas
+    // from easyrgb.com.
+    //
+    // CMYK -> CMY
+    // C = C * (1 - K) + K
+    // M = M * (1 - K) + K
+    // Y = Y * (1 - K) + K
+    //
+    // libjpeg actually gives us inverted CMYK, so we must subtract the
+    // original terms from 1.
+    // CMYK -> CMY
+    // C = (1 - C) * (1 - (1 - K)) + (1 - K)
+    // M = (1 - M) * (1 - (1 - K)) + (1 - K)
+    // Y = (1 - Y) * (1 - (1 - K)) + (1 - K)
+    //
+    // Simplifying the above expression.
+    // CMYK -> CMY
+    // C = 1 - CK
+    // M = 1 - MK
+    // Y = 1 - YK
+    //
+    // CMY -> RGB
+    // R = (1 - C) * 255
+    // G = (1 - M) * 255
+    // B = (1 - Y) * 255
+    //
+    // Therefore the full conversion is below.  This can be verified at
+    // www.rapidtables.com (assuming inverted CMYK).
+    // CMYK -> RGB
+    // R = C * K * 255
+    // G = M * K * 255
+    // B = Y * K * 255
+    //
+    // As a final note, we have treated the CMYK values as if they were on
+    // a scale from 0-1, when in fact they are 8-bit ints scaling from 0-255.
+    // We must divide each CMYK component by 255 to obtain the true conversion
+    // we should perform.
+    // CMYK -> RGB
+    // R = C * K / 255
+    // G = M * K / 255
+    // B = Y * K / 255
+    for (uint32_t x = 0; x < width; x++, row += 4) {
+        row[0] = SkMulDiv255Round(row[0], row[3]);
+        row[1] = SkMulDiv255Round(row[1], row[3]);
+        row[2] = SkMulDiv255Round(row[2], row[3]);
+        row[3] = 0xFF;
+    }
+}
+
+bool SkJpegCodec::IsJpeg(SkStream* stream) {
+    static const uint8_t jpegSig[] = { 0xFF, 0xD8, 0xFF };
+    char buffer[sizeof(jpegSig)];
+    return stream->read(buffer, sizeof(jpegSig)) == sizeof(jpegSig) &&
+            !memcmp(buffer, jpegSig, sizeof(jpegSig));
+}
+
+bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
+        JpegDecoderMgr** decoderMgrOut) {
+
+    // Create a JpegDecoderMgr to own all of the decompress information
+    SkAutoTDelete<JpegDecoderMgr> decoderMgr(SkNEW_ARGS(JpegDecoderMgr, (stream)));
+
+    // libjpeg errors will be caught and reported here
+    if (setjmp(decoderMgr->getJmpBuf())) {
+        return decoderMgr->returnFalse("setjmp");
+    }
+
+    // Initialize the decompress info and the source manager
+    decoderMgr->init();
+
+    // Read the jpeg header
+    if (JPEG_HEADER_OK != jpeg_read_header(decoderMgr->dinfo(), true)) {
+        return decoderMgr->returnFalse("read_header");
+    }
+
+    if (NULL != codecOut) {
+        // Recommend the color type to decode to
+        const SkColorType colorType = decoderMgr->getColorType();
+
+        // Create image info object and the codec
+        const SkImageInfo& imageInfo = SkImageInfo::Make(decoderMgr->dinfo()->image_width,
+                decoderMgr->dinfo()->image_height, colorType, kOpaque_SkAlphaType);
+        *codecOut = SkNEW_ARGS(SkJpegCodec, (imageInfo, stream, decoderMgr.detach()));
+    } else {
+        SkASSERT(NULL != decoderMgrOut);
+        *decoderMgrOut = decoderMgr.detach();
+    }
+    return true;
+}
+
+SkCodec* SkJpegCodec::NewFromStream(SkStream* stream) {
+    SkAutoTDelete<SkStream> streamDeleter(stream);
+    SkCodec* codec = NULL;
+    if (ReadHeader(stream,  &codec, NULL)) {
+        // Codec has taken ownership of the stream, we do not need to delete it
+        SkASSERT(codec);
+        streamDeleter.detach();
+        return codec;
+    }
+    return NULL;
+}
+
+SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream,
+        JpegDecoderMgr* decoderMgr)
+    : INHERITED(srcInfo, stream)
+    , fDecoderMgr(decoderMgr)
+    , fSwizzler(NULL)
+    , fSrcRowBytes(0)
+{}
+
+/*
+ * Return a valid set of output dimensions for this decoder, given an input scale
+ */
+SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const {
+    // libjpeg supports scaling by 1/1, 1/2, 1/4, and 1/8, so we will support these as well
+    long scale;
+    if (desiredScale > 0.75f) {
+        scale = 1;
+    } else if (desiredScale > 0.375f) {
+        scale = 2;
+    } else if (desiredScale > 0.1875f) {
+        scale = 4;
+    } else {
+        scale = 8;
+    }
+
+    // Set up a fake decompress struct in order to use libjpeg to calculate output dimensions
+    jpeg_decompress_struct dinfo;
+    sk_bzero(&dinfo, sizeof(dinfo));
+    dinfo.image_width = this->getInfo().width();
+    dinfo.image_height = this->getInfo().height();
+    dinfo.global_state = DSTATE_READY;
+    dinfo.num_components = 0;
+    dinfo.scale_num = 1;
+    dinfo.scale_denom = scale;
+    jpeg_calc_output_dimensions(&dinfo);
+
+    // Return the calculated output dimensions for the given scale
+    return SkISize::Make(dinfo.output_width, dinfo.output_height);
+}
+
+/*
+ * Checks if the conversion between the input image and the requested output
+ * image has been implemented
+ */
+static bool conversion_possible(const SkImageInfo& dst,
+                                const SkImageInfo& src) {
+    // Ensure that the profile type is unchanged
+    if (dst.profileType() != src.profileType()) {
+        return false;
+    }
+
+    // Ensure that the alpha type is opaque
+    if (kOpaque_SkAlphaType != dst.alphaType()) {
+        return false;
+    }
+
+    // Always allow kN32 as the color type
+    if (kN32_SkColorType == dst.colorType()) {
+        return true;
+    }
+
+    // Otherwise require that the destination color type match our recommendation
+    return dst.colorType() == src.colorType();
+}
+
+/*
+ * Handles rewinding the input stream if it is necessary
+ */
+bool SkJpegCodec::handleRewind() {
+    switch(this->rewindIfNeeded()) {
+        case kCouldNotRewind_RewindState:
+            return fDecoderMgr->returnFalse("could not rewind");
+        case kRewound_RewindState: {
+            JpegDecoderMgr* decoderMgr = NULL;
+            if (!ReadHeader(this->stream(), NULL, &decoderMgr)) {
+                return fDecoderMgr->returnFalse("could not rewind");
+            }
+            SkASSERT(NULL != decoderMgr);
+            fDecoderMgr.reset(decoderMgr);
+            return true;
+        }
+        case kNoRewindNecessary_RewindState:
+            return true;
+        default:
+            SkASSERT(false);
+            return false;
+    }
+}
+
+/*
+ * Checks if we can scale to the requested dimensions and scales the dimensions
+ * if possible
+ */
+bool SkJpegCodec::scaleToDimensions(uint32_t dstWidth, uint32_t dstHeight) {
+    // libjpeg can scale to 1/1, 1/2, 1/4, and 1/8
+    SkASSERT(1 == fDecoderMgr->dinfo()->scale_num);
+    SkASSERT(1 == fDecoderMgr->dinfo()->scale_denom);
+    jpeg_calc_output_dimensions(fDecoderMgr->dinfo());
+    while (fDecoderMgr->dinfo()->output_width != dstWidth ||
+            fDecoderMgr->dinfo()->output_height != dstHeight) {
+
+        // Return a failure if we have tried all of the possible scales
+        if (8 == fDecoderMgr->dinfo()->scale_denom ||
+                dstWidth > fDecoderMgr->dinfo()->output_width ||
+                dstHeight > fDecoderMgr->dinfo()->output_height) {
+            return fDecoderMgr->returnFalse("could not scale to requested dimensions");
+        }
+
+        // Try the next scale
+        fDecoderMgr->dinfo()->scale_denom *= 2;
+        jpeg_calc_output_dimensions(fDecoderMgr->dinfo());
+    }
+    return true;
+}
+
+/*
+ * Create the swizzler based on the encoded format
+ */
+void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo,
+                                     void* dst, size_t dstRowBytes,
+                                     const Options& options) {
+    SkSwizzler::SrcConfig srcConfig = get_src_config(*fDecoderMgr->dinfo());
+    fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, NULL, dstInfo, dst, dstRowBytes,
+            options.fZeroInitialized));
+    fSrcRowBytes = SkSwizzler::BytesPerPixel(srcConfig) * dstInfo.width();
+}
+
+/*
+ * Performs the jpeg decode
+ */
+SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
+                                         void* dst, size_t dstRowBytes,
+                                         const Options& options, SkPMColor*, int*) {
+
+    // Rewind the stream if needed
+    if (!this->handleRewind()) {
+        fDecoderMgr->returnFailure("could not rewind stream", kCouldNotRewind);
+    }
+
+    // Get a pointer to the decompress info since we will use it quite frequently
+    jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo();
+
+    // Set the jump location for libjpeg errors
+    if (setjmp(fDecoderMgr->getJmpBuf())) {
+        return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
+    }
+
+    // Check if we can decode to the requested destination
+    if (!conversion_possible(dstInfo, this->getInfo())) {
+        return fDecoderMgr->returnFailure("conversion_possible", kInvalidConversion);
+    }
+
+    // Perform the necessary scaling
+    if (!this->scaleToDimensions(dstInfo.width(), dstInfo.height())) {
+        fDecoderMgr->returnFailure("cannot scale to requested dims", kInvalidScale);
+    }
+
+    // Now, given valid output dimensions, we can start the decompress
+    if (!jpeg_start_decompress(dinfo)) {
+        return fDecoderMgr->returnFailure("startDecompress", kInvalidInput);
+    }
+
+    // Create the swizzler
+    this->initializeSwizzler(dstInfo, dst, dstRowBytes, options);
+    if (NULL == fSwizzler) {
+        return fDecoderMgr->returnFailure("getSwizzler", kUnimplemented);
+    }
+
+    // This is usually 1, but can also be 2 or 4.
+    // If we wanted to always read one row at a time, we could, but we will save space and time
+    // by using the recommendation from libjpeg.
+    const uint32_t rowsPerDecode = dinfo->rec_outbuf_height;
+    SkASSERT(rowsPerDecode <= 4);
+
+    // Create a buffer to contain decoded rows (libjpeg requires a 2D array)
+    SkASSERT(0 != fSrcRowBytes);
+    SkAutoTDeleteArray<uint8_t> srcBuffer(SkNEW_ARRAY(uint8_t, fSrcRowBytes * rowsPerDecode));
+    JSAMPLE* srcRows[4];
+    uint8_t* srcPtr = srcBuffer.get();
+    for (uint8_t i = 0; i < rowsPerDecode; i++) {
+        srcRows[i] = (JSAMPLE*) srcPtr;
+        srcPtr += fSrcRowBytes;
+    }
+
+    // Ensure that we loop enough times to decode all of the rows
+    // libjpeg will prevent us from reading past the bottom of the image
+    uint32_t dstHeight = dstInfo.height();
+    for (uint32_t y = 0; y < dstHeight + rowsPerDecode - 1; y += rowsPerDecode) {
+        // Read rows of the image
+        uint32_t rowsDecoded = jpeg_read_scanlines(dinfo, srcRows, rowsPerDecode);
+
+        // Convert to RGB if necessary
+        if (JCS_CMYK == dinfo->out_color_space) {
+            convert_CMYK_to_RGB(srcRows[0], dstInfo.width() * rowsDecoded);
+        }
+
+        // Swizzle to output destination
+        for (uint32_t i = 0; i < rowsDecoded; i++) {
+            fSwizzler->next(srcRows[i]);
+        }
+
+        // If we cannot read enough rows, assume the input is incomplete
+        if (rowsDecoded < rowsPerDecode && y + rowsDecoded < dstHeight) {
+            // Fill the remainder of the image with black. This error handling
+            // behavior is unspecified but SkCodec consistently uses black as
+            // the fill color for opaque images.  If the destination is kGray,
+            // the low 8 bits of SK_ColorBLACK will be used.  Conveniently,
+            // these are zeros, which is the representation for black in kGray.
+            SkSwizzler::Fill(fSwizzler->getDstRow(), dstInfo, dstRowBytes,
+                    dstHeight - y - rowsDecoded, SK_ColorBLACK, NULL);
+
+            // Prevent libjpeg from failing on incomplete decode
+            dinfo->output_scanline = dstHeight;
+
+            // Finish the decode and indicate that the input was incomplete.
+            jpeg_finish_decompress(dinfo);
+            return fDecoderMgr->returnFailure("Incomplete image data", kIncompleteInput);
+        }
+    }
+    jpeg_finish_decompress(dinfo);
+
+    return kSuccess;
+}
+
+/*
+ * Enable scanline decoding for jpegs
+ */
+class SkJpegScanlineDecoder : public SkScanlineDecoder {
+public:
+    SkJpegScanlineDecoder(const SkImageInfo& dstInfo, SkJpegCodec* codec)
+        : INHERITED(dstInfo)
+        , fCodec(codec)
+    {
+        fStorage.reset(fCodec->fSrcRowBytes);
+        fSrcRow = static_cast<uint8_t*>(fStorage.get());
+    }
+
+    SkImageGenerator::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
+        // Set the jump location for libjpeg errors
+        if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
+            return fCodec->fDecoderMgr->returnFailure("setjmp", SkImageGenerator::kInvalidInput);
+        }
+
+        // Read rows one at a time
+        for (int y = 0; y < count; y++) {
+            // Read row of the image
+            uint32_t rowsDecoded = jpeg_read_scanlines(fCodec->fDecoderMgr->dinfo(), &fSrcRow, 1);
+            if (rowsDecoded != 1) {
+                SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count - y, SK_ColorBLACK, NULL);
+                return SkImageGenerator::kIncompleteInput;
+            }
+
+            // Convert to RGB if necessary
+            if (JCS_CMYK == fCodec->fDecoderMgr->dinfo()->out_color_space) {
+                convert_CMYK_to_RGB(fSrcRow, dstInfo().width());
+            }
+
+            // Swizzle to output destination
+            fCodec->fSwizzler->setDstRow(dst);
+            fCodec->fSwizzler->next(fSrcRow);
+            dst = SkTAddOffset<void>(dst, rowBytes);
+        }
+
+        return SkImageGenerator::kSuccess;
+    }
+
+    SkImageGenerator::Result onSkipScanlines(int count) override {
+        // Set the jump location for libjpeg errors
+        if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
+            return fCodec->fDecoderMgr->returnFailure("setjmp", SkImageGenerator::kInvalidInput);
+        }
+
+        // Read rows but ignore the output
+        for (int y = 0; y < count; y++) {
+            jpeg_read_scanlines(fCodec->fDecoderMgr->dinfo(), &fSrcRow, 1);
+        }
+
+        return SkImageGenerator::kSuccess;
+    }
+
+    void onFinish() override {
+        if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
+            SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n");
+            return;
+        }
+
+        jpeg_finish_decompress(fCodec->fDecoderMgr->dinfo());
+    }
+
+private:
+    SkJpegCodec*        fCodec;     // unowned
+    SkAutoMalloc        fStorage;
+    uint8_t*            fSrcRow;    // ptr into fStorage
+
+    typedef SkScanlineDecoder INHERITED;
+};
+
+SkScanlineDecoder* SkJpegCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo,
+        const Options& options, SkPMColor ctable[], int* ctableCount) {
+
+    // Rewind the stream if needed
+    if (!this->handleRewind()) {
+        SkCodecPrintf("Could not rewind\n");
+        return NULL;
+    }
+
+    // Set the jump location for libjpeg errors
+    if (setjmp(fDecoderMgr->getJmpBuf())) {
+        SkCodecPrintf("setjmp: Error from libjpeg\n");
+        return NULL;
+    }
+
+    // Check if we can decode to the requested destination
+    if (!conversion_possible(dstInfo, this->getInfo())) {
+        SkCodecPrintf("Cannot convert to output type\n");
+        return NULL;
+    }
+
+    // Perform the necessary scaling
+    if (!this->scaleToDimensions(dstInfo.width(), dstInfo.height())) {
+        SkCodecPrintf("Cannot scale ot output dimensions\n");
+        return NULL;
+    }
+
+    // Now, given valid output dimensions, we can start the decompress
+    if (!jpeg_start_decompress(fDecoderMgr->dinfo())) {
+        SkCodecPrintf("start decompress failed\n");
+        return NULL;
+    }
+
+    // Create the swizzler
+    this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options);
+    if (NULL == fSwizzler) {
+        SkCodecPrintf("Could not create swizzler\n");
+        return NULL;
+    }
+
+    // Return the new scanline decoder
+    return SkNEW_ARGS(SkJpegScanlineDecoder, (dstInfo, this));
+}
diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h
new file mode 100644
index 0000000..3447a10
--- /dev/null
+++ b/src/codec/SkJpegCodec.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkJpegCodec_DEFINED
+#define SkJpegCodec_DEFINED
+
+#include "SkCodec.h"
+#include "SkImageInfo.h"
+#include "SkJpegDecoderMgr.h"
+#include "SkJpegUtility.h"
+#include "SkStream.h"
+
+extern "C" {
+    #include "jpeglib.h"
+}
+
+/*
+ *
+ * This class implements the decoding for jpeg images
+ *
+ */
+class SkJpegCodec : public SkCodec {
+public:
+
+    /*
+     * Checks the start of the stream to see if the image is a jpeg
+     * Does not take ownership of the stream
+     */
+    static bool IsJpeg(SkStream*);
+
+    /*
+     * Assumes IsJpeg was called and returned true
+     * Creates a jpeg decoder
+     * Takes ownership of the stream
+     */
+    static SkCodec* NewFromStream(SkStream*);
+
+protected:
+
+    /*
+     * Recommend a set of destination dimensions given a requested scale
+     */
+    SkISize onGetScaledDimensions(float desiredScale) const override;
+
+    /*
+     * Initiates the jpeg decode
+     */
+    Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&,
+            SkPMColor*, int*) override;
+
+    SkEncodedFormat onGetEncodedFormat() const override {
+        return kJPEG_SkEncodedFormat;
+    }
+
+    SkScanlineDecoder* onGetScanlineDecoder(const SkImageInfo& dstInfo, const Options& options,
+            SkPMColor ctable[], int* ctableCount) override;
+
+private:
+
+    /*
+     * Read enough of the stream to initialize the SkJpegCodec.
+     * Returns a bool representing success or failure.
+     *
+     * @param codecOut
+     * If this returns true, and codecOut was not NULL,
+     * codecOut will be set to a new SkJpegCodec.
+     *
+     * @param decoderMgrOut
+     * If this returns true, and codecOut was NULL,
+     * decoderMgrOut must be non-NULL and decoderMgrOut will be set to a new
+     * JpegDecoderMgr pointer.
+     *
+     * @param stream
+     * Deleted on failure.
+     * codecOut will take ownership of it in the case where we created a codec.
+     * Ownership is unchanged when we set decoderMgrOut.
+     *
+     */
+    static bool ReadHeader(SkStream* stream, SkCodec** codecOut,
+            JpegDecoderMgr** decoderMgrOut);
+
+    /*
+     * Creates an instance of the decoder
+     * Called only by NewFromStream
+     *
+     * @param srcInfo contains the source width and height
+     * @param stream the encoded image data
+     * @param decoderMgr holds decompress struct, src manager, and error manager
+     *                   takes ownership
+     */
+    SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, JpegDecoderMgr* decoderMgr);
+
+    /*
+     * Handles rewinding the input stream if it is necessary
+     */
+    bool handleRewind();
+
+    /*
+     * Checks if we can scale to the requested dimensions and scales the dimensions
+     * if possible
+     */
+    bool scaleToDimensions(uint32_t width, uint32_t height);
+
+    /*
+     * Create the swizzler based on the encoded format
+     */
+    void initializeSwizzler(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
+            const Options& options);
+
+    SkAutoTDelete<JpegDecoderMgr> fDecoderMgr;
+    SkAutoTDelete<SkSwizzler>     fSwizzler;
+    size_t                        fSrcRowBytes;
+
+    friend class SkJpegScanlineDecoder;
+
+    typedef SkCodec INHERITED;
+};
+
+#endif
diff --git a/src/codec/SkJpegDecoderMgr.cpp b/src/codec/SkJpegDecoderMgr.cpp
new file mode 100644
index 0000000..c1f044c
--- /dev/null
+++ b/src/codec/SkJpegDecoderMgr.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkJpegDecoderMgr.h"
+#include "SkJpegUtility.h"
+
+/*
+ * Print information, warning, and error messages
+ */
+static void print_message(const j_common_ptr info, const char caller[]) {
+    char buffer[JMSG_LENGTH_MAX];
+    info->err->format_message(info, buffer);
+    SkCodecPrintf("libjpeg error %d <%s> from %s\n", info->err->msg_code, buffer, caller);
+}
+
+/*
+ * Reporting functions for libjpeg
+ */
+static void emit_message(j_common_ptr info, int) {
+    print_message(info, "emit_message");
+}
+static void output_message(j_common_ptr info) {
+    print_message(info, "output_message");
+}
+
+/*
+ * Choose the size of the memory buffer on Android
+ */
+static void overwrite_mem_buffer_size(jpeg_decompress_struct* dinfo) {
+#ifdef SK_BUILD_FOR_ANDROID
+
+// Use 30 MB for devices with a large amount of system memory and 5MB otherwise
+// TODO: (msarett) This matches SkImageDecoder.  Why were these values chosen?
+#ifdef ANDROID_LARGE_MEMORY_DEVICE
+    dinfo->mem->max_memory_to_use = 30 * 1024 * 1024;
+#else
+    dinfo->mem->max_memory_to_use = 5 * 1024 * 1024;
+#endif
+
+#endif // SK_BUILD_FOR_ANDROID
+}
+
+bool JpegDecoderMgr::returnFalse(const char caller[]) {
+    print_message((j_common_ptr) &fDInfo, caller);
+    return false;
+}
+
+SkCodec::Result JpegDecoderMgr::returnFailure(const char caller[], SkCodec::Result result) {
+    print_message((j_common_ptr) &fDInfo, caller);
+    return result;
+}
+
+SkColorType JpegDecoderMgr::getColorType() {
+    switch (fDInfo.jpeg_color_space) {
+        case JCS_CMYK:
+        case JCS_YCCK:
+            // libjpeg cannot convert from CMYK or YCCK to RGB.
+            // Here, we ask libjpeg to give us CMYK samples back and
+            // we will later manually convert them to RGB.
+            fDInfo.out_color_space = JCS_CMYK;
+            return kN32_SkColorType;
+        case JCS_GRAYSCALE:
+            fDInfo.out_color_space = JCS_GRAYSCALE;
+            return kGray_8_SkColorType;
+        default:
+#ifdef ANDROID_RGB
+            fDInfo.out_color_space = JCS_RGBA_8888;
+#else
+            fDInfo.out_color_space = JCS_RGB;
+#endif
+            return kN32_SkColorType;
+    }
+}
+
+JpegDecoderMgr::JpegDecoderMgr(SkStream* stream)
+    : fSrcMgr(stream)
+    , fInit(false)
+{
+    // Error manager must be set before any calls to libjeg in order to handle failures
+    fDInfo.err = jpeg_std_error(&fErrorMgr);
+    fErrorMgr.error_exit = skjpeg_err_exit;
+}
+
+void JpegDecoderMgr::init() {
+    jpeg_create_decompress(&fDInfo);
+    fInit = true;
+    fDInfo.src = &fSrcMgr;
+    overwrite_mem_buffer_size(&fDInfo);
+    fDInfo.err->emit_message = &emit_message;
+    fDInfo.err->output_message = &output_message;
+}
+
+JpegDecoderMgr::~JpegDecoderMgr() {
+    if (fInit) {
+        jpeg_destroy_decompress(&fDInfo);
+    }
+}
+
+jmp_buf& JpegDecoderMgr::getJmpBuf() {
+    return fErrorMgr.fJmpBuf;
+}
+
+jpeg_decompress_struct* JpegDecoderMgr::dinfo() {
+    return &fDInfo;
+}
diff --git a/src/codec/SkJpegDecoderMgr.h b/src/codec/SkJpegDecoderMgr.h
new file mode 100644
index 0000000..444e693
--- /dev/null
+++ b/src/codec/SkJpegDecoderMgr.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkJpegDecoderMgr_DEFINED
+#define SkJpegDecoderMgr_DEFINED
+
+#include "SkCodec.h"
+#include "SkCodecPriv.h"
+#include "SkJpegUtility.h"
+#include "SkSwizzler.h"
+#include "SkTemplates.h"
+
+// stdio is needed for jpeglib
+#include <stdio.h>
+
+extern "C" {
+    #include "jpeglib.h"
+}
+
+class JpegDecoderMgr : SkNoncopyable {
+public:
+
+    /*
+     * Print a useful error message and return false
+     */
+    bool returnFalse(const char caller[]);
+
+    /*
+     * Print a useful error message and return a decode failure
+     */
+    SkCodec::Result returnFailure(const char caller[], SkCodec::Result result);
+
+    /*
+     * Create the decode manager
+     * Does not take ownership of stream
+     */
+    JpegDecoderMgr(SkStream* stream);
+
+    /*
+     * Initialize decompress struct
+     * Initialize the source manager
+     */
+    void  init();
+
+    /*
+     * Recommend a color type based on the encoded format
+     */
+    SkColorType getColorType();
+
+    /*
+     * Free memory used by the decode manager
+     */
+    ~JpegDecoderMgr();
+
+    /*
+     * Get the jump buffer in order to set an error return point
+     */
+    jmp_buf& getJmpBuf();
+
+    /*
+     * Get function for the decompress info struct
+     */
+    jpeg_decompress_struct* dinfo();
+
+private:
+
+    jpeg_decompress_struct fDInfo;
+    skjpeg_source_mgr      fSrcMgr;
+    skjpeg_error_mgr       fErrorMgr;
+    bool                   fInit;
+};
+
+#endif
diff --git a/src/codec/SkJpegUtility.cpp b/src/codec/SkJpegUtility.cpp
new file mode 100644
index 0000000..5dccf94
--- /dev/null
+++ b/src/codec/SkJpegUtility.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodecPriv.h"
+#include "SkJpegUtility.h"
+
+/*
+ * Initialize the source manager
+ */
+static void sk_init_source(j_decompress_ptr dinfo) {
+    skjpeg_source_mgr* src = (skjpeg_source_mgr*) dinfo->src;
+    src->next_input_byte = (const JOCTET*) src->fBuffer;
+    src->bytes_in_buffer = 0;
+}
+
+/*
+ * Fill the input buffer from the stream
+ */
+static boolean sk_fill_input_buffer(j_decompress_ptr dinfo) {
+    skjpeg_source_mgr* src = (skjpeg_source_mgr*) dinfo->src;
+    size_t bytes = src->fStream->read(src->fBuffer, skjpeg_source_mgr::kBufferSize);
+    
+    // libjpeg is still happy with a less than full read, as long as the result is non-zero
+    if (bytes == 0) {
+        return false;
+    }
+
+    src->next_input_byte = (const JOCTET*) src->fBuffer;
+    src->bytes_in_buffer = bytes;
+    return true;
+}
+
+/*
+ * Skip a certain number of bytes in the stream
+ */
+static void sk_skip_input_data(j_decompress_ptr dinfo, long numBytes) {
+    skjpeg_source_mgr* src = (skjpeg_source_mgr*) dinfo->src;
+    size_t bytes = (size_t) numBytes;
+
+    if (bytes > src->bytes_in_buffer) {
+        size_t bytesToSkip = bytes - src->bytes_in_buffer;
+        if (bytesToSkip != src->fStream->skip(bytesToSkip)) {
+            SkCodecPrintf("Failure to skip.\n");
+            dinfo->err->error_exit((j_common_ptr) dinfo);
+            return;
+        }
+
+        src->next_input_byte = (const JOCTET*) src->fBuffer;
+        src->bytes_in_buffer = 0;
+    } else {
+        src->next_input_byte += numBytes;
+        src->bytes_in_buffer -= numBytes;
+    }
+}
+
+/*
+ * We do not need to do anything to terminate our stream
+ */
+static void sk_term_source(j_decompress_ptr dinfo)
+{}
+
+/*
+ * Constructor for the source manager that we provide to libjpeg
+ * We provide skia implementations of all of the stream processing functions required by libjpeg
+ */
+skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream)
+    : fStream(stream)
+{
+    init_source = sk_init_source;
+    fill_input_buffer = sk_fill_input_buffer;
+    skip_input_data = sk_skip_input_data;
+    resync_to_restart = jpeg_resync_to_restart;
+    term_source = sk_term_source;
+}
+
+/*
+ * Call longjmp to continue execution on an error
+ */
+void skjpeg_err_exit(j_common_ptr dinfo) {
+    // Simply return to Skia client code
+    // JpegDecoderMgr will take care of freeing memory
+    skjpeg_error_mgr* error = (skjpeg_error_mgr*) dinfo->err;
+    (*error->output_message) (dinfo);
+    longjmp(error->fJmpBuf, 1);
+}
diff --git a/src/codec/SkJpegUtility.h b/src/codec/SkJpegUtility.h
new file mode 100644
index 0000000..42cd7af
--- /dev/null
+++ b/src/codec/SkJpegUtility.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkJpegUtility_DEFINED
+#define SkJpegUtility_DEFINED
+
+#include "SkStream.h"
+
+#include <setjmp.h>
+// stdio is needed for jpeglib
+#include <stdio.h>
+
+extern "C" {
+    #include "jpeglib.h"
+    #include "jerror.h"
+}
+
+/*
+ * Error handling struct
+ */
+struct skjpeg_error_mgr : jpeg_error_mgr {
+    jmp_buf fJmpBuf;
+};
+
+/*
+ * Error handling function
+ */
+void skjpeg_err_exit(j_common_ptr cinfo);
+
+/*
+ * Source handling struct for that allows libjpeg to use our stream object
+ */
+struct skjpeg_source_mgr : jpeg_source_mgr {
+    skjpeg_source_mgr(SkStream* stream);
+
+    SkStream* fStream; // unowned
+    enum {
+        // TODO (msarett): Experiment with different buffer sizes.
+        // This size was chosen because it matches SkImageDecoder.
+        kBufferSize = 1024
+    };
+    uint8_t fBuffer[kBufferSize];
+};
+
+#endif
diff --git a/src/codec/SkMaskSwizzler.cpp b/src/codec/SkMaskSwizzler.cpp
new file mode 100644
index 0000000..58ae11d
--- /dev/null
+++ b/src/codec/SkMaskSwizzler.cpp
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodecPriv.h"
+#include "SkColorPriv.h"
+#include "SkMaskSwizzler.h"
+
+static SkSwizzler::ResultAlpha swizzle_mask16_to_n32_opaque(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    uint16_t* srcPtr = (uint16_t*) srcRow;
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    for (int i = 0; i < width; i++) {
+        uint16_t p = srcPtr[i];
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        dstPtr[i] = SkPackARGB32NoCheck(0xFF, red, green, blue);
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+static SkSwizzler::ResultAlpha swizzle_mask16_to_n32_unpremul(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    uint16_t* srcPtr = (uint16_t*) srcRow;
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    INIT_RESULT_ALPHA;
+    for (int i = 0; i < width; i++) {
+        uint16_t p = srcPtr[i];
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        uint8_t alpha = masks->getAlpha(p);
+        UPDATE_RESULT_ALPHA(alpha);
+        dstPtr[i] = SkPackARGB32NoCheck(alpha, red, green, blue);
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+static SkSwizzler::ResultAlpha swizzle_mask16_to_n32_premul(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    uint16_t* srcPtr = (uint16_t*) srcRow;
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    INIT_RESULT_ALPHA;
+    for (int i = 0; i < width; i++) {
+        uint16_t p = srcPtr[i];
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        uint8_t alpha = masks->getAlpha(p);
+        UPDATE_RESULT_ALPHA(alpha);
+        dstPtr[i] = SkPreMultiplyARGB(alpha, red, green, blue);
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+static SkSwizzler::ResultAlpha swizzle_mask24_to_n32_opaque(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    for (int i = 0; i < 3*width; i += 3) {
+        uint32_t p = srcRow[i] | (srcRow[i + 1] << 8) | srcRow[i + 2] << 16;
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        dstPtr[i/3] = SkPackARGB32NoCheck(0xFF, red, green, blue);
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+static SkSwizzler::ResultAlpha swizzle_mask24_to_n32_unpremul(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    INIT_RESULT_ALPHA;
+    for (int i = 0; i < 3*width; i += 3) {
+        uint32_t p = srcRow[i] | (srcRow[i + 1] << 8) | srcRow[i + 2] << 16;
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        uint8_t alpha = masks->getAlpha(p);
+        UPDATE_RESULT_ALPHA(alpha);
+        dstPtr[i/3] = SkPackARGB32NoCheck(alpha, red, green, blue);
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+static SkSwizzler::ResultAlpha swizzle_mask24_to_n32_premul(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    INIT_RESULT_ALPHA;
+    for (int i = 0; i < 3*width; i += 3) {
+        uint32_t p = srcRow[i] | (srcRow[i + 1] << 8) | srcRow[i + 2] << 16;
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        uint8_t alpha = masks->getAlpha(p);
+        UPDATE_RESULT_ALPHA(alpha);
+        dstPtr[i/3] = SkPreMultiplyARGB(alpha, red, green, blue);
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+static SkSwizzler::ResultAlpha swizzle_mask32_to_n32_opaque(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    uint32_t* srcPtr = (uint32_t*) srcRow;
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    for (int i = 0; i < width; i++) {
+        uint32_t p = srcPtr[i];
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        dstPtr[i] = SkPackARGB32NoCheck(0xFF, red, green, blue);
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+static SkSwizzler::ResultAlpha swizzle_mask32_to_n32_unpremul(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    uint32_t* srcPtr = (uint32_t*) srcRow;
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    INIT_RESULT_ALPHA;
+    for (int i = 0; i < width; i++) {
+        uint32_t p = srcPtr[i];
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        uint8_t alpha = masks->getAlpha(p);
+        UPDATE_RESULT_ALPHA(alpha);
+        dstPtr[i] = SkPackARGB32NoCheck(alpha, red, green, blue);
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+static SkSwizzler::ResultAlpha swizzle_mask32_to_n32_premul(
+        void* dstRow, const uint8_t* srcRow, int width, SkMasks* masks) {
+
+    // Use the masks to decode to the destination
+    uint32_t* srcPtr = (uint32_t*) srcRow;
+    SkPMColor* dstPtr = (SkPMColor*) dstRow;
+    INIT_RESULT_ALPHA;
+    for (int i = 0; i < width; i++) {
+        uint32_t p = srcPtr[i];
+        uint8_t red = masks->getRed(p);
+        uint8_t green = masks->getGreen(p);
+        uint8_t blue = masks->getBlue(p);
+        uint8_t alpha = masks->getAlpha(p);
+        UPDATE_RESULT_ALPHA(alpha);
+        dstPtr[i] = SkPreMultiplyARGB(alpha, red, green, blue);
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+/*
+ *
+ * Create a new mask swizzler
+ *
+ */
+SkMaskSwizzler* SkMaskSwizzler::CreateMaskSwizzler(
+        const SkImageInfo& info, void* dst, size_t dstRowBytes, SkMasks* masks,
+        uint32_t bitsPerPixel) {
+
+    // Choose the appropriate row procedure
+    RowProc proc = NULL;
+    switch (bitsPerPixel) {
+        case 16:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    switch (info.alphaType()) {
+                        case kUnpremul_SkAlphaType:
+                            proc = &swizzle_mask16_to_n32_unpremul;
+                            break;
+                        case kPremul_SkAlphaType:
+                            proc = &swizzle_mask16_to_n32_premul;
+                            break;
+                        case kOpaque_SkAlphaType:
+                            proc = &swizzle_mask16_to_n32_opaque;
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case 24:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    switch (info.alphaType()) {
+                        case kUnpremul_SkAlphaType:
+                            proc = &swizzle_mask24_to_n32_unpremul;
+                            break;
+                        case kPremul_SkAlphaType:
+                            proc = &swizzle_mask24_to_n32_premul;
+                            break;
+                        case kOpaque_SkAlphaType:
+                            proc = &swizzle_mask24_to_n32_opaque;
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case 32:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    switch (info.alphaType()) {
+                        case kUnpremul_SkAlphaType:
+                            proc = &swizzle_mask32_to_n32_unpremul;
+                            break;
+                        case kPremul_SkAlphaType:
+                            proc = &swizzle_mask32_to_n32_premul;
+                            break;
+                        case kOpaque_SkAlphaType:
+                            proc = &swizzle_mask32_to_n32_opaque;
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
+                default:
+                    break;
+            }
+            break;
+        default:
+            SkASSERT(false);
+            return NULL;
+    }
+    return SkNEW_ARGS(SkMaskSwizzler, (info, dst, dstRowBytes, masks, proc));
+}
+
+/*
+ *
+ * Constructor for mask swizzler
+ *
+ */
+SkMaskSwizzler::SkMaskSwizzler(const SkImageInfo& dstInfo, void* dst,
+                               size_t dstRowBytes, SkMasks* masks, RowProc proc)
+    : fDstInfo(dstInfo)
+    , fDst(dst)
+    , fDstRowBytes(dstRowBytes)
+    , fMasks(masks)
+    , fRowProc(proc)
+{}
+
+/*
+ *
+ * Swizzle the specified row
+ *
+ */
+SkSwizzler::ResultAlpha SkMaskSwizzler::next(const uint8_t* SK_RESTRICT src,
+        int y) {
+    // Choose the row
+    void* row = SkTAddOffset<void>(fDst, y*fDstRowBytes);
+
+    // Decode the row
+    return fRowProc(row, src, fDstInfo.width(), fMasks);
+}
diff --git a/src/codec/SkMaskSwizzler.h b/src/codec/SkMaskSwizzler.h
new file mode 100644
index 0000000..6910302
--- /dev/null
+++ b/src/codec/SkMaskSwizzler.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkMasks.h"
+#include "SkSwizzler.h"
+#include "SkTypes.h"
+
+/*
+ *
+ * Used to swizzle images whose pixel components are extracted by bit masks
+ * Currently only used by bmp
+ *
+ */
+class SkMaskSwizzler {
+public:
+
+    /*
+     *
+     * Create a new swizzler
+     * @param masks Unowned pointer to helper class
+     *
+     */
+    static SkMaskSwizzler* CreateMaskSwizzler(const SkImageInfo& imageInfo,
+                                              void* dst, size_t dstRowBytes,
+                                              SkMasks* masks,
+                                              uint32_t bitsPerPixel);
+
+    /*
+     *
+     * Swizzle the row with the specified y value
+     *
+     */
+    SkSwizzler::ResultAlpha next(const uint8_t* SK_RESTRICT src, int y);
+
+private:
+
+    /*
+     *
+     * Row procedure used for swizzle
+     *
+     */
+    typedef SkSwizzler::ResultAlpha (*RowProc)(
+            void* dstRow, const uint8_t* srcRow, int width,
+            SkMasks* masks);
+
+    /*
+     *
+     * Constructor for mask swizzler
+     *
+     */
+    SkMaskSwizzler(const SkImageInfo& info, void* dst, size_t dstRowBytes,
+            SkMasks* masks, RowProc proc);
+
+    // Fields
+    const SkImageInfo& fDstInfo;
+    void*              fDst;
+    size_t             fDstRowBytes;
+    SkMasks*           fMasks;       // unowned
+    const RowProc      fRowProc;
+};
diff --git a/src/codec/SkMasks.cpp b/src/codec/SkMasks.cpp
new file mode 100644
index 0000000..4b32c61
--- /dev/null
+++ b/src/codec/SkMasks.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCodecPriv.h"
+#include "SkMasks.h"
+#include "SkTypes.h"
+
+/*
+ *
+ * Used to convert 1-7 bit color components into 8-bit color components
+ *
+ */
+const static uint8_t n_bit_to_8_bit_lookup_table[] = {
+    // 1 bit
+    0, 255,
+    // 2 bits
+    0, 85, 170, 255,
+    // 3 bits
+    0, 36, 73, 109, 146, 182, 219, 255,
+    // 4 bits
+    0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255,
+    // 5 bits
+    0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140,
+    148, 156, 165, 173, 181, 189, 197, 206, 214, 222, 230, 239, 247, 255,
+    // 6 bits
+    0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 45, 49, 53, 57, 61, 65, 69, 73,
+    77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 130, 134, 138,
+    142, 146, 150, 154, 158, 162, 166, 170, 174, 178, 182, 186, 190, 194, 198,
+    202, 206, 210, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 255,
+    // 7 bits
+    0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38,
+    40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76,
+    78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110,
+    112, 114, 116, 118, 120, 122, 124, 126, 129, 131, 133, 135, 137, 139, 141,
+    143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171,
+    173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201,
+    203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231,
+    233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255
+};
+
+/*
+ *
+ * Convert an n bit component to an 8-bit component
+ *
+ */
+static uint8_t convert_to_8(uint32_t component, uint32_t n) {
+    if (0 == n) {
+        return 0;
+    } else if (8 > n) {
+        return n_bit_to_8_bit_lookup_table[(1 << n) - 2 + component];
+    } else {
+        SkASSERT(8 == n);
+        return component;
+    }
+}
+
+static uint8_t get_comp(uint32_t pixel, uint32_t mask, uint32_t shift,
+                        uint32_t size) {
+    return convert_to_8((pixel & mask) >> shift, size);
+}
+
+/*
+ *
+ * Get a color component
+ *
+ */
+uint8_t SkMasks::getRed(uint32_t pixel) {
+    return get_comp(pixel, fRed.mask, fRed.shift, fRed.size);
+}
+uint8_t SkMasks::getGreen(uint32_t pixel) {
+    return get_comp(pixel, fGreen.mask, fGreen.shift, fGreen.size);
+}
+uint8_t SkMasks::getBlue(uint32_t pixel) {
+    return get_comp(pixel, fBlue.mask, fBlue.shift, fBlue.size);
+}
+uint8_t SkMasks::getAlpha(uint32_t pixel) {
+    return get_comp(pixel, fAlpha.mask, fAlpha.shift, fAlpha.size);
+}
+
+/*
+ *
+ * Process an input mask to obtain the necessary information
+ *
+ */
+const SkMasks::MaskInfo process_mask(uint32_t mask, uint32_t bpp) {
+    // Trim the masks to the allowed number of bits
+    if (bpp < 32) {
+        mask &= (1 << bpp) - 1;
+    }
+
+    // Determine properties of the mask
+    uint32_t tempMask = mask;
+    uint32_t shift = 0;
+    uint32_t size = 0;
+    if (tempMask != 0) {
+        // Count trailing zeros on masks
+        for (; (tempMask & 1) == 0; tempMask >>= 1) {
+            shift++;
+        }
+        // Count the size of the mask
+        for (; tempMask & 1; tempMask >>= 1) {
+            size++;
+        }
+        // Check that the mask is continuous
+        if (tempMask != 0) {
+            SkCodecPrintf("Warning: Bit masks is not continuous.\n");
+        }
+        // Truncate masks greater than 8 bits
+        if (size > 8) {
+            shift += size - 8;
+            size = 8;
+        }
+    }
+
+    // Save the calculated values
+    const SkMasks::MaskInfo info = { mask, shift, size };
+    return info;
+}
+
+/*
+ *
+ * Create the masks object
+ *
+ */
+SkMasks* SkMasks::CreateMasks(InputMasks masks, uint32_t bitsPerPixel) {
+    // Trim the input masks according to bitsPerPixel
+    if (bitsPerPixel < 32) {
+        masks.red &= (1 << bitsPerPixel) - 1;
+        masks.green &= (1 << bitsPerPixel) - 1;
+        masks.blue &= (1 << bitsPerPixel) - 1;
+        masks.alpha &= (1 << bitsPerPixel) - 1;
+    }
+
+    // Check that masks do not overlap
+    if (((masks.red & masks.green) | (masks.red & masks.blue) |
+            (masks.red & masks.alpha) | (masks.green & masks.blue) |
+            (masks.green & masks.alpha) | (masks.blue & masks.alpha)) != 0) {
+        return NULL;
+    }
+
+    // Collect information about the masks
+    const MaskInfo red = process_mask(masks.red, bitsPerPixel);
+    const MaskInfo green = process_mask(masks.green, bitsPerPixel);
+    const MaskInfo blue = process_mask(masks.blue, bitsPerPixel);
+    const MaskInfo alpha = process_mask(masks.alpha, bitsPerPixel);
+
+    return SkNEW_ARGS(SkMasks, (red, green, blue, alpha));
+}
+
+
+SkMasks::SkMasks(const MaskInfo red, const MaskInfo green,
+                 const MaskInfo blue, const MaskInfo alpha)
+    : fRed(red)
+    , fGreen(green)
+    , fBlue(blue)
+    , fAlpha(alpha)
+{}
diff --git a/src/codec/SkMasks.h b/src/codec/SkMasks.h
new file mode 100644
index 0000000..31c6849
--- /dev/null
+++ b/src/codec/SkMasks.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkTypes.h"
+
+/*
+ *
+ * Contains useful mask routines for SkMaskSwizzler
+ *
+ */
+class SkMasks {
+public:
+
+    /*
+     *
+     * Input bit masks format
+     *
+     */
+    struct InputMasks {
+        uint32_t red;
+        uint32_t green;
+        uint32_t blue;
+        uint32_t alpha;
+    };
+
+    /*
+     *
+     * Contains all of the information for a single mask
+     *
+     */
+     struct MaskInfo {
+        uint32_t mask;
+        uint32_t shift;
+        uint32_t size;
+     };
+
+    /*
+     *
+     * Create the masks object
+     *
+     */
+    static SkMasks* CreateMasks(InputMasks masks, uint32_t bpp);
+
+    /*
+     *
+     * Get a color component
+     *
+     */
+    uint8_t getRed(uint32_t pixel);
+    uint8_t getGreen(uint32_t pixel);
+    uint8_t getBlue(uint32_t pixel);
+    uint8_t getAlpha(uint32_t pixel);
+
+    /*
+     *
+     * Getter for the alpha mask
+     * The alpha mask may be used in other decoding modes
+     *
+     */
+     uint32_t getAlphaMask() {
+        return fAlpha.mask;
+     }
+
+private:
+
+    /*
+     *
+     * Constrcutor
+     *
+     */
+    SkMasks(const MaskInfo red, const MaskInfo green, const MaskInfo blue,
+            const MaskInfo alpha);
+
+    const MaskInfo fRed;
+    const MaskInfo fGreen;
+    const MaskInfo fBlue;
+    const MaskInfo fAlpha;
+};
diff --git a/src/codec/SkSwizzler.cpp b/src/codec/SkSwizzler.cpp
index 563933f..294229c 100644
--- a/src/codec/SkSwizzler.cpp
+++ b/src/codec/SkSwizzler.cpp
@@ -5,105 +5,243 @@
  * found in the LICENSE file.
  */
 
+#include "SkCodecPriv.h"
 #include "SkColorPriv.h"
 #include "SkSwizzler.h"
 #include "SkTemplates.h"
+#include "SkUtils.h"
 
-// index
-
-#define A32_MASK_IN_PLACE   (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT)
-
-static bool swizzle_index_to_n32(void* SK_RESTRICT dstRow,
-                                 const uint8_t* SK_RESTRICT src,
-                                 int width, int deltaSrc, int, const SkPMColor ctable[]) {
-
-    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
-    SkPMColor cc = A32_MASK_IN_PLACE;
-    for (int x = 0; x < width; x++) {
-        SkPMColor c = ctable[*src];
-        cc &= c;
-        dst[x] = c;
-        src += deltaSrc;
-    }
-    return cc != A32_MASK_IN_PLACE;
+SkSwizzler::ResultAlpha SkSwizzler::GetResult(uint8_t zeroAlpha,
+                                              uint8_t maxAlpha) {
+    // In the transparent case, this returns 0x0000
+    // In the opaque case, this returns 0xFFFF
+    // If the row is neither transparent nor opaque, returns something else
+    return (((uint16_t) maxAlpha) << 8) | zeroAlpha;
 }
 
-static bool swizzle_index_to_n32_skipZ(void* SK_RESTRICT dstRow,
-                                      const uint8_t* SK_RESTRICT src,
-                                      int width, int deltaSrc, int,
-                                      const SkPMColor ctable[]) {
+// kIndex1, kIndex2, kIndex4
+
+static SkSwizzler::ResultAlpha swizzle_small_index_to_index(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bitsPerPixel, int y, const SkPMColor ctable[]) {
+
+    uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow;
+    INIT_RESULT_ALPHA;
+    const uint32_t pixelsPerByte = 8 / bitsPerPixel;
+    const size_t rowBytes = compute_row_bytes_ppb(width, pixelsPerByte);
+    const uint8_t mask = (1 << bitsPerPixel) - 1;
+    int x = 0;
+    for (uint32_t byte = 0; byte < rowBytes; byte++) {
+        uint8_t pixelData = src[byte];
+        for (uint32_t p = 0; p < pixelsPerByte && x < width; p++) {
+            uint8_t index = (pixelData >> (8 - bitsPerPixel)) & mask;
+            UPDATE_RESULT_ALPHA(ctable[index] >> SK_A32_SHIFT);
+            dst[x] = index;
+            pixelData <<= bitsPerPixel;
+            x++;
+        }
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+static SkSwizzler::ResultAlpha swizzle_small_index_to_n32(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bitsPerPixel, int y, const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*) dstRow;
+    INIT_RESULT_ALPHA;
+    const uint32_t pixelsPerByte = 8 / bitsPerPixel;
+    const size_t rowBytes = compute_row_bytes_ppb(width, pixelsPerByte);
+    const uint8_t mask = (1 << bitsPerPixel) - 1;
+    int x = 0;
+    for (uint32_t byte = 0; byte < rowBytes; byte++) {
+        uint8_t pixelData = src[byte];
+        for (uint32_t p = 0; p < pixelsPerByte && x < width; p++) {
+            uint8_t index = (pixelData >> (8 - bitsPerPixel)) & mask;
+            SkPMColor c = ctable[index];
+            UPDATE_RESULT_ALPHA(c >> SK_A32_SHIFT);
+            dst[x] = c;
+            pixelData <<= bitsPerPixel;
+            x++;
+        }
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+// kIndex
+
+static SkSwizzler::ResultAlpha swizzle_index_to_index(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
+    uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow;
+    memcpy(dst, src, width);
+    // TODO (msarett): Should we skip the loop here and guess that the row is opaque/not opaque?
+    //                 SkScaledBitmap sampler just guesses that it is opaque.  This is dangerous
+    //                 and probably wrong since gif and bmp (rarely) may have alpha.
+    INIT_RESULT_ALPHA;
+    for (int x = 0; x < width; x++) {
+        UPDATE_RESULT_ALPHA(ctable[src[x]] >> SK_A32_SHIFT);
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+static SkSwizzler::ResultAlpha swizzle_index_to_n32(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
 
     SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
-    SkPMColor cc = A32_MASK_IN_PLACE;
+    INIT_RESULT_ALPHA;
     for (int x = 0; x < width; x++) {
-        SkPMColor c = ctable[*src];
-        cc &= c;
+        SkPMColor c = ctable[src[x]];
+        UPDATE_RESULT_ALPHA(c >> SK_A32_SHIFT);
+        dst[x] = c;
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+static SkSwizzler::ResultAlpha swizzle_index_to_n32_skipZ(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    INIT_RESULT_ALPHA;
+    for (int x = 0; x < width; x++) {
+        SkPMColor c = ctable[src[x]];
+        UPDATE_RESULT_ALPHA(c >> SK_A32_SHIFT);
         if (c != 0) {
             dst[x] = c;
         }
-        src += deltaSrc;
     }
-    return cc != A32_MASK_IN_PLACE;
+    return COMPUTE_RESULT_ALPHA;
 }
 
 #undef A32_MASK_IN_PLACE
 
+// kGray
+
+static SkSwizzler::ResultAlpha swizzle_gray_to_n32(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPackARGB32NoCheck(0xFF, src[x], src[x], src[x]);
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+static SkSwizzler::ResultAlpha swizzle_gray_to_gray(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+    memcpy(dstRow, src, width);
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+// kBGRX
+
+static SkSwizzler::ResultAlpha swizzle_bgrx_to_n32(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    for (int x = 0; x < width; x++) {
+        dst[x] = SkPackARGB32NoCheck(0xFF, src[2], src[1], src[0]);
+        src += bytesPerPixel;
+    }
+    return SkSwizzler::kOpaque_ResultAlpha;
+}
+
+// kBGRA
+
+static SkSwizzler::ResultAlpha swizzle_bgra_to_n32_unpremul(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    INIT_RESULT_ALPHA;
+    for (int x = 0; x < width; x++) {
+        uint8_t alpha = src[3];
+        UPDATE_RESULT_ALPHA(alpha);
+        dst[x] = SkPackARGB32NoCheck(alpha, src[2], src[1], src[0]);
+        src += bytesPerPixel;
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
+static SkSwizzler::ResultAlpha swizzle_bgra_to_n32_premul(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
+    SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
+    INIT_RESULT_ALPHA;
+    for (int x = 0; x < width; x++) {
+        uint8_t alpha = src[3];
+        UPDATE_RESULT_ALPHA(alpha);
+        dst[x] = SkPreMultiplyARGB(alpha, src[2], src[1], src[0]);
+        src += bytesPerPixel;
+    }
+    return COMPUTE_RESULT_ALPHA;
+}
+
 // n32
-static bool swizzle_rgbx_to_n32(void* SK_RESTRICT dstRow,
-                                const uint8_t* SK_RESTRICT src,
-                                int width, int deltaSrc, int, const SkPMColor[]) {
+static SkSwizzler::ResultAlpha swizzle_rgbx_to_n32(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
     SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
     for (int x = 0; x < width; x++) {
         dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]);
-        src += deltaSrc;
+        src += bytesPerPixel;
     }
-    return false;
+    return SkSwizzler::kOpaque_ResultAlpha;
 }
 
-static bool swizzle_rgba_to_n32_premul(void* SK_RESTRICT dstRow,
-                                       const uint8_t* SK_RESTRICT src,
-                                       int width, int deltaSrc, int, const SkPMColor[]) {
+static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_premul(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
     SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
-    unsigned alphaMask = 0xFF;
+    INIT_RESULT_ALPHA;
     for (int x = 0; x < width; x++) {
         unsigned alpha = src[3];
+        UPDATE_RESULT_ALPHA(alpha);
         dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
-        src += deltaSrc;
-        alphaMask &= alpha;
+        src += bytesPerPixel;
     }
-    return alphaMask != 0xFF;
+    return COMPUTE_RESULT_ALPHA;
 }
 
-static bool swizzle_rgba_to_n32_unpremul(void* SK_RESTRICT dstRow,
-                                         const uint8_t* SK_RESTRICT src,
-                                         int width, int deltaSrc, int,
-                                         const SkPMColor[]) {
+static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_unpremul(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
     uint32_t* SK_RESTRICT dst = reinterpret_cast<uint32_t*>(dstRow);
-    unsigned alphaMask = 0xFF;
+    INIT_RESULT_ALPHA;
     for (int x = 0; x < width; x++) {
         unsigned alpha = src[3];
+        UPDATE_RESULT_ALPHA(alpha);
         dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]);
-        src += deltaSrc;
-        alphaMask &= alpha;
+        src += bytesPerPixel;
     }
-    return alphaMask != 0xFF;
+    return COMPUTE_RESULT_ALPHA;
 }
 
-static bool swizzle_rgba_to_n32_premul_skipZ(void* SK_RESTRICT dstRow,
-                                             const uint8_t* SK_RESTRICT src,
-                                             int width, int deltaSrc, int,
-                                             const SkPMColor[]) {
+static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_premul_skipZ(
+        void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
+        int bytesPerPixel, int y, const SkPMColor ctable[]) {
+
     SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
-    unsigned alphaMask = 0xFF;
+    INIT_RESULT_ALPHA;
     for (int x = 0; x < width; x++) {
         unsigned alpha = src[3];
+        UPDATE_RESULT_ALPHA(alpha);
         if (0 != alpha) {
             dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]);
         }
-        src += deltaSrc;
-        alphaMask &= alpha;
+        src += bytesPerPixel;
     }
-    return alphaMask != 0xFF;
+    return COMPUTE_RESULT_ALPHA;
 }
 
 /**
@@ -114,7 +252,7 @@
     decide whether to switch to unpremul default.
 static bool swizzle_rgba_to_n32_unpremul_skipZ(void* SK_RESTRICT dstRow,
                                                const uint8_t* SK_RESTRICT src,
-                                               int width, int deltaSrc, int,
+                                               int width, int bitsPerPixel,
                                                const SkPMColor[]) {
     SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow;
     unsigned alphaMask = 0xFF;
@@ -133,31 +271,91 @@
 }
 */
 
-SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, const SkPMColor* ctable,
+SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc,
+                                       const SkPMColor* ctable,
                                        const SkImageInfo& info, void* dst,
-                                       size_t dstRowBytes, bool skipZeroes) {
-    if (info.colorType() == kUnknown_SkColorType) {
+                                       size_t dstRowBytes,
+                                       SkImageGenerator::ZeroInitialized zeroInit) {
+    if (info.colorType() == kUnknown_SkColorType || kUnknown == sc) {
         return NULL;
     }
     if (info.minRowBytes() > dstRowBytes) {
         return  NULL;
     }
-    if (kIndex == sc && NULL == ctable) {
+    if ((kIndex == sc || kIndex4 == sc || kIndex2 == sc || kIndex1 == sc)
+            && NULL == ctable) {
         return NULL;
     }
     RowProc proc = NULL;
     switch (sc) {
+        case kIndex1:
+        case kIndex2:
+        case kIndex4:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    proc = &swizzle_small_index_to_n32;
+                    break;
+                case kIndex_8_SkColorType:
+                    proc = &swizzle_small_index_to_index;
+                    break;
+                default:
+                    break;
+            }
+            break;
         case kIndex:
             switch (info.colorType()) {
                 case kN32_SkColorType:
                     // We assume the color premultiplied ctable (or not) as desired.
-                    if (skipZeroes) {
+                    if (SkImageGenerator::kYes_ZeroInitialized == zeroInit) {
                         proc = &swizzle_index_to_n32_skipZ;
+                        break;
                     } else {
                         proc = &swizzle_index_to_n32;
+                        break;
                     }
                     break;
-                    
+                case kIndex_8_SkColorType:
+                    proc = &swizzle_index_to_index;
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case kGray:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    proc = &swizzle_gray_to_n32;
+                    break;
+                case kGray_8_SkColorType:
+                    proc = &swizzle_gray_to_gray;
+                default:
+                    break;
+            }
+            break;
+        case kBGR:
+        case kBGRX:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    proc = &swizzle_bgrx_to_n32;
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case kBGRA:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    switch (info.alphaType()) {
+                        case kUnpremul_SkAlphaType:
+                            proc = &swizzle_bgra_to_n32_unpremul;
+                            break;
+                        case kPremul_SkAlphaType:
+                            proc = &swizzle_bgra_to_n32_premul;
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
                 default:
                     break;
             }
@@ -176,10 +374,10 @@
             switch (info.colorType()) {
                 case kN32_SkColorType:
                     if (info.alphaType() == kUnpremul_SkAlphaType) {
-                        // Respect skipZeroes?
+                        // Respect zeroInit?
                         proc = &swizzle_rgba_to_n32_unpremul;
                     } else {
-                        if (skipZeroes) {
+                        if (SkImageGenerator::kYes_ZeroInitialized == zeroInit) {
                             proc = &swizzle_rgba_to_n32_premul_skipZ;
                         } else {
                             proc = &swizzle_rgba_to_n32_premul;
@@ -190,32 +388,127 @@
                     break;
             }
             break;
+        case kRGB:
+            switch (info.colorType()) {
+                case kN32_SkColorType:
+                    proc = &swizzle_rgbx_to_n32;
+                    break;
+                default:
+                    break;
+            }
+            break;
         default:
             break;
     }
     if (NULL == proc) {
         return NULL;
     }
-    return SkNEW_ARGS(SkSwizzler, (proc, ctable, BytesPerPixel(sc), info, dst, dstRowBytes));
+
+    // Store deltaSrc in bytes if it is an even multiple, otherwise use bits
+    int deltaSrc = SkIsAlign8(BitsPerPixel(sc)) ? BytesPerPixel(sc) :
+            BitsPerPixel(sc);
+    return SkNEW_ARGS(SkSwizzler, (proc, ctable, deltaSrc, info, dst,
+                                   dstRowBytes));
 }
 
-SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcBpp,
-                       const SkImageInfo& info, void* dst, size_t rowBytes)
+SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable,
+                       int deltaSrc, const SkImageInfo& info, void* dst,
+                       size_t rowBytes)
     : fRowProc(proc)
     , fColorTable(ctable)
-    , fSrcPixelSize(srcBpp)
+    , fDeltaSrc(deltaSrc)
     , fDstInfo(info)
     , fDstRow(dst)
     , fDstRowBytes(rowBytes)
     , fCurrY(0)
 {
+    SkDEBUGCODE(fNextMode = kUninitialized_NextMode);
 }
 
-bool SkSwizzler::next(const uint8_t* SK_RESTRICT src) {
-    SkASSERT(fCurrY < fDstInfo.height());
-    const bool hadAlpha = fRowProc(fDstRow, src, fDstInfo.width(), fSrcPixelSize,
-                                   fCurrY, fColorTable);
+SkSwizzler::ResultAlpha SkSwizzler::next(const uint8_t* SK_RESTRICT src) {
+    SkASSERT(0 <= fCurrY && fCurrY < fDstInfo.height());
+    SkASSERT(fDstRow != NULL);
+    SkASSERT(kDesignateRow_NextMode != fNextMode);
+    SkDEBUGCODE(fNextMode = kConsecutive_NextMode);
+
+    // Decode a row
+    const ResultAlpha result = fRowProc(fDstRow, src, fDstInfo.width(),
+            fDeltaSrc, fCurrY, fColorTable);
+
+    // Move to the next row and return the result
     fCurrY++;
     fDstRow = SkTAddOffset<void>(fDstRow, fDstRowBytes);
-    return hadAlpha;
+    return result;
+}
+
+SkSwizzler::ResultAlpha SkSwizzler::next(const uint8_t* SK_RESTRICT src,
+        int y) {
+    SkASSERT(0 <= y && y < fDstInfo.height());
+    SkASSERT(kConsecutive_NextMode != fNextMode);
+    SkDEBUGCODE(fNextMode = kDesignateRow_NextMode);
+    
+    // Choose the row
+    void* row = SkTAddOffset<void>(fDstRow, y*fDstRowBytes);
+
+    // Decode the row
+    return fRowProc(row, src, fDstInfo.width(), fDeltaSrc, fCurrY,
+            fColorTable);
+}
+
+void SkSwizzler::Fill(void* dstStartRow, const SkImageInfo& dstInfo, size_t dstRowBytes,
+        uint32_t numRows, uint32_t colorOrIndex, const SkPMColor* colorTable) {
+    SkASSERT(dstStartRow != NULL);
+    SkASSERT(numRows <= (uint32_t) dstInfo.height());
+
+    // Calculate bytes to fill.  We use getSafeSize since the last row may not be padded.
+    const size_t bytesToFill = dstInfo.makeWH(dstInfo.width(), numRows).getSafeSize(dstRowBytes);
+
+    // Use the proper memset routine to fill the remaining bytes
+    switch(dstInfo.colorType()) {
+        case kN32_SkColorType:
+            // Assume input is an index if we have a color table
+            uint32_t color;
+            if (NULL != colorTable) {
+                SkASSERT(colorOrIndex == (uint8_t) colorOrIndex);
+                color = colorTable[colorOrIndex];
+            // Otherwise, assume the input is a color
+            } else {
+                color = colorOrIndex;
+            }
+
+            // We must fill row by row in the case of unaligned row bytes
+            if (SkIsAlign4((size_t) dstStartRow) && SkIsAlign4(dstRowBytes)) {
+                sk_memset32((uint32_t*) dstStartRow, color,
+                        (uint32_t) bytesToFill / sizeof(SkPMColor));
+            } else {
+                // This is an unlikely, slow case
+                SkCodecPrintf("Warning: Strange number of row bytes, fill will be slow.\n");
+                uint32_t* dstRow = (uint32_t*) dstStartRow;
+                for (uint32_t row = 0; row < numRows; row++) {
+                    for (int32_t col = 0; col < dstInfo.width(); col++) {
+                        dstRow[col] = color;
+                    }
+                    dstRow = SkTAddOffset<uint32_t>(dstRow, dstRowBytes);
+                }
+            }
+            break;
+        // On an index destination color type, always assume the input is an index
+        case kIndex_8_SkColorType:
+            SkASSERT(colorOrIndex == (uint8_t) colorOrIndex);
+            memset(dstStartRow, colorOrIndex, bytesToFill);
+            break;
+        case kGray_8_SkColorType:
+            // If the destination is kGray, the caller passes in an 8-bit color.
+            // We will not assert that the high bits of colorOrIndex must be zeroed.
+            // This allows us to take advantage of the fact that the low 8 bits of an
+            // SKPMColor may be a valid a grayscale color.  For example, the low 8
+            // bits of SK_ColorBLACK are identical to the grayscale representation
+            // for black. 
+            memset(dstStartRow, (uint8_t) colorOrIndex, bytesToFill);
+            break;
+        default:
+            SkCodecPrintf("Error: Unsupported dst color type for fill().  Doing nothing.\n");
+            SkASSERT(false);
+            break;
+    }
 }
diff --git a/src/codec/SkSwizzler.h b/src/codec/SkSwizzler.h
index 0bf2ee3..e3a38cb 100644
--- a/src/codec/SkSwizzler.h
+++ b/src/codec/SkSwizzler.h
@@ -8,7 +8,7 @@
 #ifndef SkSwizzler_DEFINED
 #define SkSwizzler_DEFINED
 
-#include "SkTypes.h"
+#include "SkCodec.h"
 #include "SkColor.h"
 #include "SkImageInfo.h"
 
@@ -18,76 +18,235 @@
      *  Enum describing the config of the source data.
      */
     enum SrcConfig {
-        kGray,  // 1 byte per pixel
-        kIndex, // 1 byte per pixel
-        kRGB,   // 3 bytes per pixel
-        kRGBX,  // 4 byes per pixel (ignore 4th)
-        kRGBA,  // 4 bytes per pixel
-        kRGB_565 // 2 bytes per pixel
+        kUnknown,  // Invalid type.
+        kGray,
+        kIndex1,
+        kIndex2,
+        kIndex4,
+        kIndex,
+        kRGB,
+        kBGR,
+        kRGBX,
+        kBGRX,
+        kRGBA,
+        kBGRA,
+        kRGB_565,
     };
 
-    static int BytesPerPixel(SrcConfig sc) {
+    /*
+     *
+     * Result code for the alpha components of a row.
+     *
+     */
+    typedef uint16_t ResultAlpha;
+    static const ResultAlpha kOpaque_ResultAlpha = 0xFFFF;
+    static const ResultAlpha kTransparent_ResultAlpha = 0x0000;
+
+    /*
+     *
+     * Checks if the result of decoding a row indicates that the row was
+     * transparent.
+     *
+     */
+    static bool IsTransparent(ResultAlpha r) {
+        return kTransparent_ResultAlpha == r;
+    }
+
+    /*
+     *
+     * Checks if the result of decoding a row indicates that the row was
+     * opaque.
+     *
+     */
+    static bool IsOpaque(ResultAlpha r) {
+        return kOpaque_ResultAlpha == r;
+    }
+
+    /*
+     *
+     * Constructs the proper result code based on accumulated alpha masks
+     *
+     */
+    static ResultAlpha GetResult(uint8_t zeroAlpha, uint8_t maxAlpha);
+
+    /*
+     *
+     * Returns bits per pixel for source config
+     *
+     */
+    static int BitsPerPixel(SrcConfig sc) {
         switch (sc) {
+            case kIndex1:
+                return 1;
+            case kIndex2:
+                return 2;
+            case kIndex4:
+                return 4;
             case kGray:
             case kIndex:
-                return 1;
+                return 8;
+            case kRGB_565:
+                return 16;
             case kRGB:
-                return 3;
+            case kBGR:
+                return 24;
             case kRGBX:
             case kRGBA:
-                return 4;
-            case kRGB_565:
-                return 2;
+            case kBGRX:
+            case kBGRA:
+                return 32;
             default:
-                SkDebugf("invalid source config passed to BytesPerPixel\n");
-                return -1;
+                SkASSERT(false);
+                return 0;
         }
     }
 
+    /*
+     *
+     * Returns bytes per pixel for source config
+     * Raises an error if each pixel is not stored in an even number of bytes
+     *
+     */
+    static int BytesPerPixel(SrcConfig sc) {
+        SkASSERT(SkIsAlign8(BitsPerPixel(sc)));
+        return BitsPerPixel(sc) >> 3;
+    }
+
     /**
      *  Create a new SkSwizzler.
-     *  @param sc SrcConfig
-     *  @param info dimensions() describe both the src and the dst.
+     *  @param SrcConfig Description of the format of the source.
+     *  @param SkImageInfo dimensions() describe both the src and the dst.
      *              Other fields describe the dst.
      *  @param dst Destination to write pixels. Must match info and dstRowBytes
      *  @param dstRowBytes rowBytes for dst.
-     *  @param skipZeroes Whether to skip writing zeroes. Useful if dst is
-     *              zero-initialized. The implementation may or may not respect this.
+     *  @param ZeroInitialized Whether dst is zero-initialized. The
+                               implementation may choose to skip writing zeroes
+     *                         if set to kYes_ZeroInitialized.
      *  @return A new SkSwizzler or NULL on failure.
      */
-    static SkSwizzler* CreateSwizzler(SrcConfig sc, const SkPMColor* ctable,
-                                      const SkImageInfo& info, void* dst,
-                                      size_t dstRowBytes, bool skipZeroes);
+    static SkSwizzler* CreateSwizzler(SrcConfig, const SkPMColor* ctable,
+                                      const SkImageInfo&, void* dst,
+                                      size_t dstRowBytes,
+                                      SkImageGenerator::ZeroInitialized);
+
+    /**
+     * Fill the remainder of the destination with a single color
+     *
+     * @param dstStartRow
+     * The destination row to fill from.
+     *
+     * @param numRows
+     * The number of rows to fill.
+     *
+     * @param colorOrIndex
+     * @param colorTable
+     * If dstInfo.colorType() is kIndex8, colorOrIndex is assumed to be a uint8_t
+     * index, and colorTable is ignored. Each 8-bit pixel will be set to (uint8_t)
+     * index.
+     *
+     * If dstInfo.colorType() is kN32, colorOrIndex is treated differently depending on
+     * whether colorTable is NULL:
+     *
+     * A NULL colorTable means colorOrIndex is treated as an SkPMColor (premul or
+     * unpremul, depending on dstInfo.alphaType()). Each 4-byte pixel will be set to
+     * colorOrIndex.
+
+     * A non-NULL colorTable means colorOrIndex is treated as a uint8_t index into
+     * the colorTable. i.e. each 4-byte pixel will be set to
+     * colorTable[(uint8_t) colorOrIndex].
+     *
+     * If dstInfo.colorType() is kGray, colorOrIndex is always treated as an 8-bit color.
+     *
+     * Other SkColorTypes are not supported.
+     *
+     */
+    static void Fill(void* dstStartRow, const SkImageInfo& dstInfo, size_t dstRowBytes,
+            uint32_t numRows, uint32_t colorOrIndex, const SkPMColor* colorTable);
+
     /**
      *  Swizzle the next line. Call height times, once for each row of source.
      *  @param src The next row of the source data.
-     *  @return Whether the row had non-opaque alpha.
+     *  @return A result code describing if the row was fully opaque, fully
+     *          transparent, or neither
      */
-    bool next(const uint8_t* SK_RESTRICT src);
+    ResultAlpha next(const uint8_t* SK_RESTRICT src);
+
+    /**
+     *
+     * Alternate version of next that allows the caller to specify the row.
+     * It is very important to only use one version of next.  Since the other
+     * version modifies the dst pointer, it will change the behavior of this
+     * function.  We will check this in Debug mode.
+     *
+     */
+    ResultAlpha next(const uint8_t* SK_RESTRICT src, int y);
+
+    /**
+     *  Update the destination row.
+     *
+     *  Typically this is done by next, but for a client that wants to manually
+     *  modify the destination row (for example, for decoding scanline one at a
+     *  time) they can call this before each call to next.
+     *  TODO: Maybe replace this with a version of next which allows supplying the
+     *  destination?
+     */
+    void setDstRow(void* dst) { fDstRow = dst; }
+
+    /**
+     *  Get the next destination row to decode to
+     */
+    void* getDstRow() {
+        // kDesignateRow_NextMode does not update the fDstRow ptr.  This function is
+        // unnecessary in that case since fDstRow will always be equal to the pointer
+        // passed to CreateSwizzler().
+        SkASSERT(kDesignateRow_NextMode != fNextMode);
+        return fDstRow;
+    }
+
 private:
+
+#ifdef SK_DEBUG
+    /*
+     *
+     * Keep track of which version of next the caller is using
+     *
+     */
+    enum NextMode {
+        kUninitialized_NextMode,
+        kConsecutive_NextMode,
+        kDesignateRow_NextMode,
+    };
+
+    NextMode fNextMode;
+#endif
+
     /**
      *  Method for converting raw data to Skia pixels.
      *  @param dstRow Row in which to write the resulting pixels.
      *  @param src Row of src data, in format specified by SrcConfig
      *  @param width Width in pixels
-     *  @param bpp bytes per pixel of the source.
+     *  @param deltaSrc if bitsPerPixel % 8 == 0, deltaSrc is bytesPerPixel
+     *                  else, deltaSrc is bitsPerPixel
      *  @param y Line of source.
      *  @param ctable Colors (used for kIndex source).
      */
-    typedef bool (*RowProc)(void* SK_RESTRICT dstRow,
-                            const uint8_t* SK_RESTRICT src,
-                            int width, int bpp, int y,
-                            const SkPMColor ctable[]);
+    typedef ResultAlpha (*RowProc)(void* SK_RESTRICT dstRow,
+                                   const uint8_t* SK_RESTRICT src,
+                                   int width, int deltaSrc, int y,
+                                   const SkPMColor ctable[]);
 
     const RowProc       fRowProc;
-    const SkPMColor*    fColorTable;    // Unowned pointer
-    const int           fSrcPixelSize;
+    const SkPMColor*    fColorTable;      // Unowned pointer
+    const int           fDeltaSrc;        // if bitsPerPixel % 8 == 0
+                                          //     deltaSrc is bytesPerPixel
+                                          // else
+                                          //     deltaSrc is bitsPerPixel
     const SkImageInfo   fDstInfo;
     void*               fDstRow;
     const size_t        fDstRowBytes;
     int                 fCurrY;
 
-    SkSwizzler(RowProc proc, const SkPMColor* ctable, int srcBpp,
+    SkSwizzler(RowProc proc, const SkPMColor* ctable, int deltaSrc,
                const SkImageInfo& info, void* dst, size_t rowBytes);
 
 };
diff --git a/src/core/Sk4px.h b/src/core/Sk4px.h
new file mode 100644
index 0000000..028630d
--- /dev/null
+++ b/src/core/Sk4px.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef Sk4px_DEFINED
+#define Sk4px_DEFINED
+
+#include "SkNx.h"
+#include "SkColor.h"
+
+// 1, 2 or 4 SkPMColors, generally vectorized.
+class Sk4px : public Sk16b {
+public:
+    Sk4px(SkAlpha a) : INHERITED(a) {} // Duplicate 16x: a    -> aaaa aaaa aaaa aaaa
+    Sk4px(SkPMColor);                  // Duplicate 4x:  argb -> argb argb argb argb
+    Sk4px(const Sk16b& v) : INHERITED(v) {}
+
+    Sk4px alphas() const;  // ARGB argb XYZW xyzw -> AAAA aaaa XXXX xxxx
+
+    // Mask away color or alpha lanes.
+    Sk4px zeroColors() const;  // ARGB argb XYZW xyzw -> A000 a000 X000 x000
+    Sk4px zeroAlphas() const;  // ARGB argb XYZW xyzw -> 0RGB 0rgb 0YZW 0yzw
+
+    Sk4px inv() const { return Sk16b(255) - *this; }
+
+    // When loading or storing fewer than 4 SkPMColors, we use the low lanes.
+    static Sk4px Load4(const SkPMColor[4]);  // PMColor[4] -> ARGB argb XYZW xyzw
+    static Sk4px Load2(const SkPMColor[2]);  // PMColor[2] -> ARGB argb ???? ????
+    static Sk4px Load1(const SkPMColor[1]);  // PMColor[1] -> ARGB ???? ???? ????
+
+    // Ditto for Alphas... Load2Alphas fills the low two lanes of Sk4px.
+    static Sk4px Load4Alphas(const SkAlpha[4]);  // AaXx -> AAAA aaaa XXXX xxxx
+    static Sk4px Load2Alphas(const SkAlpha[2]);  // Aa   -> AAAA aaaa ???? ????
+
+    void store4(SkPMColor[4]) const;
+    void store2(SkPMColor[2]) const;
+    void store1(SkPMColor[1]) const;
+
+    // 1, 2, or 4 SkPMColors with 16-bit components.
+    // This is most useful as the result of a multiply, e.g. from mulWiden().
+    class Wide : public Sk16h {
+    public:
+        Wide(const Sk16h& v) : Sk16h(v) {}
+
+        // Pack the top byte of each component back down into 4 SkPMColors.
+        Sk4px addNarrowHi(const Sk16h&) const;
+
+        Sk4px div255TruncNarrow() const { return this->addNarrowHi(*this >> 8); }
+        Sk4px div255RoundNarrow() const {
+            return Sk4px::Wide(*this + Sk16h(128)).div255TruncNarrow();
+        }
+
+    private:
+        typedef Sk16h INHERITED;
+    };
+
+    Wide widenLo() const;               // ARGB -> 0A 0R 0G 0B
+    Wide widenHi() const;               // ARGB -> A0 R0 G0 B0
+    Wide mulWiden(const Sk16b&) const;  // 8-bit x 8-bit -> 16-bit components.
+    Wide mul255Widen() const {
+        // TODO: x*255 = x*256-x, so something like this->widenHi() - this->widenLo()?
+        return this->mulWiden(Sk16b(255));
+    }
+
+    // A generic driver that maps fn over a src array into a dst array.
+    // fn should take an Sk4px (4 src pixels) and return an Sk4px (4 dst pixels).
+    template <typename Fn>
+    static void MapSrc(int count, SkPMColor* dst, const SkPMColor* src, Fn fn) {
+        // This looks a bit odd, but it helps loop-invariant hoisting across different calls to fn.
+        // Basically, we need to make sure we keep things inside a single loop.
+        while (count > 0) {
+            if (count >= 8) {
+                Sk4px dst0 = fn(Load4(src+0)),
+                      dst4 = fn(Load4(src+4));
+                dst0.store4(dst+0);
+                dst4.store4(dst+4);
+                dst += 8; src += 8; count -= 8;
+                continue;  // Keep our stride at 8 pixels as long as possible.
+            }
+            SkASSERT(count <= 7);
+            if (count >= 4) {
+                fn(Load4(src)).store4(dst);
+                dst += 4; src += 4; count -= 4;
+            }
+            if (count >= 2) {
+                fn(Load2(src)).store2(dst);
+                dst += 2; src += 2; count -= 2;
+            }
+            if (count >= 1) {
+                fn(Load1(src)).store1(dst);
+            }
+            break;
+        }
+    }
+
+    // As above, but with dst4' = fn(dst4, src4).
+    template <typename Fn>
+    static void MapDstSrc(int count, SkPMColor* dst, const SkPMColor* src, Fn fn) {
+        while (count > 0) {
+            if (count >= 8) {
+                Sk4px dst0 = fn(Load4(dst+0), Load4(src+0)),
+                      dst4 = fn(Load4(dst+4), Load4(src+4));
+                dst0.store4(dst+0);
+                dst4.store4(dst+4);
+                dst += 8; src += 8; count -= 8;
+                continue;  // Keep our stride at 8 pixels as long as possible.
+            }
+            SkASSERT(count <= 7);
+            if (count >= 4) {
+                fn(Load4(dst), Load4(src)).store4(dst);
+                dst += 4; src += 4; count -= 4;
+            }
+            if (count >= 2) {
+                fn(Load2(dst), Load2(src)).store2(dst);
+                dst += 2; src += 2; count -= 2;
+            }
+            if (count >= 1) {
+                fn(Load1(dst), Load1(src)).store1(dst);
+            }
+            break;
+        }
+    }
+
+    // As above, but with dst4' = fn(dst4, src4, alpha4).
+    template <typename Fn>
+    static void MapDstSrcAlpha(
+            int count, SkPMColor* dst, const SkPMColor* src, const SkAlpha* a, Fn fn) {
+        while (count > 0) {
+            if (count >= 8) {
+                Sk4px alpha0 = Load4Alphas(a+0),
+                      alpha4 = Load4Alphas(a+4);
+                Sk4px dst0 = fn(Load4(dst+0), Load4(src+0), alpha0),
+                      dst4 = fn(Load4(dst+4), Load4(src+4), alpha4);
+                dst0.store4(dst+0);
+                dst4.store4(dst+4);
+                dst += 8; src += 8; a += 8; count -= 8;
+                continue;  // Keep our stride at 8 pixels as long as possible.
+            }
+            SkASSERT(count <= 7);
+            if (count >= 4) {
+                Sk4px alpha = Load4Alphas(a);
+                fn(Load4(dst), Load4(src), alpha).store4(dst);
+                dst += 4; src += 4; a += 4; count -= 4;
+            }
+            if (count >= 2) {
+                Sk4px alpha = Load2Alphas(a);
+                fn(Load2(dst), Load2(src), alpha).store2(dst);
+                dst += 2; src += 2; a += 2; count -= 2;
+            }
+            if (count >= 1) {
+                Sk4px alpha(*a);
+                fn(Load1(dst), Load1(src), alpha).store1(dst);
+            }
+            break;
+        }
+    }
+
+private:
+    typedef Sk16b INHERITED;
+};
+
+#ifdef SKNX_NO_SIMD
+    #include "../opts/Sk4px_none.h"
+#else
+    #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
+        #include "../opts/Sk4px_SSE2.h"
+    #elif defined(SK_ARM_HAS_NEON)
+        #include "../opts/Sk4px_NEON.h"
+    #else
+        #include "../opts/Sk4px_none.h"
+    #endif
+#endif
+
+#endif//Sk4px_DEFINED
diff --git a/src/core/Sk4x.h b/src/core/Sk4x.h
deleted file mode 100644
index 0b9796d..0000000
--- a/src/core/Sk4x.h
+++ /dev/null
@@ -1,102 +0,0 @@
-#ifndef Sk4x_DEFINED
-#define Sk4x_DEFINED
-
-#include "SkTypes.h"
-
-#define SK4X_PREAMBLE 1
-    #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
-        #include "Sk4x_sse.h"
-    #elif defined(SK_ARM_HAS_NEON)
-        #include "Sk4x_neon.h"
-    #else
-        #include "Sk4x_portable.h"
-    #endif
-#undef SK4X_PREAMBLE
-
-template <typename T> class Sk4x;
-typedef Sk4x<float>   Sk4f;
-typedef Sk4x<int32_t> Sk4i;
-
-// Some Sk4x methods are implemented only for Sk4f or Sk4i.
-// They might be unavailable, really slow, or just a bad idea.
-// Talk to mtklein if you find yourself unable to link and
-// really need one of those methods.
-
-template <typename T> class Sk4x {
-public:
-    Sk4x();  // Uninitialized; use Sk4x(0) for zero.
-    explicit Sk4x(T);  // Same as Sk4x(T,T,T,T);
-    Sk4x(T, T, T, T);
-
-    Sk4x(const Sk4x&);
-    Sk4x& operator=(const Sk4x&);
-
-    static Sk4x Load       (const T[4]);
-    static Sk4x LoadAligned(const T[4]);
-
-    void store       (T[4]) const;
-    void storeAligned(T[4]) const;
-
-    template <typename Dst> Dst reinterpret() const;
-    template <typename Dst> Dst        cast() const;
-
-    bool allTrue() const;
-    bool anyTrue() const;
-
-    Sk4x   bitNot()            const;
-    Sk4x   bitAnd(const Sk4x&) const;
-    Sk4x    bitOr(const Sk4x&) const;
-    // TODO: Sk4x bitAndNot(const Sk4x&) const; is efficient in SSE.
-    Sk4x      add(const Sk4x&) const;
-    Sk4x subtract(const Sk4x&) const;
-    Sk4x multiply(const Sk4x&) const;
-    Sk4x   divide(const Sk4x&) const;
-
-    Sk4x rsqrt() const;   // Approximate reciprocal sqrt().
-    Sk4x  sqrt() const;   // this->multiply(this->rsqrt()) may be faster, but less precise.
-
-    Sk4i            equal(const Sk4x&) const;
-    Sk4i         notEqual(const Sk4x&) const;
-    Sk4i         lessThan(const Sk4x&) const;
-    Sk4i      greaterThan(const Sk4x&) const;
-    Sk4i    lessThanEqual(const Sk4x&) const;
-    Sk4i greaterThanEqual(const Sk4x&) const;
-
-    static Sk4x Min(const Sk4x& a, const Sk4x& b);
-    static Sk4x Max(const Sk4x& a, const Sk4x& b);
-
-    // Swizzles follow OpenCL xyzw convention.
-    Sk4x zwxy() const;
-
-    // When there's a second argument, it's abcd.
-    static Sk4x XYAB(const Sk4x& xyzw, const Sk4x& abcd);
-    static Sk4x ZWCD(const Sk4x& xyzw, const Sk4x& abcd);
-
-    // TODO: these are particularly efficient in SSE.  Useful?  Also efficient in NEON?
-    // static Sk4x XAYB(const Sk4x& xyzw, const Sk4x& abcd);
-    // static Sk4x ZCWD(const Sk4x& xyzw, const Sk4x& abcd);
-
-private:
-    // It's handy to have Sk4f and Sk4i be mutual friends.
-    template <typename S> friend class Sk4x;
-
-#define SK4X_PRIVATE 1
-    #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
-        #include "Sk4x_sse.h"
-    #elif defined(SK_ARM_HAS_NEON)
-        #include "Sk4x_neon.h"
-    #else
-        #include "Sk4x_portable.h"
-    #endif
-#undef SK4X_PRIVATE
-};
-
-#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
-    #include "Sk4x_sse.h"
-#elif defined(SK_ARM_HAS_NEON)
-    #include "Sk4x_neon.h"
-#else
-    #include "Sk4x_portable.h"
-#endif
-
-#endif//Sk4x_DEFINED
diff --git a/src/core/Sk4x_neon.h b/src/core/Sk4x_neon.h
deleted file mode 100644
index 41a30b2..0000000
--- a/src/core/Sk4x_neon.h
+++ /dev/null
@@ -1,220 +0,0 @@
-// It is important _not_ to put header guards here.
-// This file will be intentionally included three times.
-
-#if defined(SK4X_PREAMBLE)
-    #include <arm_neon.h>
-
-    // Template metaprogramming to map scalar types to vector types.
-    template <typename T> struct SkScalarToSIMD;
-    template <> struct SkScalarToSIMD<float>   { typedef float32x4_t  Type; };
-    template <> struct SkScalarToSIMD<int32_t> { typedef int32x4_t Type; };
-
-#elif defined(SK4X_PRIVATE)
-    Sk4x(float32x4_t);
-    Sk4x(int32x4_t);
-
-    typename SkScalarToSIMD<T>::Type fVec;
-
-#else
-
-// Vector Constructors
-//template <> inline Sk4f::Sk4x(int32x4_t v) : fVec(vcvtq_f32_s32(v)) {}
-template <> inline Sk4f::Sk4x(float32x4_t v) : fVec(v) {}
-template <> inline Sk4i::Sk4x(int32x4_t v) : fVec(v) {}
-//template <> inline Sk4i::Sk4x(float32x4_t v) : fVec(vcvtq_s32_f32(v)) {}
-
-// Generic Methods
-template <typename T> Sk4x<T>::Sk4x() {}
-template <typename T> Sk4x<T>::Sk4x(const Sk4x& other) { *this = other; }
-template <typename T> Sk4x<T>& Sk4x<T>::operator=(const Sk4x<T>& other) {
-    fVec = other.fVec;
-    return *this;
-}
-
-// Sk4f Methods
-#define M(...) template <> inline __VA_ARGS__ Sk4f::
-
-M() Sk4x(float v) : fVec(vdupq_n_f32(v)) {}
-M() Sk4x(float a, float b, float c, float d) {
-    // NEON lacks an intrinsic to make this easy.  It is recommended to avoid
-    // this constructor unless it is absolutely necessary.
-
-    // I am choosing to use the set lane intrinsics.  Particularly, in the case
-    // of floating point, it is likely that the values are already in the right
-    // register file, so this may be the best approach.  However, I am not
-    // certain that this is the fastest approach and experimentation might be
-    // useful.
-    fVec = vsetq_lane_f32(a, fVec, 0);
-    fVec = vsetq_lane_f32(b, fVec, 1);
-    fVec = vsetq_lane_f32(c, fVec, 2);
-    fVec = vsetq_lane_f32(d, fVec, 3);
-}
-
-// As far as I can tell, it's not possible to provide an alignment hint to
-// NEON using intrinsics.  However, I think it is possible at the assembly
-// level if we want to get into that.
-// TODO: Write our own aligned load and store.
-M(Sk4f) Load       (const float fs[4]) { return vld1q_f32(fs); }
-M(Sk4f) LoadAligned(const float fs[4]) { return vld1q_f32(fs); }
-M(void) store       (float fs[4]) const { vst1q_f32(fs, fVec); }
-M(void) storeAligned(float fs[4]) const { vst1q_f32 (fs, fVec); }
-
-template <>
-M(Sk4i) reinterpret<Sk4i>() const { return vreinterpretq_s32_f32(fVec); }
-
-template <>
-M(Sk4i) cast<Sk4i>() const { return vcvtq_s32_f32(fVec); }
-
-// We're going to skip allTrue(), anyTrue(), and bit-manipulators
-// for Sk4f.  Code that calls them probably does so accidentally.
-// Ask msarett or mtklein to fill these in if you really need them.
-M(Sk4f) add     (const Sk4f& o) const { return vaddq_f32(fVec, o.fVec); }
-M(Sk4f) subtract(const Sk4f& o) const { return vsubq_f32(fVec, o.fVec); }
-M(Sk4f) multiply(const Sk4f& o) const { return vmulq_f32(fVec, o.fVec); }
-
-M(Sk4f) divide  (const Sk4f& o) const {
-    float32x4_t est0 = vrecpeq_f32(o.fVec);
-    float32x4_t est1 = vmulq_f32(vrecpsq_f32(est0, o.fVec), est0);
-    float32x4_t est2 = vmulq_f32(vrecpsq_f32(est1, o.fVec), est1);
-    return vmulq_f32(est2, fVec);
-}
-
-M(Sk4f) rsqrt() const {
-    float32x4_t est0 = vrsqrteq_f32(fVec);
-    float32x4_t est1 = vmulq_f32(vrsqrtsq_f32(fVec, vmulq_f32(est0, est0)), est0);
-    float32x4_t est2 = vmulq_f32(vrsqrtsq_f32(fVec, vmulq_f32(est1, est1)), est1);
-    return est2;
-}
-
-M(Sk4f)  sqrt() const { return this->multiply(this->rsqrt()); }
-
-M(Sk4i) equal           (const Sk4f& o) const { return vreinterpretq_s32_u32(vceqq_f32(fVec, o.fVec)); }
-M(Sk4i) notEqual        (const Sk4f& o) const { return vreinterpretq_s32_u32(vmvnq_u32(vceqq_f32(fVec, o.fVec))); }
-M(Sk4i) lessThan        (const Sk4f& o) const { return vreinterpretq_s32_u32(vcltq_f32(fVec, o.fVec)); }
-M(Sk4i) greaterThan     (const Sk4f& o) const { return vreinterpretq_s32_u32(vcgtq_f32(fVec, o.fVec)); }
-M(Sk4i) lessThanEqual   (const Sk4f& o) const { return vreinterpretq_s32_u32(vcleq_f32(fVec, o.fVec)); }
-M(Sk4i) greaterThanEqual(const Sk4f& o) const { return vreinterpretq_s32_u32(vcgeq_f32(fVec, o.fVec)); }
-
-M(Sk4f) Min(const Sk4f& a, const Sk4f& b) { return vminq_f32(a.fVec, b.fVec); }
-M(Sk4f) Max(const Sk4f& a, const Sk4f& b) { return vmaxq_f32(a.fVec, b.fVec); }
-
-// These shuffle operations are implemented more efficiently with SSE.
-// NEON has efficient zip, unzip, and transpose, but it is more costly to
-// exploit zip and unzip in order to shuffle.
-M(Sk4f) zwxy() const {
-    float32x4x2_t zip = vzipq_f32(fVec, vdupq_n_f32(0.0));
-    return vuzpq_f32(zip.val[1], zip.val[0]).val[0];
-}
-// Note that XYAB and ZWCD share code.  If both are needed, they could be
-// implemented more efficiently together.  Also, ABXY and CDZW are available
-// as well.
-M(Sk4f) XYAB(const Sk4f& xyzw, const Sk4f& abcd) {
-    float32x4x2_t xayb_zcwd = vzipq_f32(xyzw.fVec, abcd.fVec);
-    float32x4x2_t axby_czdw = vzipq_f32(abcd.fVec, xyzw.fVec);
-    return vuzpq_f32(xayb_zcwd.val[0], axby_czdw.val[0]).val[0];
-}
-M(Sk4f) ZWCD(const Sk4f& xyzw, const Sk4f& abcd) {
-    float32x4x2_t xayb_zcwd = vzipq_f32(xyzw.fVec, abcd.fVec);
-    float32x4x2_t axby_czdw = vzipq_f32(abcd.fVec, xyzw.fVec);
-    return vuzpq_f32(xayb_zcwd.val[1], axby_czdw.val[1]).val[0];
-}
-
-// Sk4i Methods
-#undef M
-#define M(...) template <> inline __VA_ARGS__ Sk4i::
-
-M() Sk4x(int32_t v) : fVec(vdupq_n_s32(v)) {}
-M() Sk4x(int32_t a, int32_t b, int32_t c, int32_t d) {
-    // NEON lacks an intrinsic to make this easy.  It is recommended to avoid
-    // this constructor unless it is absolutely necessary.
-
-    // There are a few different implementation strategies.
-
-    // uint64_t ab_i = ((uint32_t) a) | (((uint64_t) b) << 32);
-    // uint64_t cd_i = ((uint32_t) c) | (((uint64_t) d) << 32);
-    // int32x2_t ab = vcreate_s32(ab_i);
-    // int32x2_t cd = vcreate_s32(cd_i);
-    // fVec = vcombine_s32(ab, cd);
-    // This might not be a bad idea for the integer case.  Either way I think,
-    // we will need to move values from general registers to NEON registers.
-
-    // I am choosing to use the set lane intrinsics.  I am not certain that
-    // this is the fastest approach.  It may be useful to try the above code
-    // for integers.
-    fVec = vsetq_lane_s32(a, fVec, 0);
-    fVec = vsetq_lane_s32(b, fVec, 1);
-    fVec = vsetq_lane_s32(c, fVec, 2);
-    fVec = vsetq_lane_s32(d, fVec, 3);
-}
-
-// As far as I can tell, it's not possible to provide an alignment hint to
-// NEON using intrinsics.  However, I think it is possible at the assembly
-// level if we want to get into that.
-M(Sk4i) Load       (const int32_t is[4]) { return vld1q_s32(is); }
-M(Sk4i) LoadAligned(const int32_t is[4]) { return vld1q_s32(is); }
-M(void) store       (int32_t is[4]) const { vst1q_s32(is, fVec); }
-M(void) storeAligned(int32_t is[4]) const { vst1q_s32 (is, fVec); }
-
-template <>
-M(Sk4f) reinterpret<Sk4f>() const { return vreinterpretq_f32_s32(fVec); }
-
-template <>
-M(Sk4f) cast<Sk4f>() const { return vcvtq_f32_s32(fVec); }
-
-M(bool) allTrue() const {
-    int32_t a = vgetq_lane_s32(fVec, 0);
-    int32_t b = vgetq_lane_s32(fVec, 1);
-    int32_t c = vgetq_lane_s32(fVec, 2);
-    int32_t d = vgetq_lane_s32(fVec, 3);
-    return a & b & c & d;
-}
-M(bool) anyTrue() const {
-    int32_t a = vgetq_lane_s32(fVec, 0);
-    int32_t b = vgetq_lane_s32(fVec, 1);
-    int32_t c = vgetq_lane_s32(fVec, 2);
-    int32_t d = vgetq_lane_s32(fVec, 3);
-    return a | b | c | d;
-}
-
-M(Sk4i) bitNot() const { return vmvnq_s32(fVec); }
-M(Sk4i) bitAnd(const Sk4i& o) const { return vandq_s32(fVec, o.fVec); }
-M(Sk4i) bitOr (const Sk4i& o) const { return vorrq_s32(fVec, o.fVec); }
-
-M(Sk4i) equal           (const Sk4i& o) const { return vreinterpretq_s32_u32(vceqq_s32(fVec, o.fVec)); }
-M(Sk4i) notEqual        (const Sk4i& o) const { return vreinterpretq_s32_u32(vmvnq_u32(vceqq_s32(fVec, o.fVec))); }
-M(Sk4i) lessThan        (const Sk4i& o) const { return vreinterpretq_s32_u32(vcltq_s32(fVec, o.fVec)); }
-M(Sk4i) greaterThan     (const Sk4i& o) const { return vreinterpretq_s32_u32(vcgtq_s32(fVec, o.fVec)); }
-M(Sk4i) lessThanEqual   (const Sk4i& o) const { return vreinterpretq_s32_u32(vcleq_s32(fVec, o.fVec)); }
-M(Sk4i) greaterThanEqual(const Sk4i& o) const { return vreinterpretq_s32_u32(vcgeq_s32(fVec, o.fVec)); }
-
-M(Sk4i) add     (const Sk4i& o) const { return vaddq_s32(fVec, o.fVec); }
-M(Sk4i) subtract(const Sk4i& o) const { return vsubq_s32(fVec, o.fVec); }
-M(Sk4i) multiply(const Sk4i& o) const { return vmulq_s32(fVec, o.fVec); }
-// NEON does not have integer reciprocal, sqrt, or division.
-M(Sk4i) Min(const Sk4i& a, const Sk4i& b) { return vminq_s32(a.fVec, b.fVec); }
-M(Sk4i) Max(const Sk4i& a, const Sk4i& b) { return vmaxq_s32(a.fVec, b.fVec); }
-
-// These shuffle operations are implemented more efficiently with SSE.
-// NEON has efficient zip, unzip, and transpose, but it is more costly to
-// exploit zip and unzip in order to shuffle.
-M(Sk4i) zwxy() const {
-    int32x4x2_t zip = vzipq_s32(fVec, vdupq_n_s32(0.0));
-    return vuzpq_s32(zip.val[1], zip.val[0]).val[0];
-}
-// Note that XYAB and ZWCD share code.  If both are needed, they could be
-// implemented more efficiently together.  Also, ABXY and CDZW are available
-// as well.
-M(Sk4i) XYAB(const Sk4i& xyzw, const Sk4i& abcd) {
-    int32x4x2_t xayb_zcwd = vzipq_s32(xyzw.fVec, abcd.fVec);
-    int32x4x2_t axby_czdw = vzipq_s32(abcd.fVec, xyzw.fVec);
-    return vuzpq_s32(xayb_zcwd.val[0], axby_czdw.val[0]).val[0];
-}
-M(Sk4i) ZWCD(const Sk4i& xyzw, const Sk4i& abcd) {
-    int32x4x2_t xayb_zcwd = vzipq_s32(xyzw.fVec, abcd.fVec);
-    int32x4x2_t axby_czdw = vzipq_s32(abcd.fVec, xyzw.fVec);
-    return vuzpq_s32(xayb_zcwd.val[1], axby_czdw.val[1]).val[0];
-}
-
-#undef M
-
-#endif
diff --git a/src/core/Sk4x_portable.h b/src/core/Sk4x_portable.h
deleted file mode 100644
index 440e91f..0000000
--- a/src/core/Sk4x_portable.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// It is important _not_ to put header guards here.
-// This file will be intentionally included three times.
-
-#if defined(SK4X_PREAMBLE)
-    #include "SkFloatingPoint.h"
-    #include <math.h>
-
-#elif defined(SK4X_PRIVATE)
-    typedef T Type;
-    typedef T Vector[4];
-
-    Vector fVec;
-
-    template <int m, int a, int s, int k>
-    static Sk4x Shuffle(const Sk4x&, const Sk4x&);
-
-    void set(const T vals[4]) { for (int i = 0; i < 4; i++) { fVec[i] = vals[i]; } }
-
-#else
-
-#define M(...) template <typename T> __VA_ARGS__ Sk4x<T>::
-
-M() Sk4x() {}
-M() Sk4x(T v) { fVec[0] = fVec[1] = fVec[2] = fVec[3] = v; }
-M() Sk4x(T a, T b, T c, T d) { fVec[0] = a; fVec[1] = b; fVec[2] = c; fVec[3] = d; }
-
-M()              Sk4x(const Sk4x<T>& other) { this->set(other.fVec);               }
-M(Sk4x<T>&) operator=(const Sk4x<T>& other) { this->set(other.fVec); return *this; }
-
-M(Sk4x<T>) Load       (const T vals[4]) { Sk4x r; r.set(vals); return r; }
-M(Sk4x<T>) LoadAligned(const T vals[4]) { return Load(vals);             }
-
-M(void) store       (T vals[4]) const { for (int i = 0; i < 4; i++) { vals[i] = fVec[i]; } }
-M(void) storeAligned(T vals[4]) const { this->store(vals); }
-
-M(template <typename Dst> Dst) reinterpret() const {
-    Dst d;
-    memcpy(&d.fVec, fVec, sizeof(fVec));
-    return d;
-}
-M(template <typename Dst> Dst) cast() const {
-    return Dst((typename Dst::Type)fVec[0],
-               (typename Dst::Type)fVec[1],
-               (typename Dst::Type)fVec[2],
-               (typename Dst::Type)fVec[3]);
-}
-
-M(bool) allTrue() const { return fVec[0] && fVec[1] && fVec[2] && fVec[3]; }
-M(bool) anyTrue() const { return fVec[0] || fVec[1] || fVec[2] || fVec[3]; }
-
-M(Sk4x<T>) bitNot() const { return Sk4x(~fVec[0], ~fVec[1], ~fVec[2], ~fVec[3]); }
-
-#define BINOP(op) fVec[0] op other.fVec[0], \
-                  fVec[1] op other.fVec[1], \
-                  fVec[2] op other.fVec[2], \
-                  fVec[3] op other.fVec[3]
-M(Sk4x<T>)   bitAnd(const Sk4x& other)    const { return Sk4x(BINOP(&)); }
-M(Sk4x<T>)    bitOr(const Sk4x& other)    const { return Sk4x(BINOP(|)); }
-M(Sk4x<T>)      add(const Sk4x<T>& other) const { return Sk4x(BINOP(+)); }
-M(Sk4x<T>) subtract(const Sk4x<T>& other) const { return Sk4x(BINOP(-)); }
-M(Sk4x<T>) multiply(const Sk4x<T>& other) const { return Sk4x(BINOP(*)); }
-M(Sk4x<T>)   divide(const Sk4x<T>& other) const { return Sk4x(BINOP(/)); }
-#undef BINOP
-
-template<> inline Sk4f Sk4f::rsqrt() const {
-    return Sk4f(sk_float_rsqrt(fVec[0]),
-                sk_float_rsqrt(fVec[1]),
-                sk_float_rsqrt(fVec[2]),
-                sk_float_rsqrt(fVec[3]));
-}
-
-template<> inline Sk4f Sk4f::sqrt() const {
-    return Sk4f(sqrtf(fVec[0]),
-                sqrtf(fVec[1]),
-                sqrtf(fVec[2]),
-                sqrtf(fVec[3]));
-}
-
-#define BOOL_BINOP(op) fVec[0] op other.fVec[0] ? -1 : 0, \
-                       fVec[1] op other.fVec[1] ? -1 : 0, \
-                       fVec[2] op other.fVec[2] ? -1 : 0, \
-                       fVec[3] op other.fVec[3] ? -1 : 0
-M(Sk4i)            equal(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP(==)); }
-M(Sk4i)         notEqual(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP(!=)); }
-M(Sk4i)         lessThan(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP( <)); }
-M(Sk4i)      greaterThan(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP( >)); }
-M(Sk4i)    lessThanEqual(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP(<=)); }
-M(Sk4i) greaterThanEqual(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP(>=)); }
-#undef BOOL_BINOP
-
-M(Sk4x<T>) Min(const Sk4x<T>& a, const Sk4x<T>& b) {
-    return Sk4x(SkTMin(a.fVec[0], b.fVec[0]),
-                SkTMin(a.fVec[1], b.fVec[1]),
-                SkTMin(a.fVec[2], b.fVec[2]),
-                SkTMin(a.fVec[3], b.fVec[3]));
-}
-
-M(Sk4x<T>) Max(const Sk4x<T>& a, const Sk4x<T>& b) {
-    return Sk4x(SkTMax(a.fVec[0], b.fVec[0]),
-                SkTMax(a.fVec[1], b.fVec[1]),
-                SkTMax(a.fVec[2], b.fVec[2]),
-                SkTMax(a.fVec[3], b.fVec[3]));
-}
-
-M(template <int m, int a, int s, int k> Sk4x<T>) Shuffle(const Sk4x<T>& x, const Sk4x<T>& y) {
-    return Sk4x(m < 4 ? x.fVec[m] : y.fVec[m-4],
-                a < 4 ? x.fVec[a] : y.fVec[a-4],
-                s < 4 ? x.fVec[s] : y.fVec[s-4],
-                k < 4 ? x.fVec[k] : y.fVec[k-4]);
-}
-
-M(Sk4x<T>) zwxy() const                             { return Shuffle<2,3,0,1>(*this, *this); }
-M(Sk4x<T>) XYAB(const Sk4x& xyzw, const Sk4x& abcd) { return Shuffle<0,1,4,5>( xyzw,  abcd); }
-M(Sk4x<T>) ZWCD(const Sk4x& xyzw, const Sk4x& abcd) { return Shuffle<2,3,6,7>( xyzw,  abcd); }
-
-#undef M
-
-#endif
diff --git a/src/core/Sk4x_sse.h b/src/core/Sk4x_sse.h
deleted file mode 100644
index 6077d02..0000000
--- a/src/core/Sk4x_sse.h
+++ /dev/null
@@ -1,182 +0,0 @@
-// It is important _not_ to put header guards here.
-// This file will be intentionally included three times.
-
-// Useful reading:
-//   https://software.intel.com/sites/landingpage/IntrinsicsGuide/
-
-#if defined(SK4X_PREAMBLE)
-    // Code in this file may assume SSE and SSE2.
-    #include <emmintrin.h>
-
-    // It must check for later instruction sets.
-    #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE41
-        #include <immintrin.h>
-    #endif
-
-    // A little bit of template metaprogramming to map
-    // float to __m128 and int32_t to __m128i.
-    template <typename T> struct SkScalarToSIMD;
-    template <> struct SkScalarToSIMD<float>   { typedef __m128  Type; };
-    template <> struct SkScalarToSIMD<int32_t> { typedef __m128i Type; };
-
-    // These are all free, zero instructions.
-    // MSVC insists we use _mm_castA_B(a) instead of (B)a.
-    static inline __m128  as_4f(__m128i v) { return _mm_castsi128_ps(v); }
-    static inline __m128  as_4f(__m128  v) { return                  v ; }
-    static inline __m128i as_4i(__m128i v) { return                  v ; }
-    static inline __m128i as_4i(__m128  v) { return _mm_castps_si128(v); }
-
-#elif defined(SK4X_PRIVATE)
-    // It'd be slightly faster to call _mm_cmpeq_epi32() on an unintialized register and itself,
-    // but that has caused hard to debug issues when compilers recognize dealing with uninitialized
-    // memory as undefined behavior that can be optimized away.
-    static __m128i True()  { return _mm_set1_epi8(~0); }
-
-    // Leaving these implicit makes the rest of the code below a bit less noisy to read.
-    Sk4x(__m128i);
-    Sk4x(__m128);
-
-    Sk4x andNot(const Sk4x&) const;
-
-    typename SkScalarToSIMD<T>::Type fVec;
-
-#else//Method definitions.
-
-// Helps to get these in before anything else.
-template <> inline Sk4f::Sk4x(__m128i v) : fVec(as_4f(v)) {}
-template <> inline Sk4f::Sk4x(__m128  v) : fVec(      v ) {}
-template <> inline Sk4i::Sk4x(__m128i v) : fVec(      v ) {}
-template <> inline Sk4i::Sk4x(__m128  v) : fVec(as_4i(v)) {}
-
-// Next, methods whose implementation is the same for Sk4f and Sk4i.
-template <typename T> Sk4x<T>::Sk4x() {}
-template <typename T> Sk4x<T>::Sk4x(const Sk4x& other) { *this = other; }
-template <typename T> Sk4x<T>& Sk4x<T>::operator=(const Sk4x<T>& other) {
-    fVec = other.fVec;
-    return *this;
-}
-
-// We pun in these _mm_shuffle_* methods a little to use the fastest / most available methods.
-// They're all bit-preserving operations so it shouldn't matter.
-
-template <typename T>
-Sk4x<T> Sk4x<T>::zwxy() const { return _mm_shuffle_epi32(as_4i(fVec), _MM_SHUFFLE(1,0,3,2)); }
-
-template <typename T>
-Sk4x<T> Sk4x<T>::XYAB(const Sk4x<T>& a, const Sk4x<T>& b) {
-    return _mm_movelh_ps(as_4f(a.fVec), as_4f(b.fVec));
-}
-
-template <typename T>
-Sk4x<T> Sk4x<T>::ZWCD(const Sk4x<T>& a, const Sk4x<T>& b) {
-    return _mm_movehl_ps(as_4f(b.fVec), as_4f(a.fVec));
-}
-
-// Now we'll write all Sk4f specific methods.  This M() macro will remove some noise.
-#define M(...) template <> inline __VA_ARGS__ Sk4f::
-
-M() Sk4x(float v) : fVec(_mm_set1_ps(v)) {}
-M() Sk4x(float a, float b, float c, float d) : fVec(_mm_set_ps(d,c,b,a)) {}
-
-M(Sk4f) Load       (const float fs[4]) { return _mm_loadu_ps(fs); }
-M(Sk4f) LoadAligned(const float fs[4]) { return _mm_load_ps (fs); }
-
-M(void) store       (float fs[4]) const { _mm_storeu_ps(fs, fVec); }
-M(void) storeAligned(float fs[4]) const { _mm_store_ps (fs, fVec); }
-
-template <>
-M(Sk4i) reinterpret<Sk4i>() const { return as_4i(fVec); }
-
-template <>
-M(Sk4i) cast<Sk4i>() const { return _mm_cvtps_epi32(fVec); }
-
-// We're going to try a little experiment here and skip allTrue(), anyTrue(), and bit-manipulators
-// for Sk4f.  Code that calls them probably does so accidentally.
-// Ask mtklein to fill these in if you really need them.
-
-M(Sk4f) add     (const Sk4f& o) const { return _mm_add_ps(fVec, o.fVec); }
-M(Sk4f) subtract(const Sk4f& o) const { return _mm_sub_ps(fVec, o.fVec); }
-M(Sk4f) multiply(const Sk4f& o) const { return _mm_mul_ps(fVec, o.fVec); }
-M(Sk4f) divide  (const Sk4f& o) const { return _mm_div_ps(fVec, o.fVec); }
-
-M(Sk4f) rsqrt() const { return _mm_rsqrt_ps(fVec); }
-M(Sk4f)  sqrt() const { return _mm_sqrt_ps( fVec); }
-
-M(Sk4i) equal           (const Sk4f& o) const { return _mm_cmpeq_ps (fVec, o.fVec); }
-M(Sk4i) notEqual        (const Sk4f& o) const { return _mm_cmpneq_ps(fVec, o.fVec); }
-M(Sk4i) lessThan        (const Sk4f& o) const { return _mm_cmplt_ps (fVec, o.fVec); }
-M(Sk4i) greaterThan     (const Sk4f& o) const { return _mm_cmpgt_ps (fVec, o.fVec); }
-M(Sk4i) lessThanEqual   (const Sk4f& o) const { return _mm_cmple_ps (fVec, o.fVec); }
-M(Sk4i) greaterThanEqual(const Sk4f& o) const { return _mm_cmpge_ps (fVec, o.fVec); }
-
-M(Sk4f) Min(const Sk4f& a, const Sk4f& b) { return _mm_min_ps(a.fVec, b.fVec); }
-M(Sk4f) Max(const Sk4f& a, const Sk4f& b) { return _mm_max_ps(a.fVec, b.fVec); }
-
-// Now we'll write all the Sk4i specific methods.  Same deal for M().
-#undef M
-#define M(...) template <> inline __VA_ARGS__ Sk4i::
-
-M() Sk4x(int32_t v) : fVec(_mm_set1_epi32(v)) {}
-M() Sk4x(int32_t a, int32_t b, int32_t c, int32_t d) : fVec(_mm_set_epi32(d,c,b,a)) {}
-
-M(Sk4i) Load       (const int32_t is[4]) { return _mm_loadu_si128((const __m128i*)is); }
-M(Sk4i) LoadAligned(const int32_t is[4]) { return _mm_load_si128 ((const __m128i*)is); }
-
-M(void) store       (int32_t is[4]) const { _mm_storeu_si128((__m128i*)is, fVec); }
-M(void) storeAligned(int32_t is[4]) const { _mm_store_si128 ((__m128i*)is, fVec); }
-
-template <>
-M(Sk4f) reinterpret<Sk4f>() const { return as_4f(fVec); }
-
-template <>
-M(Sk4f) cast<Sk4f>() const { return _mm_cvtepi32_ps(fVec); }
-
-M(bool) allTrue() const { return 0xf == _mm_movemask_ps(as_4f(fVec)); }
-M(bool) anyTrue() const { return 0x0 != _mm_movemask_ps(as_4f(fVec)); }
-
-M(Sk4i) bitNot() const { return _mm_xor_si128(fVec, True()); }
-M(Sk4i) bitAnd(const Sk4i& o) const { return _mm_and_si128(fVec, o.fVec); }
-M(Sk4i) bitOr (const Sk4i& o) const { return _mm_or_si128 (fVec, o.fVec); }
-
-M(Sk4i) equal           (const Sk4i& o) const { return _mm_cmpeq_epi32 (fVec, o.fVec); }
-M(Sk4i) lessThan        (const Sk4i& o) const { return _mm_cmplt_epi32 (fVec, o.fVec); }
-M(Sk4i) greaterThan     (const Sk4i& o) const { return _mm_cmpgt_epi32 (fVec, o.fVec); }
-M(Sk4i) notEqual        (const Sk4i& o) const { return this->      equal(o).bitNot();  }
-M(Sk4i) lessThanEqual   (const Sk4i& o) const { return this->greaterThan(o).bitNot();  }
-M(Sk4i) greaterThanEqual(const Sk4i& o) const { return this->   lessThan(o).bitNot();  }
-
-M(Sk4i) add     (const Sk4i& o) const { return _mm_add_epi32(fVec, o.fVec); }
-M(Sk4i) subtract(const Sk4i& o) const { return _mm_sub_epi32(fVec, o.fVec); }
-
-// SSE doesn't have integer division.  Let's see how far we can get without Sk4i::divide().
-
-// Sk4i's multiply(), Min(), and Max() all improve significantly with SSE4.1.
-#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE41
-    M(Sk4i) multiply(const Sk4i& o) const { return _mm_mullo_epi32(fVec, o.fVec); }
-    M(Sk4i) Min(const Sk4i& a, const Sk4i& b) { return _mm_min_epi32(a.fVec, b.fVec); }
-    M(Sk4i) Max(const Sk4i& a, const Sk4i& b) { return _mm_max_epi32(a.fVec, b.fVec); }
-#else
-    M(Sk4i) multiply(const Sk4i& o) const {
-        // First 2 32->64 bit multiplies.
-        __m128i mul02 = _mm_mul_epu32(fVec, o.fVec),
-                mul13 = _mm_mul_epu32(_mm_srli_si128(fVec, 4), _mm_srli_si128(o.fVec, 4));
-        // Now recombine the high bits of the two products.
-        return _mm_unpacklo_epi32(_mm_shuffle_epi32(mul02, _MM_SHUFFLE(0,0,2,0)),
-                                  _mm_shuffle_epi32(mul13, _MM_SHUFFLE(0,0,2,0)));
-    }
-
-    M(Sk4i) andNot(const Sk4i& o) const { return _mm_andnot_si128(o.fVec, fVec); }
-
-    M(Sk4i) Min(const Sk4i& a, const Sk4i& b) {
-        Sk4i less = a.lessThan(b);
-        return a.bitAnd(less).bitOr(b.andNot(less));
-    }
-    M(Sk4i) Max(const Sk4i& a, const Sk4i& b) {
-        Sk4i less = a.lessThan(b);
-        return b.bitAnd(less).bitOr(a.andNot(less));
-    }
-#endif
-
-#undef M
-
-#endif//Method definitions.
diff --git a/src/core/SkAAClip.cpp b/src/core/SkAAClip.cpp
index 45e6cc3..e55451e 100644
--- a/src/core/SkAAClip.cpp
+++ b/src/core/SkAAClip.cpp
@@ -1273,13 +1273,13 @@
        Instead we'll rely on the runtime asserts to guarantee Y monotonicity;
        any failure cases that misses may have minor artifacts.
     */
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
+    void blitV(int x, int y, int height, SkAlpha alpha) override {
         this->recordMinY(y);
         fBuilder->addColumn(x, y, alpha, height);
         fLastY = y + height - 1;
     }
 
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+    void blitRect(int x, int y, int width, int height) override {
         this->recordMinY(y);
         this->checkForYGap(y);
         fBuilder->addRectRun(x, y, width, height);
@@ -1287,28 +1287,28 @@
     }
 
     virtual void blitAntiRect(int x, int y, int width, int height,
-                     SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE {
+                     SkAlpha leftAlpha, SkAlpha rightAlpha) override {
         this->recordMinY(y);
         this->checkForYGap(y);
         fBuilder->addAntiRectRun(x, y, width, height, leftAlpha, rightAlpha);
         fLastY = y + height - 1;
     }
 
-    void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE
+    void blitMask(const SkMask&, const SkIRect& clip) override
         { unexpected(); }
 
-    const SkBitmap* justAnOpaqueColor(uint32_t*) SK_OVERRIDE {
+    const SkBitmap* justAnOpaqueColor(uint32_t*) override {
         return NULL;
     }
 
-    void blitH(int x, int y, int width) SK_OVERRIDE {
+    void blitH(int x, int y, int width) override {
         this->recordMinY(y);
         this->checkForYGap(y);
         fBuilder->addRun(x, y, 0xFF, width);
     }
 
     virtual void blitAntiH(int x, int y, const SkAlpha alpha[],
-                           const int16_t runs[]) SK_OVERRIDE {
+                           const int16_t runs[]) override {
         this->recordMinY(y);
         this->checkForYGap(y);
         for (;;) {
diff --git a/src/core/SkAAClip.h b/src/core/SkAAClip.h
index 67f45e4..1daeb38 100644
--- a/src/core/SkAAClip.h
+++ b/src/core/SkAAClip.h
@@ -109,13 +109,13 @@
         fAAClipBounds = aaclip->getBounds();
     }
 
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
     virtual void blitAntiH(int x, int y, const SkAlpha[],
-                           const int16_t runs[]) SK_OVERRIDE;
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
-    void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
-    const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
+                           const int16_t runs[]) override;
+    void blitV(int x, int y, int height, SkAlpha alpha) override;
+    void blitRect(int x, int y, int width, int height) override;
+    void blitMask(const SkMask&, const SkIRect& clip) override;
+    const SkBitmap* justAnOpaqueColor(uint32_t* value) override;
 
 private:
     SkBlitter*      fBlitter;
diff --git a/include/core/SkAdvancedTypefaceMetrics.h b/src/core/SkAdvancedTypefaceMetrics.h
similarity index 91%
rename from include/core/SkAdvancedTypefaceMetrics.h
rename to src/core/SkAdvancedTypefaceMetrics.h
index 06757c0..34c5211 100644
--- a/include/core/SkAdvancedTypefaceMetrics.h
+++ b/src/core/SkAdvancedTypefaceMetrics.h
@@ -74,16 +74,6 @@
 
     SkIRect fBBox;  // The bounding box of all glyphs (in font units).
 
-    // The type of advance data wanted.
-    enum PerGlyphInfo {
-      kNo_PerGlyphInfo         = 0x0, // Don't populate any per glyph info.
-      kHAdvance_PerGlyphInfo   = 0x1, // Populate horizontal advance data.
-      kVAdvance_PerGlyphInfo   = 0x2, // Populate vertical advance data.
-      kGlyphNames_PerGlyphInfo = 0x4, // Populate glyph names (Type 1 only).
-      kToUnicode_PerGlyphInfo  = 0x8  // Populate ToUnicode table, ignored
-                                      // for Type 1 fonts
-    };
-
     template <typename Data>
     struct AdvanceMetric {
         enum MetricType {
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 0b586cb..6f90b71 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -516,6 +516,7 @@
                 break;
             case kAlpha_8_SkColorType:
             case kIndex_8_SkColorType:
+            case kGray_8_SkColorType:
                 base += x;
                 break;
             default:
@@ -532,6 +533,10 @@
     SkASSERT((unsigned)y < (unsigned)this->height());
 
     switch (this->colorType()) {
+        case kGray_8_SkColorType: {
+            uint8_t* addr = this->getAddr8(x, y);
+            return SkColorSetRGB(*addr, *addr, *addr);
+        }
         case kAlpha_8_SkColorType: {
             uint8_t* addr = this->getAddr8(x, y);
             return SkColorSetA(0, addr[0]);
@@ -597,6 +602,7 @@
             return 0xFF == SkGetPackedA32(c);
         } break;
         case kRGB_565_SkColorType:
+        case kGray_8_SkColorType:
             return true;
             break;
         case kARGB_4444_SkColorType: {
@@ -674,6 +680,20 @@
     const int rowBytes = fRowBytes;
 
     switch (this->colorType()) {
+        case kGray_8_SkColorType: {
+            if (255 != a) {
+                r = SkMulDiv255Round(r, a);
+                g = SkMulDiv255Round(g, a);
+                b = SkMulDiv255Round(b, a);
+            }
+            int gray = SkComputeLuminance(r, g, b);
+            uint8_t* p = this->getAddr8(area.fLeft, area.fTop);
+            while (--height >= 0) {
+                memset(p, gray, width);
+                p += rowBytes;
+            }
+            break;
+        }
         case kAlpha_8_SkColorType: {
             uint8_t* p = this->getAddr8(area.fLeft, area.fTop);
             while (--height >= 0) {
@@ -684,7 +704,7 @@
         }
         case kARGB_4444_SkColorType:
         case kRGB_565_SkColorType: {
-            uint16_t* p = this->getAddr16(area.fLeft, area.fTop);;
+            uint16_t* p = this->getAddr16(area.fLeft, area.fTop);
             uint16_t v;
 
             // make rgb premultiplied
@@ -828,6 +848,16 @@
             break;
         case kARGB_4444_SkColorType:
             return sameConfigs || kN32_SkColorType == srcCT || kIndex_8_SkColorType == srcCT;
+        case kGray_8_SkColorType:
+            switch (srcCT) {
+                case kGray_8_SkColorType:
+                case kRGBA_8888_SkColorType:
+                case kBGRA_8888_SkColorType:
+                    return true;
+                default:
+                    break;
+            }
+            return false;
         default:
             return false;
     }
@@ -1245,53 +1275,6 @@
     SERIALIZE_PIXELTYPE_REF_DATA
 };
 
-void SkBitmap::legacyUnflatten(SkReadBuffer& buffer) {
-#ifdef SK_SUPPORT_LEGACY_PIXELREF_UNFLATTENABLE
-    this->reset();
-
-    SkImageInfo info;
-    info.unflatten(buffer);
-    size_t rowBytes = buffer.readInt();
-    if (!buffer.validate((info.width() >= 0) && (info.height() >= 0) &&
-                         SkColorTypeIsValid(info.fColorType) &&
-                         SkAlphaTypeIsValid(info.fAlphaType) &&
-                         SkColorTypeValidateAlphaType(info.fColorType, info.fAlphaType) &&
-                         info.validRowBytes(rowBytes))) {
-        return;
-    }
-
-    bool configIsValid = this->setInfo(info, rowBytes);
-    buffer.validate(configIsValid);
-
-    int reftype = buffer.readInt();
-    if (buffer.validate((SERIALIZE_PIXELTYPE_REF_DATA == reftype) ||
-                        (SERIALIZE_PIXELTYPE_NONE == reftype))) {
-        switch (reftype) {
-            case SERIALIZE_PIXELTYPE_REF_DATA: {
-                SkIPoint origin;
-                origin.fX = buffer.readInt();
-                origin.fY = buffer.readInt();
-                size_t offset = origin.fY * rowBytes + origin.fX * info.bytesPerPixel();
-                SkPixelRef* pr = buffer.readFlattenable<SkPixelRef>();
-                if (!buffer.validate((NULL == pr) ||
-                       (pr->getAllocatedSizeInBytes() >= (offset + this->getSafeSize())))) {
-                    origin.setZero();
-                }
-                SkSafeUnref(this->setPixelRef(pr, origin));
-                break;
-            }
-            case SERIALIZE_PIXELTYPE_NONE:
-                break;
-            default:
-                SkDEBUGFAIL("unrecognized pixeltype in serialized data");
-                sk_throw();
-        }
-    }
-#else
-    sk_throw();
-#endif
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 SkBitmap::RLEPixels::RLEPixels(int width, int height) {
diff --git a/src/core/SkBitmapCache.cpp b/src/core/SkBitmapCache.cpp
index f569db8..aabf87a 100644
--- a/src/core/SkBitmapCache.cpp
+++ b/src/core/SkBitmapCache.cpp
@@ -71,8 +71,8 @@
         , fBitmap(result)
     {}
 
-    const Key& getKey() const SK_OVERRIDE { return fKey; }
-    size_t bytesUsed() const SK_OVERRIDE { return sizeof(fKey) + fBitmap.getSize(); }
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override { return sizeof(fKey) + fBitmap.getSize(); }
 
     static bool Finder(const SkResourceCache::Rec& baseRec, void* contextBitmap) {
         const BitmapRec& rec = static_cast<const BitmapRec&>(baseRec);
@@ -171,8 +171,8 @@
         fMipMap->detachFromCacheAndUnref();
     }
 
-    const Key& getKey() const SK_OVERRIDE { return fKey; }
-    size_t bytesUsed() const SK_OVERRIDE { return sizeof(fKey) + fMipMap->size(); }
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override { return sizeof(fKey) + fMipMap->size(); }
 
     static bool Finder(const SkResourceCache::Rec& baseRec, void* contextMip) {
         const MipMapRec& rec = static_cast<const MipMapRec&>(baseRec);
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 37cbff5..5b2fc38 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -103,6 +103,11 @@
     return fBitmap.info();
 }
 
+void SkBitmapDevice::setNewSize(const SkISize& size) {
+    SkASSERT(!fBitmap.pixelRef());
+    fBitmap.setInfo(fBitmap.info().makeWH(size.fWidth, size.fHeight));
+}
+
 void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
     SkASSERT(bm.width() == fBitmap.width());
     SkASSERT(bm.height() == fBitmap.height());
@@ -110,7 +115,7 @@
     fBitmap.lockPixels();
 }
 
-SkBaseDevice* SkBitmapDevice::onCreateCompatibleDevice(const CreateInfo& cinfo) {
+SkBaseDevice* SkBitmapDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) {
     SkDeviceProperties leaky(cinfo.fPixelGeometry);
     return SkBitmapDevice::Create(cinfo.fInfo, &leaky);
 }
diff --git a/src/core/SkBitmapFilter.h b/src/core/SkBitmapFilter.h
index 139e990..93b552e 100644
--- a/src/core/SkBitmapFilter.h
+++ b/src/core/SkBitmapFilter.h
@@ -79,7 +79,7 @@
       : SkBitmapFilter(width), B(b), C(c) {
       }
 
-      float evaluate(float x) const SK_OVERRIDE {
+      float evaluate(float x) const override {
           x = fabsf(x);
           if (x > 2.f) {
               return 0;
@@ -102,7 +102,7 @@
       : SkBitmapFilter(width), alpha(a), expWidth(expf(-alpha * width * width)) {
       }
 
-      float evaluate(float x) const SK_OVERRIDE {
+      float evaluate(float x) const override {
           return SkTMax(0.f, float(expf(-alpha*x*x) - expWidth));
       }
   protected:
@@ -115,7 +115,7 @@
       : SkBitmapFilter(width) {
       }
 
-      float evaluate(float x) const SK_OVERRIDE {
+      float evaluate(float x) const override {
           return SkTMax(0.f, fWidth - fabsf(x));
       }
   protected:
@@ -127,7 +127,7 @@
       : SkBitmapFilter(width) {
       }
 
-      float evaluate(float x) const SK_OVERRIDE {
+      float evaluate(float x) const override {
           return (x >= -fWidth && x < fWidth) ? 1.0f : 0.0f;
       }
   protected:
@@ -138,7 +138,7 @@
     SkHammingFilter(float width=1.f)
     : SkBitmapFilter(width) {
     }
-    float evaluate(float x) const SK_OVERRIDE {
+    float evaluate(float x) const override {
         if (x <= -fWidth || x >= fWidth) {
             return 0.0f;  // Outside of the window.
         }
@@ -158,7 +158,7 @@
       : SkBitmapFilter(width) {
       }
 
-      float evaluate(float x) const SK_OVERRIDE {
+      float evaluate(float x) const override {
           if (x <= -fWidth || x >= fWidth) {
               return 0.0f;  // Outside of the window.
           }
diff --git a/src/core/SkBitmapHeap.h b/src/core/SkBitmapHeap.h
index 95e2724..fe171ff 100644
--- a/src/core/SkBitmapHeap.h
+++ b/src/core/SkBitmapHeap.h
@@ -117,7 +117,7 @@
      *
      * @return  The bitmap located at that slot or NULL if external storage is being used.
      */
-    SkBitmap* getBitmap(int32_t slot) const SK_OVERRIDE {
+    SkBitmap* getBitmap(int32_t slot) const override {
         SkASSERT(fExternalStorage == NULL);
         SkBitmapHeapEntry* entry = getEntry(slot);
         if (entry) {
@@ -131,7 +131,7 @@
      *
      * @return  The bitmap located at that slot or NULL if external storage is being used.
      */
-    void releaseRef(int32_t slot) SK_OVERRIDE {
+    void releaseRef(int32_t slot) override {
         SkASSERT(fExternalStorage == NULL);
         if (fOwnerCount != IGNORE_OWNERS) {
             SkBitmapHeapEntry* entry = getEntry(slot);
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index 456e0d4..c1a03fe 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -397,14 +397,14 @@
     // are provided by the caller.
     bool useBicubic = false;
     GrTextureParams::FilterMode textureFilterMode;
-    switch(paint.getFilterLevel()) {
-        case SkPaint::kNone_FilterLevel:
+    switch(paint.getFilterQuality()) {
+        case kNone_SkFilterQuality:
             textureFilterMode = GrTextureParams::kNone_FilterMode;
             break;
-        case SkPaint::kLow_FilterLevel:
+        case kLow_SkFilterQuality:
             textureFilterMode = GrTextureParams::kBilerp_FilterMode;
             break;
-        case SkPaint::kMedium_FilterLevel: {
+        case kMedium_SkFilterQuality: {
             SkMatrix matrix;
             matrix.setConcat(viewM, this->getLocalMatrix());
             if (matrix.getMinScale() < SK_Scalar1) {
@@ -415,7 +415,7 @@
             }
             break;
         }
-        case SkPaint::kHigh_FilterLevel: {
+        case kHigh_SkFilterQuality: {
             SkMatrix matrix;
             matrix.setConcat(viewM, this->getLocalMatrix());
             useBicubic = GrBicubicEffect::ShouldUseBicubic(matrix, &textureFilterMode);
diff --git a/src/core/SkBitmapProcShader.h b/src/core/SkBitmapProcShader.h
index f73d56f..bdb2d4d 100644
--- a/src/core/SkBitmapProcShader.h
+++ b/src/core/SkBitmapProcShader.h
@@ -20,10 +20,10 @@
                        const SkMatrix* localMatrix = NULL);
 
     // overrides from SkShader
-    bool isOpaque() const SK_OVERRIDE;
-    BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const SK_OVERRIDE;
+    bool isOpaque() const override;
+    BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const override;
 
-    size_t contextSize() const SK_OVERRIDE;
+    size_t contextSize() const override;
 
     static bool CanDo(const SkBitmap&, TileMode tx, TileMode ty);
 
@@ -32,7 +32,7 @@
 
 
     bool asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix& viewM, const SkMatrix*,
-                             GrColor*, GrFragmentProcessor**) const SK_OVERRIDE;
+                             GrColor*, GrFragmentProcessor**) const override;
 
     class BitmapProcShaderContext : public SkShader::Context {
     public:
@@ -41,11 +41,11 @@
         BitmapProcShaderContext(const SkBitmapProcShader&, const ContextRec&, SkBitmapProcState*);
         virtual ~BitmapProcShaderContext();
 
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
-        ShadeProc asAShadeProc(void** ctx) SK_OVERRIDE;
-        void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
+        ShadeProc asAShadeProc(void** ctx) override;
+        void shadeSpan16(int x, int y, uint16_t dstC[], int count) override;
 
-        uint32_t getFlags() const SK_OVERRIDE { return fFlags; }
+        uint32_t getFlags() const override { return fFlags; }
 
     private:
         SkBitmapProcState*  fState;
@@ -55,8 +55,8 @@
     };
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
+    Context* onCreateContext(const ContextRec&, void* storage) const override;
 
     SkBitmap    fRawBitmap;   // experimental for RLE encoding
     uint8_t     fTileModeX, fTileModeY;
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index 335924f..0b50fbc 100644
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -98,16 +98,6 @@
     return (dimension & ~0x3FFF) == 0;
 }
 
-static SkScalar effective_matrix_scale(const SkMatrix& mat) {
-    SkScalar dx = SkVector::Length(mat.getScaleX(), mat.getSkewY());
-    SkScalar dy = SkVector::Length(mat.getSkewX(), mat.getScaleY());
-#ifdef SK_SUPPORT_LEGACY_MIPMAP_EFFECTIVE_SCALE
-    return SkMaxScalar(dx, dy);
-#else
-    return SkScalarSqrt(dx * dy);
-#endif
-}
-
 // Check to see that the size of the bitmap that would be produced by
 // scaling by the given inverted matrix is less than the maximum allowed.
 static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) {
@@ -123,31 +113,15 @@
 }
 
 /*
- *  Extract the "best" scale factors from a matrix.
- */
-static bool extract_scale(const SkMatrix& matrix, SkVector* scale) {
-    SkASSERT(!matrix.hasPerspective());
-    SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
-    SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX],  matrix[SkMatrix::kMScaleY]);
-    if (!SkScalarIsFinite(sx) || !SkScalarIsFinite(sy) ||
-        SkScalarNearlyZero(sx) || SkScalarNearlyZero(sy))
-    {
-        return false;
-    }
-    scale->set(sx, sy);
-    return true;
-}
-
-/*
  *  High quality is implemented by performing up-right scale-only filtering and then
  *  using bilerp for any remaining transformations.
  */
 void SkBitmapProcState::processHQRequest() {
-    SkASSERT(SkPaint::kHigh_FilterLevel == fFilterLevel);
+    SkASSERT(kHigh_SkFilterQuality == fFilterLevel);
 
     // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
     // to a valid bitmap. If we succeed, we will set this to Low instead.
-    fFilterLevel = SkPaint::kMedium_FilterLevel;
+    fFilterLevel = kMedium_SkFilterQuality;
 
     if (kN32_SkColorType != fOrigBitmap.colorType() || !cache_size_okay(fOrigBitmap, fInvMatrix) ||
         fInvMatrix.hasPerspective())
@@ -158,12 +132,12 @@
     SkScalar invScaleX = fInvMatrix.getScaleX();
     SkScalar invScaleY = fInvMatrix.getScaleY();
     if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
-        SkVector scale;
-        if (!extract_scale(fInvMatrix, &scale)) {
-            return; // can't find suitable scale factors
+        SkSize scale;
+        if (!fInvMatrix.decomposeScale(&scale)) {
+            return;
         }
-        invScaleX = scale.x();
-        invScaleY = scale.y();
+        invScaleX = scale.width();
+        invScaleY = scale.height();
     }
     if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
         return; // no need for HQ
@@ -194,7 +168,7 @@
 
     fInvMatrix.postScale(roundedDestWidth / fOrigBitmap.width(),
                          roundedDestHeight / fOrigBitmap.height());
-    fFilterLevel = SkPaint::kLow_FilterLevel;
+    fFilterLevel = kLow_SkFilterQuality;
 }
 
 /*
@@ -202,13 +176,17 @@
  *  (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling)
  */
 void SkBitmapProcState::processMediumRequest() {
-    SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel);
+    SkASSERT(kMedium_SkFilterQuality == fFilterLevel);
 
     // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap
     // to a valid bitmap.
-    fFilterLevel = SkPaint::kLow_FilterLevel;
+    fFilterLevel = kLow_SkFilterQuality;
 
-    SkScalar invScale = effective_matrix_scale(fInvMatrix);
+    SkSize invScaleSize;
+    if (!fInvMatrix.decomposeScale(&invScaleSize, NULL)) {
+        return;
+    }
+    SkScalar invScale = SkScalarSqrt(invScaleSize.width() * invScaleSize.height());
 
     if (invScale > SK_Scalar1) {
         fCurrMip.reset(SkMipMapCache::FindAndRef(fOrigBitmap));
@@ -289,17 +267,17 @@
 
     fBitmap = NULL;
     fInvMatrix = inv;
-    fFilterLevel = paint.getFilterLevel();
+    fFilterLevel = paint.getFilterQuality();
 
-    if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
+    if (kHigh_SkFilterQuality == fFilterLevel) {
         this->processHQRequest();
     }
-    SkASSERT(fFilterLevel < SkPaint::kHigh_FilterLevel);
+    SkASSERT(fFilterLevel < kHigh_SkFilterQuality);
 
-    if (SkPaint::kMedium_FilterLevel == fFilterLevel) {
+    if (kMedium_SkFilterQuality == fFilterLevel) {
         this->processMediumRequest();
     }
-    SkASSERT(fFilterLevel < SkPaint::kMedium_FilterLevel);
+    SkASSERT(fFilterLevel < kMedium_SkFilterQuality);
 
     if (NULL == fBitmap) {
         if (!this->lockBaseBitmap()) {
@@ -362,14 +340,14 @@
 
     trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
 
-    if (SkPaint::kLow_FilterLevel == fFilterLevel) {
+    if (kLow_SkFilterQuality == fFilterLevel) {
         // Only try bilerp if the matrix is "interesting" and
         // the image has a suitable size.
 
         if (fInvType <= SkMatrix::kTranslate_Mask ||
             !valid_for_filtering(fBitmap->width() | fBitmap->height()))
         {
-            fFilterLevel = SkPaint::kNone_FilterLevel;
+            fFilterLevel = kNone_SkFilterQuality;
         }
     }
 
@@ -392,7 +370,7 @@
     // still set to HQ by the time we get here, then we must have installed
     // the shader procs above and can skip all this.
 
-    if (fFilterLevel < SkPaint::kHigh_FilterLevel) {
+    if (fFilterLevel < kHigh_SkFilterQuality) {
 
         int index = 0;
         if (fAlphaScale < 256) {  // note: this distinction is not used for D16
@@ -401,7 +379,7 @@
         if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
             index |= 2;
         }
-        if (fFilterLevel > SkPaint::kNone_FilterLevel) {
+        if (fFilterLevel > kNone_SkFilterQuality) {
             index |= 4;
         }
         // bits 3,4,5 encoding the source bitmap format
@@ -431,6 +409,10 @@
                 index |= 32;
                 fPaintPMColor = SkPreMultiplyColor(paint.getColor());
                 break;
+            case kGray_8_SkColorType:
+                index |= 40;
+                fPaintPMColor = SkPreMultiplyColor(paint.getColor());
+                break;
             default:
                 // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
                 return false;
@@ -473,7 +455,7 @@
             S4444_alpha_D32_filter_DXDY,
             S4444_opaque_D32_filter_DX,
             S4444_alpha_D32_filter_DX,
-
+            
             // A8 treats alpha/opaque the same (equally efficient)
             SA8_alpha_D32_nofilter_DXDY,
             SA8_alpha_D32_nofilter_DXDY,
@@ -482,7 +464,17 @@
             SA8_alpha_D32_filter_DXDY,
             SA8_alpha_D32_filter_DXDY,
             SA8_alpha_D32_filter_DX,
-            SA8_alpha_D32_filter_DX
+            SA8_alpha_D32_filter_DX,
+            
+            // todo: possibly specialize on opaqueness
+            SG8_alpha_D32_nofilter_DXDY,
+            SG8_alpha_D32_nofilter_DXDY,
+            SG8_alpha_D32_nofilter_DX,
+            SG8_alpha_D32_nofilter_DX,
+            SG8_alpha_D32_filter_DXDY,
+            SG8_alpha_D32_filter_DXDY,
+            SG8_alpha_D32_filter_DX,
+            SG8_alpha_D32_filter_DX
         };
 
         static const SampleProc16 gSkBitmapProcStateSample16[] = {
@@ -504,6 +496,8 @@
             // Don't support 4444 -> 565
             NULL, NULL, NULL, NULL,
             // Don't support A8 -> 565
+            NULL, NULL, NULL, NULL,
+            // Don't support G8 -> 565 (but we could)
             NULL, NULL, NULL, NULL
         };
 #endif
@@ -544,7 +538,7 @@
     SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
     SkASSERT(s.fInvKy == 0);
     SkASSERT(count > 0 && colors != NULL);
-    SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
 
     const int maxX = s.fBitmap->width() - 1;
     const int maxY = s.fBitmap->height() - 1;
@@ -618,7 +612,7 @@
     SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
     SkASSERT(s.fInvKy == 0);
     SkASSERT(count > 0 && colors != NULL);
-    SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
 
     const int stopX = s.fBitmap->width();
     const int stopY = s.fBitmap->height();
@@ -664,7 +658,7 @@
     int iY1   SK_INIT_TO_AVOID_WARNING;
     int iSubY SK_INIT_TO_AVOID_WARNING;
 
-    if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
+    if (kNone_SkFilterQuality != s.fFilterLevel) {
         SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
         uint32_t xy[2];
 
@@ -745,7 +739,7 @@
     const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
     SkPMColor color;
 
-    if (SkPaint::kNone_FilterLevel != s.fFilterLevel) {
+    if (kNone_SkFilterQuality != s.fFilterLevel) {
         const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
 
         if (s.fAlphaScale < 256) {
@@ -801,7 +795,7 @@
     static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
 
     if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
-        if (SkPaint::kNone_FilterLevel == fFilterLevel &&
+        if (kNone_SkFilterQuality == fFilterLevel &&
             fInvType <= SkMatrix::kTranslate_Mask &&
             !this->setupForTranslate()) {
             return DoNothing_shaderproc;
@@ -815,7 +809,7 @@
     if (fInvType > SkMatrix::kTranslate_Mask) {
         return NULL;
     }
-    if (SkPaint::kNone_FilterLevel != fFilterLevel) {
+    if (kNone_SkFilterQuality != fFilterLevel) {
         return NULL;
     }
 
@@ -911,9 +905,9 @@
     //  scale -vs- affine
     //  filter -vs- nofilter
     if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
-        proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_scale_filter : check_scale_nofilter;
+        proc = state.fFilterLevel != kNone_SkFilterQuality ? check_scale_filter : check_scale_nofilter;
     } else {
-        proc = state.fFilterLevel != SkPaint::kNone_FilterLevel ? check_affine_filter : check_affine_nofilter;
+        proc = state.fFilterLevel != kNone_SkFilterQuality ? check_affine_filter : check_affine_nofilter;
     }
     proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
 }
@@ -948,7 +942,7 @@
         size >>= 2;
     }
 
-    if (fFilterLevel != SkPaint::kNone_FilterLevel) {
+    if (fFilterLevel != kNone_SkFilterQuality) {
         size >>= 1;
     }
 
diff --git a/src/core/SkBitmapProcState_matrixProcs.cpp b/src/core/SkBitmapProcState_matrixProcs.cpp
index 851389c..b7a2ccb 100644
--- a/src/core/SkBitmapProcState_matrixProcs.cpp
+++ b/src/core/SkBitmapProcState_matrixProcs.cpp
@@ -477,7 +477,7 @@
 SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc(bool trivial_matrix) {
 //    test_int_tileprocs();
     // check for our special case when there is no scale/affine/perspective
-    if (trivial_matrix && SkPaint::kNone_FilterLevel == fFilterLevel) {
+    if (trivial_matrix && kNone_SkFilterQuality == fFilterLevel) {
         fIntTileProcY = choose_int_tile_proc(fTileModeY);
         switch (fTileModeX) {
             case SkShader::kClamp_TileMode:
@@ -490,7 +490,7 @@
     }
 
     int index = 0;
-    if (fFilterLevel != SkPaint::kNone_FilterLevel) {
+    if (fFilterLevel != kNone_SkFilterQuality) {
         index = 1;
     }
     if (fInvType & SkMatrix::kPerspective_Mask) {
diff --git a/src/core/SkBitmapProcState_procs.h b/src/core/SkBitmapProcState_procs.h
index 3b4cef3..a9b8750 100644
--- a/src/core/SkBitmapProcState_procs.h
+++ b/src/core/SkBitmapProcState_procs.h
@@ -224,6 +224,25 @@
 #define SRC_TO_FILTER(src)      src
 #include "SkBitmapProcState_sample.h"
 
+// SRC == Gray8
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst) \
+    do {                                                        \
+        unsigned tmp = Filter_8(x, y, a, b, c, d);              \
+        SkPMColor color = SkPackARGB32(0xFF, tmp, tmp, tmp);    \
+        *(dst) = SkAlphaMulQ(color, alphaScale);                \
+    } while (0)
+
+#define MAKENAME(suffix)        NAME_WRAP(SG8_alpha_D32 ## suffix)
+#define DSTSIZE                 32
+#define SRCTYPE                 uint8_t
+#define CHECKSTATE(state)       SkASSERT(kGray_8_SkColorType == state.fBitmap->colorType());
+#define PREAMBLE(state)         unsigned alphaScale = state.fAlphaScale
+#define RETURNDST(src)          SkAlphaMulQ(SkPackARGB32(0xFF, src, src, src), alphaScale)
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_sample.h"
+
 /*****************************************************************************
  *
  *  D16 functions
diff --git a/src/core/SkBitmapProcState_sample.h b/src/core/SkBitmapProcState_sample.h
index 5c5f199..7030516 100644
--- a/src/core/SkBitmapProcState_sample.h
+++ b/src/core/SkBitmapProcState_sample.h
@@ -1,10 +1,10 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "SkUtils.h"
 
 #if DSTSIZE==32
@@ -42,7 +42,7 @@
                               const uint32_t* SK_RESTRICT xy,
                               int count, DSTTYPE* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != NULL);
-    SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
     SkDEBUGCODE(CHECKSTATE(s);)
 
 #ifdef PREAMBLE
@@ -85,7 +85,7 @@
                             int count, DSTTYPE* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != NULL);
     SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
-    SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
     SkDEBUGCODE(CHECKSTATE(s);)
 
 #ifdef PREAMBLE
@@ -139,7 +139,7 @@
                           const uint32_t* SK_RESTRICT xy,
                            int count, DSTTYPE* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != NULL);
-    SkASSERT(s.fFilterLevel != SkPaint::kNone_FilterLevel);
+    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
     SkDEBUGCODE(CHECKSTATE(s);)
 
 #ifdef PREAMBLE
@@ -185,7 +185,7 @@
                             const uint32_t* SK_RESTRICT xy,
                             int count, DSTTYPE* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != NULL);
-    SkASSERT(s.fFilterLevel != SkPaint::kNone_FilterLevel);
+    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
     SkDEBUGCODE(CHECKSTATE(s);)
 
 #ifdef PREAMBLE
diff --git a/src/core/SkBitmapProcState_shaderproc.h b/src/core/SkBitmapProcState_shaderproc.h
index 0014b4a..7a7d45e 100644
--- a/src/core/SkBitmapProcState_shaderproc.h
+++ b/src/core/SkBitmapProcState_shaderproc.h
@@ -21,7 +21,7 @@
                              SkMatrix::kScale_Mask)) == 0);
     SkASSERT(s.fInvKy == 0);
     SkASSERT(count > 0 && colors != NULL);
-    SkASSERT(s.fFilterLevel != SkPaint::kNone_FilterLevel);
+    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
     SkDEBUGCODE(CHECKSTATE(s);)
 
     const unsigned maxX = s.fBitmap->width() - 1;
diff --git a/src/core/SkBlitRow_D16.cpp b/src/core/SkBlitRow_D16.cpp
index d508276..5aaa7a5 100644
--- a/src/core/SkBlitRow_D16.cpp
+++ b/src/core/SkBlitRow_D16.cpp
@@ -216,8 +216,7 @@
     uint32_t src_expand = pmcolor_to_expand16(src);
     unsigned scale = SkAlpha255To256(0xFF - SkGetPackedA32(src)) >> 3;
     do {
-        uint32_t dst_expand = SkExpand_rgb_16(*dst) * scale;
-        *dst = SkCompact_rgb_16((src_expand + dst_expand) >> 5);
+        *dst = SkBlend32_RGB16(src_expand, *dst, scale);
         dst += 1;
     } while (--count != 0);
 }
@@ -269,7 +268,7 @@
     // just so we don't crash
     flags &= kFlags16_Mask;
     // we ignore both kGlobalAlpha_Flag and kSrcPixelAlpha_Flag, so shift down
-    // since this factory is only used for transparent source alphas
+    // no need for the additional code specializing on opaque alpha at this time
     flags >>= 2;
 
     SkASSERT(flags < SK_ARRAY_COUNT(gDefault_565_ColorProcs));
diff --git a/src/core/SkBlitRow_D32.cpp b/src/core/SkBlitRow_D32.cpp
index 509eeeb..f5cb45d 100644
--- a/src/core/SkBlitRow_D32.cpp
+++ b/src/core/SkBlitRow_D32.cpp
@@ -131,36 +131,27 @@
     return proc;
 }
 
-SkBlitRow::Proc32 SkBlitRow::ColorProcFactory() {
-    SkBlitRow::ColorProc proc = PlatformColorProc();
-    if (NULL == proc) {
-        proc = Color32;
-    }
-    SkASSERT(proc);
-    return proc;
-}
+#include "Sk4px.h"
 
-void SkBlitRow::Color32(SkPMColor* SK_RESTRICT dst,
-                        const SkPMColor* SK_RESTRICT src,
-                        int count, SkPMColor color) {
-    if (count > 0) {
-        if (0 == color) {
-            if (src != dst) {
-                memcpy(dst, src, count * sizeof(SkPMColor));
-            }
-            return;
-        }
-        unsigned colorA = SkGetPackedA32(color);
-        if (255 == colorA) {
-            sk_memset32(dst, color, count);
-        } else {
-            unsigned scale = 256 - SkAlpha255To256(colorA);
-            do {
-                *dst = color + SkAlphaMulQ(*src, scale);
-                src += 1;
-                dst += 1;
-            } while (--count);
-        }
+// Color32 uses the blend_256_round_alt algorithm from tests/BlendTest.cpp.
+// It's not quite perfect, but it's never wrong in the interesting edge cases,
+// and it's quite a bit faster than blend_perfect.
+//
+// blend_256_round_alt is our currently blessed algorithm.  Please use it or an analogous one.
+void SkBlitRow::Color32(SkPMColor dst[], const SkPMColor src[], int count, SkPMColor color) {
+    switch (SkGetPackedA32(color)) {
+        case   0: memmove(dst, src, count * sizeof(SkPMColor)); return;
+        case 255: sk_memset32(dst, color, count);               return;
     }
-}
 
+    unsigned invA = 255 - SkGetPackedA32(color);
+    invA += invA >> 7;
+    SkASSERT(invA < 256);  // We've already handled alpha == 0 above.
+
+    Sk16h colorHighAndRound = Sk4px(color).widenHi() + Sk16h(128);
+    Sk16b invA_16x(invA);
+
+    Sk4px::MapSrc(count, dst, src, [&](const Sk4px& src4) -> Sk4px {
+        return src4.mulWiden(invA_16x).addNarrowHi(colorHighAndRound);
+    });
+}
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
index a5d2b05..d8553f1 100644
--- a/src/core/SkBlitter.cpp
+++ b/src/core/SkBlitter.cpp
@@ -580,7 +580,7 @@
         SkSafeUnref(fProxy);
     }
 
-    size_t contextSize() const SK_OVERRIDE {
+    size_t contextSize() const override {
         size_t size = sizeof(Sk3DShaderContext);
         if (fProxy) {
             size += fProxy->contextSize();
@@ -588,7 +588,7 @@
         return size;
     }
 
-    Context* onCreateContext(const ContextRec& rec, void* storage) const SK_OVERRIDE {
+    Context* onCreateContext(const ContextRec& rec, void* storage) const override {
         SkShader::Context* proxyContext = NULL;
         if (fProxy) {
             char* proxyContextStorage = (char*) storage + sizeof(Sk3DShaderContext);
@@ -620,9 +620,9 @@
             }
         }
 
-        void set3DMask(const SkMask* mask) SK_OVERRIDE { fMask = mask; }
+        void set3DMask(const SkMask* mask) override { fMask = mask; }
 
-        void shadeSpan(int x, int y, SkPMColor span[], int count) SK_OVERRIDE {
+        void shadeSpan(int x, int y, SkPMColor span[], int count) override {
             if (fProxyContext) {
                 fProxyContext->shadeSpan(x, y, span, count);
             }
@@ -697,7 +697,7 @@
     };
 
 #ifndef SK_IGNORE_TO_STRING
-    void toString(SkString* str) const SK_OVERRIDE {
+    void toString(SkString* str) const override {
         str->append("Sk3DShader: (");
 
         if (fProxy) {
@@ -714,7 +714,7 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Sk3DShader)
 
 protected:
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
+    void flatten(SkWriteBuffer& buffer) const override {
         buffer.writeFlattenable(fProxy);
     }
 
@@ -736,24 +736,24 @@
         , fShaderContext(shaderContext)
     {}
 
-    void blitH(int x, int y, int width) SK_OVERRIDE {
+    void blitH(int x, int y, int width) override {
         fProxy->blitH(x, y, width);
     }
 
     virtual void blitAntiH(int x, int y, const SkAlpha antialias[],
-                           const int16_t runs[]) SK_OVERRIDE {
+                           const int16_t runs[]) override {
         fProxy->blitAntiH(x, y, antialias, runs);
     }
 
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
+    void blitV(int x, int y, int height, SkAlpha alpha) override {
         fProxy->blitV(x, y, height, alpha);
     }
 
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+    void blitRect(int x, int y, int width, int height) override {
         fProxy->blitRect(x, y, width, height);
     }
 
-    void blitMask(const SkMask& mask, const SkIRect& clip) SK_OVERRIDE {
+    void blitMask(const SkMask& mask, const SkIRect& clip) override {
         if (mask.fFormat == SkMask::k3D_Format) {
             fShaderContext->set3DMask(&mask);
 
@@ -991,7 +991,7 @@
         // Override rec with the identity matrix, so it is guaranteed to be invertible.
         : INHERITED(shader, SkShader::ContextRec(*rec.fDevice, *rec.fPaint, SkMatrix::I())) {}
 
-    void shadeSpan(int x, int y, SkPMColor colors[], int count) SK_OVERRIDE {
+    void shadeSpan(int x, int y, SkPMColor colors[], int count) override {
         sk_bzero(colors, count * sizeof(SkPMColor));
     }
 
diff --git a/src/core/SkBlitter.h b/src/core/SkBlitter.h
index b0745c8..7cf8301 100644
--- a/src/core/SkBlitter.h
+++ b/src/core/SkBlitter.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkBlitter_DEFINED
 #define SkBlitter_DEFINED
 
@@ -55,6 +53,35 @@
     */
     virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
 
+    // (x, y), (x + 1, y)
+    virtual void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) {
+        int16_t runs[3];
+        uint8_t aa[2];
+        
+        runs[0] = 1;
+        runs[1] = 1;
+        runs[2] = 0;
+        aa[0] = SkToU8(a0);
+        aa[1] = SkToU8(a1);
+        this->blitAntiH(x, y, aa, runs);
+    }
+
+    // (x, y), (x, y + 1)
+    virtual void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) {
+        int16_t runs[2];
+        uint8_t aa[1];
+        
+        runs[0] = 1;
+        runs[1] = 0;
+        aa[0] = SkToU8(a0);
+        this->blitAntiH(x, y, aa, runs);
+        // reset in case the clipping blitter modified runs
+        runs[0] = 1;
+        runs[1] = 0;
+        aa[0] = SkToU8(a1);
+        this->blitAntiH(x, y + 1, aa, runs);
+    }
+    
     /**
      *  Special method just to identify the null blitter, which is returned
      *  from Choose() if the request cannot be fulfilled. Default impl
@@ -119,14 +146,14 @@
 */
 class SkNullBlitter : public SkBlitter {
 public:
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
     virtual void blitAntiH(int x, int y, const SkAlpha[],
-                           const int16_t runs[]) SK_OVERRIDE;
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
-    void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
-    const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
-    bool isNullBlitter() const SK_OVERRIDE;
+                           const int16_t runs[]) override;
+    void blitV(int x, int y, int height, SkAlpha alpha) override;
+    void blitRect(int x, int y, int width, int height) override;
+    void blitMask(const SkMask&, const SkIRect& clip) override;
+    const SkBitmap* justAnOpaqueColor(uint32_t* value) override;
+    bool isNullBlitter() const override;
 };
 
 /** Wraps another (real) blitter, and ensures that the real blitter is only
@@ -141,21 +168,21 @@
         fClipRect = clipRect;
     }
 
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
     virtual void blitAntiH(int x, int y, const SkAlpha[],
-                           const int16_t runs[]) SK_OVERRIDE;
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+                           const int16_t runs[]) override;
+    void blitV(int x, int y, int height, SkAlpha alpha) override;
+    void blitRect(int x, int y, int width, int height) override;
     virtual void blitAntiRect(int x, int y, int width, int height,
-                     SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE;
-    void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
-    const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
+                     SkAlpha leftAlpha, SkAlpha rightAlpha) override;
+    void blitMask(const SkMask&, const SkIRect& clip) override;
+    const SkBitmap* justAnOpaqueColor(uint32_t* value) override;
 
-    int requestRowsPreserved() const SK_OVERRIDE {
+    int requestRowsPreserved() const override {
         return fBlitter->requestRowsPreserved();
     }
 
-    void* allocBlitMemory(size_t sz) SK_OVERRIDE {
+    void* allocBlitMemory(size_t sz) override {
         return fBlitter->allocBlitMemory(sz);
     }
 
@@ -176,21 +203,21 @@
         fRgn = clipRgn;
     }
 
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
     virtual void blitAntiH(int x, int y, const SkAlpha[],
-                           const int16_t runs[]) SK_OVERRIDE;
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+                           const int16_t runs[]) override;
+    void blitV(int x, int y, int height, SkAlpha alpha) override;
+    void blitRect(int x, int y, int width, int height) override;
     virtual void blitAntiRect(int x, int y, int width, int height,
-                     SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE;
-    void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
-    const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
+                     SkAlpha leftAlpha, SkAlpha rightAlpha) override;
+    void blitMask(const SkMask&, const SkIRect& clip) override;
+    const SkBitmap* justAnOpaqueColor(uint32_t* value) override;
 
-    int requestRowsPreserved() const SK_OVERRIDE {
+    int requestRowsPreserved() const override {
         return fBlitter->requestRowsPreserved();
     }
 
-    void* allocBlitMemory(size_t sz) SK_OVERRIDE {
+    void* allocBlitMemory(size_t sz) override {
         return fBlitter->allocBlitMemory(sz);
     }
 
diff --git a/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp
index bbad6c7..f2de82a 100644
--- a/src/core/SkBlitter_ARGB32.cpp
+++ b/src/core/SkBlitter_ARGB32.cpp
@@ -52,7 +52,6 @@
     fSrcB = SkAlphaMul(SkColorGetB(color), scale);
 
     fPMColor = SkPackARGB32(fSrcA, fSrcR, fSrcG, fSrcB);
-    fColor32Proc = SkBlitRow::ColorProcFactory();
 }
 
 const SkBitmap* SkARGB32_Blitter::justAnOpaqueColor(uint32_t* value) {
@@ -72,7 +71,7 @@
     SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width());
 
     uint32_t*   device = fDevice.getAddr32(x, y);
-    fColor32Proc(device, device, width, fPMColor);
+    SkBlitRow::Color32(device, device, width, fPMColor);
 }
 
 void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
@@ -97,7 +96,7 @@
                 sk_memset32(device, color, count);
             } else {
                 uint32_t sc = SkAlphaMulQ(color, SkAlpha255To256(aa));
-                fColor32Proc(device, device, count, sc);
+                SkBlitRow::Color32(device, device, count, sc);
             }
         }
         runs += count;
@@ -106,6 +105,23 @@
     }
 }
 
+void SkARGB32_Blitter::blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) {
+    uint32_t* device = fDevice.getAddr32(x, y);
+    SkDEBUGCODE((void)fDevice.getAddr32(x + 1, y);)
+
+    device[0] = SkBlendARGB32(fPMColor, device[0], a0);
+    device[1] = SkBlendARGB32(fPMColor, device[1], a1);
+}
+
+void SkARGB32_Blitter::blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) {
+    uint32_t* device = fDevice.getAddr32(x, y);
+    SkDEBUGCODE((void)fDevice.getAddr32(x, y + 1);)
+
+    device[0] = SkBlendARGB32(fPMColor, device[0], a0);
+    device = (uint32_t*)((char*)device + fDevice.rowBytes());
+    device[0] = SkBlendARGB32(fPMColor, device[0], a1);
+}
+
 //////////////////////////////////////////////////////////////////////////////////////
 
 #define solid_8_pixels(mask, dst, color)    \
@@ -180,6 +196,23 @@
     }
 }
 
+void SkARGB32_Opaque_Blitter::blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) {
+    uint32_t* device = fDevice.getAddr32(x, y);
+    SkDEBUGCODE((void)fDevice.getAddr32(x + 1, y);)
+
+    device[0] = SkFastFourByteInterp(fPMColor, device[0], a0);
+    device[1] = SkFastFourByteInterp(fPMColor, device[1], a1);
+}
+
+void SkARGB32_Opaque_Blitter::blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) {
+    uint32_t* device = fDevice.getAddr32(x, y);
+    SkDEBUGCODE((void)fDevice.getAddr32(x, y + 1);)
+
+    device[0] = SkFastFourByteInterp(fPMColor, device[0], a0);
+    device = (uint32_t*)((char*)device + fDevice.rowBytes());
+    device[0] = SkFastFourByteInterp(fPMColor, device[0], a1);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkARGB32_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
@@ -214,7 +247,7 @@
     size_t      rowBytes = fDevice.rowBytes();
 
     while (--height >= 0) {
-        fColor32Proc(device, device, width, color);
+        SkBlitRow::Color32(device, device, width, color);
         device = (uint32_t*)((char*)device + rowBytes);
     }
 }
@@ -256,6 +289,23 @@
     }
 }
 
+void SkARGB32_Black_Blitter::blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) {
+    uint32_t* device = fDevice.getAddr32(x, y);
+    SkDEBUGCODE((void)fDevice.getAddr32(x + 1, y);)
+
+    device[0] = (a0 << SK_A32_SHIFT) + SkAlphaMulQ(device[0], 256 - a0);
+    device[1] = (a1 << SK_A32_SHIFT) + SkAlphaMulQ(device[1], 256 - a1);
+}
+
+void SkARGB32_Black_Blitter::blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) {
+    uint32_t* device = fDevice.getAddr32(x, y);
+    SkDEBUGCODE((void)fDevice.getAddr32(x, y + 1);)
+
+    device[0] = (a0 << SK_A32_SHIFT) + SkAlphaMulQ(device[0], 256 - a0);
+    device = (uint32_t*)((char*)device + fDevice.rowBytes());
+    device[0] = (a1 << SK_A32_SHIFT) + SkAlphaMulQ(device[0], 256 - a1);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 // Special version of SkBlitRow::Factory32 that knows we're in kSrc_Mode,
diff --git a/src/core/SkBlitter_RGB16.cpp b/src/core/SkBlitter_RGB16.cpp
index 306c95b..ed9e103 100644
--- a/src/core/SkBlitter_RGB16.cpp
+++ b/src/core/SkBlitter_RGB16.cpp
@@ -59,14 +59,14 @@
 class SkRGB16_Blitter : public SkRasterBlitter {
 public:
     SkRGB16_Blitter(const SkBitmap& device, const SkPaint& paint);
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
     virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
-                           const int16_t* runs) SK_OVERRIDE;
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+                           const int16_t* runs) override;
+    void blitV(int x, int y, int height, SkAlpha alpha) override;
+    void blitRect(int x, int y, int width, int height) override;
     virtual void blitMask(const SkMask&,
-                          const SkIRect&) SK_OVERRIDE;
-    const SkBitmap* justAnOpaqueColor(uint32_t*) SK_OVERRIDE;
+                          const SkIRect&) override;
+    const SkBitmap* justAnOpaqueColor(uint32_t*) override;
 
 protected:
     SkPMColor   fSrcColor32;
@@ -88,13 +88,13 @@
 class SkRGB16_Opaque_Blitter : public SkRGB16_Blitter {
 public:
     SkRGB16_Opaque_Blitter(const SkBitmap& device, const SkPaint& paint);
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
     virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
-                           const int16_t* runs) SK_OVERRIDE;
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+                           const int16_t* runs) override;
+    void blitV(int x, int y, int height, SkAlpha alpha) override;
+    void blitRect(int x, int y, int width, int height) override;
     virtual void blitMask(const SkMask&,
-                          const SkIRect&) SK_OVERRIDE;
+                          const SkIRect&) override;
 
 private:
     typedef SkRGB16_Blitter INHERITED;
@@ -104,9 +104,9 @@
 class SkRGB16_Black_Blitter : public SkRGB16_Opaque_Blitter {
 public:
     SkRGB16_Black_Blitter(const SkBitmap& device, const SkPaint& paint);
-    void blitMask(const SkMask&, const SkIRect&) SK_OVERRIDE;
+    void blitMask(const SkMask&, const SkIRect&) override;
     virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
-                           const int16_t* runs) SK_OVERRIDE;
+                           const int16_t* runs) override;
 
 private:
     typedef SkRGB16_Opaque_Blitter INHERITED;
@@ -118,10 +118,10 @@
     SkRGB16_Shader_Blitter(const SkBitmap& device, const SkPaint& paint,
                            SkShader::Context* shaderContext);
     virtual ~SkRGB16_Shader_Blitter();
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
     virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
-                           const int16_t* runs) SK_OVERRIDE;
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+                           const int16_t* runs) override;
+    void blitRect(int x, int y, int width, int height) override;
 
 protected:
     SkPMColor*          fBuffer;
@@ -140,10 +140,10 @@
 public:
     SkRGB16_Shader16_Blitter(const SkBitmap& device, const SkPaint& paint,
                              SkShader::Context* shaderContext);
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
     virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
-                           const int16_t* runs) SK_OVERRIDE;
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+                           const int16_t* runs) override;
+    void blitRect(int x, int y, int width, int height) override;
 
 private:
     typedef SkRGB16_Shader_Blitter INHERITED;
@@ -154,9 +154,9 @@
     SkRGB16_Shader_Xfermode_Blitter(const SkBitmap& device, const SkPaint& paint,
                                     SkShader::Context* shaderContext);
     virtual ~SkRGB16_Shader_Xfermode_Blitter();
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
     virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
-                           const int16_t* runs) SK_OVERRIDE;
+                           const int16_t* runs) override;
 
 private:
     SkXfermode* fXfermode;
diff --git a/src/core/SkBuffer.h b/src/core/SkBuffer.h
index 96e225b..aaafb36 100644
--- a/src/core/SkBuffer.h
+++ b/src/core/SkBuffer.h
@@ -105,7 +105,7 @@
         null and the number of bytes to read does not overflow this object's data,
         copy those bytes into buffer.
     */
-    bool read(void* buffer, size_t size) SK_OVERRIDE;
+    bool read(void* buffer, size_t size) override;
 
     /** Returns whether or not a read operation attempted to read past the end of the data.
     */
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index a31ded0..34d6aaa 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -8,6 +8,7 @@
 #include "SkCanvas.h"
 #include "SkCanvasPriv.h"
 #include "SkBitmapDevice.h"
+#include "SkColorFilter.h"
 #include "SkDeviceImageFilterProxy.h"
 #include "SkDraw.h"
 #include "SkDrawable.h"
@@ -111,13 +112,16 @@
     DeviceCM*           fNext;
     SkBaseDevice*       fDevice;
     SkRasterClip        fClip;
-    const SkMatrix*     fMatrix;
     SkPaint*            fPaint; // may be null (in the future)
+    const SkMatrix*     fMatrix;
+    SkMatrix            fMatrixStorage;
+    const bool          fDeviceIsBitmapDevice;
 
     DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
-             bool conservativeRasterClip)
+             bool conservativeRasterClip, bool deviceIsBitmapDevice)
         : fNext(NULL)
         , fClip(conservativeRasterClip)
+        , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
     {
         if (NULL != device) {
             device->ref();
@@ -135,6 +139,13 @@
         SkDELETE(fPaint);
     }
 
+    void reset(const SkIRect& bounds) {
+        SkASSERT(!fPaint);
+        SkASSERT(!fNext);
+        SkASSERT(fDevice);
+        fClip.setRect(bounds);
+    }
+
     void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
                   const SkClipStack& clipStack, SkRasterClip* updateClip) {
         int x = fDevice->getOrigin().x();
@@ -173,9 +184,6 @@
         }
 #endif
     }
-
-private:
-    SkMatrix    fMatrixStorage;
 };
 
 /*  This is the record we keep for each save/restore level in the stack.
@@ -224,6 +232,15 @@
         SkDELETE(fLayer);
         dec_rec();
     }
+
+    void reset(const SkIRect& bounds) {
+        SkASSERT(fLayer);
+        SkASSERT(fDeferredSaveCount == 0);
+
+        fMatrix.reset();
+        fRasterClip.setRect(bounds);
+        fLayer->reset(bounds);
+    }
 };
 
 class SkDrawIter : public SkDraw {
@@ -233,7 +250,7 @@
         fCanvas = canvas;
         canvas->updateDeviceCMCache();
 
-        fClipStack = canvas->fClipStack.get();
+        fClipStack = canvas->fClipStack;
         fCurrLayer = canvas->fMCRec->fTopLayer;
         fSkipEmptyClips = skipEmptyClips;
     }
@@ -283,6 +300,37 @@
 
 /////////////////////////////////////////////////////////////////////////////
 
+static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
+    return lazy->isValid() ? lazy->get() : lazy->set(orig);
+}
+
+/**
+ *  If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
+ *  colorfilter, else return NULL.
+ */
+static SkColorFilter* image_to_color_filter(const SkPaint& paint) {
+    SkImageFilter* imgf = paint.getImageFilter();
+    if (!imgf) {
+        return NULL;
+    }
+
+    SkColorFilter* imgCF;
+    if (!imgf->asAColorFilter(&imgCF)) {
+        return NULL;
+    }
+
+    SkColorFilter* paintCF = paint.getColorFilter();
+    if (NULL == paintCF) {
+        // there is no existing paint colorfilter, so we can just return the imagefilter's
+        return imgCF;
+    }
+
+    // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
+    // and we need to combine them into a single colorfilter.
+    SkAutoTUnref<SkColorFilter> autoImgCF(imgCF);
+    return SkColorFilter::CreateComposeFilter(imgCF, paintCF);
+}
+
 class AutoDrawLooper {
 public:
     AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
@@ -292,17 +340,40 @@
         fFilter = canvas->getDrawFilter();
         fPaint = &fOrigPaint;
         fSaveCount = canvas->getSaveCount();
-        fDoClearImageFilter = false;
+        fTempLayerForImageFilter = false;
         fDone = false;
 
-        if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
+        SkColorFilter* simplifiedCF = image_to_color_filter(fOrigPaint);
+        if (simplifiedCF) {
+            SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
+            paint->setColorFilter(simplifiedCF)->unref();
+            paint->setImageFilter(NULL);
+            fPaint = paint;
+        }
+
+        if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
+            /**
+             *  We implement ImageFilters for a given draw by creating a layer, then applying the
+             *  imagefilter to the pixels of that layer (its backing surface/image), and then
+             *  we call restore() to xfer that layer to the main canvas.
+             *
+             *  1. SaveLayer (with a paint containing the current imagefilter and xfermode)
+             *  2. Generate the src pixels:
+             *      Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
+             *      return (fPaint). We then draw the primitive (using srcover) into a cleared
+             *      buffer/surface.
+             *  3. Restore the layer created in #1
+             *      The imagefilter is passed the buffer/surface from the layer (now filled with the
+             *      src pixels of the primitive). It returns a new "filtered" buffer, which we
+             *      draw onto the previous layer using the xfermode from the original paint.
+             */
             SkPaint tmp;
-            tmp.setImageFilter(fOrigPaint.getImageFilter());
+            tmp.setImageFilter(fPaint->getImageFilter());
+            tmp.setXfermode(fPaint->getXfermode());
             (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
-                                            true, SkCanvas::kFullLayer_SaveLayerStrategy);
-            // we'll clear the imageFilter for the actual draws in next(), so
-            // it will only be applied during the restore().
-            fDoClearImageFilter = true;
+                                            SkCanvas::kFullLayer_SaveLayerStrategy);
+            fTempLayerForImageFilter = true;
+            // we remove the imagefilter/xfermode inside doNext()
         }
 
         if (SkDrawLooper* looper = paint.getLooper()) {
@@ -313,13 +384,13 @@
         } else {
             fLooperContext = NULL;
             // can we be marked as simple?
-            fIsSimple = !fFilter && !fDoClearImageFilter;
+            fIsSimple = !fFilter && !fTempLayerForImageFilter;
         }
 
         uint32_t oldFlags = paint.getFlags();
         fNewPaintFlags = filter_paint_flags(props, oldFlags);
         if (fIsSimple && (fNewPaintFlags != oldFlags)) {
-            SkPaint* paint = fLazyPaint.set(fOrigPaint);
+            SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
             paint->setFlags(fNewPaintFlags);
             fPaint = paint;
             // if we're not simple, doNext() will take care of calling setFlags()
@@ -327,7 +398,7 @@
     }
 
     ~AutoDrawLooper() {
-        if (fDoClearImageFilter) {
+        if (fTempLayerForImageFilter) {
             fCanvas->internalRestore();
         }
         SkASSERT(fCanvas->getSaveCount() == fSaveCount);
@@ -350,14 +421,15 @@
     }
 
 private:
-    SkLazyPaint     fLazyPaint;
+    SkLazyPaint     fLazyPaintInit; // base paint storage in case we need to modify it
+    SkLazyPaint     fLazyPaintPerLooper;  // per-draw-looper storage, so the looper can modify it
     SkCanvas*       fCanvas;
     const SkPaint&  fOrigPaint;
     SkDrawFilter*   fFilter;
     const SkPaint*  fPaint;
     int             fSaveCount;
     uint32_t        fNewPaintFlags;
-    bool            fDoClearImageFilter;
+    bool            fTempLayerForImageFilter;
     bool            fDone;
     bool            fIsSimple;
     SkDrawLooper::Context* fLooperContext;
@@ -369,13 +441,15 @@
 bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
     fPaint = NULL;
     SkASSERT(!fIsSimple);
-    SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
+    SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
 
-    SkPaint* paint = fLazyPaint.set(fOrigPaint);
+    SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
+                                             *fLazyPaintInit.get() : fOrigPaint);
     paint->setFlags(fNewPaintFlags);
 
-    if (fDoClearImageFilter) {
+    if (fTempLayerForImageFilter) {
         paint->setImageFilter(NULL);
+        paint->setXfermode(NULL);
     }
 
     if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
@@ -425,6 +499,18 @@
 
 ////////////////////////////////////////////////////////////////////////////
 
+void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
+    this->restoreToCount(1);
+    fCachedLocalClipBounds.setEmpty();
+    fCachedLocalClipBoundsDirty = true;
+    fClipStack->reset();
+    fMCRec->reset(bounds);
+
+    // We're peering through a lot of structs here.  Only at this scope do we
+    // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
+    static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
+}
+
 SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
     fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
     fCachedLocalClipBounds.setEmpty();
@@ -435,16 +521,19 @@
     fSaveCount = 1;
     fMetaData = NULL;
 
+    fClipStack.reset(SkNEW(SkClipStack));
+
     fMCRec = (MCRec*)fMCStack.push_back();
     new (fMCRec) MCRec(fConservativeRasterClip);
 
-    fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, NULL, NULL, fConservativeRasterClip));
+    SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
+    fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
+    new (fDeviceCMStorage) DeviceCM(NULL, NULL, NULL, fConservativeRasterClip, false);
+
     fMCRec->fTopLayer = fMCRec->fLayer;
 
     fSurfaceBase = NULL;
 
-    fClipStack.reset(SkNEW(SkClipStack));
-
     if (device) {
         device->initForRootLayer(fProps.pixelGeometry());
         if (device->forceConservativeRasterClip()) {
@@ -558,6 +647,7 @@
 }
 
 SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
+    this->checkForDeferredSave();
     SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
     return filter;
 }
@@ -753,7 +843,6 @@
 
 void SkCanvas::checkForDeferredSave() {
     if (fMCRec->fDeferredSaveCount > 0) {
-        fMCRec->fDeferredSaveCount -= 1;
         this->doSave();
     }
 }
@@ -782,6 +871,9 @@
 
 void SkCanvas::doSave() {
     this->willSave();
+
+    SkASSERT(fMCRec->fDeferredSaveCount > 0);
+    fMCRec->fDeferredSaveCount -= 1;
     this->internalSave();
 }
 
@@ -880,7 +972,7 @@
     }
     SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
     fSaveCount += 1;
-    this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
+    this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, strategy);
     return this->getSaveCount() - 1;
 }
 
@@ -890,12 +982,12 @@
     }
     SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
     fSaveCount += 1;
-    this->internalSaveLayer(bounds, paint, flags, false, strategy);
+    this->internalSaveLayer(bounds, paint, flags, strategy);
     return this->getSaveCount() - 1;
 }
 
 void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
-                                bool justForImageFilter, SaveLayerStrategy strategy) {
+                                 SaveLayerStrategy strategy) {
 #ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
     flags |= kClipToLayer_SaveFlag;
 #endif
@@ -917,21 +1009,15 @@
         return;
     }
 
-    // Kill the imagefilter if our device doesn't allow it
-    SkLazyPaint lazyP;
-    if (paint && paint->getImageFilter()) {
-        if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
-            if (justForImageFilter) {
-                // early exit if the layer was just for the imageFilter
-                return;
-            }
-            SkPaint* p = lazyP.set(*paint);
-            p->setImageFilter(NULL);
-            paint = p;
+    bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
+    SkPixelGeometry geo = fProps.pixelGeometry();
+    if (paint) {
+        // TODO: perhaps add a query to filters so we might preserve opaqueness...
+        if (paint->getImageFilter() || paint->getColorFilter()) {
+            isOpaque = false;
+            geo = kUnknown_SkPixelGeometry;
         }
     }
-
-    bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
     SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
                         isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
 
@@ -941,20 +1027,27 @@
         return;
     }
 
-    SkBaseDevice::Usage usage = SkBaseDevice::kSaveLayer_Usage;
-    if (paint && paint->getImageFilter()) {
-        usage = SkBaseDevice::kImageFilter_Usage;
-    }
-    device = device->onCreateCompatibleDevice(SkBaseDevice::CreateInfo(info, usage,
-                                                                       fProps.pixelGeometry()));
-    if (NULL == device) {
-        SkErrorInternals::SetError( kInternalError_SkError,
-                                    "Unable to create device for layer.");
-        return;
+    bool forceSpriteOnRestore = false;
+    {
+        const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
+        const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo);
+        SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
+        if (NULL == newDev) {
+            // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
+            newDev = SkBitmapDevice::Create(createInfo.fInfo);
+            if (NULL == newDev) {
+                SkErrorInternals::SetError(kInternalError_SkError,
+                                           "Unable to create device for layer.");
+                return;
+            }
+            forceSpriteOnRestore = true;
+        }
+        device = newDev;
     }
 
     device->setOrigin(ir.fLeft, ir.fTop);
-    DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, paint, this, fConservativeRasterClip));
+    DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, paint, this, fConservativeRasterClip,
+                                            forceSpriteOnRestore));
     device->unref();
 
     layer->fNext = fMCRec->fTopLayer;
@@ -1003,11 +1096,15 @@
         if (layer->fNext) {
             const SkIPoint& origin = layer->fDevice->getOrigin();
             this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
-                                     layer->fPaint);
+                                     layer->fPaint, layer->fDeviceIsBitmapDevice);
             // reset this, since internalDrawDevice will have set it to true
             fDeviceCMDirty = true;
+            SkDELETE(layer);
+        } else {
+            // we're at the root
+            SkASSERT(layer == (void*)fDeviceCMStorage);
+            layer->~DeviceCM();
         }
-        SkDELETE(layer);
     }
 }
 
@@ -1111,7 +1208,7 @@
 }
 
 void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
-                                  const SkPaint* paint) {
+                                  const SkPaint* paint, bool deviceIsBitmapDevice) {
     SkPaint tmp;
     if (NULL == paint) {
         paint = &tmp;
@@ -1139,6 +1236,9 @@
                 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
                                    tmpUnfiltered);
             }
+        } else if (deviceIsBitmapDevice) {
+            const SkBitmap& src = srcDev->accessBitmap(false);
+            dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
         } else {
             dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
         }
@@ -1266,7 +1366,7 @@
             fDeviceCMDirty = true;
             fCachedLocalClipBoundsDirty = true;
 
-            fClipStack.clipEmpty();
+            fClipStack->clipEmpty();
             return fMCRec->fRasterClip.setEmpty();
         }
     }
@@ -1365,7 +1465,7 @@
             fDeviceCMDirty = true;
             fCachedLocalClipBoundsDirty = true;
 
-            fClipStack.clipEmpty();
+            fClipStack->clipEmpty();
             return fMCRec->fRasterClip.setEmpty();
         }
     }
@@ -1634,30 +1734,45 @@
     this->onDrawPath(path, paint);
 }
 
-void SkCanvas::drawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint) {
-    this->onDrawImage(image, dx, dy, paint);
+void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
+    this->onDrawImage(image, x, y, paint);
 }
 
 void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
                              const SkPaint* paint) {
+    if (dst.isEmpty()) {
+        return;
+    }
     this->onDrawImageRect(image, src, dst, paint);
 }
 
 void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
+    if (bitmap.empty()) {
+        return;
+    }
     this->onDrawBitmap(bitmap, dx, dy, paint);
 }
 
 void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
                                     const SkPaint* paint, DrawBitmapRectFlags flags) {
+    if (bitmap.empty()) {
+        return;
+    }
     this->onDrawBitmapRect(bitmap, src, dst, paint, flags);
 }
 
 void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
                               const SkPaint* paint) {
+    if (bitmap.empty()) {
+        return;
+    }
     this->onDrawBitmapNine(bitmap, center, dst, paint);
 }
 
 void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
+    if (bitmap.empty()) {
+        return;
+    }
     this->onDrawSprite(bitmap, left, top, paint);
 }
 
@@ -1847,15 +1962,58 @@
     LOOPER_END
 }
 
-void SkCanvas::onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint) {
+void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
-    image->draw(this, dx, dy, paint);
+    SkRect bounds = SkRect::MakeXYWH(x, y,
+                                     SkIntToScalar(image->width()), SkIntToScalar(image->height()));
+    if (NULL == paint || paint->canComputeFastBounds()) {
+        if (paint) {
+            paint->computeFastBounds(bounds, &bounds);
+        }
+        if (this->quickReject(bounds)) {
+            return;
+        }
+    }
+    
+    SkLazyPaint lazy;
+    if (NULL == paint) {
+        paint = lazy.init();
+    }
+    
+    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &bounds)
+    
+    while (iter.next()) {
+        iter.fDevice->drawImage(iter, image, x, y, looper.paint());
+    }
+    
+    LOOPER_END
 }
 
 void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
                                const SkPaint* paint) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
-    image->drawRect(this, src, dst, paint);
+    SkRect storage;
+    const SkRect* bounds = &dst;
+    if (NULL == paint || paint->canComputeFastBounds()) {
+        if (paint) {
+            bounds = &paint->computeFastBounds(dst, &storage);
+        }
+        if (this->quickReject(*bounds)) {
+            return;
+        }
+    }
+    SkLazyPaint lazy;
+    if (NULL == paint) {
+        paint = lazy.init();
+    }
+    
+    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
+    
+    while (iter.next()) {
+        iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint());
+    }
+    
+    LOOPER_END
 }
 
 void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
@@ -2392,20 +2550,28 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-void SkCanvas::drawPicture(const SkPicture* picture) {
-    TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
-    if (picture) {
-        this->onDrawPicture(picture, NULL, NULL);
-    }
-}
+
+/**
+ *  This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
+ *  against the playback cost of recursing into the subpicture to get at its actual ops.
+ *
+ *  For now we pick a conservatively small value, though measurement (and other heuristics like
+ *  the type of ops contained) may justify changing this value.
+ */
+#define kMaxPictureOpsToUnrollInsteadOfRef  1
 
 void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
-    TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture(SkMatrix, SkPaint)");
+    TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
     if (picture) {
         if (matrix && matrix->isIdentity()) {
             matrix = NULL;
         }
-        this->onDrawPicture(picture, matrix, paint);
+        if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
+            SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
+            picture->playback(this);
+        } else {
+            this->onDrawPicture(picture, matrix, paint);
+        }
     }
 }
 
@@ -2421,7 +2587,6 @@
     }
 
     SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
-
     picture->playback(this);
 }
 
diff --git a/src/core/SkChecksum.h b/src/core/SkChecksum.h
index d9065f5..8eb1766 100644
--- a/src/core/SkChecksum.h
+++ b/src/core/SkChecksum.h
@@ -8,6 +8,8 @@
 #ifndef SkChecksum_DEFINED
 #define SkChecksum_DEFINED
 
+#include "SkString.h"
+#include "SkTLogic.h"
 #include "SkTypes.h"
 
 /**
@@ -69,21 +71,20 @@
      * This should take 2-3x longer than SkChecksum::Compute, but is a considerably better hash.
      * See en.wikipedia.org/wiki/MurmurHash.
      *
-     *  @param data Memory address of the data block to be processed. Must be 32-bit aligned.
-     *  @param size Size of the data block in bytes. Must be a multiple of 4.
+     *  @param data Memory address of the data block to be processed.
+     *  @param size Size of the data block in bytes.
      *  @param seed Initial hash seed. (optional)
      *  @return hash result
      */
-    static uint32_t Murmur3(const uint32_t* data, size_t bytes, uint32_t seed=0) {
+    static uint32_t Murmur3(const void* data, size_t bytes, uint32_t seed=0) {
         // Use may_alias to remind the compiler we're intentionally violating strict aliasing,
         // and so not to apply strict-aliasing-based optimizations.
         typedef uint32_t SK_ATTRIBUTE(may_alias) aliased_uint32_t;
+        typedef uint8_t SK_ATTRIBUTE(may_alias) aliased_uint8_t;
+
+        // Handle 4 bytes at a time while possible.
         const aliased_uint32_t* safe_data = (const aliased_uint32_t*)data;
-
-        SkASSERTF(SkIsAlign4(bytes), "Expected 4-byte multiple, got %zu", bytes);
         const size_t words = bytes/4;
-
-
         uint32_t hash = seed;
         for (size_t i = 0; i < words; i++) {
             uint32_t k = safe_data[i];
@@ -96,6 +97,20 @@
             hash *= 5;
             hash += 0xe6546b64;
         }
+
+        // Handle last 0-3 bytes.
+        const aliased_uint8_t* safe_tail = (const uint8_t*)(safe_data + words);
+        uint32_t k = 0;
+        switch (bytes & 3) {
+            case 3: k ^= safe_tail[2] << 16;
+            case 2: k ^= safe_tail[1] <<  8;
+            case 1: k ^= safe_tail[0] <<  0;
+                    k *= 0xcc9e2d51;
+                    k = (k << 15) | (k >> 17);
+                    k *= 0x1b873593;
+                    hash ^= k;
+        }
+
         hash ^= bytes;
         return Mix(hash);
     }
@@ -165,4 +180,19 @@
     }
 };
 
+// SkGoodHash should usually be your first choice in hashing data.
+// It should be both reasonably fast and high quality.
+
+template <typename K>
+uint32_t SkGoodHash(const K& k) {
+    if (sizeof(K) == 4) {
+        return SkChecksum::Mix(*(const uint32_t*)&k);
+    }
+    return SkChecksum::Murmur3(&k, sizeof(K));
+}
+
+inline uint32_t SkGoodHash(const SkString& k) {
+    return SkChecksum::Murmur3(k.c_str(), k.size());
+}
+
 #endif
diff --git a/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
index 0a9cd93..8007271 100644
--- a/src/core/SkColorFilter.cpp
+++ b/src/core/SkColorFilter.cpp
@@ -22,15 +22,6 @@
     return false;
 }
 
-void SkColorFilter::filterSpan16(const uint16_t s[], int count, uint16_t d[]) const {
-    SkASSERT(this->getFlags() & SkColorFilter::kHasFilter16_Flag);
-    SkDEBUGFAIL("missing implementation of SkColorFilter::filterSpan16");
-
-    if (d != s) {
-        memcpy(d, s, count * sizeof(uint16_t));
-    }
-}
-
 SkColor SkColorFilter::filterColor(SkColor c) const {
     SkPMColor dst, src = SkPreMultiplyColor(c);
     this->filterSpan(&src, 1, &dst);
@@ -51,24 +42,18 @@
 
 class SkComposeColorFilter : public SkColorFilter {
 public:
-    uint32_t getFlags() const SK_OVERRIDE {
+    uint32_t getFlags() const override {
         // Can only claim alphaunchanged and 16bit support if both our proxys do.
         return fOuter->getFlags() & fInner->getFlags();
     }
     
-    void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) const SK_OVERRIDE {
+    void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) const override {
         fInner->filterSpan(shader, count, result);
         fOuter->filterSpan(result, count, result);
     }
     
-    void filterSpan16(const uint16_t shader[], int count, uint16_t result[]) const SK_OVERRIDE {
-        SkASSERT(this->getFlags() & kHasFilter16_Flag);
-        fInner->filterSpan16(shader, count, result);
-        fOuter->filterSpan16(result, count, result);
-    }
-    
 #ifndef SK_IGNORE_TO_STRING
-    void toString(SkString* str) const SK_OVERRIDE {
+    void toString(SkString* str) const override {
         SkString outerS, innerS;
         fOuter->toString(&outerS);
         fInner->toString(&innerS);
@@ -78,7 +63,7 @@
 
 #if SK_SUPPORT_GPU
     bool asFragmentProcessors(GrContext* context,
-                              SkTDArray<GrFragmentProcessor*>* array) const SK_OVERRIDE {
+                              SkTDArray<GrFragmentProcessor*>* array) const override {
         bool hasFrags = fInner->asFragmentProcessors(context, array);
         hasFrags |= fOuter->asFragmentProcessors(context, array);
         return hasFrags;
@@ -88,7 +73,7 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeColorFilter)
     
 protected:
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
+    void flatten(SkWriteBuffer& buffer) const override {
         buffer.writeFlattenable(fOuter);
         buffer.writeFlattenable(fInner);
     }
@@ -103,7 +88,7 @@
         SkASSERT(composedFilterCount <= SK_MAX_COMPOSE_COLORFILTER_COUNT);
     }
 
-    int privateComposedFilterCount() const SK_OVERRIDE {
+    int privateComposedFilterCount() const override {
         return fComposedFilterCount;
     }
 
diff --git a/src/core/SkColorShader.h b/src/core/SkColorShader.h
index 9fbd5e1..918e19e 100644
--- a/src/core/SkColorShader.h
+++ b/src/core/SkColorShader.h
@@ -23,9 +23,9 @@
     */
     explicit SkColorShader(SkColor c);
 
-    bool isOpaque() const SK_OVERRIDE;
+    bool isOpaque() const override;
 
-    size_t contextSize() const SK_OVERRIDE {
+    size_t contextSize() const override {
         return sizeof(ColorShaderContext);
     }
 
@@ -33,11 +33,11 @@
     public:
         ColorShaderContext(const SkColorShader& shader, const ContextRec&);
 
-        uint32_t getFlags() const SK_OVERRIDE;
-        uint8_t getSpan16Alpha() const SK_OVERRIDE;
-        void shadeSpan(int x, int y, SkPMColor span[], int count) SK_OVERRIDE;
-        void shadeSpan16(int x, int y, uint16_t span[], int count) SK_OVERRIDE;
-        void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) SK_OVERRIDE;
+        uint32_t getFlags() const override;
+        uint8_t getSpan16Alpha() const override;
+        void shadeSpan(int x, int y, SkPMColor span[], int count) override;
+        void shadeSpan16(int x, int y, uint16_t span[], int count) override;
+        void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) override;
 
     private:
         SkPMColor   fPMColor;
@@ -50,22 +50,22 @@
     // we return false for this, use asAGradient
     virtual BitmapType asABitmap(SkBitmap* outTexture,
                                  SkMatrix* outMatrix,
-                                 TileMode xy[2]) const SK_OVERRIDE;
+                                 TileMode xy[2]) const override;
 
-    GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
+    GradientType asAGradient(GradientInfo* info) const override;
 
     virtual bool asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix& viewM,
                                      const SkMatrix*, GrColor*,
-                                     GrFragmentProcessor**) const SK_OVERRIDE;
+                                     GrFragmentProcessor**) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorShader)
 
 protected:
     SkColorShader(SkReadBuffer&);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
-    bool onAsLuminanceColor(SkColor* lum) const SK_OVERRIDE {
+    void flatten(SkWriteBuffer&) const override;
+    Context* onCreateContext(const ContextRec&, void* storage) const override;
+    bool onAsLuminanceColor(SkColor* lum) const override {
         *lum = fColor;
         return true;
     }
diff --git a/src/core/SkConfig8888.cpp b/src/core/SkConfig8888.cpp
index 85e208f..d28941c 100644
--- a/src/core/SkConfig8888.cpp
+++ b/src/core/SkConfig8888.cpp
@@ -137,6 +137,47 @@
     }
 }
 
+static void copy_g8_to_32(void* dst, size_t dstRB, const void* src, size_t srcRB, int w, int h) {
+    uint32_t* dst32 = (uint32_t*)dst;
+    const uint8_t* src8 = (const uint8_t*)src;
+    
+    for (int y = 0; y < h; ++y) {
+        for (int x = 0; x < w; ++x) {
+            dst32[x] = SkPackARGB32(0xFF, src8[x], src8[x], src8[x]);
+        }
+        dst32 = (uint32_t*)((char*)dst32 + dstRB);
+        src8 += srcRB;
+    }
+}
+
+static void copy_32_to_g8(void* dst, size_t dstRB, const void* src, size_t srcRB,
+                          const SkImageInfo& srcInfo) {
+    uint8_t* dst8 = (uint8_t*)dst;
+    const uint32_t* src32 = (const uint32_t*)src;
+
+    const int w = srcInfo.width();
+    const int h = srcInfo.height();
+    const bool isBGRA = (kBGRA_8888_SkColorType == srcInfo.colorType());
+
+    for (int y = 0; y < h; ++y) {
+        if (isBGRA) {
+            // BGRA
+            for (int x = 0; x < w; ++x) {
+                uint32_t s = src32[x];
+                dst8[x] = SkComputeLuminance((s >> 16) & 0xFF, (s >> 8) & 0xFF, s & 0xFF);
+            }
+        } else {
+            // RGBA
+            for (int x = 0; x < w; ++x) {
+                uint32_t s = src32[x];
+                dst8[x] = SkComputeLuminance(s & 0xFF, (s >> 8) & 0xFF, (s >> 16) & 0xFF);
+            }
+        }
+        src32 = (const uint32_t*)((const char*)src32 + srcRB);
+        dst8 += dstRB;
+    }
+}
+
 bool SkPixelInfo::CopyPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
                              const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRB,
                              SkColorTable* ctable) {
@@ -170,6 +211,7 @@
         switch (srcInfo.colorType()) {
             case kRGB_565_SkColorType:
             case kAlpha_8_SkColorType:
+            case kGray_8_SkColorType:
                 break;
             case kIndex_8_SkColorType:
             case kARGB_4444_SkColorType:
@@ -189,6 +231,15 @@
      *  are supported.
      */
 
+    if (kGray_8_SkColorType == srcInfo.colorType() && 4 == dstInfo.bytesPerPixel()) {
+        copy_g8_to_32(dstPixels, dstRB, srcPixels, srcRB, width, height);
+        return true;
+    }
+    if (kGray_8_SkColorType == dstInfo.colorType() && 4 == srcInfo.bytesPerPixel()) {
+        copy_32_to_g8(dstPixels, dstRB, srcPixels, srcRB, srcInfo);
+        return true;
+    }
+
     // Can no longer draw directly into 4444, but we can manually whack it for a few combinations
     if (kARGB_4444_SkColorType == dstInfo.colorType() &&
         (kN32_SkColorType == srcInfo.colorType() || kIndex_8_SkColorType == srcInfo.colorType())) {
diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h
index e6be38a..18821dc 100644
--- a/src/core/SkCoreBlitters.h
+++ b/src/core/SkCoreBlitters.h
@@ -41,9 +41,9 @@
       *  Will create the context at the same location as the old one (this is safe
       *  because the shader itself is unchanged).
       */
-    bool resetShaderContext(const SkShader::ContextRec&) SK_OVERRIDE;
+    bool resetShaderContext(const SkShader::ContextRec&) override;
 
-    SkShader::Context* getShaderContext() const SK_OVERRIDE { return fShaderContext; }
+    SkShader::Context* getShaderContext() const override { return fShaderContext; }
 
 protected:
     uint32_t            fShaderFlags;
@@ -62,12 +62,12 @@
 class SkA8_Coverage_Blitter : public SkRasterBlitter {
 public:
     SkA8_Coverage_Blitter(const SkBitmap& device, const SkPaint& paint);
-    void blitH(int x, int y, int width) SK_OVERRIDE;
-    void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) SK_OVERRIDE;
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
-    void blitMask(const SkMask&, const SkIRect&) SK_OVERRIDE;
-    const SkBitmap* justAnOpaqueColor(uint32_t*) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
+    void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override;
+    void blitV(int x, int y, int height, SkAlpha alpha) override;
+    void blitRect(int x, int y, int width, int height) override;
+    void blitMask(const SkMask&, const SkIRect&) override;
+    const SkBitmap* justAnOpaqueColor(uint32_t*) override;
 };
 
 class SkA8_Blitter : public SkRasterBlitter {
@@ -120,11 +120,12 @@
     virtual void blitRect(int x, int y, int width, int height);
     virtual void blitMask(const SkMask&, const SkIRect&);
     virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
+    void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) override;
+    void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) override;
 
 protected:
     SkColor                fColor;
     SkPMColor              fPMColor;
-    SkBlitRow::ColorProc   fColor32Proc;
 
 private:
     unsigned fSrcA, fSrcR, fSrcG, fSrcB;
@@ -140,6 +141,8 @@
     SkARGB32_Opaque_Blitter(const SkBitmap& device, const SkPaint& paint)
         : INHERITED(device, paint) { SkASSERT(paint.getAlpha() == 0xFF); }
     virtual void blitMask(const SkMask&, const SkIRect&);
+    void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) override;
+    void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) override;
 
 private:
     typedef SkARGB32_Blitter INHERITED;
@@ -150,6 +153,8 @@
     SkARGB32_Black_Blitter(const SkBitmap& device, const SkPaint& paint)
         : INHERITED(device, paint) {}
     virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
+    void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) override;
+    void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) override;
 
 private:
     typedef SkARGB32_Opaque_Blitter INHERITED;
@@ -160,11 +165,11 @@
     SkARGB32_Shader_Blitter(const SkBitmap& device, const SkPaint& paint,
                             SkShader::Context* shaderContext);
     virtual ~SkARGB32_Shader_Blitter();
-    void blitH(int x, int y, int width) SK_OVERRIDE;
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
-    void blitAntiH(int x, int y, const SkAlpha[], const int16_t[]) SK_OVERRIDE;
-    void blitMask(const SkMask&, const SkIRect&) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
+    void blitV(int x, int y, int height, SkAlpha alpha) override;
+    void blitRect(int x, int y, int width, int height) override;
+    void blitAntiH(int x, int y, const SkAlpha[], const int16_t[]) override;
+    void blitMask(const SkMask&, const SkIRect&) override;
 
 private:
     SkXfermode*         fXfermode;
diff --git a/src/core/SkDescriptor.h b/src/core/SkDescriptor.h
index 22ec293..0693ccf 100644
--- a/src/core/SkDescriptor.h
+++ b/src/core/SkDescriptor.h
@@ -134,7 +134,13 @@
 
 class SkAutoDescriptor : SkNoncopyable {
 public:
-    SkAutoDescriptor(size_t size) {
+    SkAutoDescriptor() : fDesc(NULL) {}
+    SkAutoDescriptor(size_t size) : fDesc(NULL) { this->reset(size); }
+
+    ~SkAutoDescriptor() { this->free(); }
+
+    void reset(size_t size) {
+        this->free();
         if (size <= sizeof(fStorage)) {
             fDesc = (SkDescriptor*)(void*)fStorage;
         } else {
@@ -142,14 +148,14 @@
         }
     }
 
-    ~SkAutoDescriptor() {
+    SkDescriptor* getDesc() const { SkASSERT(fDesc); return fDesc; }
+private:
+    void free() {
         if (fDesc != (SkDescriptor*)(void*)fStorage) {
             SkDescriptor::Free(fDesc);
         }
     }
 
-    SkDescriptor* getDesc() const { return fDesc; }
-private:
     enum {
         kStorageSize =  sizeof(SkDescriptor)
                         + sizeof(SkDescriptor::Entry) + sizeof(SkScalerContext::Rec)    // for rec
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index a77e54c..37fafd8 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -9,6 +9,7 @@
 #include "SkDeviceProperties.h"
 #include "SkDraw.h"
 #include "SkDrawFilter.h"
+#include "SkImage_Base.h"
 #include "SkMetaData.h"
 #include "SkPatchUtils.h"
 #include "SkPathMeasure.h"
@@ -64,19 +65,19 @@
 }
 
 SkPixelGeometry SkBaseDevice::CreateInfo::AdjustGeometry(const SkImageInfo& info,
-                                                         Usage usage,
+                                                         TileUsage tileUsage,
                                                          SkPixelGeometry geo) {
-    switch (usage) {
-        case kGeneral_Usage:
+    switch (tileUsage) {
+        case kPossible_TileUsage:
+            // (we think) for compatibility with old clients, we assume this layer can support LCD
+            // even though they may not have marked it as opaque... seems like we should update
+            // our callers (reed/robertphilips).
             break;
-        case kSaveLayer_Usage:
+        case kNever_TileUsage:
             if (info.alphaType() != kOpaque_SkAlphaType) {
                 geo = kUnknown_SkPixelGeometry;
             }
             break;
-        case kImageFilter_Usage:
-            geo = kUnknown_SkPixelGeometry;
-            break;
     }
     return geo;
 }
@@ -86,7 +87,7 @@
     // anyway to document logically what is going on.
     //
     fLeakyProperties->setPixelGeometry(CreateInfo::AdjustGeometry(this->imageInfo(),
-                                                                  kGeneral_Usage,
+                                                                  kPossible_TileUsage,
                                                                   geo));
 }
 
@@ -165,6 +166,24 @@
     }
 }
 
+void SkBaseDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y,
+                             const SkPaint& paint) {
+    // Default impl : turns everything into raster bitmap
+    SkBitmap bm;
+    if (as_IB(image)->getROPixels(&bm)) {
+        this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
+    }
+}
+
+void SkBaseDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const SkRect* src,
+                                 const SkRect& dst, const SkPaint& paint) {
+    // Default impl : turns everything into raster bitmap
+    SkBitmap bm;
+    if (as_IB(image)->getROPixels(&bm)) {
+        this->drawBitmapRect(draw, bm, src, dst, paint, SkCanvas::kNone_DrawBitmapRectFlag);
+    }
+}
+
 bool SkBaseDevice::readPixels(const SkImageInfo& info, void* dstP, size_t rowBytes, int x, int y) {
 #ifdef SK_DEBUG
     SkASSERT(info.width() > 0 && info.height() > 0);
diff --git a/src/core/SkDeviceImageFilterProxy.h b/src/core/SkDeviceImageFilterProxy.h
index 0ae686d..79ff957 100644
--- a/src/core/SkDeviceImageFilterProxy.h
+++ b/src/core/SkDeviceImageFilterProxy.h
@@ -8,6 +8,7 @@
 #ifndef SkDeviceImageFilterProxy_DEFINED
 #define SkDeviceImageFilterProxy_DEFINED
 
+#include "SkBitmapDevice.h"
 #include "SkDevice.h"
 #include "SkImageFilter.h"
 #include "SkSurfaceProps.h"
@@ -16,28 +17,30 @@
 public:
     SkDeviceImageFilterProxy(SkBaseDevice* device, const SkSurfaceProps& props)
         : fDevice(device)
-        , fProps(props.flags(),
-                 SkBaseDevice::CreateInfo::AdjustGeometry(SkImageInfo(),
-                                                          SkBaseDevice::kImageFilter_Usage,
-                                                          props.pixelGeometry()))
+        , fProps(props.flags(), kUnknown_SkPixelGeometry)
     {}
 
-    SkBaseDevice* createDevice(int w, int h) SK_OVERRIDE {
+    SkBaseDevice* createDevice(int w, int h) override {
         SkBaseDevice::CreateInfo cinfo(SkImageInfo::MakeN32Premul(w, h),
-                                       SkBaseDevice::kImageFilter_Usage,
-                                       kUnknown_SkPixelGeometry);
-        return fDevice->onCreateCompatibleDevice(cinfo);
+                                       SkBaseDevice::kNever_TileUsage,
+                                       kUnknown_SkPixelGeometry,
+                                       true /*forImageFilter*/);
+        SkBaseDevice* dev = fDevice->onCreateDevice(cinfo, NULL);
+        if (NULL == dev) {
+            dev = SkBitmapDevice::Create(cinfo.fInfo);
+        }
+        return dev;
     }
-    bool canHandleImageFilter(const SkImageFilter* filter) SK_OVERRIDE {
+    bool canHandleImageFilter(const SkImageFilter* filter) override {
         return fDevice->canHandleImageFilter(filter);
     }
     virtual bool filterImage(const SkImageFilter* filter, const SkBitmap& src,
                              const SkImageFilter::Context& ctx,
-                             SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
+                             SkBitmap* result, SkIPoint* offset) override {
         return fDevice->filterImage(filter, src, ctx, result, offset);
     }
 
-    const SkSurfaceProps* surfaceProps() const SK_OVERRIDE {
+    const SkSurfaceProps* surfaceProps() const override {
         return &fProps;
     }
 
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index bd4e40e..878dca5 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.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.
  */
+#define __STDC_LIMIT_MACROS
 
 #include "SkDraw.h"
 #include "SkBlitter.h"
@@ -33,7 +34,6 @@
 #include "SkDrawProcs.h"
 #include "SkMatrixUtils.h"
 
-
 //#define TRACE_BITMAP_DRAWS
 
 
@@ -381,15 +381,13 @@
 static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
                               int count, SkBlitter* blitter) {
     for (int i = 0; i < count; i += 2) {
-        SkScan::HairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
+        SkScan::HairLine(&devPts[i], 2, *rec.fRC, blitter);
     }
 }
 
 static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
                               int count, SkBlitter* blitter) {
-    for (int i = 0; i < count - 1; i++) {
-        SkScan::HairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
-    }
+    SkScan::HairLine(devPts, count, *rec.fRC, blitter);
 }
 
 // aa versions
@@ -397,15 +395,13 @@
 static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
                               int count, SkBlitter* blitter) {
     for (int i = 0; i < count; i += 2) {
-        SkScan::AntiHairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
+        SkScan::AntiHairLine(&devPts[i], 2, *rec.fRC, blitter);
     }
 }
 
 static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
                               int count, SkBlitter* blitter) {
-    for (int i = 0; i < count - 1; i++) {
-        SkScan::AntiHairLine(devPts[i], devPts[i+1], *rec.fRC, blitter);
-    }
+    SkScan::AntiHairLine(devPts, count, *rec.fRC, blitter);
 }
 
 // square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
@@ -1444,6 +1440,15 @@
 //////////////////////////////////////////////////////////////////////////////
 
 static void D1G_RectClip(const SkDraw1Glyph& state, Sk48Dot16 fx, Sk48Dot16 fy, const SkGlyph& glyph) {
+    // Prevent glyphs from being drawn outside of or straddling the edge of device space.
+    if ((fx >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) ||
+        (fx >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/) ||
+        (fy >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) ||
+        (fy >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/))
+    {
+        return;
+    }
+
     int left = Sk48Dot16FloorToInt(fx);
     int top = Sk48Dot16FloorToInt(fy);
     SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
@@ -1867,10 +1872,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-typedef void (*HairProc)(const SkPoint&, const SkPoint&, const SkRasterClip&,
-                         SkBlitter*);
-
-static HairProc ChooseHairProc(bool doAntiAlias) {
+static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) {
     return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
 }
 
@@ -1891,7 +1893,7 @@
 public:
     SkTriColorShader() {}
 
-    size_t contextSize() const SK_OVERRIDE;
+    size_t contextSize() const override;
 
     class TriColorShaderContext : public SkShader::Context {
     public:
@@ -1900,7 +1902,7 @@
 
         bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
 
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
 
     private:
         SkMatrix    fDstToUnit;
@@ -1912,10 +1914,10 @@
     SK_TO_STRING_OVERRIDE()
 
     // For serialization.  This will never be called.
-    Factory getFactory() const SK_OVERRIDE { sk_throw(); return NULL; }
+    Factory getFactory() const override { sk_throw(); return NULL; }
 
 protected:
-    Context* onCreateContext(const ContextRec& rec, void* storage) const SK_OVERRIDE {
+    Context* onCreateContext(const ContextRec& rec, void* storage) const override {
         return SkNEW_PLACEMENT_ARGS(storage, TriColorShaderContext, (*this, rec));
     }
 
@@ -2136,12 +2138,13 @@
         }
     } else {
         // no colors[] and no texture, stroke hairlines with paint's color.
-        HairProc hairProc = ChooseHairProc(paint.isAntiAlias());
+        SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias());
         const SkRasterClip& clip = *fRC;
         while (vertProc(&state)) {
-            hairProc(devVerts[state.f0], devVerts[state.f1], clip, blitter.get());
-            hairProc(devVerts[state.f1], devVerts[state.f2], clip, blitter.get());
-            hairProc(devVerts[state.f2], devVerts[state.f0], clip, blitter.get());
+            SkPoint array[] = {
+                devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0]
+            };
+            hairProc(array, 4, clip, blitter.get());
         }
     }
 }
@@ -2181,11 +2184,7 @@
     }
 
     //  init our bounds from the path
-    {
-        SkRect pathBounds = devPath.getBounds();
-        pathBounds.inset(-SK_ScalarHalf, -SK_ScalarHalf);
-        pathBounds.roundOut(bounds);
-    }
+    *bounds = devPath.getBounds().makeOutset(SK_ScalarHalf, SK_ScalarHalf).roundOut();
 
     SkIPoint margin = SkIPoint::Make(0, 0);
     if (filter) {
@@ -2204,7 +2203,6 @@
     // (possibly) trim the bounds to reflect the clip
     // (plus whatever slop the filter needs)
     if (clipBounds) {
-        SkIRect tmp = *clipBounds;
         // Ugh. Guard against gigantic margins from wacky filters. Without this
         // check we can request arbitrary amounts of slop beyond our visible
         // clip, and bring down the renderer (at least on finite RAM machines
@@ -2212,9 +2210,8 @@
         // quality of large filters like blurs, and the corresponding memory
         // requests.
         static const int MAX_MARGIN = 128;
-        tmp.inset(-SkMin32(margin.fX, MAX_MARGIN),
-                  -SkMin32(margin.fY, MAX_MARGIN));
-        if (!bounds->intersect(tmp)) {
+        if (!bounds->intersect(clipBounds->makeOutset(SkMin32(margin.fX, MAX_MARGIN),
+                                                      SkMin32(margin.fY, MAX_MARGIN)))) {
             return false;
         }
     }
diff --git a/src/core/SkEdge.cpp b/src/core/SkEdge.cpp
index 11d954e..f91f5f8 100644
--- a/src/core/SkEdge.cpp
+++ b/src/core/SkEdge.cpp
@@ -332,8 +332,7 @@
     return SkMax32(SkAbs32(oneThird), SkAbs32(twoThird));
 }
 
-int SkCubicEdge::setCubic(const SkPoint pts[4], const SkIRect* clip, int shift)
-{
+int SkCubicEdge::setCubic(const SkPoint pts[4], int shift) {
     SkFDot6 x0, y0, x1, y1, x2, y2, x3, y3;
 
     {
@@ -376,10 +375,6 @@
     if (top == bot)
         return 0;
 
-    // are we completely above or below the clip?
-    if (clip && (top >= clip->fBottom || bot <= clip->fTop))
-        return 0;
-
     // compute number of steps needed (1 << shift)
     {
         // Can't use (center of curve - center of baseline), since center-of-curve
@@ -433,16 +428,6 @@
     fCLastX = SkFDot6ToFixed(x3);
     fCLastY = SkFDot6ToFixed(y3);
 
-    if (clip)
-    {
-        do {
-            if (!this->updateCubic()) {
-                return 0;
-            }
-        } while (!this->intersectsClip(*clip));
-        this->chopLineWithClip(*clip);
-        return 1;
-    }
     return this->updateCubic();
 }
 
diff --git a/src/core/SkEdge.h b/src/core/SkEdge.h
index 09c021c..db6f430 100644
--- a/src/core/SkEdge.h
+++ b/src/core/SkEdge.h
@@ -36,8 +36,7 @@
     uint8_t fCubicDShift;   // applied to fCDx and fCDy only in cubic
     int8_t  fWinding;       // 1 or -1
 
-    int setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip,
-                int shiftUp);
+    int setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip, int shiftUp);
     // call this version if you know you don't have a clip
     inline int setLine(const SkPoint& p0, const SkPoint& p1, int shiftUp);
     inline int updateLine(SkFixed ax, SkFixed ay, SkFixed bx, SkFixed by);
@@ -81,7 +80,7 @@
     SkFixed fCDDDx, fCDDDy;
     SkFixed fCLastX, fCLastY;
 
-    int setCubic(const SkPoint pts[4], const SkIRect* clip, int shiftUp);
+    int setCubic(const SkPoint pts[4], int shiftUp);
     int updateCubic();
 };
 
diff --git a/src/core/SkEdgeBuilder.cpp b/src/core/SkEdgeBuilder.cpp
index 6a8ea89..43181fd 100644
--- a/src/core/SkEdgeBuilder.cpp
+++ b/src/core/SkEdgeBuilder.cpp
@@ -42,7 +42,7 @@
 
 void SkEdgeBuilder::addCubic(const SkPoint pts[]) {
     SkCubicEdge* edge = typedAllocThrow<SkCubicEdge>(fAlloc);
-    if (edge->setCubic(pts, NULL, fShiftUp)) {
+    if (edge->setCubic(pts, fShiftUp)) {
         fList.push(edge);
     } else {
         // TODO: unallocate edge from storage...
diff --git a/src/core/SkEdgeClipper.cpp b/src/core/SkEdgeClipper.cpp
index 32277bc..96fac61 100644
--- a/src/core/SkEdgeClipper.cpp
+++ b/src/core/SkEdgeClipper.cpp
@@ -225,72 +225,18 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C,
-                                 SkScalar D, SkScalar t) {
-    return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
-}
-
-/*  Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the
-    t value such that cubic(t) = target
- */
-static bool chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
-                           SkScalar target, SkScalar* t) {
- //   SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3);
-    SkASSERT(c0 < target && target < c3);
-
-    SkScalar D = c0 - target;
-    SkScalar A = c3 + 3*(c1 - c2) - c0;
-    SkScalar B = 3*(c2 - c1 - c1 + c0);
-    SkScalar C = 3*(c1 - c0);
-
-    const SkScalar TOLERANCE = SK_Scalar1 / 4096;
-    SkScalar minT = 0;
-    SkScalar maxT = SK_Scalar1;
-    SkScalar mid;
-
-    // This is a lot of iterations. Is there a faster way?
-    for (int i = 0; i < 24; i++) {
-        mid = SkScalarAve(minT, maxT);
-        SkScalar delta = eval_cubic_coeff(A, B, C, D, mid);
-        if (delta < 0) {
-            minT = mid;
-            delta = -delta;
-        } else {
-            maxT = mid;
-        }
-        if (delta < TOLERANCE) {
-            break;
-        }
-    }
-    *t = mid;
-//    SkDebugf("-- evalCubicAt %d delta %g\n", i, eval_cubic_coeff(A, B, C, D, *t));
-    return true;
-}
-
-static bool chopMonoCubicAtY(SkPoint pts[4], SkScalar y, SkScalar* t) {
-    return chopMonoCubicAt(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY, y, t);
-}
-
-static bool chopMonoCubicAtX(SkPoint pts[4], SkScalar x, SkScalar* t) {
-    return chopMonoCubicAt(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, x, t);
-}
-
 // Modify pts[] in place so that it is clipped in Y to the clip rect
 static void chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
 
     // are we partially above
     if (pts[0].fY < clip.fTop) {
-        SkScalar t;
-        if (chopMonoCubicAtY(pts, clip.fTop, &t)) {
-            SkPoint tmp[7];
-            SkChopCubicAt(pts, tmp, t);
-
-            // tmp[3, 4, 5].fY should all be to the below clip.fTop.
+        SkPoint tmp[7];
+        if (SkChopMonoCubicAtY(pts, clip.fTop, tmp)) {
+            // tmp[3, 4].fY should all be to the below clip.fTop.
             // 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, clip.fTop);
 
             pts[0] = tmp[3];
             pts[1] = tmp[4];
@@ -306,10 +252,8 @@
 
     // are we partially below
     if (pts[3].fY > clip.fBottom) {
-        SkScalar t;
-        if (chopMonoCubicAtY(pts, clip.fBottom, &t)) {
-            SkPoint tmp[7];
-            SkChopCubicAt(pts, tmp, t);
+        SkPoint tmp[7];
+        if (SkChopMonoCubicAtY(pts, clip.fBottom, tmp)) {
             tmp[3].fY = clip.fBottom;
             clamp_le(tmp[2].fY, clip.fBottom);
 
@@ -360,18 +304,15 @@
 
     // are we partially to the left
     if (pts[0].fX < clip.fLeft) {
-        SkScalar t;
-        if (chopMonoCubicAtX(pts, clip.fLeft, &t)) {
-            SkPoint tmp[7];
-            SkChopCubicAt(pts, tmp, t);
+        SkPoint tmp[7];
+        if (SkChopMonoCubicAtX(pts, clip.fLeft, tmp)) {
             this->appendVLine(clip.fLeft, tmp[0].fY, tmp[3].fY, reverse);
 
-            // tmp[3, 4, 5].fX should all be to the right of clip.fLeft.
+            // tmp[3, 4].fX should all be to the right of clip.fLeft.
             // Since we can't trust the numerics of
             // the chopper, we force those conditions now
             tmp[3].fX = clip.fLeft;
             clamp_ge(tmp[4].fX, clip.fLeft);
-            clamp_ge(tmp[5].fX, clip.fLeft);
 
             pts[0] = tmp[3];
             pts[1] = tmp[4];
@@ -386,13 +327,10 @@
 
     // are we partially to the right
     if (pts[3].fX > clip.fRight) {
-        SkScalar t;
-        if (chopMonoCubicAtX(pts, clip.fRight, &t)) {
-            SkPoint tmp[7];
-            SkChopCubicAt(pts, tmp, t);
+        SkPoint tmp[7];
+        if (SkChopMonoCubicAtX(pts, clip.fRight, tmp)) {
             tmp[3].fX = clip.fRight;
             clamp_le(tmp[2].fX, clip.fRight);
-            clamp_le(tmp[1].fX, clip.fRight);
 
             this->appendCubic(tmp, reverse);
             this->appendVLine(clip.fRight, tmp[3].fY, tmp[6].fY, reverse);
diff --git a/src/core/SkEmptyShader.h b/src/core/SkEmptyShader.h
index 0de85ca..7b87c28 100644
--- a/src/core/SkEmptyShader.h
+++ b/src/core/SkEmptyShader.h
@@ -20,7 +20,7 @@
 public:
     SkEmptyShader() {}
 
-    size_t contextSize() const SK_OVERRIDE {
+    size_t contextSize() const override {
         // Even though createContext returns NULL we have to return a value of at least
         // sizeof(SkShader::Context) to satisfy SkSmallAllocator.
         return sizeof(SkShader::Context);
@@ -30,10 +30,16 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmptyShader)
 
 protected:
-    SkShader::Context* onCreateContext(const ContextRec&, void*) const SK_OVERRIDE {
+    SkShader::Context* onCreateContext(const ContextRec&, void*) const override {
         return NULL;
     }
 
+    void flatten(SkWriteBuffer& buffer) const override {
+        // Do nothing.
+        // We just don't want to fall through to SkShader::flatten(),
+        // which will write data we don't care to serialize or decode.
+    }
+
 private:
     typedef SkShader INHERITED;
 };
diff --git a/src/core/SkFilterShader.cpp b/src/core/SkFilterShader.cpp
index 48c4b8b..a10a7c9 100644
--- a/src/core/SkFilterShader.cpp
+++ b/src/core/SkFilterShader.cpp
@@ -46,13 +46,12 @@
     uint32_t shaderF = fShaderContext->getFlags();
     uint32_t filterF = filterShader.fFilter->getFlags();
 
-    // if the filter doesn't support 16bit, clear the matching bit in the shader
-    if (!(filterF & SkColorFilter::kHasFilter16_Flag)) {
-        shaderF &= ~SkShader::kHasSpan16_Flag;
-    }
+    // filters don't support 16bit, so clear the matching bit in the shader
+    shaderF &= ~SkShader::kHasSpan16_Flag;
+
     // if the filter might change alpha, clear the opaque flag in the shader
     if (!(filterF & SkColorFilter::kAlphaUnchanged_Flag)) {
-        shaderF &= ~(SkShader::kOpaqueAlpha_Flag | SkShader::kHasSpan16_Flag);
+        shaderF &= ~SkShader::kOpaqueAlpha_Flag;
     }
     return shaderF;
 }
@@ -87,16 +86,6 @@
     filterShader.fFilter->filterSpan(result, count, result);
 }
 
-void SkFilterShader::FilterShaderContext::shadeSpan16(int x, int y, uint16_t result[], int count) {
-    const SkFilterShader& filterShader = static_cast<const SkFilterShader&>(fShader);
-
-    SkASSERT(fShaderContext->getFlags() & SkShader::kHasSpan16_Flag);
-    SkASSERT(filterShader.fFilter->getFlags() & SkColorFilter::kHasFilter16_Flag);
-
-    fShaderContext->shadeSpan16(x, y, result, count);
-    filterShader.fFilter->filterSpan16(result, count, result);
-}
-
 #ifndef SK_IGNORE_TO_STRING
 void SkFilterShader::toString(SkString* str) const {
     str->append("SkFilterShader: (");
diff --git a/src/core/SkFilterShader.h b/src/core/SkFilterShader.h
index dca9567..ea5eaed 100644
--- a/src/core/SkFilterShader.h
+++ b/src/core/SkFilterShader.h
@@ -17,7 +17,7 @@
     SkFilterShader(SkShader* shader, SkColorFilter* filter);
     virtual ~SkFilterShader();
 
-    size_t contextSize() const SK_OVERRIDE;
+    size_t contextSize() const override;
 
     class FilterShaderContext : public SkShader::Context {
     public:
@@ -25,12 +25,11 @@
         FilterShaderContext(const SkFilterShader&, SkShader::Context*, const ContextRec&);
         virtual ~FilterShaderContext();
 
-        uint32_t getFlags() const SK_OVERRIDE;
+        uint32_t getFlags() const override;
 
-        void shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE;
-        void shadeSpan16(int x, int y, uint16_t[], int count) SK_OVERRIDE;
+        void shadeSpan(int x, int y, SkPMColor[], int count) override;
 
-        void set3DMask(const SkMask* mask) SK_OVERRIDE {
+        void set3DMask(const SkMask* mask) override {
             // forward to our proxy
             fShaderContext->set3DMask(mask);
         }
@@ -45,8 +44,8 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkFilterShader)
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
+    Context* onCreateContext(const ContextRec&, void* storage) const override;
 
 
 private:
diff --git a/src/core/SkFlate.cpp b/src/core/SkFlate.cpp
index 09975c0..1d764e8 100644
--- a/src/core/SkFlate.cpp
+++ b/src/core/SkFlate.cpp
@@ -11,8 +11,6 @@
 #include "SkFlate.h"
 #include "SkStream.h"
 
-#ifndef SK_NO_FLATE
-
 namespace {
 
 #ifdef ZLIB_INCLUDE
@@ -225,7 +223,3 @@
 size_t SkDeflateWStream::bytesWritten() const {
     return fImpl->fZStream.total_in + fImpl->fInBufferIndex;
 }
-
-
-#endif  // SK_NO_FLATE
-
diff --git a/src/core/SkFlate.h b/src/core/SkFlate.h
index e9c40ae..0104c45 100644
--- a/src/core/SkFlate.h
+++ b/src/core/SkFlate.h
@@ -12,8 +12,6 @@
 
 #include "SkTypes.h"
 
-#ifndef Sk_NO_FLATE
-
 #include "SkStream.h"
 class SkData;
 
@@ -66,13 +64,12 @@
     void finalize();
 
     // The SkWStream interface:
-    bool write(const void*, size_t) SK_OVERRIDE;
-    size_t bytesWritten() const SK_OVERRIDE;
+    bool write(const void*, size_t) override;
+    size_t bytesWritten() const override;
 
 private:
     struct Impl;
     SkAutoTDelete<Impl> fImpl;
 };
 
-#endif  // SK_NO_FLATE
 #endif  // SkFlate_DEFINED
diff --git a/src/core/SkFontDescriptor.cpp b/src/core/SkFontDescriptor.cpp
index b2622d9..4a6b5db 100644
--- a/src/core/SkFontDescriptor.cpp
+++ b/src/core/SkFontDescriptor.cpp
@@ -18,7 +18,7 @@
     // These count backwards from 0xFF, so as not to collide with the SFNT
     // defines for names in its 'name' table.
     kFontIndex      = 0xFD,
-    kFontFileName   = 0xFE,
+    kFontFileName   = 0xFE,  // Remove when MIN_PICTURE_VERSION > 41
     kSentinel       = 0xFF,
 };
 
@@ -32,6 +32,14 @@
     }
 }
 
+// Remove when MIN_PICTURE_VERSION > 41
+static void skip_string(SkStream* stream) {
+    const uint32_t length = SkToU32(stream->readPackedUInt());
+    if (length > 0) {
+        stream->skip(length);
+    }
+}
+
 static void write_string(SkWStream* stream, const SkString& string,
                          uint32_t id) {
     if (!string.isEmpty()) {
@@ -67,8 +75,8 @@
             case kFontIndex:
                 fFontIndex = read_uint(stream);
                 break;
-            case kFontFileName:
-                read_string(stream, &fFontFileName);
+            case kFontFileName:  // Remove when MIN_PICTURE_VERSION > 41
+                skip_string(stream);
                 break;
             default:
                 SkDEBUGFAIL("Unknown id used by a font descriptor");
@@ -91,7 +99,6 @@
     write_string(stream, fFamilyName, kFontFamilyName);
     write_string(stream, fFullName, kFullName);
     write_string(stream, fPostscriptName, kPostscriptName);
-    write_string(stream, fFontFileName, kFontFileName);
     if (fFontIndex) {
         write_uint(stream, fFontIndex, kFontIndex);
     }
diff --git a/src/core/SkFontDescriptor.h b/src/core/SkFontDescriptor.h
index 13f9cb7..66707dd 100644
--- a/src/core/SkFontDescriptor.h
+++ b/src/core/SkFontDescriptor.h
@@ -26,7 +26,6 @@
     const char* getFamilyName() const { return fFamilyName.c_str(); }
     const char* getFullName() const { return fFullName.c_str(); }
     const char* getPostscriptName() const { return fPostscriptName.c_str(); }
-    const char* getFontFileName() const { return fFontFileName.c_str(); }
     bool hasFontData() const { return fFontData.get() != NULL; }
     // Transfers ownership to the caller.
     SkStreamAsset* transferFontData() { return fFontData.detach(); }
@@ -35,7 +34,6 @@
     void setFamilyName(const char* name) { fFamilyName.set(name); }
     void setFullName(const char* name) { fFullName.set(name); }
     void setPostscriptName(const char* name) { fPostscriptName.set(name); }
-    void setFontFileName(const char* name) { fFontFileName.set(name); }
     /** Set the font data only if it is necessary for serialization.
      *  This method takes ownership of the stream (both reference and cursor).
      */
@@ -46,7 +44,6 @@
     SkString fFamilyName;
     SkString fFullName;
     SkString fPostscriptName;
-    SkString fFontFileName;
     SkAutoTDelete<SkStreamAsset> fFontData;
     int fFontIndex;
 
diff --git a/src/core/SkFontMgr.cpp b/src/core/SkFontMgr.cpp
index 4e0f549..a03e05e 100644
--- a/src/core/SkFontMgr.cpp
+++ b/src/core/SkFontMgr.cpp
@@ -15,15 +15,15 @@
 
 class SkEmptyFontStyleSet : public SkFontStyleSet {
 public:
-    int count() SK_OVERRIDE { return 0; }
-    void getStyle(int, SkFontStyle*, SkString*) SK_OVERRIDE {
+    int count() override { return 0; }
+    void getStyle(int, SkFontStyle*, SkString*) override {
         SkDEBUGFAIL("SkFontStyleSet::getStyle called on empty set");
     }
-    SkTypeface* createTypeface(int index) SK_OVERRIDE {
+    SkTypeface* createTypeface(int index) override {
         SkDEBUGFAIL("SkFontStyleSet::createTypeface called on empty set");
         return NULL;
     }
-    SkTypeface* matchStyle(const SkFontStyle&) SK_OVERRIDE {
+    SkTypeface* matchStyle(const SkFontStyle&) override {
         return NULL;
     }
 };
@@ -36,46 +36,46 @@
 
 class SkEmptyFontMgr : public SkFontMgr {
 protected:
-    int onCountFamilies() const SK_OVERRIDE {
+    int onCountFamilies() const override {
         return 0;
     }
-    void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(int index, SkString* familyName) const override {
         SkDEBUGFAIL("onGetFamilyName called with bad index");
     }
-    SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
+    SkFontStyleSet* onCreateStyleSet(int index) const override {
         SkDEBUGFAIL("onCreateStyleSet called with bad index");
         return NULL;
     }
-    SkFontStyleSet* onMatchFamily(const char[]) const SK_OVERRIDE {
+    SkFontStyleSet* onMatchFamily(const char[]) const override {
         return SkFontStyleSet::CreateEmpty();
     }
 
     virtual SkTypeface* onMatchFamilyStyle(const char[],
-                                           const SkFontStyle&) const SK_OVERRIDE {
+                                           const SkFontStyle&) const override {
         return NULL;
     }
     virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
                                                     const SkFontStyle& style,
                                                     const char* bcp47[],
                                                     int bcp47Count,
-                                                    SkUnichar character) const SK_OVERRIDE {
+                                                    SkUnichar character) const override {
         return NULL;
     }
     virtual SkTypeface* onMatchFaceStyle(const SkTypeface*,
-                                         const SkFontStyle&) const SK_OVERRIDE {
+                                         const SkFontStyle&) const override {
         return NULL;
     }
-    SkTypeface* onCreateFromData(SkData*, int) const SK_OVERRIDE {
+    SkTypeface* onCreateFromData(SkData*, int) const override {
         return NULL;
     }
-    SkTypeface* onCreateFromStream(SkStreamAsset* stream, int) const SK_OVERRIDE {
+    SkTypeface* onCreateFromStream(SkStreamAsset* stream, int) const override {
         SkDELETE(stream);
         return NULL;
     }
-    SkTypeface* onCreateFromFile(const char[], int) const SK_OVERRIDE {
+    SkTypeface* onCreateFromFile(const char[], int) const override {
         return NULL;
     }
-    SkTypeface* onLegacyCreateTypeface(const char [], unsigned) const SK_OVERRIDE {
+    SkTypeface* onLegacyCreateTypeface(const char [], unsigned) const override {
         return NULL;
     }
 };
diff --git a/src/core/SkFunction.h b/src/core/SkFunction.h
new file mode 100644
index 0000000..429c6f5
--- /dev/null
+++ b/src/core/SkFunction.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkFunction_DEFINED
+#define SkFunction_DEFINED
+
+// TODO: document, more pervasive move support in constructors, small-Fn optimization
+
+#include "SkTemplates.h"
+#include "SkTypes.h"
+
+template <typename> class SkFunction;
+
+template <typename R, typename... Args>
+class SkFunction<R(Args...)> {
+public:
+    SkFunction() {}
+
+    template <typename Fn>
+    SkFunction(const Fn& fn) : fFunction(SkNEW_ARGS(LambdaImpl<Fn>, (fn))) {}
+
+    SkFunction(R (*fn)(Args...)) : fFunction(SkNEW_ARGS(FnPtrImpl, (fn))) {}
+
+    SkFunction(const SkFunction& other) { *this = other; }
+    SkFunction& operator=(const SkFunction& other) {
+        if (this != &other) {
+            fFunction.reset(other.fFunction ? other.fFunction->clone() : nullptr);
+        }
+        return *this;
+    }
+
+    R operator()(Args... args) const {
+        SkASSERT(fFunction.get());
+        return fFunction->call(Forward(args)...);
+    }
+
+private:
+    // ~= std::forward.  This moves its argument if possible, falling back to a copy if not.
+    template <typename T> static T&& Forward(T& v) { return (T&&)v; }
+
+    struct Interface {
+        virtual ~Interface() {}
+        virtual R call(Args...) const = 0;
+        virtual Interface* clone() const = 0;
+    };
+
+    template <typename Fn>
+    class LambdaImpl final : public Interface {
+    public:
+        LambdaImpl(const Fn& fn) : fFn(fn) {}
+
+        R call(Args... args) const override { return fFn(Forward(args)...); }
+        Interface* clone() const { return SkNEW_ARGS(LambdaImpl<Fn>, (fFn)); }
+    private:
+        Fn fFn;
+    };
+
+    class FnPtrImpl final : public Interface {
+    public:
+        FnPtrImpl(R (*fn)(Args...)) : fFn(fn) {}
+
+        R call(Args... args) const override { return fFn(Forward(args)...); }
+        Interface* clone() const { return SkNEW_ARGS(FnPtrImpl, (fFn)); }
+    private:
+        R (*fFn)(Args...);
+    };
+
+    SkAutoTDelete<Interface> fFunction;
+};
+
+#endif//SkFunction_DEFINED
diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
index 5331bb7..b4489d2 100644
--- a/src/core/SkGeometry.cpp
+++ b/src/core/SkGeometry.cpp
@@ -7,60 +7,24 @@
 
 #include "SkGeometry.h"
 #include "SkMatrix.h"
+#include "SkNx.h"
 
-bool SkXRayCrossesLine(const SkXRay& pt,
-                       const SkPoint pts[2],
-                       bool* ambiguous) {
-    if (ambiguous) {
-        *ambiguous = false;
-    }
-    // Determine quick discards.
-    // Consider query line going exactly through point 0 to not
-    // intersect, for symmetry with SkXRayCrossesMonotonicCubic.
-    if (pt.fY == pts[0].fY) {
-        if (ambiguous) {
-            *ambiguous = true;
-        }
-        return false;
-    }
-    if (pt.fY < pts[0].fY && pt.fY < pts[1].fY)
-        return false;
-    if (pt.fY > pts[0].fY && pt.fY > pts[1].fY)
-        return false;
-    if (pt.fX > pts[0].fX && pt.fX > pts[1].fX)
-        return false;
-    // Determine degenerate cases
-    if (SkScalarNearlyZero(pts[0].fY - pts[1].fY))
-        return false;
-    if (SkScalarNearlyZero(pts[0].fX - pts[1].fX)) {
-        // We've already determined the query point lies within the
-        // vertical range of the line segment.
-        if (pt.fX <= pts[0].fX) {
-            if (ambiguous) {
-                *ambiguous = (pt.fY == pts[1].fY);
-            }
-            return true;
-        }
-        return false;
-    }
-    // Ambiguity check
-    if (pt.fY == pts[1].fY) {
-        if (pt.fX <= pts[1].fX) {
-            if (ambiguous) {
-                *ambiguous = true;
-            }
-            return true;
-        }
-        return false;
-    }
-    // Full line segment evaluation
-    SkScalar delta_y = pts[1].fY - pts[0].fY;
-    SkScalar delta_x = pts[1].fX - pts[0].fX;
-    SkScalar slope = SkScalarDiv(delta_y, delta_x);
-    SkScalar b = pts[0].fY - SkScalarMul(slope, pts[0].fX);
-    // Solve for x coordinate at y = pt.fY
-    SkScalar x = SkScalarDiv(pt.fY - b, slope);
-    return pt.fX <= x;
+#if 0
+static Sk2s from_point(const SkPoint& point) {
+    return Sk2s::Load(&point.fX);
+}
+
+static SkPoint to_point(const Sk2s& x) {
+    SkPoint point;
+    x.store(&point.fX);
+    return point;
+}
+#endif
+
+static SkVector to_vector(const Sk2s& x) {
+    SkVector vector;
+    x.store(&vector.fX);
+    return vector;
 }
 
 /** If defined, this makes eval_quad and eval_cubic do more setup (sometimes
@@ -98,7 +62,7 @@
         return 0;
     }
 
-    SkScalar r = SkScalarDiv(numer, denom);
+    SkScalar r = numer / denom;
     if (SkScalarIsNaN(r)) {
         return 0;
     }
@@ -146,6 +110,10 @@
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
+static Sk2s quad_poly_eval(const Sk2s& A, const Sk2s& B, const Sk2s& C, const Sk2s& t) {
+    return (A * t + B) * t + C;
+}
+
 static SkScalar eval_quad(const SkScalar src[], SkScalar t) {
     SkASSERT(src);
     SkASSERT(t >= 0 && t <= SK_Scalar1);
@@ -169,14 +137,19 @@
     return 2 * SkScalarMulAdd(A, t, B);
 }
 
-static SkScalar eval_quad_derivative_at_half(const SkScalar src[]) {
-    SkScalar A = src[4] - 2 * src[2] + src[0];
-    SkScalar B = src[2] - src[0];
-    return A + 2 * B;
+void SkQuadToCoeff(const SkPoint pts[3], SkPoint coeff[3]) {
+    Sk2s p0 = from_point(pts[0]);
+    Sk2s p1 = from_point(pts[1]);
+    Sk2s p2 = from_point(pts[2]);
+
+    Sk2s p1minus2 = p1 - p0;
+
+    coeff[0] = to_point(p2 - p1 - p1 + p0);     // A * t^2
+    coeff[1] = to_point(p1minus2 + p1minus2);   // B * t
+    coeff[2] = pts[0];                          // C
 }
 
-void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt,
-                  SkVector* tangent) {
+void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent) {
     SkASSERT(src);
     SkASSERT(t >= 0 && t <= SK_Scalar1);
 
@@ -189,51 +162,61 @@
     }
 }
 
-void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent) {
+SkPoint SkEvalQuadAt(const SkPoint src[3], SkScalar t) {
     SkASSERT(src);
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
 
-    if (pt) {
-        SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
-        SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
-        SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
-        SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
-        pt->set(SkScalarAve(x01, x12), SkScalarAve(y01, y12));
-    }
-    if (tangent) {
-        tangent->set(eval_quad_derivative_at_half(&src[0].fX),
-                     eval_quad_derivative_at_half(&src[0].fY));
-    }
+    const Sk2s t2(t);
+
+    Sk2s P0 = from_point(src[0]);
+    Sk2s P1 = from_point(src[1]);
+    Sk2s P2 = from_point(src[2]);
+
+    Sk2s B = P1 - P0;
+    Sk2s A = P2 - P1 - B;
+
+    return to_point((A * t2 + B+B) * t2 + P0);
 }
 
-static void interp_quad_coords(const SkScalar* src, SkScalar* dst, SkScalar t) {
-    SkScalar    ab = SkScalarInterp(src[0], src[2], t);
-    SkScalar    bc = SkScalarInterp(src[2], src[4], t);
+SkVector SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t) {
+    SkASSERT(src);
+    SkASSERT(t >= 0 && t <= SK_Scalar1);
 
-    dst[0] = src[0];
-    dst[2] = ab;
-    dst[4] = SkScalarInterp(ab, bc, t);
-    dst[6] = bc;
-    dst[8] = src[4];
+    Sk2s P0 = from_point(src[0]);
+    Sk2s P1 = from_point(src[1]);
+    Sk2s P2 = from_point(src[2]);
+
+    Sk2s B = P1 - P0;
+    Sk2s A = P2 - P1 - B;
+    Sk2s T = A * Sk2s(t) + B;
+
+    return to_vector(T + T);
+}
+
+static inline Sk2s interp(const Sk2s& v0, const Sk2s& v1, const Sk2s& t) {
+    return v0 + (v1 - v0) * t;
 }
 
 void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t) {
     SkASSERT(t > 0 && t < SK_Scalar1);
 
-    interp_quad_coords(&src[0].fX, &dst[0].fX, t);
-    interp_quad_coords(&src[0].fY, &dst[0].fY, t);
+    Sk2s p0 = from_point(src[0]);
+    Sk2s p1 = from_point(src[1]);
+    Sk2s p2 = from_point(src[2]);
+    Sk2s tt(t);
+
+    Sk2s p01 = interp(p0, p1, tt);
+    Sk2s p12 = interp(p1, p2, tt);
+
+    dst[0] = to_point(p0);
+    dst[1] = to_point(p01);
+    dst[2] = to_point(interp(p01, p12, tt));
+    dst[3] = to_point(p12);
+    dst[4] = to_point(p2);
 }
 
 void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]) {
-    SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
-    SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
-    SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
-    SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
-
-    dst[0] = src[0];
-    dst[1].set(x01, y01);
-    dst[2].set(SkScalarAve(x01, x12), SkScalarAve(y01, y12));
-    dst[3].set(x12, y12);
-    dst[4] = src[2];
+    SkChopQuadAt(src, dst, 0.5f); return;
 }
 
 /** Quad'(t) = At + B, where
@@ -342,15 +325,15 @@
     }
 }
 
-#define SK_ScalarTwoThirds  (0.666666666f)
-
 void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4]) {
-    const SkScalar scale = SK_ScalarTwoThirds;
+    Sk2s scale(SkDoubleToScalar(2.0 / 3.0));
+    Sk2s s0 = from_point(src[0]);
+    Sk2s s1 = from_point(src[1]);
+    Sk2s s2 = from_point(src[2]);
+
     dst[0] = src[0];
-    dst[1].set(src[0].fX + SkScalarMul(src[1].fX - src[0].fX, scale),
-               src[0].fY + SkScalarMul(src[1].fY - src[0].fY, scale));
-    dst[2].set(src[2].fX + SkScalarMul(src[1].fX - src[2].fX, scale),
-               src[2].fY + SkScalarMul(src[1].fY - src[2].fY, scale));
+    dst[1] = to_point(s0 + (s1 - s0) * scale);
+    dst[2] = to_point(s2 + (s1 - s2) * scale);
     dst[3] = src[2];
 }
 
@@ -358,24 +341,6 @@
 ///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS /////
 //////////////////////////////////////////////////////////////////////////////
 
-static void get_cubic_coeff(const SkScalar pt[], SkScalar coeff[4]) {
-    coeff[0] = pt[6] + 3*(pt[2] - pt[4]) - pt[0];
-    coeff[1] = 3*(pt[4] - pt[2] - pt[2] + pt[0]);
-    coeff[2] = 3*(pt[2] - pt[0]);
-    coeff[3] = pt[0];
-}
-
-void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4]) {
-    SkASSERT(pts);
-
-    if (cx) {
-        get_cubic_coeff(&pts[0].fX, cx);
-    }
-    if (cy) {
-        get_cubic_coeff(&pts[0].fY, cy);
-    }
-}
-
 static SkScalar eval_cubic(const SkScalar src[], SkScalar t) {
     SkASSERT(src);
     SkASSERT(t >= 0 && t <= SK_Scalar1);
@@ -458,29 +423,49 @@
     return SkFindUnitQuadRoots(A, B, C, tValues);
 }
 
-static void interp_cubic_coords(const SkScalar* src, SkScalar* dst,
-                                SkScalar t) {
-    SkScalar    ab = SkScalarInterp(src[0], src[2], t);
-    SkScalar    bc = SkScalarInterp(src[2], src[4], t);
-    SkScalar    cd = SkScalarInterp(src[4], src[6], t);
-    SkScalar    abc = SkScalarInterp(ab, bc, t);
-    SkScalar    bcd = SkScalarInterp(bc, cd, t);
-    SkScalar    abcd = SkScalarInterp(abc, bcd, t);
-
-    dst[0] = src[0];
-    dst[2] = ab;
-    dst[4] = abc;
-    dst[6] = abcd;
-    dst[8] = bcd;
-    dst[10] = cd;
-    dst[12] = src[6];
-}
-
 void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t) {
     SkASSERT(t > 0 && t < SK_Scalar1);
 
-    interp_cubic_coords(&src[0].fX, &dst[0].fX, t);
-    interp_cubic_coords(&src[0].fY, &dst[0].fY, t);
+    Sk2s    p0 = from_point(src[0]);
+    Sk2s    p1 = from_point(src[1]);
+    Sk2s    p2 = from_point(src[2]);
+    Sk2s    p3 = from_point(src[3]);
+    Sk2s    tt(t);
+
+    Sk2s    ab = interp(p0, p1, tt);
+    Sk2s    bc = interp(p1, p2, tt);
+    Sk2s    cd = interp(p2, p3, tt);
+    Sk2s    abc = interp(ab, bc, tt);
+    Sk2s    bcd = interp(bc, cd, tt);
+    Sk2s    abcd = interp(abc, bcd, tt);
+
+    dst[0] = src[0];
+    dst[1] = to_point(ab);
+    dst[2] = to_point(abc);
+    dst[3] = to_point(abcd);
+    dst[4] = to_point(bcd);
+    dst[5] = to_point(cd);
+    dst[6] = src[3];
+}
+
+void SkCubicToCoeff(const SkPoint pts[4], SkPoint coeff[4]) {
+    Sk2s p0 = from_point(pts[0]);
+    Sk2s p1 = from_point(pts[1]);
+    Sk2s p2 = from_point(pts[2]);
+    Sk2s p3 = from_point(pts[3]);
+
+    const Sk2s three(3);
+    Sk2s p1minusp2 = p1 - p2;
+
+    Sk2s D = p0;
+    Sk2s A = p3 + three * p1minusp2 - D;
+    Sk2s B = three * (D - p1minusp2 - p1);
+    Sk2s C = three * (p1 - D);
+
+    coeff[0] = to_point(A);
+    coeff[1] = to_point(B);
+    coeff[2] = to_point(C);
+    coeff[3] = to_point(D);
 }
 
 /*  http://code.google.com/p/skia/issues/detail?id=32
@@ -550,25 +535,7 @@
 }
 
 void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7]) {
-    SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
-    SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
-    SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
-    SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
-    SkScalar x23 = SkScalarAve(src[2].fX, src[3].fX);
-    SkScalar y23 = SkScalarAve(src[2].fY, src[3].fY);
-
-    SkScalar x012 = SkScalarAve(x01, x12);
-    SkScalar y012 = SkScalarAve(y01, y12);
-    SkScalar x123 = SkScalarAve(x12, x23);
-    SkScalar y123 = SkScalarAve(y12, y23);
-
-    dst[0] = src[0];
-    dst[1].set(x01, y01);
-    dst[2].set(x012, y012);
-    dst[3].set(SkScalarAve(x012, x123), SkScalarAve(y012, y123));
-    dst[4].set(x123, y123);
-    dst[5].set(x23, y23);
-    dst[6] = src[3];
+    SkChopCubicAt(src, dst, 0.5f);
 }
 
 static void flatten_double_cubic_extrema(SkScalar coords[14]) {
@@ -949,128 +916,31 @@
     return count + 1;
 }
 
-bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4],
-                                 bool* ambiguous) {
-    if (ambiguous) {
-        *ambiguous = false;
-    }
+#include "../pathops/SkPathOpsCubic.h"
 
-    // Find the minimum and maximum y of the extrema, which are the
-    // first and last points since this cubic is monotonic
-    SkScalar min_y = SkMinScalar(cubic[0].fY, cubic[3].fY);
-    SkScalar max_y = SkMaxScalar(cubic[0].fY, cubic[3].fY);
+typedef int (SkDCubic::*InterceptProc)(double intercept, double roots[3]) const;
 
-    if (pt.fY == cubic[0].fY
-        || pt.fY < min_y
-        || pt.fY > max_y) {
-        // The query line definitely does not cross the curve
-        if (ambiguous) {
-            *ambiguous = (pt.fY == cubic[0].fY);
-        }
-        return false;
-    }
-
-    bool pt_at_extremum = (pt.fY == cubic[3].fY);
-
-    SkScalar min_x =
-        SkMinScalar(
-            SkMinScalar(
-                SkMinScalar(cubic[0].fX, cubic[1].fX),
-                cubic[2].fX),
-            cubic[3].fX);
-    if (pt.fX < min_x) {
-        // The query line definitely crosses the curve
-        if (ambiguous) {
-            *ambiguous = pt_at_extremum;
-        }
-        return true;
-    }
-
-    SkScalar max_x =
-        SkMaxScalar(
-            SkMaxScalar(
-                SkMaxScalar(cubic[0].fX, cubic[1].fX),
-                cubic[2].fX),
-            cubic[3].fX);
-    if (pt.fX > max_x) {
-        // The query line definitely does not cross the curve
-        return false;
-    }
-
-    // Do a binary search to find the parameter value which makes y as
-    // close as possible to the query point. See whether the query
-    // line's origin is to the left of the associated x coordinate.
-
-    // kMaxIter is chosen as the number of mantissa bits for a float,
-    // since there's no way we are going to get more precision by
-    // iterating more times than that.
-    const int kMaxIter = 23;
-    SkPoint eval;
-    int iter = 0;
-    SkScalar upper_t;
-    SkScalar lower_t;
-    // Need to invert direction of t parameter if cubic goes up
-    // instead of down
-    if (cubic[3].fY > cubic[0].fY) {
-        upper_t = SK_Scalar1;
-        lower_t = 0;
-    } else {
-        upper_t = 0;
-        lower_t = SK_Scalar1;
-    }
-    do {
-        SkScalar t = SkScalarAve(upper_t, lower_t);
-        SkEvalCubicAt(cubic, t, &eval, NULL, NULL);
-        if (pt.fY > eval.fY) {
-            lower_t = t;
-        } else {
-            upper_t = t;
-        }
-    } while (++iter < kMaxIter
-             && !SkScalarNearlyZero(eval.fY - pt.fY));
-    if (pt.fX <= eval.fX) {
-        if (ambiguous) {
-            *ambiguous = pt_at_extremum;
+static bool cubic_dchop_at_intercept(const SkPoint src[4], SkScalar intercept, SkPoint dst[7],
+                                     InterceptProc method) {
+    SkDCubic cubic;
+    double roots[3];
+    int count = (cubic.set(src).*method)(intercept, roots);
+    if (count > 0) {
+        SkDCubicPair pair = cubic.chopAt(roots[0]);
+        for (int i = 0; i < 7; ++i) {
+            dst[i] = pair.pts[i].asSkPoint();
         }
         return true;
     }
     return false;
 }
 
-int SkNumXRayCrossingsForCubic(const SkXRay& pt,
-                               const SkPoint cubic[4],
-                               bool* ambiguous) {
-    int num_crossings = 0;
-    SkPoint monotonic_cubics[10];
-    int num_monotonic_cubics = SkChopCubicAtYExtrema(cubic, monotonic_cubics);
-    if (ambiguous) {
-        *ambiguous = false;
-    }
-    bool locally_ambiguous;
-    if (SkXRayCrossesMonotonicCubic(pt,
-                                    &monotonic_cubics[0],
-                                    &locally_ambiguous))
-        ++num_crossings;
-    if (ambiguous) {
-        *ambiguous |= locally_ambiguous;
-    }
-    if (num_monotonic_cubics > 0)
-        if (SkXRayCrossesMonotonicCubic(pt,
-                                        &monotonic_cubics[3],
-                                        &locally_ambiguous))
-            ++num_crossings;
-    if (ambiguous) {
-        *ambiguous |= locally_ambiguous;
-    }
-    if (num_monotonic_cubics > 1)
-        if (SkXRayCrossesMonotonicCubic(pt,
-                                        &monotonic_cubics[6],
-                                        &locally_ambiguous))
-            ++num_crossings;
-    if (ambiguous) {
-        *ambiguous |= locally_ambiguous;
-    }
-    return num_crossings;
+bool SkChopMonoCubicAtY(SkPoint src[4], SkScalar y, SkPoint dst[7]) {
+    return cubic_dchop_at_intercept(src, y, dst, &SkDCubic::horizontalIntersect);
+}
+
+bool SkChopMonoCubicAtX(SkPoint src[4], SkScalar x, SkPoint dst[7]) {
+    return cubic_dchop_at_intercept(src, x, dst, &SkDCubic::verticalIntersect);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1279,7 +1149,7 @@
     A = -B;
     SkScalar denom = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
 
-    return SkScalarDiv(numer, denom);
+    return numer / denom;
 }
 
 // F' = 2 (C t (1 + t (-1 + w)) - A (-1 + t) (t (-1 + w) - w) + B (1 - 2 t) w)
@@ -1395,28 +1265,74 @@
     dst[1].fW = tmp2[2].fZ / root;
 }
 
+static Sk2s times_2(const Sk2s& value) {
+    return value + value;
+}
+
+SkPoint SkConic::evalAt(SkScalar t) const {
+    Sk2s p0 = from_point(fPts[0]);
+    Sk2s p1 = from_point(fPts[1]);
+    Sk2s p2 = from_point(fPts[2]);
+    Sk2s tt(t);
+    Sk2s ww(fW);
+    Sk2s one(1);
+
+    Sk2s p1w = p1 * ww;
+    Sk2s C = p0;
+    Sk2s A = p2 - times_2(p1w) + p0;
+    Sk2s B = times_2(p1w - C);
+    Sk2s numer = quad_poly_eval(A, B, C, tt);
+
+    B = times_2(ww - one);
+    A = -B;
+    Sk2s denom = quad_poly_eval(A, B, one, tt);
+
+    return to_point(numer / denom);
+}
+
+SkVector SkConic::evalTangentAt(SkScalar t) const {
+    Sk2s p0 = from_point(fPts[0]);
+    Sk2s p1 = from_point(fPts[1]);
+    Sk2s p2 = from_point(fPts[2]);
+    Sk2s ww(fW);
+
+    Sk2s p20 = p2 - p0;
+    Sk2s p10 = p1 - p0;
+
+    Sk2s C = ww * p10;
+    Sk2s A = ww * p20 - p20;
+    Sk2s B = p20 - C - C;
+
+    return to_vector(quad_poly_eval(A, B, C, Sk2s(t)));
+}
+
 static SkScalar subdivide_w_value(SkScalar w) {
     return SkScalarSqrt(SK_ScalarHalf + w * SK_ScalarHalf);
 }
 
-void SkConic::chop(SkConic dst[2]) const {
-    SkScalar scale = SkScalarInvert(SK_Scalar1 + fW);
-    SkScalar p1x = fW * fPts[1].fX;
-    SkScalar p1y = fW * fPts[1].fY;
-    SkScalar mx = (fPts[0].fX + 2 * p1x + fPts[2].fX) * scale * SK_ScalarHalf;
-    SkScalar my = (fPts[0].fY + 2 * p1y + fPts[2].fY) * scale * SK_ScalarHalf;
+static Sk2s twice(const Sk2s& value) {
+    return value + value;
+}
+
+void SkConic::chop(SkConic * SK_RESTRICT dst) const {
+    Sk2s scale = Sk2s(SkScalarInvert(SK_Scalar1 + fW));
+    SkScalar newW = subdivide_w_value(fW);
+
+    Sk2s p0 = from_point(fPts[0]);
+    Sk2s p1 = from_point(fPts[1]);
+    Sk2s p2 = from_point(fPts[2]);
+    Sk2s ww(fW);
+
+    Sk2s wp1 = ww * p1;
+    Sk2s m = (p0 + twice(wp1) + p2) * scale * Sk2s(0.5f);
 
     dst[0].fPts[0] = fPts[0];
-    dst[0].fPts[1].set((fPts[0].fX + p1x) * scale,
-                       (fPts[0].fY + p1y) * scale);
-    dst[0].fPts[2].set(mx, my);
-
-    dst[1].fPts[0].set(mx, my);
-    dst[1].fPts[1].set((p1x + fPts[2].fX) * scale,
-                       (p1y + fPts[2].fY) * scale);
+    dst[0].fPts[1] = to_point((p0 + wp1) * scale);
+    dst[0].fPts[2] = dst[1].fPts[0] = to_point(m);
+    dst[1].fPts[1] = to_point((wp1 + p2) * scale);
     dst[1].fPts[2] = fPts[2];
 
-    dst[0].fW = dst[1].fW = subdivide_w_value(fW);
+    dst[0].fW = dst[1].fW = newW;
 }
 
 /*
@@ -1570,7 +1486,7 @@
     }
 
     SkP3D src[3], dst[3];
-    
+
     ratquad_mapTo3D(pts, w, src);
 
     matrix.mapHomogeneousPoints(&dst[0].fX, &src[0].fX, 3);
@@ -1641,7 +1557,7 @@
     const SkScalar dot = SkVector::DotProduct(lastQ, finalP);
     SkASSERT(0 <= dot && dot <= SK_Scalar1 + SK_ScalarNearlyZero);
 
-    if (dot < 1 - SK_ScalarNearlyZero) {
+    if (dot < 1) {
         SkVector offCurve = { lastQ.x() + x, lastQ.y() + y };
         // compute the bisector vector, and then rescale to be the off-curve point.
         // we compute its length from cos(theta/2) = length / 1, using half-angle identity we get
diff --git a/src/core/SkGeometry.h b/src/core/SkGeometry.h
index 0b23685..a0c13d6 100644
--- a/src/core/SkGeometry.h
+++ b/src/core/SkGeometry.h
@@ -9,21 +9,22 @@
 #define SkGeometry_DEFINED
 
 #include "SkMatrix.h"
+#include "SkNx.h"
 
-/** An XRay is a half-line that runs from the specific point/origin to
-    +infinity in the X direction. e.g. XRay(3,5) is the half-line
-    (3,5)....(infinity, 5)
- */
-typedef SkPoint SkXRay;
+static inline Sk2s from_point(const SkPoint& point) {
+    return Sk2s::Load(&point.fX);
+}
 
-/** Given a line segment from pts[0] to pts[1], and an xray, return true if
-    they intersect. Optional outgoing "ambiguous" argument indicates
-    whether the answer is ambiguous because the query occurred exactly at
-    one of the endpoints' y coordinates, indicating that another query y
-    coordinate is preferred for robustness.
-*/
-bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2],
-                       bool* ambiguous = NULL);
+static inline SkPoint to_point(const Sk2s& x) {
+    SkPoint point;
+    x.store(&point.fX);
+    return point;
+}
+
+static inline Sk2s sk2s_cubic_eval(const Sk2s& A, const Sk2s& B, const Sk2s& C, const Sk2s& D,
+                                   const Sk2s& t) {
+    return ((A * t + B) * t + C) * t + D;
+}
 
 /** Given a quadratic equation Ax^2 + Bx + C = 0, return 0, 1, 2 roots for the
     equation.
@@ -32,13 +33,23 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+SkPoint SkEvalQuadAt(const SkPoint src[3], SkScalar t);
+SkPoint SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t);
+
 /** Set pt to the point on the src quadratic specified by t. t must be
     0 <= t <= 1.0
 */
-void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt,
-                  SkVector* tangent = NULL);
-void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt,
-                      SkVector* tangent = NULL);
+void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent = NULL);
+
+/**
+ *  output is : eval(t) == coeff[0] * t^2 + coeff[1] * t + coeff[2]
+ */
+void SkQuadToCoeff(const SkPoint pts[3], SkPoint coeff[3]);
+
+/**
+ *  output is : eval(t) == coeff[0] * t^3 + coeff[1] * t^2 + coeff[2] * t + coeff[3]
+ */
+void SkCubicToCoeff(const SkPoint pts[4], SkPoint coeff[4]);
 
 /** Given a src quadratic bezier, chop it at the specified t value,
     where 0 < t < 1, and return the two new quadratics in dst:
@@ -93,11 +104,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-/** Convert from parametric from (pts) to polynomial coefficients
-    coeff[0]*T^3 + coeff[1]*T^2 + coeff[2]*T + coeff[3]
-*/
-void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4]);
-
 /** Set pt to the point on the src cubic specified by t. t must be
     0 <= t <= 1.0
 */
@@ -109,6 +115,7 @@
     dst[0..3] and dst[3..6]
 */
 void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t);
+
 /** Given a src cubic bezier, chop it at the specified t values,
     where 0 < t < 1, and return the new cubics in dst:
     dst[0..3],dst[3..6],...,dst[3*t_count..3*(t_count+1)]
@@ -159,35 +166,8 @@
 int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13],
                               SkScalar tValues[3] = NULL);
 
-/** Given a monotonic cubic bezier, determine whether an xray intersects the
-    cubic.
-    By definition the cubic is open at the starting point; in other
-    words, if pt.fY is equivalent to cubic[0].fY, and pt.fX is to the
-    left of the curve, the line is not considered to cross the curve,
-    but if it is equal to cubic[3].fY then it is considered to
-    cross.
-    Optional outgoing "ambiguous" argument indicates whether the answer is
-    ambiguous because the query occurred exactly at one of the endpoints' y
-    coordinates, indicating that another query y coordinate is preferred
-    for robustness.
- */
-bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4],
-                                 bool* ambiguous = NULL);
-
-/** Given an arbitrary cubic bezier, return the number of times an xray crosses
-    the cubic. Valid return values are [0..3]
-    By definition the cubic is open at the starting point; in other
-    words, if pt.fY is equivalent to cubic[0].fY, and pt.fX is to the
-    left of the curve, the line is not considered to cross the curve,
-    but if it is equal to cubic[3].fY then it is considered to
-    cross.
-    Optional outgoing "ambiguous" argument indicates whether the answer is
-    ambiguous because the query occurred exactly at one of the endpoints' y
-    coordinates or at a tangent point, indicating that another query y
-    coordinate is preferred for robustness.
- */
-int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4],
-                               bool* ambiguous = NULL);
+bool SkChopMonoCubicAtX(SkPoint src[4], SkScalar y, SkPoint dst[7]);
+bool SkChopMonoCubicAtY(SkPoint src[4], SkScalar x, SkPoint dst[7]);
 
 enum SkCubicType {
     kSerpentine_SkCubicType,
@@ -263,6 +243,9 @@
     void chopAt(SkScalar t, SkConic dst[2]) const;
     void chop(SkConic dst[2]) const;
 
+    SkPoint evalAt(SkScalar t) const;
+    SkVector evalTangentAt(SkScalar t) const;
+
     void computeAsQuadError(SkVector* err) const;
     bool asQuadTol(SkScalar tol) const;
 
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index 2837b7c..0a5aca8 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -11,6 +11,7 @@
 #include "SkChecksum.h"
 #include "SkDevice.h"
 #include "SkLazyPtr.h"
+#include "SkMatrixImageFilter.h"
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
 #include "SkRect.h"
@@ -23,7 +24,11 @@
 #include "SkGr.h"
 #endif
 
-enum { kDefaultCacheSize = 128 * 1024 * 1024 };
+#ifdef SK_BUILD_FOR_IOS
+  enum { kDefaultCacheSize = 2 * 1024 * 1024 };
+#else 
+  enum { kDefaultCacheSize = 128 * 1024 * 1024 };
+#endif 
 
 static int32_t next_image_filter_unique_id() {
     static int32_t gImageFilterUniqueID;
@@ -100,18 +105,21 @@
 
     uint32_t flags = buffer.readUInt();
     fCropRect = CropRect(rect, flags);
-    fUniqueID = buffer.readUInt();
+    if (buffer.isVersionLT(SkReadBuffer::kImageFilterNoUniqueID_Version)) {
+
+        (void) buffer.readUInt();
+    }
     return buffer.isValid();
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect, uint32_t uniqueID)
+SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect)
   : fInputCount(inputCount),
     fInputs(new SkImageFilter*[inputCount]),
     fUsesSrcInput(false),
     fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)),
-    fUniqueID(uniqueID ? uniqueID : next_image_filter_unique_id()) {
+    fUniqueID(next_image_filter_unique_id()) {
     for (int i = 0; i < inputCount; ++i) {
         if (NULL == inputs[i] || inputs[i]->usesSrcInput()) {
             fUsesSrcInput = true;
@@ -129,7 +137,8 @@
 }
 
 SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer)
-  : fUsesSrcInput(false) {
+  : fUsesSrcInput(false)
+  , fUniqueID(next_image_filter_unique_id()) {
     Common common;
     if (common.unflatten(buffer, inputCount)) {
         fCropRect = common.cropRect();
@@ -141,7 +150,6 @@
                 fUsesSrcInput = true;
             }
         }
-        fUniqueID = buffer.isCrossProcess() ? next_image_filter_unique_id() : common.uniqueID();
     } else {
         fInputCount = 0;
         fInputs = NULL;
@@ -159,7 +167,6 @@
     }
     buffer.writeRect(fCropRect.rect());
     buffer.writeUInt(fCropRect.flags());
-    buffer.writeUInt(fUniqueID);
 }
 
 bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
@@ -190,7 +197,6 @@
 
 bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
                                  SkIRect* dst) const {
-    SkASSERT(&src);
     SkASSERT(dst);
     return this->onFilterBounds(src, ctm, dst);
 }
@@ -251,8 +257,8 @@
     desc.fHeight = bounds.height();
     desc.fConfig = kRGBA_8888_GrPixelConfig;
 
-    SkAutoTUnref<GrTexture> dst(
-        context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch));
+    SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture(
+        desc, GrTextureProvider::kApprox_ScratchTexMatch));
     if (!dst) {
         return false;
     }
@@ -366,6 +372,12 @@
     return false;
 }
 
+SkImageFilter* SkImageFilter::CreateMatrixFilter(const SkMatrix& matrix,
+                                                 SkFilterQuality filterQuality,
+                                                 SkImageFilter* input) {
+    return SkMatrixImageFilter::Create(matrix, filterQuality, input);
+}
+
 #if SK_SUPPORT_GPU
 
 void SkImageFilter::WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result) {
@@ -431,7 +443,7 @@
         }
         SK_DECLARE_INTERNAL_LLIST_INTERFACE(Value);
     };
-    bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE {
+    bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const override {
         SkAutoMutexAcquire mutex(fMutex);
         if (Value* v = fLookup.find(key)) {
             *result = v->fBitmap;
@@ -444,7 +456,7 @@
         }
         return false;
     }
-    void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) SK_OVERRIDE {
+    void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) override {
         SkAutoMutexAcquire mutex(fMutex);
         if (Value* v = fLookup.find(key)) {
             removeInternal(v);
diff --git a/src/core/SkImageGenerator.cpp b/src/core/SkImageGenerator.cpp
index aabe83e..f18dce2 100644
--- a/src/core/SkImageGenerator.cpp
+++ b/src/core/SkImageGenerator.cpp
@@ -7,17 +7,9 @@
 
 #include "SkImageGenerator.h"
 
-bool SkImageGenerator::getInfo(SkImageInfo* info) {
-    SkImageInfo dummy;
-    if (NULL == info) {
-        info = &dummy;
-    }
-    return this->onGetInfo(info);
-}
-
 SkImageGenerator::Result SkImageGenerator::getPixels(const SkImageInfo& info, void* pixels,
-                                                     size_t rowBytes, SkPMColor ctable[],
-                                                     int* ctableCount) {
+                                                     size_t rowBytes, const Options* options,
+                                                     SkPMColor ctable[], int* ctableCount) {
     if (kUnknown_SkColorType == info.colorType()) {
         return kInvalidConversion;
     }
@@ -40,7 +32,12 @@
         ctable = NULL;
     }
 
-    const Result result = this->onGetPixels(info, pixels, rowBytes, ctable, ctableCount);
+    // Default options.
+    Options optsStorage;
+    if (NULL == options) {
+        options = &optsStorage;
+    }
+    const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount);
 
     if ((kIncompleteInput == result || kSuccess == result) && ctableCount) {
         SkASSERT(*ctableCount >= 0 && *ctableCount <= 256);
@@ -54,7 +51,7 @@
     if (kIndex_8_SkColorType == info.colorType()) {
         return kInvalidConversion;
     }
-    return this->getPixels(info, pixels, rowBytes, NULL, NULL);
+    return this->getPixels(info, pixels, rowBytes, NULL, NULL, NULL);
 }
 
 bool SkImageGenerator::getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
@@ -115,11 +112,19 @@
     return NULL;
 }
 
-bool SkImageGenerator::onGetInfo(SkImageInfo*) {
-    return false;
-}
-
+#ifdef SK_SUPPORT_LEGACY_OPTIONLESS_GET_PIXELS
 SkImageGenerator::Result SkImageGenerator::onGetPixels(const SkImageInfo&, void*, size_t,
                                                        SkPMColor*, int*) {
     return kUnimplemented;
 }
+#endif
+
+SkImageGenerator::Result SkImageGenerator::onGetPixels(const SkImageInfo& info, void* dst,
+                                                       size_t rb, const Options& options,
+                                                       SkPMColor* colors, int* colorCount) {
+#ifdef SK_SUPPORT_LEGACY_OPTIONLESS_GET_PIXELS
+    return this->onGetPixels(info, dst, rb, colors, colorCount);
+#else
+    return kUnimplemented;
+#endif
+}
diff --git a/src/core/SkImageInfo.cpp b/src/core/SkImageInfo.cpp
index 0e8b0b1..8429ef2 100644
--- a/src/core/SkImageInfo.cpp
+++ b/src/core/SkImageInfo.cpp
@@ -66,6 +66,7 @@
             }
             break;
         case kRGB_565_SkColorType:
+        case kGray_8_SkColorType:
             alphaType = kOpaque_SkAlphaType;
             break;
         default:
diff --git a/src/core/SkLineClipper.cpp b/src/core/SkLineClipper.cpp
index 4558430..189f03a 100644
--- a/src/core/SkLineClipper.cpp
+++ b/src/core/SkLineClipper.cpp
@@ -78,7 +78,7 @@
                                   SkPoint dst[2]) {
     SkRect bounds;
 
-    bounds.set(src, 2);
+    bounds.set(src[0], src[1]);
     if (containsNoEmptyCheck(clip, bounds)) {
         if (src != dst) {
             memcpy(dst, src, 2 * sizeof(SkPoint));
@@ -137,7 +137,7 @@
         tmp[index1].set(clip.fRight, sect_with_vertical(src, clip.fRight));
     }
 #ifdef SK_DEBUG
-    bounds.set(tmp, 2);
+    bounds.set(tmp[0], tmp[1]);
     SkASSERT(containsNoEmptyCheck(clip, bounds));
 #endif
     memcpy(dst, tmp, sizeof(tmp));
diff --git a/src/core/SkLocalMatrixShader.h b/src/core/SkLocalMatrixShader.h
index be78bee..1a46994 100644
--- a/src/core/SkLocalMatrixShader.h
+++ b/src/core/SkLocalMatrixShader.h
@@ -19,16 +19,16 @@
     , fProxyShader(SkRef(proxy))
     {}
 
-    size_t contextSize() const SK_OVERRIDE {
+    size_t contextSize() const override {
         return fProxyShader->contextSize();
     }
 
     virtual BitmapType asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
-                                 TileMode* mode) const SK_OVERRIDE {
+                                 TileMode* mode) const override {
         return fProxyShader->asABitmap(bitmap, matrix, mode);
     }
 
-    GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
+    GradientType asAGradient(GradientInfo* info) const override {
         return fProxyShader->asAGradient(info);
     }
 
@@ -36,7 +36,7 @@
 
     virtual bool asFragmentProcessor(GrContext* context, const SkPaint& paint,
                                      const SkMatrix& viewM, const SkMatrix* localMatrix,
-                                     GrColor* grColor, GrFragmentProcessor** fp) const SK_OVERRIDE {
+                                     GrColor* grColor, GrFragmentProcessor** fp) const override {
         SkMatrix tmp = this->getLocalMatrix();
         if (localMatrix) {
             tmp.preConcat(*localMatrix);
@@ -48,14 +48,14 @@
 
     virtual bool asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&,
                                      const SkMatrix*, GrColor*,
-                                     GrFragmentProcessor**) const SK_OVERRIDE {
+                                     GrFragmentProcessor**) const override {
         SkDEBUGFAIL("Should not call in GPU-less build");
         return false;
     }
 
 #endif
 
-    SkShader* refAsALocalMatrixShader(SkMatrix* localMatrix) const SK_OVERRIDE {
+    SkShader* refAsALocalMatrixShader(SkMatrix* localMatrix) const override {
         if (localMatrix) {
             *localMatrix = this->getLocalMatrix();
         }
@@ -66,8 +66,8 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLocalMatrixShader)
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void*) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
+    Context* onCreateContext(const ContextRec&, void*) const override;
 
 private:
     SkAutoTUnref<SkShader> fProxyShader;
diff --git a/src/core/SkMaskCache.cpp b/src/core/SkMaskCache.cpp
index b0399b1..31a789f 100644
--- a/src/core/SkMaskCache.cpp
+++ b/src/core/SkMaskCache.cpp
@@ -51,8 +51,8 @@
     RRectBlurKey   fKey;
     MaskValue      fValue;
 
-    const Key& getKey() const SK_OVERRIDE { return fKey; }
-    size_t bytesUsed() const SK_OVERRIDE { return sizeof(*this) + fValue.fData->size(); }
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override { return sizeof(*this) + fValue.fData->size(); }
 
     static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextData) {
         const RRectBlurRec& rec = static_cast<const RRectBlurRec&>(baseRec);
@@ -142,8 +142,8 @@
     RectsBlurKey   fKey;
     MaskValue      fValue;
 
-    const Key& getKey() const SK_OVERRIDE { return fKey; }
-    size_t bytesUsed() const SK_OVERRIDE { return sizeof(*this) + fValue.fData->size(); }
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override { return sizeof(*this) + fValue.fData->size(); }
 
     static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextData) {
         const RectsBlurRec& rec = static_cast<const RectsBlurRec&>(baseRec);
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
index 54a22fc..5e20c15 100644
--- a/src/core/SkMaskFilter.cpp
+++ b/src/core/SkMaskFilter.cpp
@@ -208,7 +208,7 @@
 }
 
 static int countNestedRects(const SkPath& path, SkRect rects[2]) {
-    if (path.isNestedRects(rects)) {
+    if (path.isNestedFillRects(rects)) {
         return 2;
     }
     return path.isRect(&rects[0]);
diff --git a/src/core/SkMaskGamma.cpp b/src/core/SkMaskGamma.cpp
index abc729b..bbe72c4 100644
--- a/src/core/SkMaskGamma.cpp
+++ b/src/core/SkMaskGamma.cpp
@@ -12,27 +12,27 @@
 #include "SkMaskGamma.h"
 
 class SkLinearColorSpaceLuminance : public SkColorSpaceLuminance {
-    SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const SK_OVERRIDE {
+    SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const override {
         SkASSERT(SK_Scalar1 == gamma);
         return luminance;
     }
-    SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const SK_OVERRIDE {
+    SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const override {
         SkASSERT(SK_Scalar1 == gamma);
         return luma;
     }
 };
 
 class SkGammaColorSpaceLuminance : public SkColorSpaceLuminance {
-    SkScalar toLuma(SkScalar gamma, SkScalar luminance) const SK_OVERRIDE {
+    SkScalar toLuma(SkScalar gamma, SkScalar luminance) const override {
         return SkScalarPow(luminance, gamma);
     }
-    SkScalar fromLuma(SkScalar gamma, SkScalar luma) const SK_OVERRIDE {
+    SkScalar fromLuma(SkScalar gamma, SkScalar luma) const override {
         return SkScalarPow(luma, SkScalarInvert(gamma));
     }
 };
 
 class SkSRGBColorSpaceLuminance : public SkColorSpaceLuminance {
-    SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const SK_OVERRIDE {
+    SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const override {
         SkASSERT(0 == gamma);
         //The magic numbers are derived from the sRGB specification.
         //See http://www.color.org/chardata/rgb/srgb.xalter .
@@ -42,7 +42,7 @@
         return SkScalarPow((luminance + 0.055f) / 1.055f,
                         2.4f);
     }
-    SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const SK_OVERRIDE {
+    SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const override {
         SkASSERT(0 == gamma);
         //The magic numbers are derived from the sRGB specification.
         //See http://www.color.org/chardata/rgb/srgb.xalter .
diff --git a/src/core/SkMath.cpp b/src/core/SkMath.cpp
index e33fe55..af93d7e 100644
--- a/src/core/SkMath.cpp
+++ b/src/core/SkMath.cpp
@@ -43,27 +43,6 @@
     return zeros;
 }
 
-SkFixed SkFixedMul_portable(SkFixed a, SkFixed b) {
-#if defined(SkLONGLONG)
-    return static_cast<SkFixed>((int64_t)a * b >> 16);
-#else
-    int sa = SkExtractSign(a);
-    int sb = SkExtractSign(b);
-    // now make them positive
-    a = SkApplySign(a, sa);
-    b = SkApplySign(b, sb);
-
-    uint32_t    ah = a >> 16;
-    uint32_t    al = a & 0xFFFF;
-    uint32_t bh = b >> 16;
-    uint32_t bl = b & 0xFFFF;
-
-    uint32_t R = ah * b + al * bh + (al * bl >> 16);
-
-    return SkApplySign(R, sa ^ sb);
-#endif
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 #define DIVBITS_ITER(n)                                 \
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 9658177..9c9c4375 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -8,6 +8,7 @@
 #include "SkMatrix.h"
 #include "SkFloatBits.h"
 #include "SkString.h"
+#include "SkNx.h"
 
 #include <stddef.h>
 
@@ -867,108 +868,72 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[],
-                            const SkPoint src[], int count) {
+void SkMatrix::Identity_pts(const SkMatrix& m, SkPoint dst[], const SkPoint src[], int count) {
     SkASSERT(m.getType() == 0);
 
-    if (dst != src && count > 0)
+    if (dst != src && count > 0) {
         memcpy(dst, src, count * sizeof(SkPoint));
+    }
 }
 
-void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[],
-                         const SkPoint src[], int count) {
-    SkASSERT(m.getType() == kTranslate_Mask);
+void SkMatrix::Trans_pts(const SkMatrix& m, SkPoint dst[], const SkPoint src[], int count) {
+    SkASSERT(m.getType() <= kTranslate_Mask);
 
     if (count > 0) {
-        SkScalar tx = m.fMat[kMTransX];
-        SkScalar ty = m.fMat[kMTransY];
-        do {
-            dst->fY = src->fY + ty;
+        SkScalar tx = m.getTranslateX();
+        SkScalar ty = m.getTranslateY();
+        if (count & 1) {
             dst->fX = src->fX + tx;
+            dst->fY = src->fY + ty;
             src += 1;
             dst += 1;
-        } while (--count);
+        }
+        Sk4s trans4(tx, ty, tx, ty);
+        count >>= 1;
+        if (count & 1) {
+            (Sk4s::Load(&src->fX) + trans4).store(&dst->fX);
+            src += 2;
+            dst += 2;
+        }
+        count >>= 1;
+        for (int i = 0; i < count; ++i) {
+            (Sk4s::Load(&src[0].fX) + trans4).store(&dst[0].fX);
+            (Sk4s::Load(&src[2].fX) + trans4).store(&dst[2].fX);
+            src += 4;
+            dst += 4;
+        }
     }
 }
 
-void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[],
-                         const SkPoint src[], int count) {
-    SkASSERT(m.getType() == kScale_Mask);
+void SkMatrix::Scale_pts(const SkMatrix& m, SkPoint dst[], const SkPoint src[], int count) {
+    SkASSERT(m.getType() <= (kScale_Mask | kTranslate_Mask));
 
     if (count > 0) {
-        SkScalar mx = m.fMat[kMScaleX];
-        SkScalar my = m.fMat[kMScaleY];
-        do {
-            dst->fY = src->fY * my;
-            dst->fX = src->fX * mx;
+        SkScalar tx = m.getTranslateX();
+        SkScalar ty = m.getTranslateY();
+        SkScalar sx = m.getScaleX();
+        SkScalar sy = m.getScaleY();
+        if (count & 1) {
+            dst->fX = src->fX * sx + tx;
+            dst->fY = src->fY * sy + ty;
             src += 1;
             dst += 1;
-        } while (--count);
-    }
-}
-
-void SkMatrix::ScaleTrans_pts(const SkMatrix& m, SkPoint dst[],
-                              const SkPoint src[], int count) {
-    SkASSERT(m.getType() == (kScale_Mask | kTranslate_Mask));
-
-    if (count > 0) {
-        SkScalar mx = m.fMat[kMScaleX];
-        SkScalar my = m.fMat[kMScaleY];
-        SkScalar tx = m.fMat[kMTransX];
-        SkScalar ty = m.fMat[kMTransY];
-        do {
-            dst->fY = src->fY * my + ty;
-            dst->fX = src->fX * mx + tx;
-            src += 1;
-            dst += 1;
-        } while (--count);
-    }
-}
-
-void SkMatrix::Rot_pts(const SkMatrix& m, SkPoint dst[],
-                       const SkPoint src[], int count) {
-    SkASSERT((m.getType() & (kPerspective_Mask | kTranslate_Mask)) == 0);
-
-    if (count > 0) {
-        SkScalar mx = m.fMat[kMScaleX];
-        SkScalar my = m.fMat[kMScaleY];
-        SkScalar kx = m.fMat[kMSkewX];
-        SkScalar ky = m.fMat[kMSkewY];
-        do {
-            SkScalar sy = src->fY;
-            SkScalar sx = src->fX;
-            src += 1;
-            dst->fY = sdot(sx, ky, sy, my);
-            dst->fX = sdot(sx, mx, sy, kx);
-            dst += 1;
-        } while (--count);
-    }
-}
-
-void SkMatrix::RotTrans_pts(const SkMatrix& m, SkPoint dst[],
-                            const SkPoint src[], int count) {
-    SkASSERT(!m.hasPerspective());
-
-    if (count > 0) {
-        SkScalar mx = m.fMat[kMScaleX];
-        SkScalar my = m.fMat[kMScaleY];
-        SkScalar kx = m.fMat[kMSkewX];
-        SkScalar ky = m.fMat[kMSkewY];
-        SkScalar tx = m.fMat[kMTransX];
-        SkScalar ty = m.fMat[kMTransY];
-        do {
-            SkScalar sy = src->fY;
-            SkScalar sx = src->fX;
-            src += 1;
-#ifdef SK_LEGACY_MATRIX_MATH_ORDER
-            dst->fY = sx * ky + (sy * my + ty);
-            dst->fX = sx * mx + (sy * kx + tx);
-#else
-            dst->fY = sdot(sx, ky, sy, my) + ty;
-            dst->fX = sdot(sx, mx, sy, kx) + tx;
-#endif
-            dst += 1;
-        } while (--count);
+        }
+        Sk4s trans4(tx, ty, tx, ty);
+        Sk4s scale4(sx, sy, sx, sy);
+        count >>= 1;
+        if (count & 1) {
+            (Sk4s::Load(&src->fX) * scale4 + trans4).store(&dst->fX);
+            src += 2;
+            dst += 2;
+        }
+        count >>= 1;
+        for (int i = 0; i < count; ++i) {
+            (Sk4s::Load(&src[0].fX) * scale4 + trans4).store(&dst[0].fX);
+            (Sk4s::Load(&src[2].fX) * scale4 + trans4).store(&dst[2].fX);
+            src += 4;
+            dst += 4;
+        }
     }
 }
 
@@ -1000,11 +965,41 @@
     }
 }
 
+void SkMatrix::Affine_vpts(const SkMatrix& m, SkPoint dst[], const SkPoint src[], int count) {
+    SkASSERT(m.getType() != kPerspective_Mask);
+
+    if (count > 0) {
+        SkScalar tx = m.getTranslateX();
+        SkScalar ty = m.getTranslateY();
+        SkScalar sx = m.getScaleX();
+        SkScalar sy = m.getScaleY();
+        SkScalar kx = m.getSkewX();
+        SkScalar ky = m.getSkewY();
+        if (count & 1) {
+            dst->set(src->fX * sx + src->fY * kx + tx,
+                     src->fX * ky + src->fY * sy + ty);
+            src += 1;
+            dst += 1;
+        }
+        Sk4s trans4(tx, ty, tx, ty);
+        Sk4s scale4(sx, sy, sx, sy);
+        Sk4s  skew4(kx, ky, kx, ky);    // applied to swizzle of src4
+        count >>= 1;
+        for (int i = 0; i < count; ++i) {
+            Sk4s src4 = Sk4s::Load(&src->fX);
+            Sk4s swz4(src[0].fY, src[0].fX, src[1].fY, src[1].fX);  // need ABCD -> BADC
+            (src4 * scale4 + swz4 * skew4 + trans4).store(&dst->fX);
+            src += 2;
+            dst += 2;
+        }
+    }
+}
+
 const SkMatrix::MapPtsProc SkMatrix::gMapPtsProcs[] = {
     SkMatrix::Identity_pts, SkMatrix::Trans_pts,
-    SkMatrix::Scale_pts,    SkMatrix::ScaleTrans_pts,
-    SkMatrix::Rot_pts,      SkMatrix::RotTrans_pts,
-    SkMatrix::Rot_pts,      SkMatrix::RotTrans_pts,
+    SkMatrix::Scale_pts,   SkMatrix::Scale_pts,
+    SkMatrix::Affine_vpts,  SkMatrix::Affine_vpts,
+    SkMatrix::Affine_vpts,  SkMatrix::Affine_vpts,
     // repeat the persp proc 8 times
     SkMatrix::Persp_pts,    SkMatrix::Persp_pts,
     SkMatrix::Persp_pts,    SkMatrix::Persp_pts,
@@ -1012,14 +1007,6 @@
     SkMatrix::Persp_pts,    SkMatrix::Persp_pts
 };
 
-void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const {
-    SkASSERT((dst && src && count > 0) || 0 == count);
-    // no partial overlap
-    SkASSERT(src == dst || &dst[count] <= &src[0] || &src[count] <= &dst[0]);
-
-    this->getMapPtsProc()(*this, dst, src, count);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkMatrix::mapHomogeneousPoints(SkScalar dst[], const SkScalar src[], int count) const {
@@ -1603,6 +1590,28 @@
     return invalid.asSkMatrix();
 }
 
+bool SkMatrix::decomposeScale(SkSize* scale, SkMatrix* remaining) const {
+    if (this->hasPerspective()) {
+        return false;
+    }
+
+    const SkScalar sx = SkVector::Length(this->getScaleX(), this->getSkewY());
+    const SkScalar sy = SkVector::Length(this->getSkewX(), this->getScaleY());
+    if (!SkScalarIsFinite(sx) || !SkScalarIsFinite(sy) ||
+        SkScalarNearlyZero(sx) || SkScalarNearlyZero(sy)) {
+        return false;
+    }
+
+    if (scale) {
+        scale->set(sx, sy);
+    }
+    if (remaining) {
+        *remaining = *this;
+        remaining->postScale(SkScalarInvert(sx), SkScalarInvert(sy));
+    }
+    return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 size_t SkMatrix::writeToMemory(void* buffer) const {
diff --git a/src/effects/SkMatrixImageFilter.cpp b/src/core/SkMatrixImageFilter.cpp
similarity index 83%
rename from src/effects/SkMatrixImageFilter.cpp
rename to src/core/SkMatrixImageFilter.cpp
index 4d9b1fa..a61867e 100644
--- a/src/effects/SkMatrixImageFilter.cpp
+++ b/src/core/SkMatrixImageFilter.cpp
@@ -16,33 +16,31 @@
 #include "SkRect.h"
 
 SkMatrixImageFilter::SkMatrixImageFilter(const SkMatrix& transform,
-                                         SkPaint::FilterLevel filterLevel,
-                                         SkImageFilter* input,
-                                         uint32_t uniqueID)
-  : INHERITED(1, &input, NULL, uniqueID),
+                                         SkFilterQuality filterQuality,
+                                         SkImageFilter* input)
+  : INHERITED(1, &input),
     fTransform(transform),
-    fFilterLevel(filterLevel) {
+    fFilterQuality(filterQuality) {
 }
 
 SkMatrixImageFilter* SkMatrixImageFilter::Create(const SkMatrix& transform,
-                                                 SkPaint::FilterLevel filterLevel,
-                                                 SkImageFilter* input,
-                                                 uint32_t uniqueID) {
-    return SkNEW_ARGS(SkMatrixImageFilter, (transform, filterLevel, input, uniqueID));
+                                                 SkFilterQuality filterQuality,
+                                                 SkImageFilter* input) {
+    return SkNEW_ARGS(SkMatrixImageFilter, (transform, filterQuality, input));
 }
 
 SkFlattenable* SkMatrixImageFilter::CreateProc(SkReadBuffer& buffer) {
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
     SkMatrix matrix;
     buffer.readMatrix(&matrix);
-    SkPaint::FilterLevel level = static_cast<SkPaint::FilterLevel>(buffer.readInt());
-    return Create(matrix, level, common.getInput(0), common.uniqueID());
+    SkFilterQuality quality = static_cast<SkFilterQuality>(buffer.readInt());
+    return Create(matrix, quality, common.getInput(0));
 }
 
 void SkMatrixImageFilter::flatten(SkWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
     buffer.writeMatrix(fTransform);
-    buffer.writeInt(fFilterLevel);
+    buffer.writeInt(fFilterQuality);
 }
 
 SkMatrixImageFilter::~SkMatrixImageFilter() {
@@ -84,7 +82,7 @@
     SkPaint paint;
 
     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-    paint.setFilterLevel(fFilterLevel);
+    paint.setFilterQuality(fFilterQuality);
     canvas.drawBitmap(src, srcRect.x(), srcRect.y(), &paint);
 
     *result = device.get()->accessBitmap(false);
@@ -142,7 +140,7 @@
 
     str->append("<dt>FilterLevel:</dt><dd>");
     static const char* gFilterLevelStrings[] = { "None", "Low", "Medium", "High" };
-    str->append(gFilterLevelStrings[fFilterLevel]);    
+    str->append(gFilterLevelStrings[fFilterQuality]);
     str->append("</dd>");
 
     str->appendf(")");
diff --git a/include/effects/SkMatrixImageFilter.h b/src/core/SkMatrixImageFilter.h
similarity index 68%
rename from include/effects/SkMatrixImageFilter.h
rename to src/core/SkMatrixImageFilter.h
index 7d855b7..012a360 100644
--- a/include/effects/SkMatrixImageFilter.h
+++ b/src/core/SkMatrixImageFilter.h
@@ -29,31 +29,36 @@
      */
 
     static SkMatrixImageFilter* Create(const SkMatrix& transform,
-                                       SkPaint::FilterLevel,
-                                       SkImageFilter* input = NULL,
-                                       uint32_t uniqueID = 0);
+                                       SkFilterQuality,
+                                       SkImageFilter* input = NULL);
+#ifdef SK_SUPPORT_LEGACY_FILTERLEVEL_ENUM
+    static SkMatrixImageFilter* Create(const SkMatrix& transform,
+                                       SkPaint::FilterLevel level,
+                                       SkImageFilter* input = NULL) {
+        return Create(transform, SkFilterQuality(level), input);
+    }
+#endif
     virtual ~SkMatrixImageFilter();
 
-    void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
+    void computeFastBounds(const SkRect&, SkRect*) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMatrixImageFilter)
 
 protected:
     SkMatrixImageFilter(const SkMatrix& transform,
-                        SkPaint::FilterLevel,
-                        SkImageFilter* input,
-                        uint32_t uniqueID);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+                        SkFilterQuality,
+                        SkImageFilter* input);
+    void flatten(SkWriteBuffer&) const override;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* loc) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* loc) const override;
     virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
-                                SkIRect* dst) const SK_OVERRIDE;
+                                SkIRect* dst) const override;
 
 private:
     SkMatrix              fTransform;
-    SkPaint::FilterLevel  fFilterLevel;
+    SkFilterQuality       fFilterQuality;
     typedef SkImageFilter INHERITED;
 };
 
diff --git a/src/core/SkMipMap.cpp b/src/core/SkMipMap.cpp
index 9e0e5e1..4a548b8 100644
--- a/src/core/SkMipMap.cpp
+++ b/src/core/SkMipMap.cpp
@@ -135,6 +135,39 @@
    *((uint16_t*)dst) = (uint16_t)collaps4444(c >> 2);
 }
 
+static void downsample8_nocheck(void* dst, int, int, const void* srcPtr, const SkBitmap& srcBM) {
+    const size_t rb = srcBM.rowBytes();
+    const uint8_t* p = static_cast<const uint8_t*>(srcPtr);
+    *(uint8_t*)dst = (p[0] + p[1] + p[rb] + p[rb + 1]) >> 2;
+}
+
+static void downsample8_check(void* dst, int x, int y, const void* srcPtr, const SkBitmap& srcBM) {
+    const uint8_t* p = static_cast<const uint8_t*>(srcPtr);
+    const uint8_t* baseP = p;
+
+    x <<= 1;
+    y <<= 1;
+    SkASSERT(srcBM.getAddr8(x, y) == p);
+
+    unsigned c = *p;
+    if (x < srcBM.width() - 1) {
+        p += 1;
+    }
+    c += *p;
+
+    p = baseP;
+    if (y < srcBM.height() - 1) {
+        p += srcBM.rowBytes();
+    }
+    c += *p;
+    if (x < srcBM.width() - 1) {
+        p += 1;
+    }
+    c += *p;
+
+    *(uint8_t*)dst = c >> 2;
+}
+
 size_t SkMipMap::AllocLevelsSize(int levelCount, size_t pixelSize) {
     if (levelCount < 0) {
         return 0;
@@ -167,6 +200,11 @@
             proc_check = downsample4444;
             proc_nocheck = proc_check;
             break;
+        case kAlpha_8_SkColorType:
+        case kGray_8_SkColorType:
+            proc_check = downsample8_check;
+            proc_nocheck = downsample8_nocheck;
+            break;
         default:
             return NULL; // don't build mipmaps for any other colortypes (yet)
     }
@@ -296,8 +334,9 @@
         return false;
     }
     SkASSERT(L >= 0);
-    int level = SkScalarRoundToInt(L);
-//    SkDebugf("mipmap scale=%g L=%g level=%d\n", scale, L, level);
+//    int rndLevel = SkScalarRoundToInt(L);
+    int level = SkScalarFloorToInt(L);
+//    SkDebugf("mipmap scale=%g L=%g level=%d rndLevel=%d\n", scale, L, level, rndLevel);
 
     SkASSERT(level >= 0);
     if (level <= 0) {
diff --git a/src/core/SkMipMap.h b/src/core/SkMipMap.h
index d403968..e22c0a2 100644
--- a/src/core/SkMipMap.h
+++ b/src/core/SkMipMap.h
@@ -30,7 +30,7 @@
     bool extractLevel(SkScalar scale, Level*) const;
 
 protected:
-    void onDataChange(void* oldData, void* newData) SK_OVERRIDE {
+    void onDataChange(void* oldData, void* newData) override {
         fLevels = (Level*)newData; // could be NULL
     }
 
diff --git a/src/core/SkNx.h b/src/core/SkNx.h
new file mode 100644
index 0000000..af71948
--- /dev/null
+++ b/src/core/SkNx.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkNx_DEFINED
+#define SkNx_DEFINED
+
+
+#define SKNX_NO_SIMDx  // Remove the x to disable SIMD for all SkNx types.
+
+
+#include "SkScalar.h"
+#include "SkTypes.h"
+#include <math.h>
+#define REQUIRE(x) static_assert(x, #x)
+
+// The default implementations just fall back on a pair of size N/2.
+
+// SkNb is a _very_ minimal class representing a vector of bools returned by comparison operators.
+// We pass along the byte size of the compared types (Bytes) to help platform specializations.
+template <int N, int Bytes>
+class SkNb {
+public:
+    SkNb() {}
+    SkNb(const SkNb<N/2, Bytes>& lo, const SkNb<N/2, Bytes>& hi) : fLo(lo), fHi(hi) {}
+
+    bool allTrue() const { return fLo.allTrue() && fHi.allTrue(); }
+    bool anyTrue() const { return fLo.anyTrue() || fHi.anyTrue(); }
+
+protected:
+    REQUIRE(0 == (N & (N-1)));
+    SkNb<N/2, Bytes> fLo, fHi;
+};
+
+template <int N, typename T>
+class SkNi {
+public:
+    SkNi() {}
+    SkNi(const SkNi<N/2, T>& lo, const SkNi<N/2, T>& hi) : fLo(lo), fHi(hi) {}
+    explicit SkNi(T val) : fLo(val), fHi(val) {}
+    static SkNi Load(const T vals[N]) {
+        return SkNi(SkNi<N/2,T>::Load(vals), SkNi<N/2,T>::Load(vals+N/2));
+    }
+
+    SkNi(T a, T b)                                : fLo(a),       fHi(b)       { REQUIRE(N==2); }
+    SkNi(T a, T b, T c, T d)                      : fLo(a,b),     fHi(c,d)     { REQUIRE(N==4); }
+    SkNi(T a, T b, T c, T d,  T e, T f, T g, T h) : fLo(a,b,c,d), fHi(e,f,g,h) { REQUIRE(N==8); }
+    SkNi(T a, T b, T c, T d,  T e, T f, T g, T h,
+         T i, T j, T k, T l,  T m, T n, T o, T p)
+        : fLo(a,b,c,d, e,f,g,h), fHi(i,j,k,l, m,n,o,p) { REQUIRE(N==16); }
+
+    void store(T vals[N]) const {
+        fLo.store(vals);
+        fHi.store(vals+N/2);
+    }
+
+    SkNi saturatedAdd(const SkNi& o) const {
+        return SkNi(fLo.saturatedAdd(o.fLo), fHi.saturatedAdd(o.fHi));
+    }
+
+    SkNi operator + (const SkNi& o) const { return SkNi(fLo + o.fLo, fHi + o.fHi); }
+    SkNi operator - (const SkNi& o) const { return SkNi(fLo - o.fLo, fHi - o.fHi); }
+    SkNi operator * (const SkNi& o) const { return SkNi(fLo * o.fLo, fHi * o.fHi); }
+
+    SkNi operator << (int bits) const { return SkNi(fLo << bits, fHi << bits); }
+    SkNi operator >> (int bits) const { return SkNi(fLo >> bits, fHi >> bits); }
+
+    static SkNi Min(const SkNi& a, const SkNi& b) {
+        return SkNi(SkNi<N/2, T>::Min(a.fLo, b.fLo), SkNi<N/2, T>::Min(a.fHi, b.fHi));
+    }
+
+    // TODO: comparisons, max?
+
+    template <int k> T kth() const {
+        SkASSERT(0 <= k && k < N);
+        return k < N/2 ? fLo.template kth<k>() : fHi.template kth<k-N/2>();
+    }
+
+protected:
+    REQUIRE(0 == (N & (N-1)));
+
+    SkNi<N/2, T> fLo, fHi;
+};
+
+template <int N, typename T>
+class SkNf {
+    typedef SkNb<N, sizeof(T)> Nb;
+
+    static int32_t MyNi(float);
+    static int64_t MyNi(double);
+    typedef SkNi<N, decltype(MyNi(T()))> Ni;
+public:
+    SkNf() {}
+    explicit SkNf(T val) : fLo(val),  fHi(val) {}
+    static SkNf Load(const T vals[N]) {
+        return SkNf(SkNf<N/2,T>::Load(vals), SkNf<N/2,T>::Load(vals+N/2));
+    }
+
+    SkNf(T a, T b)                               : fLo(a),       fHi(b)       { REQUIRE(N==2); }
+    SkNf(T a, T b, T c, T d)                     : fLo(a,b),     fHi(c,d)     { REQUIRE(N==4); }
+    SkNf(T a, T b, T c, T d, T e, T f, T g, T h) : fLo(a,b,c,d), fHi(e,f,g,h) { REQUIRE(N==8); }
+
+    void store(T vals[N]) const {
+        fLo.store(vals);
+        fHi.store(vals+N/2);
+    }
+
+    Ni castTrunc() const { return Ni(fLo.castTrunc(), fHi.castTrunc()); }
+
+    SkNf operator + (const SkNf& o) const { return SkNf(fLo + o.fLo, fHi + o.fHi); }
+    SkNf operator - (const SkNf& o) const { return SkNf(fLo - o.fLo, fHi - o.fHi); }
+    SkNf operator * (const SkNf& o) const { return SkNf(fLo * o.fLo, fHi * o.fHi); }
+    SkNf operator / (const SkNf& o) const { return SkNf(fLo / o.fLo, fHi / o.fHi); }
+
+    Nb operator == (const SkNf& o) const { return Nb(fLo == o.fLo, fHi == o.fHi); }
+    Nb operator != (const SkNf& o) const { return Nb(fLo != o.fLo, fHi != o.fHi); }
+    Nb operator  < (const SkNf& o) const { return Nb(fLo  < o.fLo, fHi  < o.fHi); }
+    Nb operator  > (const SkNf& o) const { return Nb(fLo  > o.fLo, fHi  > o.fHi); }
+    Nb operator <= (const SkNf& o) const { return Nb(fLo <= o.fLo, fHi <= o.fHi); }
+    Nb operator >= (const SkNf& o) const { return Nb(fLo >= o.fLo, fHi >= o.fHi); }
+
+    static SkNf Min(const SkNf& l, const SkNf& r) {
+        return SkNf(SkNf<N/2,T>::Min(l.fLo, r.fLo), SkNf<N/2,T>::Min(l.fHi, r.fHi));
+    }
+    static SkNf Max(const SkNf& l, const SkNf& r) {
+        return SkNf(SkNf<N/2,T>::Max(l.fLo, r.fLo), SkNf<N/2,T>::Max(l.fHi, r.fHi));
+    }
+
+    SkNf  sqrt() const { return SkNf(fLo. sqrt(), fHi. sqrt()); }
+
+    // Generally, increasing precision, increasing cost.
+    SkNf rsqrt0() const { return SkNf(fLo.rsqrt0(), fHi.rsqrt0()); }
+    SkNf rsqrt1() const { return SkNf(fLo.rsqrt1(), fHi.rsqrt1()); }
+    SkNf rsqrt2() const { return SkNf(fLo.rsqrt2(), fHi.rsqrt2()); }
+
+    SkNf       invert() const { return SkNf(fLo.      invert(), fHi.      invert()); }
+    SkNf approxInvert() const { return SkNf(fLo.approxInvert(), fHi.approxInvert()); }
+
+    template <int k> T kth() const {
+        SkASSERT(0 <= k && k < N);
+        return k < N/2 ? fLo.template kth<k>() : fHi.template kth<k-N/2>();
+    }
+
+protected:
+    REQUIRE(0 == (N & (N-1)));
+    SkNf(const SkNf<N/2, T>& lo, const SkNf<N/2, T>& hi) : fLo(lo), fHi(hi) {}
+
+    SkNf<N/2, T> fLo, fHi;
+};
+
+
+// Bottom out the default implementations with scalars when nothing's been specialized.
+
+template <int Bytes>
+class SkNb<1, Bytes> {
+public:
+    SkNb() {}
+    explicit SkNb(bool val) : fVal(val) {}
+    bool allTrue() const { return fVal; }
+    bool anyTrue() const { return fVal; }
+protected:
+    bool fVal;
+};
+
+template <typename T>
+class SkNi<1,T> {
+public:
+    SkNi() {}
+    explicit SkNi(T val) : fVal(val) {}
+    static SkNi Load(const T vals[1]) { return SkNi(vals[0]); }
+
+    void store(T vals[1]) const { vals[0] = fVal; }
+
+    SkNi saturatedAdd(const SkNi& o) const {
+        SkASSERT((T)(~0) > 0); // TODO: support signed T
+        T sum = fVal + o.fVal;
+        return SkNi(sum > fVal ? sum : (T)(~0));
+    }
+
+    SkNi operator + (const SkNi& o) const { return SkNi(fVal + o.fVal); }
+    SkNi operator - (const SkNi& o) const { return SkNi(fVal - o.fVal); }
+    SkNi operator * (const SkNi& o) const { return SkNi(fVal * o.fVal); }
+
+    SkNi operator << (int bits) const { return SkNi(fVal << bits); }
+    SkNi operator >> (int bits) const { return SkNi(fVal >> bits); }
+
+    static SkNi Min(const SkNi& a, const SkNi& b) { return SkNi(SkTMin(a.fVal, b.fVal)); }
+
+    template <int k> T kth() const {
+        SkASSERT(0 == k);
+        return fVal;
+    }
+
+protected:
+    T fVal;
+};
+
+template <typename T>
+class SkNf<1,T> {
+    typedef SkNb<1, sizeof(T)> Nb;
+
+    static int32_t MyNi(float);
+    static int64_t MyNi(double);
+    typedef SkNi<1, decltype(MyNi(T()))> Ni;
+public:
+    SkNf() {}
+    explicit SkNf(T val) : fVal(val) {}
+    static SkNf Load(const T vals[1]) { return SkNf(vals[0]); }
+
+    void store(T vals[1]) const { vals[0] = fVal; }
+
+    Ni castTrunc() const { return Ni(fVal); }
+
+    SkNf operator + (const SkNf& o) const { return SkNf(fVal + o.fVal); }
+    SkNf operator - (const SkNf& o) const { return SkNf(fVal - o.fVal); }
+    SkNf operator * (const SkNf& o) const { return SkNf(fVal * o.fVal); }
+    SkNf operator / (const SkNf& o) const { return SkNf(fVal / o.fVal); }
+
+    Nb operator == (const SkNf& o) const { return Nb(fVal == o.fVal); }
+    Nb operator != (const SkNf& o) const { return Nb(fVal != o.fVal); }
+    Nb operator  < (const SkNf& o) const { return Nb(fVal  < o.fVal); }
+    Nb operator  > (const SkNf& o) const { return Nb(fVal  > o.fVal); }
+    Nb operator <= (const SkNf& o) const { return Nb(fVal <= o.fVal); }
+    Nb operator >= (const SkNf& o) const { return Nb(fVal >= o.fVal); }
+
+    static SkNf Min(const SkNf& l, const SkNf& r) { return SkNf(SkTMin(l.fVal, r.fVal)); }
+    static SkNf Max(const SkNf& l, const SkNf& r) { return SkNf(SkTMax(l.fVal, r.fVal)); }
+
+    SkNf  sqrt() const { return SkNf(Sqrt(fVal));        }
+    SkNf rsqrt0() const { return SkNf((T)1 / Sqrt(fVal)); }
+    SkNf rsqrt1() const { return this->rsqrt0(); }
+    SkNf rsqrt2() const { return this->rsqrt1(); }
+
+    SkNf       invert() const { return SkNf((T)1 / fVal); }
+    SkNf approxInvert() const { return this->invert();    }
+
+    template <int k> T kth() const {
+        SkASSERT(k == 0);
+        return fVal;
+    }
+
+protected:
+    // We do double sqrts natively, or via floats for any other type.
+    template <typename U>
+    static U      Sqrt(U      val) { return (U) ::sqrtf((float)val); }
+    static double Sqrt(double val) { return     ::sqrt (       val); }
+
+    T fVal;
+};
+
+
+// Generic syntax sugar that should work equally well for all implementations.
+template <typename T> T operator - (const T& l) { return T(0) - l; }
+
+template <typename L, typename R> L& operator += (L& l, const R& r) { return (l = l + r); }
+template <typename L, typename R> L& operator -= (L& l, const R& r) { return (l = l - r); }
+template <typename L, typename R> L& operator *= (L& l, const R& r) { return (l = l * r); }
+template <typename L, typename R> L& operator /= (L& l, const R& r) { return (l = l / r); }
+
+template <typename L> L& operator <<= (L& l, int bits) { return (l = l << bits); }
+template <typename L> L& operator >>= (L& l, int bits) { return (l = l >> bits); }
+
+// Include platform specific specializations if available.
+#ifndef SKNX_NO_SIMD
+    #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
+        #include "../opts/SkNx_sse.h"
+    #elif defined(SK_ARM_HAS_NEON)
+        #include "../opts/SkNx_neon.h"
+    #endif
+#endif
+
+#undef REQUIRE
+
+typedef SkNf<2,    float> Sk2f;
+typedef SkNf<2,   double> Sk2d;
+typedef SkNf<2, SkScalar> Sk2s;
+
+typedef SkNf<4,    float> Sk4f;
+typedef SkNf<4,   double> Sk4d;
+typedef SkNf<4, SkScalar> Sk4s;
+
+typedef SkNi<4,  uint16_t> Sk4h;
+typedef SkNi<8,  uint16_t> Sk8h;
+typedef SkNi<16, uint16_t> Sk16h;
+
+typedef SkNi<16, uint8_t> Sk16b;
+
+typedef SkNi<4,  int32_t> Sk4i;
+typedef SkNi<4, uint32_t> Sk4u;
+
+#endif//SkNx_DEFINED
diff --git a/src/core/SkPMFloat.h b/src/core/SkPMFloat.h
index 4534f11..eb575f2 100644
--- a/src/core/SkPMFloat.h
+++ b/src/core/SkPMFloat.h
@@ -1,55 +1,60 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #ifndef SkPM_DEFINED
 #define SkPM_DEFINED
 
 #include "SkTypes.h"
 #include "SkColor.h"
-#include "Sk4x.h"
+#include "SkColorPriv.h"
+#include "SkNx.h"
 
 // A pre-multiplied color storing each component in the same order as SkPMColor,
 // but as a float in the range [0, 255].
-class SK_STRUCT_ALIGN(16) SkPMFloat {
+class SkPMFloat : public Sk4f {
 public:
     static SkPMFloat FromPMColor(SkPMColor c) { return SkPMFloat(c); }
     static SkPMFloat FromARGB(float a, float r, float g, float b) { return SkPMFloat(a,r,g,b); }
 
     // May be more efficient than one at a time.  No special alignment assumed for SkPMColors.
-    static void From4PMColors(SkPMFloat[4], const SkPMColor[4]);
-
-    explicit SkPMFloat(SkPMColor);
-    SkPMFloat(float a, float r, float g, float b) {
-        // TODO: faster when specialized?
-        fColor[SK_A32_SHIFT / 8] = a;
-        fColor[SK_R32_SHIFT / 8] = r;
-        fColor[SK_G32_SHIFT / 8] = g;
-        fColor[SK_B32_SHIFT / 8] = b;
-    }
+    static void From4PMColors(const SkPMColor[4], SkPMFloat*, SkPMFloat*, SkPMFloat*, SkPMFloat*);
 
     // Uninitialized.
     SkPMFloat() {}
+    explicit SkPMFloat(SkPMColor);
+    SkPMFloat(float a, float r, float g, float b)
+    #ifdef SK_PMCOLOR_IS_RGBA
+        : INHERITED(r,g,b,a) {}
+    #else
+        : INHERITED(b,g,r,a) {}
+    #endif
 
-    // Copy and assign are fastest if we remind the compiler we work best as Sk4f.
-    SkPMFloat(const SkPMFloat& that) { Sk4f(that).storeAligned(fColor); }
-    SkPMFloat& operator=(const SkPMFloat& that) {
-        Sk4f(that).storeAligned(fColor);
-        return *this;
-    }
+    SkPMFloat(const Sk4f& fs) : INHERITED(fs) {}
 
-    // Freely autoconvert between SkPMFloat and Sk4f.
-    /*implicit*/ SkPMFloat(const Sk4f& fs) { fs.storeAligned(fColor); }
-    /*implicit*/ operator Sk4f() const { return Sk4f::LoadAligned(fColor); }
+    float a() const { return this->kth<SK_A32_SHIFT / 8>(); }
+    float r() const { return this->kth<SK_R32_SHIFT / 8>(); }
+    float g() const { return this->kth<SK_G32_SHIFT / 8>(); }
+    float b() const { return this->kth<SK_B32_SHIFT / 8>(); }
 
-    float a() const { return fColor[SK_A32_SHIFT / 8]; }
-    float r() const { return fColor[SK_R32_SHIFT / 8]; }
-    float g() const { return fColor[SK_G32_SHIFT / 8]; }
-    float b() const { return fColor[SK_B32_SHIFT / 8]; }
+    // N.B. All methods returning an SkPMColor call SkPMColorAssert on that result before returning.
 
-    // get() and clamped() round component values to the nearest integer.
-    SkPMColor     get() const;  // May SkASSERT(this->isValid()).  Some implementations may clamp.
-    SkPMColor clamped() const;  // Will clamp all values to [0, 255].  Then may assert isValid().
+    // round() and roundClamp() round component values to the nearest integer.
+    SkPMColor round() const;  // Assumes all values in [0, 255].  Some implementations may clamp.
+    SkPMColor roundClamp() const;  // Will clamp all values to [0, 255].
 
-    // 4-at-a-time versions of get() and clamped().  Like From4PMColors(), no alignment assumed.
-    static void To4PMColors(SkPMColor[4], const SkPMFloat[4]);
-    static void ClampTo4PMColors(SkPMColor[4], const SkPMFloat[4]);
+    // Like round(), but truncates instead of rounding.
+    // The domain of this function is (-1.0f, 256.0f).  Values in (-1.0f, 0.0f] trunc to a zero.
+    SkPMColor trunc() const;
+
+    // 4-at-a-time versions of round() and roundClamp(). Like From4PMColors(), no alignment assumed.
+    static void RoundTo4PMColors(
+            const SkPMFloat&, const SkPMFloat&, const SkPMFloat&, const SkPMFloat&, SkPMColor[4]);
+    static void RoundClampTo4PMColors(
+            const SkPMFloat&, const SkPMFloat&, const SkPMFloat&, const SkPMFloat&, SkPMColor[4]);
 
     bool isValid() const {
         return this->a() >= 0 && this->a() <= 255
@@ -59,17 +64,22 @@
     }
 
 private:
-    float fColor[4];
+    typedef Sk4f INHERITED;
 };
 
-#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSSE3
-    #include "../opts/SkPMFloat_SSSE3.h"
-#elif SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
-    #include "../opts/SkPMFloat_SSE2.h"
-#elif defined(__ARM_NEON__)
-    #include "../opts/SkPMFloat_neon.h"
-#else
+#ifdef SKNX_NO_SIMD
+    // Platform implementations of SkPMFloat assume Sk4f uses SSE or NEON.  _none is generic.
     #include "../opts/SkPMFloat_none.h"
+#else
+    #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSSE3
+        #include "../opts/SkPMFloat_SSSE3.h"
+    #elif SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
+        #include "../opts/SkPMFloat_SSE2.h"
+    #elif defined(SK_ARM_HAS_NEON)
+        #include "../opts/SkPMFloat_neon.h"
+    #else
+        #include "../opts/SkPMFloat_none.h"
+    #endif
 #endif
 
 #endif//SkPM_DEFINED
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index b93236f..c00dcc6 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -116,8 +116,6 @@
 #define COPY(field) field = src.field
 #define REF_COPY(field) SkSafeUnref(field); field = SkSafeRef(src.field)
 
-    SkASSERT(&src);
-
     REF_COPY(fTypeface);
     REF_COPY(fPathEffect);
     REF_COPY(fShader);
@@ -1291,9 +1289,9 @@
     return true;
 }
 
-static SkColor computeLuminanceColor(const SkPaint& paint) {
+SkColor SkPaint::computeLuminanceColor() const {
     SkColor c;
-    if (!justAColor(paint, &c)) {
+    if (!justAColor(*this, &c)) {
         c = SkColorSetRGB(0x7F, 0x80, 0x7F);
     }
     return c;
@@ -1463,7 +1461,7 @@
     // these modify fFlags, so do them after assigning fFlags
     rec->setHinting(computeHinting(paint));
 
-    rec->setLuminanceColor(computeLuminanceColor(paint));
+    rec->setLuminanceColor(paint.computeLuminanceColor());
 
     if (NULL == deviceProperties) {
         rec->setDeviceGamma(SK_GAMMA_EXPONENT);
@@ -1589,6 +1587,147 @@
     #define TEST_DESC
 #endif
 
+static void write_out_descriptor(SkDescriptor* desc, const SkScalerContext::Rec& rec,
+                                 const SkPathEffect* pe, SkWriteBuffer* peBuffer,
+                                 const SkMaskFilter* mf, SkWriteBuffer* mfBuffer,
+                                 const SkRasterizer* ra, SkWriteBuffer* raBuffer,
+                                 size_t descSize) {
+    desc->init();
+    desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+
+    if (pe) {
+        add_flattenable(desc, kPathEffect_SkDescriptorTag, peBuffer);
+    }
+    if (mf) {
+        add_flattenable(desc, kMaskFilter_SkDescriptorTag, mfBuffer);
+    }
+    if (ra) {
+        add_flattenable(desc, kRasterizer_SkDescriptorTag, raBuffer);
+    }
+
+    desc->computeChecksum();
+}
+
+static size_t fill_out_rec(const SkPaint& paint, SkScalerContext::Rec* rec,
+                           const SkDeviceProperties* deviceProperties,
+                           const SkMatrix* deviceMatrix, bool ignoreGamma,
+                           const SkPathEffect* pe, SkWriteBuffer* peBuffer,
+                           const SkMaskFilter* mf, SkWriteBuffer* mfBuffer,
+                           const SkRasterizer* ra, SkWriteBuffer* raBuffer) {
+    SkScalerContext::MakeRec(paint, deviceProperties, deviceMatrix, rec);
+    if (ignoreGamma) {
+        rec->ignorePreBlend();
+    }
+
+    int entryCount = 1;
+    size_t descSize = sizeof(*rec);
+
+    if (pe) {
+        peBuffer->writeFlattenable(pe);
+        descSize += peBuffer->bytesWritten();
+        entryCount += 1;
+        rec->fMaskFormat = SkMask::kA8_Format;  // force antialiasing when we do the scan conversion
+        // seems like we could support kLCD as well at this point...
+    }
+    if (mf) {
+        mfBuffer->writeFlattenable(mf);
+        descSize += mfBuffer->bytesWritten();
+        entryCount += 1;
+        rec->fMaskFormat = SkMask::kA8_Format;   // force antialiasing with maskfilters
+        /* Pre-blend is not currently applied to filtered text.
+           The primary filter is blur, for which contrast makes no sense,
+           and for which the destination guess error is more visible.
+           Also, all existing users of blur have calibrated for linear. */
+        rec->ignorePreBlend();
+    }
+    if (ra) {
+        raBuffer->writeFlattenable(ra);
+        descSize += raBuffer->bytesWritten();
+        entryCount += 1;
+        rec->fMaskFormat = SkMask::kA8_Format;  // force antialiasing when we do the scan conversion
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Now that we're done tweaking the rec, call the PostMakeRec cleanup
+    SkScalerContext::PostMakeRec(paint, rec);
+
+    descSize += SkDescriptor::ComputeOverhead(entryCount);
+    return descSize;
+}
+
+#ifdef TEST_DESC
+static void test_desc(const SkScalerContext::Rec& rec,
+                      const SkPathEffect* pe, SkWriteBuffer* peBuffer,
+                      const SkMaskFilter* mf, SkWriteBuffer* mfBuffer,
+                      const SkRasterizer* ra, SkWriteBuffer* raBuffer,
+                      const SkDescriptor* desc, size_t descSize) {
+    // Check that we completely write the bytes in desc (our key), and that
+    // there are no uninitialized bytes. If there were, then we would get
+    // false-misses (or worse, false-hits) in our fontcache.
+    //
+    // We do this buy filling 2 others, one with 0s and the other with 1s
+    // and create those, and then check that all 3 are identical.
+    SkAutoDescriptor    ad1(descSize);
+    SkAutoDescriptor    ad2(descSize);
+    SkDescriptor*       desc1 = ad1.getDesc();
+    SkDescriptor*       desc2 = ad2.getDesc();
+
+    memset(desc1, 0x00, descSize);
+    memset(desc2, 0xFF, descSize);
+
+    desc1->init();
+    desc2->init();
+    desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+    desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
+
+    if (pe) {
+        add_flattenable(desc1, kPathEffect_SkDescriptorTag, peBuffer);
+        add_flattenable(desc2, kPathEffect_SkDescriptorTag, peBuffer);
+    }
+    if (mf) {
+        add_flattenable(desc1, kMaskFilter_SkDescriptorTag, mfBuffer);
+        add_flattenable(desc2, kMaskFilter_SkDescriptorTag, mfBuffer);
+    }
+    if (ra) {
+        add_flattenable(desc1, kRasterizer_SkDescriptorTag, raBuffer);
+        add_flattenable(desc2, kRasterizer_SkDescriptorTag, raBuffer);
+    }
+
+    SkASSERT(descSize == desc1->getLength());
+    SkASSERT(descSize == desc2->getLength());
+    desc1->computeChecksum();
+    desc2->computeChecksum();
+    SkASSERT(!memcmp(desc, desc1, descSize));
+    SkASSERT(!memcmp(desc, desc2, descSize));
+}
+#endif
+
+/* see the note on ignoreGamma on descriptorProc */
+void SkPaint::getScalerContextDescriptor(SkAutoDescriptor* ad,
+                                         const SkDeviceProperties* deviceProperties,
+                                         const SkMatrix* deviceMatrix, bool ignoreGamma) const {
+    SkScalerContext::Rec    rec;
+
+    SkPathEffect*   pe = this->getPathEffect();
+    SkMaskFilter*   mf = this->getMaskFilter();
+    SkRasterizer*   ra = this->getRasterizer();
+
+    SkWriteBuffer   peBuffer, mfBuffer, raBuffer;
+    size_t descSize = fill_out_rec(*this, &rec, deviceProperties, deviceMatrix, ignoreGamma,
+                                   pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer);
+
+    ad->reset(descSize);
+    SkDescriptor* desc = ad->getDesc();
+
+    write_out_descriptor(desc, rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, descSize);
+
+    SkASSERT(descSize == desc->getLength());
+
+#ifdef TEST_DESC
+    test_desc(rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, desc, descSize);
+#endif
+}
+
 /*
  *  ignoreGamma tells us that the caller just wants metrics that are unaffected
  *  by gamma correction, so we set the rec to ignore preblend: i.e. gamma = 1,
@@ -1600,110 +1739,23 @@
                              void* context, bool ignoreGamma) const {
     SkScalerContext::Rec    rec;
 
-    SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
-    if (ignoreGamma) {
-        rec.ignorePreBlend();
-    }
-
-    size_t          descSize = sizeof(rec);
-    int             entryCount = 1;
     SkPathEffect*   pe = this->getPathEffect();
     SkMaskFilter*   mf = this->getMaskFilter();
     SkRasterizer*   ra = this->getRasterizer();
 
-    SkWriteBuffer    peBuffer, mfBuffer, raBuffer;
-
-    if (pe) {
-        peBuffer.writeFlattenable(pe);
-        descSize += peBuffer.bytesWritten();
-        entryCount += 1;
-        rec.fMaskFormat = SkMask::kA8_Format;   // force antialiasing when we do the scan conversion
-        // seems like we could support kLCD as well at this point...
-    }
-    if (mf) {
-        mfBuffer.writeFlattenable(mf);
-        descSize += mfBuffer.bytesWritten();
-        entryCount += 1;
-        rec.fMaskFormat = SkMask::kA8_Format;   // force antialiasing with maskfilters
-        /* Pre-blend is not currently applied to filtered text.
-           The primary filter is blur, for which contrast makes no sense,
-           and for which the destination guess error is more visible.
-           Also, all existing users of blur have calibrated for linear. */
-        rec.ignorePreBlend();
-    }
-    if (ra) {
-        raBuffer.writeFlattenable(ra);
-        descSize += raBuffer.bytesWritten();
-        entryCount += 1;
-        rec.fMaskFormat = SkMask::kA8_Format;   // force antialiasing when we do the scan conversion
-    }
-
-    ///////////////////////////////////////////////////////////////////////////
-    // Now that we're done tweaking the rec, call the PostMakeRec cleanup
-    SkScalerContext::PostMakeRec(*this, &rec);
-
-    descSize += SkDescriptor::ComputeOverhead(entryCount);
+    SkWriteBuffer   peBuffer, mfBuffer, raBuffer;
+    size_t descSize = fill_out_rec(*this, &rec, deviceProperties, deviceMatrix, ignoreGamma,
+                                   pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer);
 
     SkAutoDescriptor    ad(descSize);
     SkDescriptor*       desc = ad.getDesc();
 
-    desc->init();
-    desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
-
-    if (pe) {
-        add_flattenable(desc, kPathEffect_SkDescriptorTag, &peBuffer);
-    }
-    if (mf) {
-        add_flattenable(desc, kMaskFilter_SkDescriptorTag, &mfBuffer);
-    }
-    if (ra) {
-        add_flattenable(desc, kRasterizer_SkDescriptorTag, &raBuffer);
-    }
+    write_out_descriptor(desc, rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, descSize);
 
     SkASSERT(descSize == desc->getLength());
-    desc->computeChecksum();
 
 #ifdef TEST_DESC
-    {
-        // Check that we completely write the bytes in desc (our key), and that
-        // there are no uninitialized bytes. If there were, then we would get
-        // false-misses (or worse, false-hits) in our fontcache.
-        //
-        // We do this buy filling 2 others, one with 0s and the other with 1s
-        // and create those, and then check that all 3 are identical.
-        SkAutoDescriptor    ad1(descSize);
-        SkAutoDescriptor    ad2(descSize);
-        SkDescriptor*       desc1 = ad1.getDesc();
-        SkDescriptor*       desc2 = ad2.getDesc();
-
-        memset(desc1, 0x00, descSize);
-        memset(desc2, 0xFF, descSize);
-
-        desc1->init();
-        desc2->init();
-        desc1->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
-        desc2->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
-
-        if (pe) {
-            add_flattenable(desc1, kPathEffect_SkDescriptorTag, &peBuffer);
-            add_flattenable(desc2, kPathEffect_SkDescriptorTag, &peBuffer);
-        }
-        if (mf) {
-            add_flattenable(desc1, kMaskFilter_SkDescriptorTag, &mfBuffer);
-            add_flattenable(desc2, kMaskFilter_SkDescriptorTag, &mfBuffer);
-        }
-        if (ra) {
-            add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
-            add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
-        }
-
-        SkASSERT(descSize == desc1->getLength());
-        SkASSERT(descSize == desc2->getLength());
-        desc1->computeChecksum();
-        desc2->computeChecksum();
-        SkASSERT(!memcmp(desc, desc1, descSize));
-        SkASSERT(!memcmp(desc, desc2, descSize));
-    }
+    test_desc(rec, pe, &peBuffer, mf, &mfBuffer, ra, &raBuffer, desc, descSize);
 #endif
 
     proc(fTypeface, desc, context);
@@ -1839,7 +1891,7 @@
     paint->setFlags(packed >> 16);
     paint->setHinting((SkPaint::Hinting)((packed >> 14) & BPF_Mask(kHint_BPF)));
     paint->setTextAlign((SkPaint::Align)((packed >> 12) & BPF_Mask(kAlign_BPF)));
-    paint->setFilterLevel((SkPaint::FilterLevel)((packed >> 10) & BPF_Mask(kFilter_BPF)));
+    paint->setFilterQuality((SkFilterQuality)((packed >> 10) & BPF_Mask(kFilter_BPF)));
     return (FlatFlags)(packed & kFlatFlagMask);
 }
 
@@ -1880,7 +1932,7 @@
     *ptr++ = this->getColor();
 
     *ptr++ = pack_paint_flags(this->getFlags(), this->getHinting(), this->getTextAlign(),
-                              this->getFilterLevel(), flatFlags);
+                              this->getFilterQuality(), flatFlags);
     *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
                     this->getStyle(), this->getTextEncoding());
 
@@ -2085,8 +2137,6 @@
         str->append(descriptor.getFullName());
         str->append("</dd><dt>Font PS Name:</dt><dd>");
         str->append(descriptor.getPostscriptName());
-        str->append("</dd><dt>Font File Name:</dt><dd>");
-        str->append(descriptor.getFontFileName());
         str->append("</dd>");
     }
 
@@ -2200,8 +2250,8 @@
     str->append(")</dd>");
 
     str->append("<dt>FilterLevel:</dt><dd>");
-    static const char* gFilterLevelStrings[] = { "None", "Low", "Medium", "High" };
-    str->append(gFilterLevelStrings[this->getFilterLevel()]);
+    static const char* gFilterQualityStrings[] = { "None", "Low", "Medium", "High" };
+    str->append(gFilterQualityStrings[this->getFilterQuality()]);
     str->append("</dd>");
 
     str->append("<dt>TextAlign:</dt><dd>");
@@ -2263,7 +2313,7 @@
         fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
         fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
         if (has_thick_frame(fPaint)) {
-            fPaint.setStrokeWidth(SkScalarDiv(fPaint.getStrokeWidth(), fScale));
+            fPaint.setStrokeWidth(fPaint.getStrokeWidth() / fScale);
         }
     } else {
         fScale = SK_Scalar1;
diff --git a/src/core/SkPaintPriv.cpp b/src/core/SkPaintPriv.cpp
index c6957cd..3ced573 100644
--- a/src/core/SkPaintPriv.cpp
+++ b/src/core/SkPaintPriv.cpp
@@ -9,6 +9,7 @@
 
 #include "SkBitmap.h"
 #include "SkColorFilter.h"
+#include "SkImage.h"
 #include "SkPaint.h"
 #include "SkShader.h"
 
@@ -49,3 +50,8 @@
 
     return isPaintOpaque(paint, contentType);
 }
+
+bool isPaintOpaque(const SkPaint* paint, const SkImage* image) {
+    return isPaintOpaque(paint, image->isOpaque() ?
+                         kOpaque_SkPaintBitmapOpacity : kUnknown_SkPaintBitmapOpacity);
+}
diff --git a/src/core/SkPaintPriv.h b/src/core/SkPaintPriv.h
index 88fc4fc..be441b8 100644
--- a/src/core/SkPaintPriv.h
+++ b/src/core/SkPaintPriv.h
@@ -8,11 +8,12 @@
 #ifndef SkPaintPriv_DEFINED
 #define SkPaintPriv_DEFINED
 
-class SkBitmap;
-class SkPaint;
-
 #include "SkTypes.h"
 
+class SkBitmap;
+class SkImage;
+class SkPaint;
+
 enum SkPaintBitmapOpacity {
     // No content replaces the paint's color
     kNoBitmap_SkPaintBitmapOpacity = 0,
@@ -40,6 +41,7 @@
         shader.
     @return true if paint is opaque
 */
-bool isPaintOpaque(const SkPaint* paint,
-                   const SkBitmap* bmpReplacesShader = NULL);
+bool isPaintOpaque(const SkPaint* paint, const SkBitmap* bmpReplacesShader = NULL);
+bool isPaintOpaque(const SkPaint* paint, const SkImage* image);
+
 #endif
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index dfbff97..967fbe5 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -179,8 +179,6 @@
 }
 
 void SkPath::swap(SkPath& that) {
-    SkASSERT(&that != NULL);
-
     if (this != &that) {
         fPathRef.swap(&that.fPathRef);
         SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex);
@@ -396,13 +394,16 @@
     int nextDirection = 0;
     bool closedOrMoved = false;
     bool autoClose = false;
+    bool insertClose = false;
     int verbCnt = fPathRef->countVerbs();
     while (*currVerb < verbCnt && (!allowPartial || !autoClose)) {
-        switch (fPathRef->atVerb(*currVerb)) {
+        uint8_t verb = insertClose ? (uint8_t) kClose_Verb : fPathRef->atVerb(*currVerb);
+        switch (verb) {
             case kClose_Verb:
                 savePts = pts;
                 pts = *ptsPtr;
                 autoClose = true;
+                insertClose = false;
             case kLine_Verb: {
                 SkScalar left = last.fX;
                 SkScalar top = last.fY;
@@ -455,6 +456,11 @@
             case kCubic_Verb:
                 return false; // quadratic, cubic not allowed
             case kMove_Verb:
+                if (allowPartial && !autoClose && firstDirection) {
+                    insertClose = true;
+                    *currVerb -= 1;  // try move again afterwards
+                    goto addMissingClose;
+                }
                 last = *pts++;
                 closedOrMoved = true;
                 break;
@@ -464,6 +470,8 @@
         }
         *currVerb += 1;
         lastDirection = nextDirection;
+addMissingClose:
+        ;
     }
     // Success if 4 corners and first point equals last
     bool result = 4 == corners && (first == last || autoClose);
@@ -518,7 +526,7 @@
     return true;
 }
 
-bool SkPath::isNestedRects(SkRect rects[2], Direction dirs[2]) const {
+bool SkPath::isNestedFillRects(SkRect rects[2], Direction dirs[2]) const {
     SkDEBUGCODE(this->validate();)
     int currVerb = 0;
     const SkPoint* pts = fPathRef->points();
@@ -529,8 +537,12 @@
     }
     const SkPoint* last = pts;
     SkRect testRects[2];
-    if (isRectContour(false, &currVerb, &pts, NULL, &testDirs[1])) {
+    bool isClosed;
+    if (isRectContour(false, &currVerb, &pts, &isClosed, &testDirs[1])) {
         testRects[0].set(first, SkToS32(last - first));
+        if (!isClosed) {
+            pts = fPathRef->points() + fPathRef->countPoints();
+        }
         testRects[1].set(last, SkToS32(pts - last));
         if (testRects[0].contains(testRects[1])) {
             if (rects) {
@@ -617,6 +629,18 @@
     return false;
 }
 
+void SkPath::setPt(int index, SkScalar x, SkScalar y) {
+    SkDEBUGCODE(this->validate();)
+
+    int count = fPathRef->countPoints();
+    if (count <= index) {
+        return;
+    } else {
+        SkPathRef::Editor ed(&fPathRef);
+        ed.atPoint(index)->set(x, y);
+    }
+}
+
 void SkPath::setLastPt(SkScalar x, SkScalar y) {
     SkDEBUGCODE(this->validate();)
 
@@ -924,17 +948,6 @@
     *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection;
 }
 
-#ifdef SK_SUPPORT_LEGACY_ARCTO_QUADS
-static int build_arc_points(const SkRect& oval, const SkVector& start, const SkVector& stop,
-                            SkRotationDirection dir, SkPoint pts[kSkBuildQuadArcStorage]) {
-    SkMatrix    matrix;
-
-    matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
-    matrix.postTranslate(oval.centerX(), oval.centerY());
-
-    return SkBuildQuadArc(start, stop, dir, &matrix, pts);
-}
-#else
 /**
  *  If this returns 0, then the caller should just line-to the singlePt, else it should
  *  ignore singlePt and append the specified number of conics.
@@ -953,7 +966,6 @@
     }
     return count;
 }
-#endif
 
 void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
                           Direction dir) {
@@ -1130,17 +1142,6 @@
     SkRotationDirection dir;
     angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
 
-#ifdef SK_SUPPORT_LEGACY_ARCTO_QUADS
-    SkPoint pts[kSkBuildQuadArcStorage];
-    int count = build_arc_points(oval, startV, stopV, dir, pts);
-    SkASSERT((count & 1) == 1);
-
-    this->incReserve(count);
-    forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]);
-    for (int i = 1; i < count; i += 2) {
-        this->quadTo(pts[i], pts[i+1]);
-    }
-#else
     SkPoint singlePt;
     SkConic conics[SkConic::kMaxConicsForArc];
     int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
@@ -1154,7 +1155,6 @@
     } else {
         forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
     }
-#endif
 }
 
 void SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
@@ -1777,7 +1777,7 @@
 #ifdef SK_DEBUG
     fPts = NULL;
     fConicWeights = NULL;
-    fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
+    fMoveTo.fX = fMoveTo.fY = 0;
 #endif
     // need to init enough to make next() harmlessly return kDone_Verb
     fVerbs = NULL;
@@ -1794,7 +1794,6 @@
     fVerbStop = path.fPathRef->verbsMemBegin();
     fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind
     fMoveTo.fX = fMoveTo.fY = 0;
-    fLastPt.fX = fLastPt.fY = 0;
 }
 
 SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
@@ -1809,34 +1808,31 @@
 
     switch (verb) {
         case kMove_Verb:
-            pts[0] = *srcPts;
-            fMoveTo = srcPts[0];
-            fLastPt = fMoveTo;
+            fMoveTo = pts[0] = srcPts[0];
             srcPts += 1;
             break;
         case kLine_Verb:
-            pts[0] = fLastPt;
+            pts[0] = srcPts[-1];
             pts[1] = srcPts[0];
-            fLastPt = srcPts[0];
             srcPts += 1;
             break;
         case kConic_Verb:
             fConicWeights += 1;
             // fall-through
         case kQuad_Verb:
-            pts[0] = fLastPt;
-            memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
-            fLastPt = srcPts[1];
+            pts[0] = srcPts[-1];
+            pts[1] = srcPts[0];
+            pts[2] = srcPts[1];
             srcPts += 2;
             break;
         case kCubic_Verb:
-            pts[0] = fLastPt;
-            memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
-            fLastPt = srcPts[2];
+            pts[0] = srcPts[-1];
+            pts[1] = srcPts[0];
+            pts[2] = srcPts[1];
+            pts[3] = srcPts[2];
             srcPts += 3;
             break;
         case kClose_Verb:
-            fLastPt = fMoveTo;
             pts[0] = fMoveTo;
             break;
     }
@@ -1976,11 +1972,13 @@
                 verb = kDone_Verb;  // stop the loop
                 break;
         }
+        if (!wStream && builder.size()) {
+            SkDebugf("%s", builder.c_str());
+            builder.reset();
+        }
     }
     if (wStream) {
         wStream->writeText(builder.c_str());
-    } else {
-        SkDebugf("%s", builder.c_str());
     }
 }
 
@@ -1994,7 +1992,6 @@
 
 #ifdef SK_DEBUG
 void SkPath::validate() const {
-    SkASSERT(this != NULL);
     SkASSERT((fFillType & ~3) == 0);
 
 #ifdef SK_DEBUG_PATH
diff --git a/src/core/SkPathMeasure.cpp b/src/core/SkPathMeasure.cpp
index 9d16075..a5dd840 100644
--- a/src/core/SkPathMeasure.cpp
+++ b/src/core/SkPathMeasure.cpp
@@ -341,8 +341,7 @@
                 if (SK_Scalar1 == stopT) {
                     dst->quadTo(tmp0[3], tmp0[4]);
                 } else {
-                    SkChopQuadAt(&tmp0[2], tmp1, SkScalarDiv(stopT - startT,
-                                                         SK_Scalar1 - startT));
+                    SkChopQuadAt(&tmp0[2], tmp1, (stopT - startT) / (1 - startT));
                     dst->quadTo(tmp1[1], tmp1[2]);
                 }
             }
@@ -383,8 +382,7 @@
                 if (SK_Scalar1 == stopT) {
                     dst->cubicTo(tmp0[4], tmp0[5], tmp0[6]);
                 } else {
-                    SkChopCubicAt(&tmp0[3], tmp1, SkScalarDiv(stopT - startT,
-                                                        SK_Scalar1 - startT));
+                    SkChopCubicAt(&tmp0[3], tmp1, (stopT - startT) / (1 - startT));
                     dst->cubicTo(tmp1[1], tmp1[2], tmp1[3]);
                 }
             }
@@ -442,6 +440,36 @@
     return fLength;
 }
 
+template <typename T, typename K>
+int SkTKSearch(const T base[], int count, const K& key) {
+    SkASSERT(count >= 0);
+    if (count <= 0) {
+        return ~0;
+    }
+    
+    SkASSERT(base != NULL); // base may be NULL if count is zero
+    
+    int lo = 0;
+    int hi = count - 1;
+    
+    while (lo < hi) {
+        int mid = (hi + lo) >> 1;
+        if (base[mid].fDistance < key) {
+            lo = mid + 1;
+        } else {
+            hi = mid;
+        }
+    }
+    
+    if (base[hi].fDistance < key) {
+        hi += 1;
+        hi = ~hi;
+    } else if (key < base[hi].fDistance) {
+        hi = ~hi;
+    }
+    return hi;
+}
+
 const SkPathMeasure::Segment* SkPathMeasure::distanceToSegment(
                                             SkScalar distance, SkScalar* t) {
     SkDEBUGCODE(SkScalar length = ) this->getLength();
@@ -450,7 +478,7 @@
     const Segment*  seg = fSegments.begin();
     int             count = fSegments.count();
 
-    int index = SkTSearch<SkScalar>(&seg->fDistance, count, distance, sizeof(Segment));
+    int index = SkTKSearch<Segment, SkScalar>(seg, count, distance);
     // don't care if we hit an exact match or not, so we xor index if it is negative
     index ^= (index >> 31);
     seg = &seg[index];
diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
index c7e36e7..0145cbe 100644
--- a/src/core/SkPicture.cpp
+++ b/src/core/SkPicture.cpp
@@ -12,6 +12,7 @@
 #include "SkPictureRecord.h"
 #include "SkPictureRecorder.h"
 
+#include "SkAtomics.h"
 #include "SkBitmapDevice.h"
 #include "SkCanvas.h"
 #include "SkChunkAlloc.h"
@@ -46,18 +47,6 @@
     return obj ? obj->count() : 0;
 }
 
-static int32_t gPictureGenerationID;
-
-// never returns a 0
-static int32_t next_picture_generation_id() {
-    // Loop in case our global wraps around.
-    int32_t genID;
-    do {
-        genID = sk_atomic_inc(&gPictureGenerationID) + 1;
-    } while (0 == genID);
-    return genID;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 namespace {
@@ -144,27 +133,18 @@
 struct SkPicture::PathCounter {
     SK_CREATE_MEMBER_DETECTOR(paint);
 
-    PathCounter()
-        : numPaintWithPathEffectUses (0)
-        , numFastPathDashEffects (0)
-        , numAAConcavePaths (0)
-        , numAAHairlineConcavePaths (0)
-        , numAADFEligibleConcavePaths(0) {
-    }
+    PathCounter() : fNumSlowPathsAndDashEffects(0) {}
 
     // Recurse into nested pictures.
     void operator()(const SkRecords::DrawPicture& op) {
-        const SkPicture::Analysis& analysis = op.picture->fAnalysis;
-        numPaintWithPathEffectUses += analysis.fNumPaintWithPathEffectUses;
-        numFastPathDashEffects     += analysis.fNumFastPathDashEffects;
-        numAAConcavePaths          += analysis.fNumAAConcavePaths;
-        numAAHairlineConcavePaths  += analysis.fNumAAHairlineConcavePaths;
-        numAADFEligibleConcavePaths  += analysis.fNumAADFEligibleConcavePaths;
+        const SkPicture::Analysis& analysis = op.picture->analysis();
+        fNumSlowPathsAndDashEffects += analysis.fNumSlowPathsAndDashEffects;
     }
 
     void checkPaint(const SkPaint* paint) {
         if (paint && paint->getPathEffect()) {
-            numPaintWithPathEffectUses++;
+            // Initially assume it's slow.
+            fNumSlowPathsAndDashEffects++;
         }
     }
 
@@ -176,7 +156,7 @@
             SkPathEffect::DashType dashType = effect->asADash(&info);
             if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() &&
                 SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) {
-                numFastPathDashEffects++;
+                fNumSlowPathsAndDashEffects--;
             }
         }
     }
@@ -184,16 +164,16 @@
     void operator()(const SkRecords::DrawPath& op) {
         this->checkPaint(&op.paint);
         if (op.paint.isAntiAlias() && !op.path.isConvex()) {
-            numAAConcavePaths++;
-
             SkPaint::Style paintStyle = op.paint.getStyle();
             const SkRect& pathBounds = op.path.getBounds();
             if (SkPaint::kStroke_Style == paintStyle &&
                 0 == op.paint.getStrokeWidth()) {
-                numAAHairlineConcavePaths++;
+                // AA hairline concave path is not slow.
             } else if (SkPaint::kFill_Style == paintStyle && pathBounds.width() < 64.f &&
                        pathBounds.height() < 64.f && !op.path.isVolatile()) {
-                numAADFEligibleConcavePaths++;
+                // AADF eligible concave path is not slow.
+            } else {
+                fNumSlowPathsAndDashEffects++;
             }
         }
     }
@@ -206,11 +186,7 @@
     template <typename T>
     SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ }
 
-    int numPaintWithPathEffectUses;
-    int numFastPathDashEffects;
-    int numAAConcavePaths;
-    int numAAHairlineConcavePaths;
-    int numAADFEligibleConcavePaths;
+    int fNumSlowPathsAndDashEffects;
 };
 
 SkPicture::Analysis::Analysis(const SkRecord& record) {
@@ -220,11 +196,7 @@
     for (unsigned i = 0; i < record.count(); i++) {
         record.visit<void>(i, counter);
     }
-    fNumPaintWithPathEffectUses = counter.numPaintWithPathEffectUses;
-    fNumFastPathDashEffects     = counter.numFastPathDashEffects;
-    fNumAAConcavePaths          = counter.numAAConcavePaths;
-    fNumAAHairlineConcavePaths  = counter.numAAHairlineConcavePaths;
-    fNumAADFEligibleConcavePaths  = counter.numAADFEligibleConcavePaths;
+    fNumSlowPathsAndDashEffects = SkTMin<int>(counter.fNumSlowPathsAndDashEffects, 255);
 
     fHasText = false;
     TextHunter text;
@@ -241,17 +213,7 @@
     // TODO: the heuristic used here needs to be refined
     static const int kNumSlowPathsTol = 6;
 
-    int numSlowPathDashedPaths = fNumPaintWithPathEffectUses;
-    if (0 == sampleCount) {
-        // The fast dashing path only works when MSAA is disabled
-        numSlowPathDashedPaths -= fNumFastPathDashEffects;
-    }
-
-    int numSlowPaths = fNumAAConcavePaths - 
-                       fNumAAHairlineConcavePaths -
-                       fNumAADFEligibleConcavePaths;
-
-    bool ret = numSlowPathDashedPaths + numSlowPaths < kNumSlowPathsTol;
+    bool ret = fNumSlowPathsAndDashEffects < kNumSlowPathsTol;
 
     if (!ret && reason) {
         *reason = "Too many slow paths (either concave or dashed).";
@@ -270,13 +232,13 @@
 }
 
 SkPicture::~SkPicture() {
-    SkPicture::DeletionMessage msg;
-    msg.fUniqueID = this->uniqueID();
-    SkMessageBus<SkPicture::DeletionMessage>::Post(msg);
-}
-
-void SkPicture::EXPERIMENTAL_addAccelData(const SkPicture::AccelData* data) const {
-    fAccelData.reset(SkRef(data));
+    // If the ID is still zero, no one has read it, so no need to send this message.
+    uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed);
+    if (id != 0) {
+        SkPicture::DeletionMessage msg;
+        msg.fUniqueID = id;
+        SkMessageBus<SkPicture::DeletionMessage>::Post(msg);
+    }
 }
 
 const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData(
@@ -476,22 +438,49 @@
     }
 }
 
+const SkPicture::Analysis& SkPicture::analysis() const {
+    auto create = [&](){ return SkNEW_ARGS(Analysis, (*fRecord)); };
+    return *fAnalysis.get(create);
+}
+
 #if SK_SUPPORT_GPU
 bool SkPicture::suitableForGpuRasterization(GrContext*, const char **reason) const {
-    return fAnalysis.suitableForGpuRasterization(reason, 0);
+    return this->analysis().suitableForGpuRasterization(reason, 0);
 }
 #endif
 
-bool SkPicture::hasText()             const { return fAnalysis.fHasText; }
-bool SkPicture::willPlayBackBitmaps() const { return fAnalysis.fWillPlaybackBitmaps; }
+bool SkPicture::hasText()             const { return this->analysis().fHasText; }
+bool SkPicture::willPlayBackBitmaps() const { return this->analysis().fWillPlaybackBitmaps; }
 int  SkPicture::approximateOpCount()  const { return fRecord->count(); }
 
-SkPicture::SkPicture(const SkRect& cullRect, SkRecord* record, SnapshotArray* drawablePicts,
-                     SkBBoxHierarchy* bbh)
-    : fUniqueID(next_picture_generation_id())
+SkPicture::SkPicture(const SkRect& cullRect,
+                     SkRecord* record,
+                     SnapshotArray* drawablePicts,
+                     SkBBoxHierarchy* bbh,
+                     AccelData* accelData,
+                     size_t approxBytesUsedBySubPictures)
+    : fUniqueID(0)
     , fCullRect(cullRect)
-    , fRecord(SkRef(record))
-    , fBBH(SkSafeRef(bbh))
-    , fDrawablePicts(drawablePicts)     // take ownership
-    , fAnalysis(*fRecord)
+    , fRecord(record)               // Take ownership of caller's ref.
+    , fDrawablePicts(drawablePicts) // Take ownership.
+    , fBBH(bbh)                     // Take ownership of caller's ref.
+    , fAccelData(accelData)         // Take ownership of caller's ref.
+    , fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures)
 {}
+
+
+static uint32_t gNextID = 1;
+uint32_t SkPicture::uniqueID() const {
+    uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed);
+    while (id == 0) {
+        uint32_t next = sk_atomic_fetch_add(&gNextID, 1u);
+        if (sk_atomic_compare_exchange(&fUniqueID, &id, next,
+                                       sk_memory_order_relaxed,
+                                       sk_memory_order_relaxed)) {
+            id = next;
+        } else {
+            // sk_atomic_compare_exchange replaced id with the current value of fUniqueID.
+        }
+    }
+    return id;
+}
diff --git a/src/core/SkPictureContentInfo.h b/src/core/SkPictureContentInfo.h
index f255f7d..81c8a27 100644
--- a/src/core/SkPictureContentInfo.h
+++ b/src/core/SkPictureContentInfo.h
@@ -8,6 +8,8 @@
 #ifndef SkPictureContentInfo_DEFINED
 #define SkPictureContentInfo_DEFINED
 
+#include "SkTDArray.h"
+
 class GrContext;
 
 class SkPictureContentInfo {
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 53ecdd0..07811fb 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -7,6 +7,7 @@
 
 #include "SkPictureRecord.h"
 #include "SkDevice.h"
+#include "SkImage_Base.h"
 #include "SkPatchUtils.h"
 #include "SkPixelRef.h"
 #include "SkRRect.h"
@@ -563,6 +564,22 @@
     this->validate(initialOffset, size);
 }
 
+void SkPictureRecord::onDrawImage(const SkImage* image, SkScalar x, SkScalar y,
+                                  const SkPaint* paint) {
+    SkBitmap bm;
+    if (as_IB(image)->getROPixels(&bm)) {
+        this->SkPictureRecord::onDrawBitmap(bm, x, y, paint);
+    }
+}
+
+void SkPictureRecord::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                                      const SkPaint* paint) {
+    SkBitmap bm;
+    if (as_IB(image)->getROPixels(&bm)) {
+        this->SkPictureRecord::onDrawBitmapRect(bm, src, dst, paint, kNone_DrawBitmapRectFlag);
+    }
+}
+
 void SkPictureRecord::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
                                        const SkRect& dst, const SkPaint* paint) {
     // op + paint index + bitmap id + center + dst rect
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index 3ce0007..7a6fc81 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -29,9 +29,9 @@
     SkPictureRecord(const SkISize& dimensions, uint32_t recordFlags);
     virtual ~SkPictureRecord();
 
-    void beginCommentGroup(const char* description) SK_OVERRIDE;
-    void addComment(const char* kywd, const char* value) SK_OVERRIDE;
-    void endCommentGroup() SK_OVERRIDE;
+    void beginCommentGroup(const char* description) override;
+    void addComment(const char* kywd, const char* value) override;
+    void endCommentGroup() override;
 
     const SkTDArray<const SkPicture* >& getPictureRefs() const {
         return fPictureRefs;
@@ -146,65 +146,62 @@
         SkASSERT(fWriter.bytesWritten() == initialOffset + size);
     }
 
-    SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) SK_OVERRIDE;
-    const void* onPeekPixels(SkImageInfo*, size_t*) SK_OVERRIDE {
+    SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
+    const void* onPeekPixels(SkImageInfo*, size_t*) override {
         return NULL;
     }
 
-    void willSave() SK_OVERRIDE;
-    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) SK_OVERRIDE;
-    void willRestore() SK_OVERRIDE;
+    void willSave() override;
+    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+    void willRestore() override;
 
-    void didConcat(const SkMatrix&) SK_OVERRIDE;
-    void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
+    void didConcat(const SkMatrix&) override;
+    void didSetMatrix(const SkMatrix&) override;
 
-    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
+    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
 
     virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                            const SkPaint&) SK_OVERRIDE;
+                            const SkPaint&) override;
     virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                               const SkPaint&) SK_OVERRIDE;
+                               const SkPaint&) override;
     virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                                SkScalar constY, const SkPaint&) SK_OVERRIDE;
+                                SkScalar constY, const SkPaint&) override;
     virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                                  const SkMatrix* matrix, const SkPaint&) SK_OVERRIDE;
+                                  const SkMatrix* matrix, const SkPaint&) override;
     virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                const SkPaint& paint) SK_OVERRIDE;
+                                const SkPaint& paint) override;
 
     virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
                              const SkPoint texCoords[4], SkXfermode* xmode,
-                             const SkPaint& paint) SK_OVERRIDE;
+                             const SkPaint& paint) override;
 
-    void onDrawPaint(const SkPaint&) SK_OVERRIDE;
-    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) SK_OVERRIDE;
-    void onDrawRect(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawOval(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawRRect(const SkRRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawPath(const SkPath&, const SkPaint&) SK_OVERRIDE;
-    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+    void onDrawPaint(const SkPaint&) override;
+    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
+    void onDrawRect(const SkRect&, const SkPaint&) override;
+    void onDrawOval(const SkRect&, const SkPaint&) override;
+    void onDrawRRect(const SkRRect&, const SkPaint&) override;
+    void onDrawPath(const SkPath&, const SkPaint&) override;
+    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
-                          DrawBitmapRectFlags flags) SK_OVERRIDE;
-#if 0
-    // rely on conversion to bitmap (for now)
-    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+                          DrawBitmapRectFlags flags) override;
+    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
-                         const SkPaint*) SK_OVERRIDE;
-#endif
+                         const SkPaint*) override;
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
-                          const SkPaint*) SK_OVERRIDE;
-    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) SK_OVERRIDE;
+                          const SkPaint*) override;
+    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
     void onDrawVertices(VertexMode vmode, int vertexCount,
                         const SkPoint vertices[], const SkPoint texs[],
                         const SkColor colors[], SkXfermode* xmode,
                         const uint16_t indices[], int indexCount,
-                        const SkPaint&) SK_OVERRIDE;
+                        const SkPaint&) override;
 
-    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRegion(const SkRegion&, SkRegion::Op) SK_OVERRIDE;
+    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRegion(const SkRegion&, SkRegion::Op) override;
 
-    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) SK_OVERRIDE;
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
 
     int addPathToHeap(const SkPath& path);  // does not write to ops stream
 
diff --git a/src/core/SkPictureRecorder.cpp b/src/core/SkPictureRecorder.cpp
index 1972ad3..282e2c2 100644
--- a/src/core/SkPictureRecorder.cpp
+++ b/src/core/SkPictureRecorder.cpp
@@ -9,13 +9,17 @@
 #include "SkDrawable.h"
 #include "SkLayerInfo.h"
 #include "SkPictureRecorder.h"
+#include "SkPictureUtils.h"
 #include "SkRecord.h"
 #include "SkRecordDraw.h"
-#include "SkRecorder.h"
 #include "SkRecordOpts.h"
+#include "SkRecorder.h"
 #include "SkTypes.h"
 
-SkPictureRecorder::SkPictureRecorder() {}
+SkPictureRecorder::SkPictureRecorder() {
+    fActivelyRecording = false;
+    fRecorder.reset(SkNEW_ARGS(SkRecorder, (nullptr, SkRect::MakeWH(0,0))));
+}
 
 SkPictureRecorder::~SkPictureRecorder() {}
 
@@ -31,15 +35,18 @@
     }
 
     fRecord.reset(SkNEW(SkRecord));
-    fRecorder.reset(SkNEW_ARGS(SkRecorder, (fRecord.get(), cullRect)));
+    fRecorder->reset(fRecord.get(), cullRect);
+    fActivelyRecording = true;
     return this->getRecordingCanvas();
 }
 
 SkCanvas* SkPictureRecorder::getRecordingCanvas() {
-    return fRecorder.get();
+    return fActivelyRecording ? fRecorder.get() : nullptr;
 }
 
 SkPicture* SkPictureRecorder::endRecordingAsPicture() {
+    fActivelyRecording = false;
+    fRecorder->restoreToCount(1);  // If we were missing any restores, add them now.
     // TODO: delay as much of this work until just before first playback?
     SkRecordOptimize(fRecord);
 
@@ -66,18 +73,16 @@
         fCullRect = bbhBound;
     }
 
-    SkPicture* pict = SkNEW_ARGS(SkPicture, (fCullRect, fRecord, pictList, fBBH));
-
-    if (saveLayerData) {
-        pict->EXPERIMENTAL_addAccelData(saveLayerData);
+    size_t subPictureBytes = fRecorder->approxBytesUsedBySubPictures();
+    for (int i = 0; pictList && i < pictList->count(); i++) {
+        subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]);
     }
-
-    // release our refs now, so only the picture will be the owner.
-    fRecorder.reset(NULL);
-    fRecord.reset(NULL);
-    fBBH.reset(NULL);
-
-    return pict;
+    return SkNEW_ARGS(SkPicture, (fCullRect,
+                                  fRecord.detach(),
+                                  pictList,
+                                  fBBH.detach(),
+                                  saveLayerData.detach(),
+                                  subPictureBytes));
 }
 
 void SkPictureRecorder::partialReplay(SkCanvas* canvas) const {
@@ -115,9 +120,9 @@
     {}
 
 protected:
-    SkRect onGetBounds() SK_OVERRIDE { return fBounds; }
+    SkRect onGetBounds() override { return fBounds; }
 
-    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+    void onDraw(SkCanvas* canvas) override {
         SkDrawable* const* drawables = NULL;
         int drawableCount = 0;
         if (fDrawableList) {
@@ -127,7 +132,7 @@
         SkRecordDraw(*fRecord, canvas, NULL, drawables, drawableCount, fBBH, NULL/*callback*/);
     }
 
-    SkPicture* onNewPictureSnapshot() SK_OVERRIDE {
+    SkPicture* onNewPictureSnapshot() override {
         SkPicture::SnapshotArray* pictList = NULL;
         if (fDrawableList) {
             // TODO: should we plumb-down the BBHFactory and recordFlags from our host
@@ -148,16 +153,24 @@
             SkRecordComputeLayers(fBounds, *fRecord, pictList, bbh, saveLayerData);
         }
 
-        SkPicture* pict = SkNEW_ARGS(SkPicture, (fBounds, fRecord, pictList, fBBH));
-
-        if (saveLayerData) {
-            pict->EXPERIMENTAL_addAccelData(saveLayerData);
+        size_t subPictureBytes = 0;
+        for (int i = 0; pictList && i < pictList->count(); i++) {
+            subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]);
         }
-        return pict;
+        // SkPicture will take ownership of a ref on both fRecord and fBBH.
+        // We're not willing to give up our ownership, so we must ref them for SkPicture.
+        return SkNEW_ARGS(SkPicture, (fBounds,
+                                      SkRef(fRecord.get()),
+                                      pictList,
+                                      SkSafeRef(fBBH.get()),
+                                      saveLayerData.detach(),
+                                      subPictureBytes));
     }
 };
 
 SkDrawable* SkPictureRecorder::endRecordingAsDrawable() {
+    fActivelyRecording = false;
+    fRecorder->restoreToCount(1);  // If we were missing any restores, add them now.
     // TODO: delay as much of this work until just before first playback?
     SkRecordOptimize(fRecord);
 
@@ -171,7 +184,6 @@
                                          SkToBool(fFlags & kComputeSaveLayerInfo_RecordFlag)));
 
     // release our refs now, so only the drawable will be the owner.
-    fRecorder.reset(NULL);
     fRecord.reset(NULL);
     fBBH.reset(NULL);
 
diff --git a/src/core/SkPictureShader.cpp b/src/core/SkPictureShader.cpp
index 17d1289..c1c4755 100644
--- a/src/core/SkPictureShader.cpp
+++ b/src/core/SkPictureShader.cpp
@@ -70,8 +70,8 @@
     SkAutoTUnref<SkShader> fShader;
     size_t                 fBitmapBytes;
 
-    const Key& getKey() const SK_OVERRIDE { return fKey; }
-    size_t bytesUsed() const SK_OVERRIDE {
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override {
         return sizeof(fKey) + sizeof(SkShader) + fBitmapBytes;
     }
 
@@ -141,7 +141,8 @@
     fPicture->flatten(buffer);
 }
 
-SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatrix* localM) const {
+SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatrix* localM,
+                                            const int maxTextureSize) const {
     SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
 
     SkMatrix m;
@@ -152,6 +153,9 @@
 
     // Use a rotation-invariant scale
     SkPoint scale;
+    //
+    // TODO: replace this with decomposeScale() -- but beware LayoutTest rebaselines!
+    //
     if (!SkDecomposeUpper2x2(m, NULL, &scale, NULL)) {
         // Decomposition failed, use an approximation.
         scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
@@ -160,14 +164,24 @@
     SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()),
                                      SkScalarAbs(scale.y() * fTile.height()));
 
-    // Clamp the tile size to about 16M pixels
-    static const SkScalar kMaxTileArea = 4096 * 4096;
+    // Clamp the tile size to about 4M pixels
+    static const SkScalar kMaxTileArea = 2048 * 2048;
     SkScalar tileArea = SkScalarMul(scaledSize.width(), scaledSize.height());
     if (tileArea > kMaxTileArea) {
-        SkScalar clampScale = SkScalarSqrt(SkScalarDiv(kMaxTileArea, tileArea));
+        SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
         scaledSize.set(SkScalarMul(scaledSize.width(), clampScale),
                        SkScalarMul(scaledSize.height(), clampScale));
     }
+#if SK_SUPPORT_GPU
+    // Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture
+    if (maxTextureSize) {
+        if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
+            SkScalar downScale = maxTextureSize / SkMax32(scaledSize.width(), scaledSize.height());
+            scaledSize.set(SkScalarFloorToScalar(SkScalarMul(scaledSize.width(), downScale)),
+                           SkScalarFloorToScalar(SkScalarMul(scaledSize.height(), downScale)));
+        }
+    }
+#endif
 
     SkISize tileSize = scaledSize.toRound();
     if (tileSize.isEmpty()) {
@@ -296,7 +310,11 @@
                                           const SkMatrix& viewM, const SkMatrix* localMatrix,
                                           GrColor* paintColor,
                                           GrFragmentProcessor** fp) const {
-    SkAutoTUnref<SkShader> bitmapShader(this->refBitmapShader(viewM, localMatrix));
+    int maxTextureSize = 0;
+    if (context) {
+        maxTextureSize = context->getMaxTextureSize();
+    }
+    SkAutoTUnref<SkShader> bitmapShader(this->refBitmapShader(viewM, localMatrix, maxTextureSize));
     if (!bitmapShader) {
         return false;
     }
diff --git a/src/core/SkPictureShader.h b/src/core/SkPictureShader.h
index 8df9f53..3fa27b5 100644
--- a/src/core/SkPictureShader.h
+++ b/src/core/SkPictureShader.h
@@ -25,23 +25,23 @@
                                    const SkRect*);
     virtual ~SkPictureShader();
 
-    size_t contextSize() const SK_OVERRIDE;
+    size_t contextSize() const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPictureShader)
 
     bool asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix& viewM, const SkMatrix*,
-                             GrColor*, GrFragmentProcessor**) const SK_OVERRIDE;
+                             GrColor*, GrFragmentProcessor**) const override;
 
 protected:
     SkPictureShader(SkReadBuffer&);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
+    Context* onCreateContext(const ContextRec&, void* storage) const override;
 
 private:
     SkPictureShader(const SkPicture*, TileMode, TileMode, const SkMatrix*, const SkRect*);
 
-    SkShader* refBitmapShader(const SkMatrix&, const SkMatrix* localMatrix) const;
+    SkShader* refBitmapShader(const SkMatrix&, const SkMatrix* localMatrix, const int maxTextureSize = 0) const;
 
     const SkPicture* fPicture;
     SkRect           fTile;
@@ -54,11 +54,11 @@
 
         virtual ~PictureShaderContext();
 
-        uint32_t getFlags() const SK_OVERRIDE;
+        uint32_t getFlags() const override;
 
-        ShadeProc asAShadeProc(void** ctx) SK_OVERRIDE;
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
-        void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+        ShadeProc asAShadeProc(void** ctx) override;
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
+        void shadeSpan16(int x, int y, uint16_t dstC[], int count) override;
 
     private:
         PictureShaderContext(const SkPictureShader&, const ContextRec&, SkShader* bitmapShader);
diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp
index dea8c8a..20a8b34 100644
--- a/src/core/SkPixelRef.cpp
+++ b/src/core/SkPixelRef.cpp
@@ -8,6 +8,7 @@
 #include "SkBitmapCache.h"
 #include "SkPixelRef.h"
 #include "SkThread.h"
+#include "SkTraceEvent.h"
 
 #ifdef SK_BUILD_FOR_WIN32
     // We don't have SK_BASE_MUTEX_INIT on Windows.
@@ -155,7 +156,9 @@
     SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);
 
     if (!fPreLocked) {
+        TRACE_EVENT_BEGIN0("skia", "SkPixelRef::lockPixelsMutex");
         SkAutoMutexAcquire  ac(*fMutex);
+        TRACE_EVENT_END0("skia", "SkPixelRef::lockPixelsMutex");
 
         if (1 == ++fLockCount) {
             SkASSERT(fRec.isZero());
diff --git a/src/core/SkRRect.cpp b/src/core/SkRRect.cpp
index f292764..0d1887d 100644
--- a/src/core/SkRRect.cpp
+++ b/src/core/SkRRect.cpp
@@ -26,8 +26,7 @@
     }
 
     if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) {
-        SkScalar scale = SkMinScalar(SkScalarDiv(rect.width(), xRad + xRad),
-                                     SkScalarDiv(rect.height(), yRad + yRad));
+        SkScalar scale = SkMinScalar(rect.width() / (xRad + xRad), rect.height() / (yRad + yRad));
         SkASSERT(scale < SK_Scalar1);
         xRad = SkScalarMul(xRad, scale);
         yRad = SkScalarMul(yRad, scale);
@@ -66,10 +65,10 @@
 
     SkScalar scale = SK_Scalar1;
     if (leftRad + rightRad > rect.width()) {
-        scale = SkScalarDiv(rect.width(), leftRad + rightRad);
+        scale = rect.width() / (leftRad + rightRad);
     }
     if (topRad + bottomRad > rect.height()) {
-        scale = SkMinScalar(scale, SkScalarDiv(rect.height(), topRad + bottomRad));
+        scale = SkMinScalar(scale, rect.height() / (topRad + bottomRad));
     }
 
     if (scale < SK_Scalar1) {
@@ -128,6 +127,16 @@
     return rad;
 }
 
+// These parameters intentionally double. Apropos crbug.com/463920, if one of the
+// radii is huge while the other is small, single precision math can completely
+// miss the fact that a scale is required.
+static double compute_min_scale(double rad1, double rad2, double limit, double curMin) {
+    if ((rad1 + rad2) > limit) {
+        return SkTMin(curMin, limit / (rad1 + rad2));
+    }
+    return curMin;
+}
+
 void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
     if (rect.isEmpty() || !rect.isFinite()) {
         this->setEmpty();
@@ -173,26 +182,14 @@
     //   and Ltop = Lbottom = the width of the box,
     //   and Lleft = Lright = the height of the box.
     // If f < 1, then all corner radii are reduced by multiplying them by f."
-    SkScalar scale = SK_Scalar1;
+    double scale = 1.0;
 
-    if (fRadii[0].fX + fRadii[1].fX > rect.width()) {
-        scale = SkMinScalar(scale,
-                            SkScalarDiv(rect.width(), fRadii[0].fX + fRadii[1].fX));
-    }
-    if (fRadii[1].fY + fRadii[2].fY > rect.height()) {
-        scale = SkMinScalar(scale,
-                            SkScalarDiv(rect.height(), fRadii[1].fY + fRadii[2].fY));
-    }
-    if (fRadii[2].fX + fRadii[3].fX > rect.width()) {
-        scale = SkMinScalar(scale,
-                            SkScalarDiv(rect.width(), fRadii[2].fX + fRadii[3].fX));
-    }
-    if (fRadii[3].fY + fRadii[0].fY > rect.height()) {
-        scale = SkMinScalar(scale,
-                            SkScalarDiv(rect.height(), fRadii[3].fY + fRadii[0].fY));
-    }
+    scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, rect.width(),  scale);
+    scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, rect.height(), scale);
+    scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, rect.width(),  scale);
+    scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, rect.height(), scale);
 
-    if (scale < SK_Scalar1) {
+    if (scale < 1.0) {
         for (int i = 0; i < 4; ++i) {
             fRadii[i].fX *= scale;
             fRadii[i].fY *= scale;
@@ -453,9 +450,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
-    SkRect r = fRect;
+    const SkRect r = fRect.makeInset(dx, dy);
 
-    r.inset(dx, dy);
     if (r.isEmpty()) {
         dst->setEmpty();
         return;
diff --git a/src/core/SkRTree.h b/src/core/SkRTree.h
index 9a118d4..9db6d33 100644
--- a/src/core/SkRTree.h
+++ b/src/core/SkRTree.h
@@ -41,9 +41,9 @@
     explicit SkRTree(SkScalar aspectRatio = 1);
     virtual ~SkRTree() {}
 
-    void insert(const SkRect[], int N) SK_OVERRIDE;
-    void search(const SkRect& query, SkTDArray<unsigned>* results) const SK_OVERRIDE;
-    size_t bytesUsed() const SK_OVERRIDE;
+    void insert(const SkRect[], int N) override;
+    void search(const SkRect& query, SkTDArray<unsigned>* results) const override;
+    size_t bytesUsed() const override;
 
     // Methods and constants below here are only public for tests.
 
@@ -53,7 +53,7 @@
     int getCount() const { return fCount; }
 
     // Get the root bound.
-    SkRect getRootBound() const SK_OVERRIDE;
+    SkRect getRootBound() const override;
 
     // These values were empirically determined to produce reasonable performance in most cases.
     static const int kMinChildren = 6,
diff --git a/src/core/SkRWBuffer.cpp b/src/core/SkRWBuffer.cpp
new file mode 100644
index 0000000..33d82af
--- /dev/null
+++ b/src/core/SkRWBuffer.cpp
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkRWBuffer.h"
+#include "SkStream.h"
+
+// Force small chunks to be a page's worth
+static const size_t kMinAllocSize = 4096;
+
+struct SkBufferBlock {
+    SkBufferBlock*  fNext;
+    size_t          fUsed;
+    size_t          fCapacity;
+    
+    const void* startData() const { return this + 1; };
+    
+    size_t avail() const { return fCapacity - fUsed; }
+    void* availData() { return (char*)this->startData() + fUsed; }
+    
+    static SkBufferBlock* Alloc(size_t length) {
+        size_t capacity = LengthToCapacity(length);
+        SkBufferBlock* block = (SkBufferBlock*)sk_malloc_throw(sizeof(SkBufferBlock) + capacity);
+        block->fNext = NULL;
+        block->fUsed = 0;
+        block->fCapacity = capacity;
+        return block;
+    }
+
+    // Return number of bytes actually appended
+    size_t append(const void* src, size_t length) {
+        this->validate();
+        size_t amount = SkTMin(this->avail(), length);
+        memcpy(this->availData(), src, amount);
+        fUsed += amount;
+        this->validate();
+        return amount;
+    }
+
+    void validate() const {
+#ifdef SK_DEBUG
+        SkASSERT(fCapacity > 0);
+        SkASSERT(fUsed <= fCapacity);
+#endif
+    }
+
+private:
+    static size_t LengthToCapacity(size_t length) {
+        const size_t minSize = kMinAllocSize - sizeof(SkBufferBlock);
+        return SkTMax(length, minSize);
+    }
+};
+
+struct SkBufferHead {
+    mutable int32_t fRefCnt;
+    SkBufferBlock   fBlock;
+
+    static size_t LengthToCapacity(size_t length) {
+        const size_t minSize = kMinAllocSize - sizeof(SkBufferHead);
+        return SkTMax(length, minSize);
+    }
+
+    static SkBufferHead* Alloc(size_t length) {
+        size_t capacity = LengthToCapacity(length);
+        size_t size = sizeof(SkBufferHead) + capacity;
+        SkBufferHead* head = (SkBufferHead*)sk_malloc_throw(size);
+        head->fRefCnt = 1;
+        head->fBlock.fNext = NULL;
+        head->fBlock.fUsed = 0;
+        head->fBlock.fCapacity = capacity;
+        return head;
+    }
+    
+    void ref() const {
+        SkASSERT(fRefCnt > 0);
+        sk_atomic_inc(&fRefCnt);
+    }
+    
+    void unref() const {
+        SkASSERT(fRefCnt > 0);
+        // A release here acts in place of all releases we "should" have been doing in ref().
+        if (1 == sk_atomic_fetch_add(&fRefCnt, -1, sk_memory_order_acq_rel)) {
+            // Like unique(), the acquire is only needed on success.
+            SkBufferBlock* block = fBlock.fNext;
+            sk_free((void*)this);
+            while (block) {
+                SkBufferBlock* next = block->fNext;
+                sk_free(block);
+                block = next;
+            }
+        }
+    }
+    
+    void validate(size_t minUsed, SkBufferBlock* tail = NULL) const {
+#ifdef SK_DEBUG
+        SkASSERT(fRefCnt > 0);
+        size_t totalUsed = 0;
+        const SkBufferBlock* block = &fBlock;
+        const SkBufferBlock* lastBlock = block;
+        while (block) {
+            block->validate();
+            totalUsed += block->fUsed;
+            lastBlock = block;
+            block = block->fNext;
+        }
+        SkASSERT(minUsed <= totalUsed);
+        if (tail) {
+            SkASSERT(tail == lastBlock);
+        }
+#endif
+    }
+};
+
+SkROBuffer::SkROBuffer(const SkBufferHead* head, size_t used) : fHead(head), fUsed(used) {
+    if (head) {
+        fHead->ref();
+        SkASSERT(used > 0);
+        head->validate(used);
+    } else {
+        SkASSERT(0 == used);
+    }
+}
+
+SkROBuffer::~SkROBuffer() {
+    if (fHead) {
+        fHead->validate(fUsed);
+        fHead->unref();
+    }
+}
+
+SkROBuffer::Iter::Iter(const SkROBuffer* buffer) {
+    this->reset(buffer);
+}
+
+void SkROBuffer::Iter::reset(const SkROBuffer* buffer) {
+    if (buffer) {
+        fBlock = &buffer->fHead->fBlock;
+        fRemaining = buffer->fUsed;
+    } else {
+        fBlock = NULL;
+        fRemaining = 0;
+    }
+}
+
+const void* SkROBuffer::Iter::data() const {
+    return fRemaining ? fBlock->startData() : NULL;
+}
+
+size_t SkROBuffer::Iter::size() const {
+    return SkTMin(fBlock->fUsed, fRemaining);
+}
+
+bool SkROBuffer::Iter::next() {
+    if (fRemaining) {
+        fRemaining -= this->size();
+        fBlock = fBlock->fNext;
+    }
+    return fRemaining != 0;
+}
+
+SkRWBuffer::SkRWBuffer(size_t initialCapacity) : fHead(NULL), fTail(NULL), fTotalUsed(0) {}
+
+SkRWBuffer::~SkRWBuffer() {
+    this->validate();
+    fHead->unref();
+}
+
+void SkRWBuffer::append(const void* src, size_t length) {
+    this->validate();
+    if (0 == length) {
+        return;
+    }
+
+    fTotalUsed += length;
+
+    if (NULL == fHead) {
+        fHead = SkBufferHead::Alloc(length);
+        fTail = &fHead->fBlock;
+    }
+
+    size_t written = fTail->append(src, length);
+    SkASSERT(written <= length);
+    src = (const char*)src + written;
+    length -= written;
+
+    if (length) {
+        SkBufferBlock* block = SkBufferBlock::Alloc(length);
+        fTail->fNext = block;
+        fTail = block;
+        written = fTail->append(src, length);
+        SkASSERT(written == length);
+    }
+    this->validate();
+}
+
+void* SkRWBuffer::append(size_t length) {
+    this->validate();
+    if (0 == length) {
+        return NULL;
+    }
+
+    fTotalUsed += length;
+    
+    if (NULL == fHead) {
+        fHead = SkBufferHead::Alloc(length);
+        fTail = &fHead->fBlock;
+    } else if (fTail->avail() < length) {
+        SkBufferBlock* block = SkBufferBlock::Alloc(length);
+        fTail->fNext = block;
+        fTail = block;
+    }
+
+    fTail->fUsed += length;
+    this->validate();
+    return (char*)fTail->availData() - length;
+}
+
+#ifdef SK_DEBUG
+void SkRWBuffer::validate() const {
+    if (fHead) {
+        fHead->validate(fTotalUsed, fTail);
+    } else {
+        SkASSERT(NULL == fTail);
+        SkASSERT(0 == fTotalUsed);
+    }
+}
+#endif
+
+SkROBuffer* SkRWBuffer::newRBufferSnapshot() const {
+    return SkNEW_ARGS(SkROBuffer, (fHead, fTotalUsed));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkROBufferStreamAsset : public SkStreamAsset {
+    void validate() const {
+#ifdef SK_DEBUG
+        SkASSERT(fGlobalOffset <= fBuffer->size());
+        SkASSERT(fLocalOffset <= fIter.size());
+        SkASSERT(fLocalOffset <= fGlobalOffset);
+#endif
+    }
+
+#ifdef SK_DEBUG
+    class AutoValidate {
+        SkROBufferStreamAsset* fStream;
+    public:
+        AutoValidate(SkROBufferStreamAsset* stream) : fStream(stream) { stream->validate(); }
+        ~AutoValidate() { fStream->validate(); }
+    };
+    #define AUTO_VALIDATE   AutoValidate av(this);
+#else
+    #define AUTO_VALIDATE
+#endif
+
+public:
+    SkROBufferStreamAsset(const SkROBuffer* buffer) : fBuffer(SkRef(buffer)), fIter(buffer) {
+        fGlobalOffset = fLocalOffset = 0;
+    }
+
+    virtual ~SkROBufferStreamAsset() { fBuffer->unref(); }
+
+    size_t getLength() const override { return fBuffer->size(); }
+
+    bool rewind() override {
+        AUTO_VALIDATE
+        fIter.reset(fBuffer);
+        fGlobalOffset = fLocalOffset = 0;
+        return true;
+    }
+
+    size_t read(void* dst, size_t request) override {
+        AUTO_VALIDATE
+        size_t bytesRead = 0;
+        for (;;) {
+            size_t size = fIter.size();
+            SkASSERT(fLocalOffset <= size);
+            size_t avail = SkTMin(size - fLocalOffset, request - bytesRead);
+            if (dst) {
+                memcpy(dst, (const char*)fIter.data() + fLocalOffset, avail);
+                dst = (char*)dst + avail;
+            }
+            bytesRead += avail;
+            fLocalOffset += avail;
+            SkASSERT(bytesRead <= request);
+            if (bytesRead == request) {
+                break;
+            }
+            // If we get here, we've exhausted the current iter
+            SkASSERT(fLocalOffset == size);
+            fLocalOffset = 0;
+            if (!fIter.next()) {
+                break;   // ran out of data
+            }
+        }
+        fGlobalOffset += bytesRead;
+        SkASSERT(fGlobalOffset <= fBuffer->size());
+        return bytesRead;
+    }
+
+    bool isAtEnd() const override {
+        return fBuffer->size() == fGlobalOffset;
+    }
+    
+    SkStreamAsset* duplicate() const override {
+        return SkNEW_ARGS(SkROBufferStreamAsset, (fBuffer));
+    }
+    
+    size_t getPosition() const {
+        return fGlobalOffset;
+    }
+    
+    bool seek(size_t position) {
+        AUTO_VALIDATE
+        if (position < fGlobalOffset) {
+            this->rewind();
+        }
+        (void)this->skip(position - fGlobalOffset);
+        return true;
+    }
+    
+    bool move(long offset) {
+        AUTO_VALIDATE
+        offset += fGlobalOffset;
+        if (offset <= 0) {
+            this->rewind();
+        } else {
+            (void)this->seek(SkToSizeT(offset));
+        }
+        return true;
+    }
+    
+    SkStreamAsset* fork() const override {
+        SkStreamAsset* clone = this->duplicate();
+        clone->seek(this->getPosition());
+        return clone;
+    }
+    
+
+private:
+    const SkROBuffer*   fBuffer;
+    SkROBuffer::Iter    fIter;
+    size_t              fLocalOffset;
+    size_t              fGlobalOffset;
+};
+
+SkStreamAsset* SkRWBuffer::newStreamSnapshot() const {
+    SkAutoTUnref<SkROBuffer> buffer(this->newRBufferSnapshot());
+    return SkNEW_ARGS(SkROBufferStreamAsset, (buffer));
+}
diff --git a/src/core/SkRWBuffer.h b/src/core/SkRWBuffer.h
new file mode 100644
index 0000000..89cb425
--- /dev/null
+++ b/src/core/SkRWBuffer.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRWBuffer_DEFINED
+#define SkRWBuffer_DEFINED
+
+#include "SkRefCnt.h"
+
+struct SkBufferBlock;
+struct SkBufferHead;
+class SkRWBuffer;
+class SkStreamAsset;
+
+/**
+ *  Contains a read-only, thread-sharable block of memory. To access the memory, the caller must
+ *  instantiate a local iterator, as the memory is stored in 1 or more contiguous blocks.
+ */
+class SkROBuffer : public SkRefCnt {
+public:
+    /**
+     *  Return the logical length of the data owned/shared by this buffer. It may be stored in
+     *  multiple contiguous blocks, accessible via the iterator.
+     */
+    size_t size() const { return fUsed; }
+    
+    class Iter {
+    public:
+        Iter(const SkROBuffer*);
+
+        void reset(const SkROBuffer*);
+
+        /**
+         *  Return the current continuous block of memory, or NULL if the iterator is exhausted
+         */
+        const void* data() const;
+
+        /**
+         *  Returns the number of bytes in the current continguous block of memory, or 0 if the
+         *  iterator is exhausted.
+         */
+        size_t size() const;
+
+        /**
+         *  Advance to the next contiguous block of memory, returning true if there is another
+         *  block, or false if the iterator is exhausted.
+         */
+        bool next();
+        
+    private:
+        const SkBufferBlock* fBlock;
+        size_t               fRemaining;
+    };
+
+private:
+    SkROBuffer(const SkBufferHead* head, size_t used);
+    virtual ~SkROBuffer();
+    
+    const SkBufferHead* fHead;
+    const size_t        fUsed;
+    
+    friend class SkRWBuffer;
+};
+
+/**
+ *  Accumulates bytes of memory that are "appended" to it, growing internal storage as needed.
+ *  The growth is done such that at any time, a RBuffer or StreamAsset can be snapped off, which
+ *  can see the previously stored bytes, but which will be unaware of any future writes.
+ */
+class SkRWBuffer {
+public:
+    SkRWBuffer(size_t initialCapacity = 0);
+    ~SkRWBuffer();
+    
+    size_t size() const { return fTotalUsed; }
+    void append(const void* buffer, size_t length);
+    void* append(size_t length);
+
+    SkROBuffer* newRBufferSnapshot() const;
+    SkStreamAsset* newStreamSnapshot() const;
+    
+#ifdef SK_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+    
+private:
+    SkBufferHead*   fHead;
+    SkBufferBlock*  fTail;
+    size_t          fTotalUsed;
+};
+
+#endif
diff --git a/src/core/SkRasterizer.cpp b/src/core/SkRasterizer.cpp
index 3a7af95..ab9e011 100644
--- a/src/core/SkRasterizer.cpp
+++ b/src/core/SkRasterizer.cpp
@@ -27,8 +27,7 @@
         if (!filter->filterMask(&dstM, srcM, matrix, &margin)) {
             return false;
         }
-        storage = *clipBounds;
-        storage.inset(-margin.fX, -margin.fY);
+        storage = clipBounds->makeOutset(margin.fX, margin.fY);
         clipBounds = &storage;
     }
 
diff --git a/src/core/SkReadBuffer.h b/src/core/SkReadBuffer.h
index a24ca55..1299eda 100644
--- a/src/core/SkReadBuffer.h
+++ b/src/core/SkReadBuffer.h
@@ -55,6 +55,8 @@
         kDropShadowMode_Version            = 37,
         kPictureImageFilterResolution_Version = 38,
         kPictureImageFilterLevel_Version   = 39,
+        kImageFilterNoUniqueID_Version     = 40,
+        kBitmapSourceFilterQuality_Version = 41
     };
 
     /**
diff --git a/src/core/SkRecord.cpp b/src/core/SkRecord.cpp
index e2d919b..349fbf6 100644
--- a/src/core/SkRecord.cpp
+++ b/src/core/SkRecord.cpp
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "SkRecord.h"
 
 SkRecord::~SkRecord() {
@@ -9,13 +16,17 @@
 
 void SkRecord::grow() {
     SkASSERT(fCount == fReserved);
-    fReserved = SkTMax<unsigned>(kFirstReserveCount, fReserved*2);
+    SkASSERT(fReserved > 0);
+    fReserved *= 2;
     fRecords.realloc(fReserved);
-    fTypes.realloc(fReserved);
 }
 
 size_t SkRecord::bytesUsed() const {
-    return fAlloc.approxBytesAllocated() +
-           fReserved * (sizeof(Record) + sizeof(Type8)) +
-           sizeof(SkRecord);
+    size_t bytes = fAlloc.approxBytesAllocated() + sizeof(SkRecord);
+    // If fReserved <= kInlineRecords, we've already accounted for fRecords with sizeof(SkRecord).
+    // When we go over that limit, they're allocated on the heap (and the inline space is wasted).
+    if (fReserved > kInlineRecords) {
+        bytes += fReserved * sizeof(Record);
+    }
+    return bytes;
 }
diff --git a/src/core/SkRecord.h b/src/core/SkRecord.h
index fb08294..0fe316e 100644
--- a/src/core/SkRecord.h
+++ b/src/core/SkRecord.h
@@ -13,7 +13,7 @@
 #include "SkTemplates.h"
 #include "SkVarAlloc.h"
 
-// SkRecord (REC-ord) represents a sequence of SkCanvas calls, saved for future use.
+// SkRecord represents a sequence of SkCanvas calls, saved for future use.
 // These future uses may include: replay, optimization, serialization, or combinations of those.
 //
 // Though an enterprising user may find calling alloc(), append(), visit(), and mutate() enough to
@@ -27,10 +27,16 @@
 
 class SkRecord : public SkNVRefCnt<SkRecord> {
     enum {
-        kFirstReserveCount = 64 / sizeof(void*),
+        // TODO: tune these two constants.
+        kInlineRecords      = 4, // Ideally our lower limit on recorded ops per picture.
+        kInlineAllocLgBytes = 8, // 1<<8 == 256 bytes inline, then SkVarAlloc starting at 512 bytes.
     };
 public:
-    SkRecord() : fCount(0), fReserved(0), fAlloc(8/*start block sizes at 256 bytes*/) {}
+    SkRecord()
+        : fCount(0)
+        , fReserved(kInlineRecords)
+        , fAlloc(kInlineAllocLgBytes+1,  // First malloc'd block is 2x as large as fInlineAlloc.
+                 fInlineAlloc, sizeof(fInlineAlloc)) {}
     ~SkRecord();
 
     // Returns the number of canvas commands in this SkRecord.
@@ -43,7 +49,7 @@
     template <typename R, typename F>
     R visit(unsigned i, F& f) const {
         SkASSERT(i < this->count());
-        return fRecords[i].visit<R>(fTypes[i], f);
+        return fRecords[i].visit<R>(f);
     }
 
     // Mutate the i-th canvas command with a functor matching this interface:
@@ -53,15 +59,15 @@
     template <typename R, typename F>
     R mutate(unsigned i, F& f) {
         SkASSERT(i < this->count());
-        return fRecords[i].mutate<R>(fTypes[i], f);
+        return fRecords[i].mutate<R>(f);
     }
-    // TODO: It'd be nice to infer R from F for visit and mutate if we ever get std::result_of.
+
+    // TODO: It'd be nice to infer R from F for visit and mutate.
 
     // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed.
     // Here T can be any class, not just those from SkRecords.  Throws on failure.
     template <typename T>
     T* alloc(size_t count = 1) {
-        // Bump up to the next pointer width if needed, so all allocations start pointer-aligned.
         return (T*)fAlloc.alloc(sizeof(T) * count, SK_MALLOC_THROW);
     }
 
@@ -72,7 +78,6 @@
         if (fCount == fReserved) {
             this->grow();
         }
-        fTypes[fCount] = T::kType;
         return fRecords[fCount++].set(this->allocCommand<T>());
     }
 
@@ -86,7 +91,6 @@
         Destroyer destroyer;
         this->mutate<void>(i, destroyer);
 
-        fTypes[i] = T::kType;
         return fRecords[i].set(this->allocCommand<T>());
     }
 
@@ -97,10 +101,9 @@
     T* replace(unsigned i, const SkRecords::Adopted<Existing>& proofOfAdoption) {
         SkASSERT(i < this->count());
 
-        SkASSERT(Existing::kType == fTypes[i]);
-        SkASSERT(proofOfAdoption == fRecords[i].ptr<Existing>());
+        SkASSERT(Existing::kType == fRecords[i].type());
+        SkASSERT(proofOfAdoption == fRecords[i].ptr());
 
-        fTypes[i] = T::kType;
         return fRecords[i].set(this->allocCommand<T>());
     }
 
@@ -109,9 +112,7 @@
     size_t bytesUsed() const;
 
 private:
-    // Implementation notes!
-    //
-    // Logically an SkRecord is structured as an array of pointers into a big chunk of memory where
+    // An SkRecord is structured as an array of pointers into a big chunk of memory where
     // records representing each canvas draw call are stored:
     //
     // fRecords:  [*][*][*]...
@@ -123,27 +124,8 @@
     //             v                    v                        v
     //   fAlloc:  [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::DrawRect]...
     //
-    // In the scheme above, the pointers in fRecords are void*: they have no type.  The type is not
-    // stored in fAlloc either; we just write raw data there.  But we need that type information.
-    // Here are some options:
-    //   1) use inheritance, virtuals, and vtables to make the fRecords pointers smarter
-    //   2) store the type data manually in fAlloc at the start of each record
-    //   3) store the type data manually somewhere with fRecords
-    //
-    // This code uses approach 3).  The implementation feels very similar to 1), but it's
-    // devirtualized instead of using the language's polymorphism mechanisms.  This lets us work
-    // with the types themselves (as SkRecords::Type), a sort of limited free RTTI; it lets us pay
-    // only 1 byte to store the type instead of a full pointer (4-8 bytes); and it leads to better
-    // decoupling between the SkRecords::* record types and the operations performed on them in
-    // visit() or mutate().  The recorded canvas calls don't have to have any idea about the
-    // operations performed on them.
-    //
-    // We store the types in a parallel fTypes array, mainly so that they can be tightly packed as
-    // single bytes.  This has the side effect of allowing very fast analysis passes over an
-    // SkRecord looking for just patterns of draw commands (or using this as a quick reject
-    // mechanism) though there's admittedly not a very good API exposed publically for this.
-    //
-    // The cost to append a T into this structure is 1 + sizeof(void*) + sizeof(T).
+    // We store the types of each of the pointers alongside the pointer.
+    // The cost to append a T to this structure is 8 + sizeof(T) bytes.
 
     // A mutator that can be used with replace to destroy canvas commands.
     struct Destroyer {
@@ -151,19 +133,6 @@
         void operator()(T* record) { record->~T(); }
     };
 
-    // Logically the same as SkRecords::Type, but packed into 8 bits.
-    struct Type8 {
-    public:
-        // This intentionally converts implicitly back and forth.
-        Type8(SkRecords::Type type) : fType(type) { SkASSERT(*this == type); }
-        operator SkRecords::Type () { return (SkRecords::Type)fType; }
-
-    private:
-        uint8_t fType;
-    };
-
-    // No point in allocating any more than one of an empty struct.
-    // We could just return NULL but it's sort of confusing to return NULL on success.
     template <typename T>
     SK_WHEN(SkTIsEmpty<T>, T*) allocCommand() {
         static T singleton = {};
@@ -173,65 +142,57 @@
     template <typename T>
     SK_WHEN(!SkTIsEmpty<T>, T*) allocCommand() { return this->alloc<T>(); }
 
-    // Called when we've run out of room to record new commands.
     void grow();
 
-    // An untyped pointer to some bytes in fAlloc.  This is the interface for polymorphic dispatch:
-    // visit() and mutate() work with the parallel fTypes array to do the work of a vtable.
+    // A typed pointer to some bytes in fAlloc.  visit() and mutate() allow polymorphic dispatch.
     struct Record {
-    public:
+        // On 32-bit machines we store type in 4 bytes, followed by a pointer.  Simple.
+        // On 64-bit machines we store a pointer with the type slotted into two top (unused) bytes.
+        // FWIW, SkRecords::Type is tiny.  It can easily fit in one byte.
+        uint64_t fTypeAndPtr;
+        static const int kTypeShift = sizeof(void*) == 4 ? 32 : 48;
+
         // Point this record to its data in fAlloc.  Returns ptr for convenience.
         template <typename T>
         T* set(T* ptr) {
-            fPtr = ptr;
+            fTypeAndPtr = ((uint64_t)T::kType) << kTypeShift | (uintptr_t)ptr;
+            SkASSERT(this->ptr() == ptr && this->type() == T::kType);
             return ptr;
         }
 
-        // Get the data in fAlloc, assuming it's of type T.
-        template <typename T>
-        T* ptr() const { return (T*)fPtr; }
+        SkRecords::Type type() const { return (SkRecords::Type)(fTypeAndPtr >> kTypeShift); }
+        void* ptr() const { return (void*)(fTypeAndPtr & ((1ull<<kTypeShift)-1)); }
 
-        // Visit this record with functor F (see public API above) assuming the record we're
-        // pointing to has this type.
+        // Visit this record with functor F (see public API above).
         template <typename R, typename F>
-        R visit(Type8 type, F& f) const {
-        #define CASE(T) case SkRecords::T##_Type: return f(*this->ptr<SkRecords::T>());
-            switch(type) { SK_RECORD_TYPES(CASE) }
+        R visit(F& f) const {
+        #define CASE(T) case SkRecords::T##_Type: return f(*(const SkRecords::T*)this->ptr());
+            switch(this->type()) { SK_RECORD_TYPES(CASE) }
         #undef CASE
             SkDEBUGFAIL("Unreachable");
             return R();
         }
 
-        // Mutate this record with functor F (see public API above) assuming the record we're
-        // pointing to has this type.
+        // Mutate this record with functor F (see public API above).
         template <typename R, typename F>
-        R mutate(Type8 type, F& f) {
-        #define CASE(T) case SkRecords::T##_Type: return f(this->ptr<SkRecords::T>());
-            switch(type) { SK_RECORD_TYPES(CASE) }
+        R mutate(F& f) {
+        #define CASE(T) case SkRecords::T##_Type: return f((SkRecords::T*)this->ptr());
+            switch(this->type()) { SK_RECORD_TYPES(CASE) }
         #undef CASE
             SkDEBUGFAIL("Unreachable");
             return R();
         }
-
-    private:
-        void* fPtr;
     };
 
+    // fRecords needs to be a data structure that can append fixed length data, and need to
+    // support efficient random access and forward iteration.  (It doesn't need to be contiguous.)
+    unsigned fCount, fReserved;
+    SkAutoSTMalloc<kInlineRecords, Record> fRecords;
+
     // fAlloc needs to be a data structure which can append variable length data in contiguous
     // chunks, returning a stable handle to that data for later retrieval.
-    //
-    // fRecords and fTypes need to be data structures that can append fixed length data, and need to
-    // support efficient random access and forward iteration.  (They don't need to be contiguous.)
-
-    // fCount and fReserved measure both fRecords and fTypes, which always grow in lock step.
-    unsigned fCount;
-    unsigned fReserved;
-    SkAutoTMalloc<Record> fRecords;
-    SkAutoTMalloc<Type8> fTypes;
     SkVarAlloc fAlloc;
-    // Strangely the order of these fields matters.  If the unsigneds don't go first we're 56 bytes.
-    // tomhudson and mtklein have no idea why.
+    char fInlineAlloc[1 << kInlineAllocLgBytes];
 };
-SK_COMPILE_ASSERT(sizeof(SkRecord) <= 56, SkRecordSize);
 
 #endif//SkRecord_DEFINED
diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp
index aafb540..0a2d43e 100644
--- a/src/core/SkRecorder.cpp
+++ b/src/core/SkRecorder.cpp
@@ -5,9 +5,10 @@
  * found in the LICENSE file.
  */
 
-#include "SkRecorder.h"
 #include "SkPatchUtils.h"
 #include "SkPicture.h"
+#include "SkPictureUtils.h"
+#include "SkRecorder.h"
 
 SkDrawableList::~SkDrawableList() {
     fArray.unrefAll();
@@ -33,14 +34,23 @@
 
 SkRecorder::SkRecorder(SkRecord* record, int width, int height)
     : SkCanvas(SkIRect::MakeWH(width, height), SkCanvas::kConservativeRasterClip_InitFlag)
+    , fApproxBytesUsedBySubPictures(0)
     , fRecord(record) {}
 
 SkRecorder::SkRecorder(SkRecord* record, const SkRect& bounds)
     : SkCanvas(bounds.roundOut(), SkCanvas::kConservativeRasterClip_InitFlag)
+    , fApproxBytesUsedBySubPictures(0)
     , fRecord(record) {}
 
+void SkRecorder::reset(SkRecord* record, const SkRect& bounds) {
+    this->forgetRecord();
+    fRecord = record;
+    this->resetForNextPicture(bounds.roundOut());
+}
+
 void SkRecorder::forgetRecord() {
     fDrawableList.reset(NULL);
+    fApproxBytesUsedBySubPictures = 0;
     fRecord = NULL;
 }
 
@@ -242,6 +252,7 @@
 }
 
 void SkRecorder::onDrawPicture(const SkPicture* pic, const SkMatrix* matrix, const SkPaint* paint) {
+    fApproxBytesUsedBySubPictures += SkPictureUtils::ApproximateBytesUsed(pic);
     APPEND(DrawPicture, this->copy(paint), pic, matrix ? *matrix : SkMatrix::I());
 }
 
diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h
index 130dce9..d0a992f 100644
--- a/src/core/SkRecorder.h
+++ b/src/core/SkRecorder.h
@@ -39,82 +39,86 @@
     SkRecorder(SkRecord*, int width, int height);   // legacy version
     SkRecorder(SkRecord*, const SkRect& bounds);
 
+    void reset(SkRecord*, const SkRect& bounds);
+
+    size_t approxBytesUsedBySubPictures() const { return fApproxBytesUsedBySubPictures; }
+
     SkDrawableList* getDrawableList() const { return fDrawableList.get(); }
     SkDrawableList* detachDrawableList() { return fDrawableList.detach(); }
 
     // Make SkRecorder forget entirely about its SkRecord*; all calls to SkRecorder will fail.
     void forgetRecord();
 
-    void willSave() SK_OVERRIDE;
-    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SkCanvas::SaveFlags) SK_OVERRIDE;
-    void willRestore() SK_OVERRIDE {}
-    void didRestore() SK_OVERRIDE;
+    void willSave() override;
+    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SkCanvas::SaveFlags) override;
+    void willRestore() override {}
+    void didRestore() override;
 
-    void didConcat(const SkMatrix&) SK_OVERRIDE;
-    void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
+    void didConcat(const SkMatrix&) override;
+    void didSetMatrix(const SkMatrix&) override;
 
-    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawDrawable(SkDrawable*) SK_OVERRIDE;
+    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
+    void onDrawDrawable(SkDrawable*) override;
     void onDrawText(const void* text,
                     size_t byteLength,
                     SkScalar x,
                     SkScalar y,
-                    const SkPaint& paint) SK_OVERRIDE;
+                    const SkPaint& paint) override;
     void onDrawPosText(const void* text,
                        size_t byteLength,
                        const SkPoint pos[],
-                       const SkPaint& paint) SK_OVERRIDE;
+                       const SkPaint& paint) override;
     void onDrawPosTextH(const void* text,
                         size_t byteLength,
                         const SkScalar xpos[],
                         SkScalar constY,
-                        const SkPaint& paint) SK_OVERRIDE;
+                        const SkPaint& paint) override;
     void onDrawTextOnPath(const void* text,
                           size_t byteLength,
                           const SkPath& path,
                           const SkMatrix* matrix,
-                          const SkPaint& paint) SK_OVERRIDE;
+                          const SkPaint& paint) override;
     void onDrawTextBlob(const SkTextBlob* blob,
                         SkScalar x,
                         SkScalar y,
-                        const SkPaint& paint) SK_OVERRIDE;
+                        const SkPaint& paint) override;
     void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
                      const SkPoint texCoords[4], SkXfermode* xmode,
-                     const SkPaint& paint) SK_OVERRIDE;
+                     const SkPaint& paint) override;
 
-    void onDrawPaint(const SkPaint&) SK_OVERRIDE;
-    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) SK_OVERRIDE;
-    void onDrawRect(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawOval(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawRRect(const SkRRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawPath(const SkPath&, const SkPaint&) SK_OVERRIDE;
-    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+    void onDrawPaint(const SkPaint&) override;
+    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
+    void onDrawRect(const SkRect&, const SkPaint&) override;
+    void onDrawOval(const SkRect&, const SkPaint&) override;
+    void onDrawRRect(const SkRRect&, const SkPaint&) override;
+    void onDrawPath(const SkPath&, const SkPaint&) override;
+    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
-                          DrawBitmapRectFlags flags) SK_OVERRIDE;
-    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+                          DrawBitmapRectFlags flags) override;
+    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
-                         const SkPaint*) SK_OVERRIDE;
+                         const SkPaint*) override;
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
-                          const SkPaint*) SK_OVERRIDE;
-    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) SK_OVERRIDE;
+                          const SkPaint*) override;
+    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
     void onDrawVertices(VertexMode vmode, int vertexCount,
                         const SkPoint vertices[], const SkPoint texs[],
                         const SkColor colors[], SkXfermode* xmode,
                         const uint16_t indices[], int indexCount,
-                        const SkPaint&) SK_OVERRIDE;
+                        const SkPaint&) override;
 
-    void onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) SK_OVERRIDE;
-    void onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) SK_OVERRIDE;
-    void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) SK_OVERRIDE;
-    void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE;
+    void onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) override;
+    void onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) override;
+    void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) override;
+    void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override;
 
-    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) SK_OVERRIDE;
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
 
-    void beginCommentGroup(const char*) SK_OVERRIDE;
-    void addComment(const char*, const char*) SK_OVERRIDE;
-    void endCommentGroup() SK_OVERRIDE;
+    void beginCommentGroup(const char*) override;
+    void addComment(const char*, const char*) override;
+    void endCommentGroup() override;
 
-    SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) SK_OVERRIDE { return NULL; }
+    SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override { return NULL; }
 
 private:
     template <typename T>
@@ -129,8 +133,8 @@
         return devBounds;
     }
 
+    size_t fApproxBytesUsedBySubPictures;
     SkRecord* fRecord;
-
     SkAutoTDelete<SkDrawableList> fDrawableList;
 };
 
diff --git a/src/core/SkRect.cpp b/src/core/SkRect.cpp
index ecf6fa2..a28af27 100644
--- a/src/core/SkRect.cpp
+++ b/src/core/SkRect.cpp
@@ -45,6 +45,13 @@
     quad[3].set(fLeft, fBottom);
 }
 
+#include "SkNx.h"
+
+static inline bool is_finite(const Sk4s& value) {
+    auto finite = value * Sk4s(0) == Sk4s(0);
+    return finite.allTrue();
+}
+
 bool SkRect::setBoundsCheck(const SkPoint pts[], int count) {
     SkASSERT((pts && count > 0) || count == 0);
 
@@ -53,39 +60,46 @@
     if (count <= 0) {
         sk_bzero(this, sizeof(SkRect));
     } else {
-        SkScalar    l, t, r, b;
+        Sk4s min, max, accum;
 
-        l = r = pts[0].fX;
-        t = b = pts[0].fY;
+        if (count & 1) {
+            min = Sk4s(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
+            pts += 1;
+            count -= 1;
+        } else {
+            min = Sk4s::Load(&pts[0].fX);
+            pts += 2;
+            count -= 2;
+        }
+        accum = max = min;
+        accum *= Sk4s(0);
 
-        // If all of the points are finite, accum should stay 0. If we encounter
-        // a NaN or infinity, then accum should become NaN.
-        float accum = 0;
-        accum *= l; accum *= t;
-
-        for (int i = 1; i < count; i++) {
-            SkScalar x = pts[i].fX;
-            SkScalar y = pts[i].fY;
-
-            accum *= x; accum *= y;
-
-            // we use if instead of if/else, so we can generate min/max
-            // float instructions (at least on SSE)
-            if (x < l) l = x;
-            if (x > r) r = x;
-
-            if (y < t) t = y;
-            if (y > b) b = y;
+        count >>= 1;
+        for (int i = 0; i < count; ++i) {
+            Sk4s xy = Sk4s::Load(&pts->fX);
+            accum *= xy;
+            min = Sk4s::Min(min, xy);
+            max = Sk4s::Max(max, xy);
+            pts += 2;
         }
 
-        SkASSERT(!accum || !SkScalarIsFinite(accum));
-        if (accum) {
-            l = t = r = b = 0;
+        /**
+         *  With some trickery, we may be able to use Min/Max to also propogate non-finites,
+         *  in which case we could eliminate accum entirely, and just check min and max for
+         *  "is_finite".
+         */
+        if (is_finite(accum)) {
+            float minArray[4], maxArray[4];
+            min.store(minArray);
+            max.store(maxArray);
+            this->set(SkTMin(minArray[0], minArray[2]), SkTMin(minArray[1], minArray[3]),
+                      SkTMax(maxArray[0], maxArray[2]), SkTMax(maxArray[1], maxArray[3]));
+        } else {
+            // we hit a non-finite value, so zero everything and return false
+            this->setEmpty();
             isFinite = false;
         }
-        this->set(l, t, r, b);
     }
-
     return isFinite;
 }
 
diff --git a/src/core/SkRegion_path.cpp b/src/core/SkRegion_path.cpp
index e9224d8..ad01cac 100644
--- a/src/core/SkRegion_path.cpp
+++ b/src/core/SkRegion_path.cpp
@@ -43,7 +43,7 @@
     void    copyToRect(SkIRect*) const;
     void    copyToRgn(SkRegion::RunType runs[]) const;
 
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
 
 #ifdef SK_DEBUG
     void dump() const {
@@ -268,7 +268,7 @@
     return gPathVerbToMaxEdges[verb];
 }
 
-
+// If returns 0, ignore itop and ibot
 static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
     SkPath::Iter    iter(path, true);
     SkPoint         pts[4];
@@ -298,13 +298,24 @@
             }
         }
     }
-    SkASSERT(top <= bot);
+    if (0 == maxEdges) {
+        return 0;   // we have only moves+closes
+    }
 
+    SkASSERT(top <= bot);
     *itop = SkScalarRoundToInt(top);
     *ibot = SkScalarRoundToInt(bot);
     return maxEdges;
 }
 
+static bool check_inverse_on_empty_return(SkRegion* dst, const SkPath& path, const SkRegion& clip) {
+    if (path.isInverseFillType()) {
+        return dst->set(clip);
+    } else {
+        return dst->setEmpty();
+    }
+}
+
 bool SkRegion::setPath(const SkPath& path, const SkRegion& clip) {
     SkDEBUGCODE(this->validate();)
 
@@ -313,26 +324,24 @@
     }
 
     if (path.isEmpty()) {
-        if (path.isInverseFillType()) {
-            return this->set(clip);
-        } else {
-            return this->setEmpty();
-        }
+        return check_inverse_on_empty_return(this, path, clip);
     }
 
     //  compute worst-case rgn-size for the path
     int pathTop, pathBot;
     int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot);
-    int clipTop, clipBot;
-    int clipTransitions;
+    if (0 == pathTransitions) {
+        return check_inverse_on_empty_return(this, path, clip);
+    }
 
-    clipTransitions = clip.count_runtype_values(&clipTop, &clipBot);
+    int clipTop, clipBot;
+    int clipTransitions = clip.count_runtype_values(&clipTop, &clipBot);
 
     int top = SkMax32(pathTop, clipTop);
     int bot = SkMin32(pathBot, clipBot);
-
-    if (top >= bot)
-        return this->setEmpty();
+    if (top >= bot) {
+        return check_inverse_on_empty_return(this, path, clip);
+    }
 
     SkRgnBuilder builder;
 
diff --git a/src/core/SkResourceCache.cpp b/src/core/SkResourceCache.cpp
index 43e752b..0fdb54b 100644
--- a/src/core/SkResourceCache.cpp
+++ b/src/core/SkResourceCache.cpp
@@ -80,9 +80,9 @@
     ~SkOneShotDiscardablePixelRef();
 
 protected:
-    bool onNewLockPixels(LockRec*) SK_OVERRIDE;
-    void onUnlockPixels() SK_OVERRIDE;
-    size_t getAllocatedSizeInBytes() const SK_OVERRIDE;
+    bool onNewLockPixels(LockRec*) override;
+    void onUnlockPixels() override;
+    size_t getAllocatedSizeInBytes() const override;
 
 private:
     SkDiscardableMemory* fDM;
@@ -150,7 +150,7 @@
         fFactory = factory;
     }
 
-    bool allocPixelRef(SkBitmap*, SkColorTable*) SK_OVERRIDE;
+    bool allocPixelRef(SkBitmap*, SkColorTable*) override;
 
 private:
     SkResourceCache::DiscardableFactory fFactory;
diff --git a/src/core/SkScalar.cpp b/src/core/SkScalar.cpp
index c48d389..e8f6e93 100644
--- a/src/core/SkScalar.cpp
+++ b/src/core/SkScalar.cpp
@@ -31,6 +31,6 @@
     // Otherwise, interpolate between right - 1 and right.
     SkScalar rightKey = keys[right];
     SkScalar leftKey = keys[right-1];
-    SkScalar fract = SkScalarDiv(searchKey-leftKey,rightKey-leftKey);
+    SkScalar fract = (searchKey - leftKey) / (rightKey - leftKey);
     return SkScalarInterp(values[right-1], values[right], fract);
 }
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
index e3b5d80..7747cae 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -843,21 +843,21 @@
         : SkScalerContext(face, desc) {}
 
 protected:
-    unsigned generateGlyphCount() SK_OVERRIDE {
+    unsigned generateGlyphCount() override {
         return 0;
     }
-    uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE {
+    uint16_t generateCharToGlyph(SkUnichar uni) override {
         return 0;
     }
-    void generateAdvance(SkGlyph* glyph) SK_OVERRIDE {
+    void generateAdvance(SkGlyph* glyph) override {
         glyph->zeroMetrics();
     }
-    void generateMetrics(SkGlyph* glyph) SK_OVERRIDE {
+    void generateMetrics(SkGlyph* glyph) override {
         glyph->zeroMetrics();
     }
-    void generateImage(const SkGlyph& glyph) SK_OVERRIDE {}
-    void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE {}
-    void generateFontMetrics(SkPaint::FontMetrics* metrics) SK_OVERRIDE {
+    void generateImage(const SkGlyph& glyph) override {}
+    void generatePath(const SkGlyph& glyph, SkPath* path) override {}
+    void generateFontMetrics(SkPaint::FontMetrics* metrics) override {
         if (metrics) {
             sk_bzero(metrics, sizeof(*metrics));
         }
diff --git a/src/core/SkScan.h b/src/core/SkScan.h
index 46a8107..ef44873 100644
--- a/src/core/SkScan.h
+++ b/src/core/SkScan.h
@@ -24,6 +24,15 @@
 
 class SkScan {
 public:
+    /*
+     *  Draws count-1 line segments, one at a time:
+     *      line(pts[0], pts[1])
+     *      line(pts[1], pts[2])
+     *      line(......, pts[count - 1])
+     */
+    typedef void (*HairRgnProc)(const SkPoint[], int count, const SkRegion*, SkBlitter*);
+    typedef void (*HairRCProc)(const SkPoint[], int count, const SkRasterClip&, SkBlitter*);
+
     static void FillPath(const SkPath&, const SkIRect&, SkBlitter*);
 
     ///////////////////////////////////////////////////////////////////////////
@@ -41,10 +50,8 @@
     static void AntiFrameRect(const SkRect&, const SkPoint& strokeSize,
                               const SkRasterClip&, SkBlitter*);
     static void FillTriangle(const SkPoint pts[], const SkRasterClip&, SkBlitter*);
-    static void HairLine(const SkPoint&, const SkPoint&, const SkRasterClip&,
-                         SkBlitter*);
-    static void AntiHairLine(const SkPoint&, const SkPoint&, const SkRasterClip&,
-                             SkBlitter*);
+    static void HairLine(const SkPoint[], int count, const SkRasterClip&, SkBlitter*);
+    static void AntiHairLine(const SkPoint[], int count, const SkRasterClip&, SkBlitter*);
     static void HairRect(const SkRect&, const SkRasterClip&, SkBlitter*);
     static void AntiHairRect(const SkRect&, const SkRasterClip&, SkBlitter*);
     static void HairPath(const SkPath&, const SkRasterClip&, SkBlitter*);
@@ -66,10 +73,8 @@
 
     static void AntiFrameRect(const SkRect&, const SkPoint& strokeSize,
                               const SkRegion*, SkBlitter*);
-    static void HairLineRgn(const SkPoint&, const SkPoint&, const SkRegion*,
-                         SkBlitter*);
-    static void AntiHairLineRgn(const SkPoint&, const SkPoint&, const SkRegion*,
-                             SkBlitter*);
+    static void HairLineRgn(const SkPoint[], int count, const SkRegion*, SkBlitter*);
+    static void AntiHairLineRgn(const SkPoint[], int count, const SkRegion*, SkBlitter*);
 };
 
 /** Assign an SkXRect from a SkIRect, by promoting the src rect's coordinates
diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
index ed11f64..3f95a68 100644
--- a/src/core/SkScan_AntiPath.cpp
+++ b/src/core/SkScan_AntiPath.cpp
@@ -44,11 +44,11 @@
 
     /// Must be explicitly defined on subclasses.
     virtual void blitAntiH(int x, int y, const SkAlpha antialias[],
-                           const int16_t runs[]) SK_OVERRIDE {
+                           const int16_t runs[]) override {
         SkDEBUGFAIL("How did I get here?");
     }
     /// May not be called on BaseSuperBlitter because it blits out of order.
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
+    void blitV(int x, int y, int height, SkAlpha alpha) override {
         SkDEBUGFAIL("How did I get here?");
     }
 
@@ -115,10 +115,10 @@
 
     /// Blits a row of pixels, with location and width specified
     /// in supersampled coordinates.
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
     /// Blits a rectangle of pixels, with location and size specified
     /// in supersampled coordinates.
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+    void blitRect(int x, int y, int width, int height) override;
 
 private:
     // The next three variables are used to track a circular buffer that
@@ -400,7 +400,7 @@
         fRealBlitter->blitMask(fMask, fClipRect);
     }
 
-    void blitH(int x, int y, int width) SK_OVERRIDE;
+    void blitH(int x, int y, int width) override;
 
     static bool CanHandleRect(const SkIRect& bounds) {
 #ifdef FORCE_RLE
diff --git a/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp
index d034151..546ced0 100644
--- a/src/core/SkScan_Antihair.cpp
+++ b/src/core/SkScan_Antihair.cpp
@@ -106,7 +106,7 @@
 
 class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
 public:
-    SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) SK_OVERRIDE {
+    SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) override {
         fy += SK_Fixed1/2;
 
         int y = fy >> 16;
@@ -128,7 +128,7 @@
     }
 
     virtual SkFixed drawLine(int x, int stopx, SkFixed fy,
-                             SkFixed slope) SK_OVERRIDE {
+                             SkFixed slope) override {
         SkASSERT(x < stopx);
         int count = stopx - x;
         fy += SK_Fixed1/2;
@@ -153,78 +153,37 @@
 
 class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
 public:
-    SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) SK_OVERRIDE {
-        int16_t runs[2];
-        uint8_t  aa[1];
-
-        runs[0] = 1;
-        runs[1] = 0;
-
+    SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) override {
         fy += SK_Fixed1/2;
-        SkBlitter* blitter = this->getBlitter();
-
+        
         int lower_y = fy >> 16;
         uint8_t  a = (uint8_t)(fy >> 8);
-        unsigned ma = SmallDot6Scale(a, mod64);
-        if (ma) {
-            aa[0] = ApplyGamma(gamma, ma);
-            blitter->blitAntiH(x, lower_y, aa, runs);
-            // the clipping blitters might edit runs, but should not affect us
-            SkASSERT(runs[0] == 1);
-            SkASSERT(runs[1] == 0);
-        }
-        ma = SmallDot6Scale(255 - a, mod64);
-        if (ma) {
-            aa[0] = ApplyGamma(gamma, ma);
-            blitter->blitAntiH(x, lower_y - 1, aa, runs);
-            // the clipping blitters might edit runs, but should not affect us
-            SkASSERT(runs[0] == 1);
-            SkASSERT(runs[1] == 0);
-        }
-        fy += dy;
-
-        return fy - SK_Fixed1/2;
+        unsigned a0 = SmallDot6Scale(255 - a, mod64);
+        unsigned a1 = SmallDot6Scale(a, mod64);
+        this->getBlitter()->blitAntiV2(x, lower_y - 1, a0, a1);
+        
+        return fy + dy - SK_Fixed1/2;
     }
-
-    SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) SK_OVERRIDE {
+    
+    SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) override {
         SkASSERT(x < stopx);
-
-        int16_t runs[2];
-        uint8_t  aa[1];
-
-        runs[0] = 1;
-        runs[1] = 0;
-
+        
         fy += SK_Fixed1/2;
         SkBlitter* blitter = this->getBlitter();
         do {
             int lower_y = fy >> 16;
             uint8_t  a = (uint8_t)(fy >> 8);
-            if (a) {
-                aa[0] = a;
-                blitter->blitAntiH(x, lower_y, aa, runs);
-                // the clipping blitters might edit runs, but should not affect us
-                SkASSERT(runs[0] == 1);
-                SkASSERT(runs[1] == 0);
-            }
-            a = 255 - a;
-            if (a) {
-                aa[0] = a;
-                blitter->blitAntiH(x, lower_y - 1, aa, runs);
-                // the clipping blitters might edit runs, but should not affect us
-                SkASSERT(runs[0] == 1);
-                SkASSERT(runs[1] == 0);
-            }
+            blitter->blitAntiV2(x, lower_y - 1, 255 - a, a);
             fy += dy;
         } while (++x < stopx);
-
+        
         return fy - SK_Fixed1/2;
     }
 };
 
 class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
 public:
-    SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
+    SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
         SkASSERT(0 == dx);
         fx += SK_Fixed1/2;
 
@@ -243,7 +202,7 @@
         return fx - SK_Fixed1/2;
     }
 
-    SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE {
+    SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
         SkASSERT(y < stopy);
         SkASSERT(0 == dx);
         fx += SK_Fixed1/2;
@@ -265,54 +224,27 @@
 
 class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
 public:
-    SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
-        int16_t runs[3];
-        uint8_t  aa[2];
-
-        runs[0] = 1;
-        runs[2] = 0;
-
+    SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
         fx += SK_Fixed1/2;
+        
         int x = fx >> 16;
-        uint8_t  a = (uint8_t)(fx >> 8);
-
-        aa[0] = SmallDot6Scale(255 - a, mod64);
-        aa[1] = SmallDot6Scale(a, mod64);
-        // the clippng blitters might overwrite this guy, so we have to reset it each time
-        runs[1] = 1;
-        this->getBlitter()->blitAntiH(x - 1, y, aa, runs);
-        // the clipping blitters might edit runs, but should not affect us
-        SkASSERT(runs[0] == 1);
-        SkASSERT(runs[2] == 0);
-        fx += dx;
-
-        return fx - SK_Fixed1/2;
+        uint8_t a = (uint8_t)(fx >> 8);
+        this->getBlitter()->blitAntiH2(x - 1, y,
+                                       SmallDot6Scale(255 - a, mod64), SmallDot6Scale(a, mod64));
+        
+        return fx + dx - SK_Fixed1/2;
     }
-
-    SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE {
+    
+    SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
         SkASSERT(y < stopy);
-        int16_t runs[3];
-        uint8_t  aa[2];
-
-        runs[0] = 1;
-        runs[2] = 0;
-
         fx += SK_Fixed1/2;
         do {
             int x = fx >> 16;
-            uint8_t  a = (uint8_t)(fx >> 8);
-
-            aa[0] = 255 - a;
-            aa[1] = a;
-            // the clippng blitters might overwrite this guy, so we have to reset it each time
-            runs[1] = 1;
-            this->getBlitter()->blitAntiH(x - 1, y, aa, runs);
-            // the clipping blitters might edit runs, but should not affect us
-            SkASSERT(runs[0] == 1);
-            SkASSERT(runs[2] == 0);
+            uint8_t a = (uint8_t)(fx >> 8);
+            this->getBlitter()->blitAntiH2(x - 1, y, 255 - a, a);
             fx += dx;
         } while (++y < stopy);
-
+        
         return fx - SK_Fixed1/2;
     }
 };
@@ -588,8 +520,8 @@
     }
 }
 
-void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
-                             const SkRegion* clip, SkBlitter* blitter) {
+void SkScan::AntiHairLineRgn(const SkPoint array[], int arrayCount, const SkRegion* clip,
+                             SkBlitter* blitter) {
     if (clip && clip->isEmpty()) {
         return;
     }
@@ -600,86 +532,83 @@
     build_gamma_table();
 #endif
 
-    SkPoint pts[2] = { pt0, pt1 };
+    const SkScalar max = SkIntToScalar(32767);
+    const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
 
-    // We have to pre-clip the line to fit in a SkFixed, so we just chop
-    // the line. TODO find a way to actually draw beyond that range.
-    {
-        SkRect fixedBounds;
-        const SkScalar max = SkIntToScalar(32767);
-        fixedBounds.set(-max, -max, max, max);
-        if (!SkLineClipper::IntersectLine(pts, fixedBounds, pts)) {
-            return;
-        }
-    }
-
+    SkRect clipBounds;
     if (clip) {
-        SkRect clipBounds;
         clipBounds.set(clip->getBounds());
         /*  We perform integral clipping later on, but we do a scalar clip first
-            to ensure that our coordinates are expressible in fixed/integers.
-
-            antialiased hairlines can draw up to 1/2 of a pixel outside of
-            their bounds, so we need to outset the clip before calling the
-            clipper. To make the numerics safer, we outset by a whole pixel,
-            since the 1/2 pixel boundary is important to the antihair blitter,
-            we don't want to risk numerical fate by chopping on that edge.
+         to ensure that our coordinates are expressible in fixed/integers.
+         
+         antialiased hairlines can draw up to 1/2 of a pixel outside of
+         their bounds, so we need to outset the clip before calling the
+         clipper. To make the numerics safer, we outset by a whole pixel,
+         since the 1/2 pixel boundary is important to the antihair blitter,
+         we don't want to risk numerical fate by chopping on that edge.
          */
-        clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
-
-        if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
-            return;
-        }
+        clipBounds.outset(SK_Scalar1, SK_Scalar1);
     }
 
-    SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
-    SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
-    SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
-    SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
+    for (int i = 0; i < arrayCount - 1; ++i) {
+        SkPoint pts[2];
 
-    if (clip) {
-        SkFDot6 left = SkMin32(x0, x1);
-        SkFDot6 top = SkMin32(y0, y1);
-        SkFDot6 right = SkMax32(x0, x1);
-        SkFDot6 bottom = SkMax32(y0, y1);
-        SkIRect ir;
-
-        ir.set( SkFDot6Floor(left) - 1,
-                SkFDot6Floor(top) - 1,
-                SkFDot6Ceil(right) + 1,
-                SkFDot6Ceil(bottom) + 1);
-
-        if (clip->quickReject(ir)) {
-            return;
+        // We have to pre-clip the line to fit in a SkFixed, so we just chop
+        // the line. TODO find a way to actually draw beyond that range.
+        if (!SkLineClipper::IntersectLine(&array[i], fixedBounds, pts)) {
+            continue;
         }
-        if (!clip->quickContains(ir)) {
-            SkRegion::Cliperator iter(*clip, ir);
-            const SkIRect*       r = &iter.rect();
 
-            while (!iter.done()) {
-                do_anti_hairline(x0, y0, x1, y1, r, blitter);
-                iter.next();
+        if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
+            continue;
+        }
+
+        SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
+        SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
+        SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
+        SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
+
+        if (clip) {
+            SkFDot6 left = SkMin32(x0, x1);
+            SkFDot6 top = SkMin32(y0, y1);
+            SkFDot6 right = SkMax32(x0, x1);
+            SkFDot6 bottom = SkMax32(y0, y1);
+            SkIRect ir;
+
+            ir.set( SkFDot6Floor(left) - 1,
+                    SkFDot6Floor(top) - 1,
+                    SkFDot6Ceil(right) + 1,
+                    SkFDot6Ceil(bottom) + 1);
+
+            if (clip->quickReject(ir)) {
+                continue;
             }
-            return;
+            if (!clip->quickContains(ir)) {
+                SkRegion::Cliperator iter(*clip, ir);
+                const SkIRect*       r = &iter.rect();
+
+                while (!iter.done()) {
+                    do_anti_hairline(x0, y0, x1, y1, r, blitter);
+                    iter.next();
+                }
+                continue;
+            }
+            // fall through to no-clip case
         }
-        // fall through to no-clip case
+        do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
     }
-    do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
 }
 
 void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
                           SkBlitter* blitter) {
-    SkPoint p0, p1;
+    SkPoint pts[5];
 
-    p0.set(rect.fLeft, rect.fTop);
-    p1.set(rect.fRight, rect.fTop);
-    SkScan::AntiHairLine(p0, p1, clip, blitter);
-    p0.set(rect.fRight, rect.fBottom);
-    SkScan::AntiHairLine(p0, p1, clip, blitter);
-    p1.set(rect.fLeft, rect.fBottom);
-    SkScan::AntiHairLine(p0, p1, clip, blitter);
-    p0.set(rect.fLeft, rect.fTop);
-    SkScan::AntiHairLine(p0, p1, clip, blitter);
+    pts[0].set(rect.fLeft, rect.fTop);
+    pts[1].set(rect.fRight, rect.fTop);
+    pts[2].set(rect.fRight, rect.fBottom);
+    pts[3].set(rect.fLeft, rect.fBottom);
+    pts[4] = pts[0];
+    SkScan::AntiHairLine(pts, 5, clip, blitter);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp
index 7ca54ea..4ed5ec2 100644
--- a/src/core/SkScan_Hairline.cpp
+++ b/src/core/SkScan_Hairline.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkScan.h"
 #include "SkBlitter.h"
 #include "SkRasterClip.h"
@@ -40,103 +38,105 @@
 }
 #endif
 
-void SkScan::HairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
-                         const SkRegion* clip, SkBlitter* blitter) {
+void SkScan::HairLineRgn(const SkPoint array[], int arrayCount, const SkRegion* clip,
+                         SkBlitter* origBlitter) {
     SkBlitterClipper    clipper;
-    SkRect  r;
     SkIRect clipR, ptsR;
-    SkPoint pts[2] = { pt0, pt1 };
 
-    // We have to pre-clip the line to fit in a SkFixed, so we just chop
-    // the line. TODO find a way to actually draw beyond that range.
-    {
-        SkRect fixedBounds;
-        const SkScalar max = SkIntToScalar(32767);
-        fixedBounds.set(-max, -max, max, max);
-        if (!SkLineClipper::IntersectLine(pts, fixedBounds, pts)) {
-            return;
-        }
+    const SkScalar max = SkIntToScalar(32767);
+    const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
+
+    SkRect clipBounds;
+    if (clip) {
+        clipBounds.set(clip->getBounds());
     }
 
-    if (clip) {
+    for (int i = 0; i < arrayCount - 1; ++i) {
+        SkBlitter* blitter = origBlitter;
+
+        SkPoint pts[2];
+
+        // We have to pre-clip the line to fit in a SkFixed, so we just chop
+        // the line. TODO find a way to actually draw beyond that range.
+        if (!SkLineClipper::IntersectLine(&array[i], fixedBounds, pts)) {
+            continue;
+        }
+
         // Perform a clip in scalar space, so we catch huge values which might
         // be missed after we convert to SkFDot6 (overflow)
-        r.set(clip->getBounds());
-        if (!SkLineClipper::IntersectLine(pts, r, pts)) {
-            return;
-        }
-    }
-
-    SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
-    SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
-    SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
-    SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
-
-    SkASSERT(canConvertFDot6ToFixed(x0));
-    SkASSERT(canConvertFDot6ToFixed(y0));
-    SkASSERT(canConvertFDot6ToFixed(x1));
-    SkASSERT(canConvertFDot6ToFixed(y1));
-
-    if (clip) {
-        // now perform clipping again, as the rounding to dot6 can wiggle us
-        // our rects are really dot6 rects, but since we've already used
-        // lineclipper, we know they will fit in 32bits (26.6)
-        const SkIRect& bounds = clip->getBounds();
-
-        clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop),
-                  SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom));
-        ptsR.set(x0, y0, x1, y1);
-        ptsR.sort();
-
-        // outset the right and bottom, to account for how hairlines are
-        // actually drawn, which may hit the pixel to the right or below of
-        // the coordinate
-        ptsR.fRight += SK_FDot6One;
-        ptsR.fBottom += SK_FDot6One;
-
-        if (!SkIRect::Intersects(ptsR, clipR)) {
-            return;
-        }
-        if (clip->isRect() && clipR.contains(ptsR)) {
-            clip = NULL;
-        } else {
-            blitter = clipper.apply(blitter, clip);
-        }
-    }
-
-    SkFDot6 dx = x1 - x0;
-    SkFDot6 dy = y1 - y0;
-
-    if (SkAbs32(dx) > SkAbs32(dy)) { // mostly horizontal
-        if (x0 > x1) {   // we want to go left-to-right
-            SkTSwap<SkFDot6>(x0, x1);
-            SkTSwap<SkFDot6>(y0, y1);
-        }
-        int ix0 = SkFDot6Round(x0);
-        int ix1 = SkFDot6Round(x1);
-        if (ix0 == ix1) {// too short to draw
-            return;
+        if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
+            continue;
         }
 
-        SkFixed slope = SkFixedDiv(dy, dx);
-        SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6);
+        SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
+        SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
+        SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
+        SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
 
-        horiline(ix0, ix1, startY, slope, blitter);
-    } else {              // mostly vertical
-        if (y0 > y1) {   // we want to go top-to-bottom
-            SkTSwap<SkFDot6>(x0, x1);
-            SkTSwap<SkFDot6>(y0, y1);
-        }
-        int iy0 = SkFDot6Round(y0);
-        int iy1 = SkFDot6Round(y1);
-        if (iy0 == iy1) { // too short to draw
-            return;
+        SkASSERT(canConvertFDot6ToFixed(x0));
+        SkASSERT(canConvertFDot6ToFixed(y0));
+        SkASSERT(canConvertFDot6ToFixed(x1));
+        SkASSERT(canConvertFDot6ToFixed(y1));
+
+        if (clip) {
+            // now perform clipping again, as the rounding to dot6 can wiggle us
+            // our rects are really dot6 rects, but since we've already used
+            // lineclipper, we know they will fit in 32bits (26.6)
+            const SkIRect& bounds = clip->getBounds();
+
+            clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop),
+                      SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom));
+            ptsR.set(x0, y0, x1, y1);
+            ptsR.sort();
+
+            // outset the right and bottom, to account for how hairlines are
+            // actually drawn, which may hit the pixel to the right or below of
+            // the coordinate
+            ptsR.fRight += SK_FDot6One;
+            ptsR.fBottom += SK_FDot6One;
+
+            if (!SkIRect::Intersects(ptsR, clipR)) {
+                continue;
+            }
+            if (!clip->isRect() || !clipR.contains(ptsR)) {
+                blitter = clipper.apply(origBlitter, clip);
+            }
         }
 
-        SkFixed slope = SkFixedDiv(dx, dy);
-        SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6);
+        SkFDot6 dx = x1 - x0;
+        SkFDot6 dy = y1 - y0;
 
-        vertline(iy0, iy1, startX, slope, blitter);
+        if (SkAbs32(dx) > SkAbs32(dy)) { // mostly horizontal
+            if (x0 > x1) {   // we want to go left-to-right
+                SkTSwap<SkFDot6>(x0, x1);
+                SkTSwap<SkFDot6>(y0, y1);
+            }
+            int ix0 = SkFDot6Round(x0);
+            int ix1 = SkFDot6Round(x1);
+            if (ix0 == ix1) {// too short to draw
+                continue;
+            }
+
+            SkFixed slope = SkFixedDiv(dy, dx);
+            SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6);
+
+            horiline(ix0, ix1, startY, slope, blitter);
+        } else {              // mostly vertical
+            if (y0 > y1) {   // we want to go top-to-bottom
+                SkTSwap<SkFDot6>(x0, x1);
+                SkTSwap<SkFDot6>(y0, y1);
+            }
+            int iy0 = SkFDot6Round(y0);
+            int iy1 = SkFDot6Round(y1);
+            if (iy0 == iy1) { // too short to draw
+                continue;
+            }
+
+            SkFixed slope = SkFixedDiv(dx, dy);
+            SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6);
+
+            vertline(iy0, iy1, startX, slope, blitter);
+        }
     }
 }
 
@@ -190,6 +190,10 @@
 
 #include "SkPath.h"
 #include "SkGeometry.h"
+#include "SkNx.h"
+
+#define kMaxCubicSubdivideLevel 6
+#define kMaxQuadSubdivideLevel  5
 
 static int compute_int_quad_dist(const SkPoint pts[3]) {
     // compute the vector between the control point ([1]) and the middle of the
@@ -210,37 +214,124 @@
     }
 }
 
-typedef void (*LineProc)(const SkPoint&, const SkPoint&, const SkRegion*,
-                         SkBlitter*);
-
 static void hairquad(const SkPoint pts[3], const SkRegion* clip,
-                     SkBlitter* blitter, int level, LineProc lineproc) {
-    if (level > 0) {
-        SkPoint tmp[5];
+                     SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
+    SkASSERT(level <= kMaxQuadSubdivideLevel);
 
-        SkChopQuadAtHalf(pts, tmp);
-        hairquad(tmp, clip, blitter, level - 1, lineproc);
-        hairquad(&tmp[2], clip, blitter, level - 1, lineproc);
-    } else {
-        lineproc(pts[0], pts[2], clip, blitter);
+    SkPoint coeff[3];
+    SkQuadToCoeff(pts, coeff);
+
+    const int lines = 1 << level;
+    Sk2s t(0);
+    Sk2s dt(SK_Scalar1 / lines);
+
+    SkPoint tmp[(1 << kMaxQuadSubdivideLevel) + 1];
+    SkASSERT((unsigned)lines < SK_ARRAY_COUNT(tmp));
+
+    tmp[0] = pts[0];
+    Sk2s A = Sk2s::Load(&coeff[0].fX);
+    Sk2s B = Sk2s::Load(&coeff[1].fX);
+    Sk2s C = Sk2s::Load(&coeff[2].fX);
+    for (int i = 1; i < lines; ++i) {
+        t += dt;
+        ((A * t + B) * t + C).store(&tmp[i].fX);
     }
+    tmp[lines] = pts[2];
+    lineproc(tmp, lines + 1, clip, blitter);
 }
 
-static void haircubic(const SkPoint pts[4], const SkRegion* clip,
-                      SkBlitter* blitter, int level, LineProc lineproc) {
-    if (level > 0) {
-        SkPoint tmp[7];
-
-        SkChopCubicAt(pts, tmp, SK_Scalar1/2);
-        haircubic(tmp, clip, blitter, level - 1, lineproc);
-        haircubic(&tmp[3], clip, blitter, level - 1, lineproc);
-    } else {
-        lineproc(pts[0], pts[3], clip, blitter);
-    }
+static inline Sk2s abs(const Sk2s& value) {
+    return Sk2s::Max(value, -value);
 }
 
-#define kMaxCubicSubdivideLevel 6
-#define kMaxQuadSubdivideLevel  5
+static inline SkScalar max_component(const Sk2s& value) {
+    SkScalar components[2];
+    value.store(components);
+    return SkTMax(components[0], components[1]);
+}
+
+static inline int compute_cubic_segs(const SkPoint pts[4]) {
+    Sk2s p0 = from_point(pts[0]);
+    Sk2s p1 = from_point(pts[1]);
+    Sk2s p2 = from_point(pts[2]);
+    Sk2s p3 = from_point(pts[3]);
+
+    const Sk2s oneThird(1.0f / 3.0f);
+    const Sk2s twoThird(2.0f / 3.0f);
+
+    Sk2s p13 = oneThird * p3 + twoThird * p0;
+    Sk2s p23 = oneThird * p0 + twoThird * p3;
+
+    SkScalar diff = max_component(Sk2s::Max(abs(p1 - p13), abs(p2 - p23)));
+    SkScalar tol = SK_Scalar1 / 8;
+
+    for (int i = 0; i < kMaxCubicSubdivideLevel; ++i) {
+        if (diff < tol) {
+            return 1 << i;
+        }
+        tol *= 4;
+    }
+    return 1 << kMaxCubicSubdivideLevel;
+}
+
+static bool lt_90(SkPoint p0, SkPoint pivot, SkPoint p2) {
+    return SkVector::DotProduct(p0 - pivot, p2 - pivot) >= 0;
+}
+
+// The off-curve points are "inside" the limits of the on-curve pts
+static bool quick_cubic_niceness_check(const SkPoint pts[4]) {
+    return lt_90(pts[1], pts[0], pts[3]) &&
+           lt_90(pts[2], pts[0], pts[3]) &&
+           lt_90(pts[1], pts[3], pts[0]) &&
+           lt_90(pts[2], pts[3], pts[0]);
+}
+
+static void hair_cubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter,
+                       SkScan::HairRgnProc lineproc) {
+    const int lines = compute_cubic_segs(pts);
+    SkASSERT(lines > 0);
+    if (1 == lines) {
+        SkPoint tmp[2] = { pts[0], pts[3] };
+        lineproc(tmp, 2, clip, blitter);
+        return;
+    }
+
+    SkPoint coeff[4];
+    SkCubicToCoeff(pts, coeff);
+    
+    const Sk2s dt(SK_Scalar1 / lines);
+    Sk2s t(0);
+
+    SkPoint tmp[(1 << kMaxCubicSubdivideLevel) + 1];
+    SkASSERT((unsigned)lines < SK_ARRAY_COUNT(tmp));
+
+    tmp[0] = pts[0];
+    Sk2s A = Sk2s::Load(&coeff[0].fX);
+    Sk2s B = Sk2s::Load(&coeff[1].fX);
+    Sk2s C = Sk2s::Load(&coeff[2].fX);
+    Sk2s D = Sk2s::Load(&coeff[3].fX);
+    for (int i = 1; i < lines; ++i) {
+        t += dt;
+        (((A * t + B) * t + C) * t + D).store(&tmp[i].fX);
+    }
+    tmp[lines] = pts[3];
+    lineproc(tmp, lines + 1, clip, blitter);
+}
+
+static inline void haircubic(const SkPoint pts[4], const SkRegion* clip,
+                      SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
+    if (quick_cubic_niceness_check(pts)) {
+        hair_cubic(pts, clip, blitter, lineproc);
+    } else {
+        SkPoint  tmp[13];
+        SkScalar tValues[3];
+
+        int count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
+        for (int i = 0; i < count; i++) {
+            hair_cubic(&tmp[i * 3], clip, blitter, lineproc);
+        }
+    }
+}
 
 static int compute_quad_level(const SkPoint pts[3]) {
     int d = compute_int_quad_dist(pts);
@@ -257,8 +348,8 @@
     return level;
 }
 
-static void hair_path(const SkPath& path, const SkRasterClip& rclip,
-                      SkBlitter* blitter, LineProc lineproc) {
+static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
+                      SkScan::HairRgnProc lineproc) {
     if (path.isEmpty()) {
         return;
     }
@@ -267,9 +358,7 @@
     const SkRegion* clip = NULL;
 
     {
-        SkIRect ibounds;
-        path.getBounds().roundOut(&ibounds);
-        ibounds.inset(-1, -1);
+        const SkIRect ibounds = path.getBounds().roundOut().makeOutset(1, 1);
 
         if (rclip.quickReject(ibounds)) {
             return;
@@ -295,7 +384,7 @@
             case SkPath::kMove_Verb:
                 break;
             case SkPath::kLine_Verb:
-                lineproc(pts[0], pts[1], clip, blitter);
+                lineproc(pts, 2, clip, blitter);
                 break;
             case SkPath::kQuad_Verb:
                 hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc);
@@ -312,9 +401,9 @@
                 }
                 break;
             }
-            case SkPath::kCubic_Verb:
+            case SkPath::kCubic_Verb: {
                 haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc);
-                break;
+            } break;
             case SkPath::kClose_Verb:
                 break;
             case SkPath::kDone_Verb:
@@ -323,13 +412,11 @@
     }
 }
 
-void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip,
-                      SkBlitter* blitter) {
+void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
     hair_path(path, clip, blitter, SkScan::HairLineRgn);
 }
 
-void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip,
-                          SkBlitter* blitter) {
+void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
     hair_path(path, clip, blitter, SkScan::AntiHairLineRgn);
 }
 
@@ -370,16 +457,16 @@
     SkScan::FillRect(tmp, clip, blitter);
 }
 
-void SkScan::HairLine(const SkPoint& p0, const SkPoint& p1,
-                      const SkRasterClip& clip, SkBlitter* blitter) {
+void SkScan::HairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
+                      SkBlitter* blitter) {
     if (clip.isBW()) {
-        HairLineRgn(p0, p1, &clip.bwRgn(), blitter);
+        HairLineRgn(pts, count, &clip.bwRgn(), blitter);
     } else {
         const SkRegion* clipRgn = NULL;
+
         SkRect r;
-        r.set(p0.fX, p0.fY, p1.fX, p1.fY);
-        r.sort();
-        r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+        r.set(pts, count);
+        r.outset(SK_ScalarHalf, SK_ScalarHalf);
 
         SkAAClipBlitterWrapper wrap;
         if (!clip.quickContains(r.roundOut())) {
@@ -387,29 +474,26 @@
             blitter = wrap.getBlitter();
             clipRgn = &wrap.getRgn();
         }
-        HairLineRgn(p0, p1, clipRgn, blitter);
+        HairLineRgn(pts, count, clipRgn, blitter);
     }
 }
 
-void SkScan::AntiHairLine(const SkPoint& p0, const SkPoint& p1,
-                          const SkRasterClip& clip, SkBlitter* blitter) {
+void SkScan::AntiHairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
+                          SkBlitter* blitter) {
     if (clip.isBW()) {
-        AntiHairLineRgn(p0, p1, &clip.bwRgn(), blitter);
+        AntiHairLineRgn(pts, count, &clip.bwRgn(), blitter);
     } else {
         const SkRegion* clipRgn = NULL;
+
         SkRect r;
-        SkIRect ir;
-        r.set(p0.fX, p0.fY, p1.fX, p1.fY);
-        r.sort();
-        r.roundOut(&ir);
-        ir.inset(-1, -1);
+        r.set(pts, count);
 
         SkAAClipBlitterWrapper wrap;
-        if (!clip.quickContains(ir)) {
+        if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) {
             wrap.init(clip, blitter);
             blitter = wrap.getBlitter();
             clipRgn = &wrap.getRgn();
         }
-        AntiHairLineRgn(p0, p1, clipRgn, blitter);
+        AntiHairLineRgn(pts, count, clipRgn, blitter);
     }
 }
diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp
index d51cb56..9b4b9fd 100644
--- a/src/core/SkScan_Path.cpp
+++ b/src/core/SkScan_Path.cpp
@@ -323,7 +323,7 @@
     }
 
     // overrides
-    void blitH(int x, int y, int width) SK_OVERRIDE {
+    void blitH(int x, int y, int width) override {
         int invWidth = x - fPrevX;
         if (invWidth > 0) {
             fBlitter->blitH(fPrevX, y, invWidth);
@@ -332,19 +332,19 @@
     }
 
     // we do not expect to get called with these entrypoints
-    void blitAntiH(int, int, const SkAlpha[], const int16_t runs[]) SK_OVERRIDE {
+    void blitAntiH(int, int, const SkAlpha[], const int16_t runs[]) override {
         SkDEBUGFAIL("blitAntiH unexpected");
     }
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
+    void blitV(int x, int y, int height, SkAlpha alpha) override {
         SkDEBUGFAIL("blitV unexpected");
     }
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+    void blitRect(int x, int y, int width, int height) override {
         SkDEBUGFAIL("blitRect unexpected");
     }
-    void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE {
+    void blitMask(const SkMask&, const SkIRect& clip) override {
         SkDEBUGFAIL("blitMask unexpected");
     }
-    const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE {
+    const SkBitmap* justAnOpaqueColor(uint32_t* value) override {
         SkDEBUGFAIL("justAnOpaqueColor unexpected");
         return NULL;
     }
diff --git a/src/core/SkSpriteBlitter_ARGB32.cpp b/src/core/SkSpriteBlitter_ARGB32.cpp
index 0993a4c..c3f63fe 100644
--- a/src/core/SkSpriteBlitter_ARGB32.cpp
+++ b/src/core/SkSpriteBlitter_ARGB32.cpp
@@ -34,7 +34,7 @@
         fAlpha = alpha;
     }
 
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+    void blitRect(int x, int y, int width, int height) override {
         SkASSERT(width > 0 && height > 0);
         uint32_t* SK_RESTRICT dst = fDevice->getAddr32(x, y);
         const uint32_t* SK_RESTRICT src = fSource->getAddr32(x - fLeft,
@@ -92,7 +92,7 @@
     }
 
     virtual void setup(const SkBitmap& device, int left, int top,
-                       const SkPaint& paint) SK_OVERRIDE {
+                       const SkPaint& paint) override {
         this->INHERITED::setup(device, left, top, paint);
 
         int width = device.width();
@@ -122,7 +122,7 @@
     Sprite_D32_S32A_XferFilter(const SkBitmap& source, const SkPaint& paint)
         : Sprite_D32_XferFilter(source, paint) {}
 
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+    void blitRect(int x, int y, int width, int height) override {
         SkASSERT(width > 0 && height > 0);
         uint32_t* SK_RESTRICT dst = fDevice->getAddr32(x, y);
         const uint32_t* SK_RESTRICT src = fSource->getAddr32(x - fLeft,
@@ -169,7 +169,7 @@
     Sprite_D32_S4444_XferFilter(const SkBitmap& source, const SkPaint& paint)
         : Sprite_D32_XferFilter(source, paint) {}
 
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+    void blitRect(int x, int y, int width, int height) override {
         SkASSERT(width > 0 && height > 0);
         SkPMColor* SK_RESTRICT dst = fDevice->getAddr32(x, y);
         const SkPMColor16* SK_RESTRICT src = fSource->getAddr16(x - fLeft,
@@ -216,7 +216,7 @@
 public:
     Sprite_D32_S4444_Opaque(const SkBitmap& source) : SkSpriteBlitter(source) {}
 
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+    void blitRect(int x, int y, int width, int height) override {
         SkASSERT(width > 0 && height > 0);
         SkPMColor* SK_RESTRICT dst = fDevice->getAddr32(x, y);
         const SkPMColor16* SK_RESTRICT src = fSource->getAddr16(x - fLeft,
@@ -245,7 +245,7 @@
 public:
     Sprite_D32_S4444(const SkBitmap& source) : SkSpriteBlitter(source) {}
 
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+    void blitRect(int x, int y, int width, int height) override {
         SkASSERT(width > 0 && height > 0);
         SkPMColor* SK_RESTRICT dst = fDevice->getAddr32(x, y);
         const SkPMColor16* SK_RESTRICT src = fSource->getAddr16(x - fLeft,
diff --git a/src/core/SkSpriteBlitter_RGB16.cpp b/src/core/SkSpriteBlitter_RGB16.cpp
index 7707336..ca0eb50 100644
--- a/src/core/SkSpriteBlitter_RGB16.cpp
+++ b/src/core/SkSpriteBlitter_RGB16.cpp
@@ -54,7 +54,7 @@
         : SkSpriteBlitter(source) {}
 
     // overrides
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+    void blitRect(int x, int y, int width, int height) override {
         uint16_t* SK_RESTRICT dst = fDevice->getAddr16(x, y);
         const uint16_t* SK_RESTRICT src = fSource->getAddr16(x - fLeft,
                                                              y - fTop);
@@ -264,7 +264,7 @@
     // overrides
 
     virtual void setup(const SkBitmap& device, int left, int top,
-                       const SkPaint& paint) SK_OVERRIDE {
+                       const SkPaint& paint) override {
         this->INHERITED::setup(device, left, top, paint);
 
         unsigned flags = 0;
@@ -281,7 +281,7 @@
         fProc = SkBlitRow::Factory16(flags);
     }
 
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+    void blitRect(int x, int y, int width, int height) override {
         uint16_t* SK_RESTRICT dst = fDevice->getAddr16(x, y);
         const SkPMColor* SK_RESTRICT src = fSource->getAddr32(x - fLeft,
                                                               y - fTop);
diff --git a/src/core/SkStream.cpp b/src/core/SkStream.cpp
index 65e1bee..ad67a0b 100644
--- a/src/core/SkStream.cpp
+++ b/src/core/SkStream.cpp
@@ -367,6 +367,20 @@
     return size;
 }
 
+bool SkMemoryStream::peek(void* buffer, size_t size) const {
+    SkASSERT(buffer != NULL);
+    const size_t position = fOffset;
+    if (size > fData->size() - position) {
+        // The stream is not large enough to satisfy this request.
+        return false;
+    }
+    SkMemoryStream* nonConstThis = const_cast<SkMemoryStream*>(this);
+    SkDEBUGCODE(const size_t bytesRead =) nonConstThis->read(buffer, size);
+    SkASSERT(bytesRead == size);
+    nonConstThis->fOffset = position;
+    return true;
+}
+
 bool SkMemoryStream::isAtEnd() const {
     return fOffset == fData->size();
 }
@@ -680,7 +694,7 @@
         : fBlockMemory(SkRef(headRef)), fCurrent(fBlockMemory->fHead)
         , fSize(size) , fOffset(0), fCurrentOffset(0) { }
 
-    size_t read(void* buffer, size_t rawCount) SK_OVERRIDE {
+    size_t read(void* buffer, size_t rawCount) override {
         size_t count = rawCount;
         if (fOffset + count > fSize) {
             count = fSize - fOffset;
@@ -706,26 +720,26 @@
         return 0;
     }
 
-    bool isAtEnd() const SK_OVERRIDE {
+    bool isAtEnd() const override {
         return fOffset == fSize;
     }
 
-    bool rewind() SK_OVERRIDE {
+    bool rewind() override {
         fCurrent = fBlockMemory->fHead;
         fOffset = 0;
         fCurrentOffset = 0;
         return true;
     }
 
-    SkBlockMemoryStream* duplicate() const SK_OVERRIDE {
+    SkBlockMemoryStream* duplicate() const override {
         return SkNEW_ARGS(SkBlockMemoryStream, (fBlockMemory.get(), fSize));
     }
 
-    size_t getPosition() const SK_OVERRIDE {
+    size_t getPosition() const override {
         return fOffset;
     }
 
-    bool seek(size_t position) SK_OVERRIDE {
+    bool seek(size_t position) override {
         // If possible, skip forward.
         if (position >= fOffset) {
             size_t skipAmount = position - fOffset;
@@ -742,11 +756,11 @@
         return this->rewind() && this->skip(position) == position;
     }
 
-    bool move(long offset) SK_OVERRIDE {
+    bool move(long offset) override {
         return seek(fOffset + offset);
     }
 
-    SkBlockMemoryStream* fork() const SK_OVERRIDE {
+    SkBlockMemoryStream* fork() const override {
         SkAutoTDelete<SkBlockMemoryStream> that(this->duplicate());
         that->fCurrent = this->fCurrent;
         that->fOffset = this->fOffset;
@@ -754,11 +768,11 @@
         return that.detach();
     }
 
-    size_t getLength() const SK_OVERRIDE {
+    size_t getLength() const override {
         return fSize;
     }
 
-    const void* getMemoryBase() SK_OVERRIDE {
+    const void* getMemoryBase() override {
         if (NULL == fBlockMemory->fHead->fNext) {
             return fBlockMemory->fHead->start();
         }
diff --git a/src/core/SkString.cpp b/src/core/SkString.cpp
index b433515..c10347d 100644
--- a/src/core/SkString.cpp
+++ b/src/core/SkString.cpp
@@ -247,48 +247,32 @@
         SkASSERT(fRec->fRefCnt > 0);
         SkASSERT(0 == fRec->data()[fRec->fLength]);
     }
-    SkASSERT(fStr == c_str());
 }
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
 SkString::SkString() : fRec(const_cast<Rec*>(&gEmptyRec)) {
-#ifdef SK_DEBUG
-    fStr = fRec->data();
-#endif
 }
 
 SkString::SkString(size_t len) {
     fRec = AllocRec(NULL, len);
-#ifdef SK_DEBUG
-    fStr = fRec->data();
-#endif
 }
 
 SkString::SkString(const char text[]) {
     size_t  len = text ? strlen(text) : 0;
 
     fRec = AllocRec(text, len);
-#ifdef SK_DEBUG
-    fStr = fRec->data();
-#endif
 }
 
 SkString::SkString(const char text[], size_t len) {
     fRec = AllocRec(text, len);
-#ifdef SK_DEBUG
-    fStr = fRec->data();
-#endif
 }
 
 SkString::SkString(const SkString& src) {
     src.validate();
 
     fRec = RefRec(src.fRec);
-#ifdef SK_DEBUG
-    fStr = fRec->data();
-#endif
 }
 
 SkString::~SkString() {
@@ -346,9 +330,6 @@
     }
 
     fRec = const_cast<Rec*>(&gEmptyRec);
-#ifdef SK_DEBUG
-    fStr = fRec->data();
-#endif
 }
 
 char* SkString::writable_str() {
@@ -364,9 +345,6 @@
                 sk_free(fRec);
             }
             fRec = rec;
-        #ifdef SK_DEBUG
-            fStr = fRec->data();
-        #endif
         }
     }
     return fRec->data();
@@ -633,9 +611,6 @@
     other.validate();
 
     SkTSwap<Rec*>(fRec, other.fRec);
-#ifdef SK_DEBUG
-    SkTSwap<const char*>(fStr, other.fStr);
-#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
index 062406a..6454f16 100644
--- a/src/core/SkStroke.cpp
+++ b/src/core/SkStroke.cpp
@@ -454,8 +454,7 @@
         normalB = pts[2] - pts[0];
         normalB.rotateCCW();
         SkScalar dot = SkPoint::DotProduct(unitNormalAB, *unitNormalBC);
-        SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
-                                     SkScalarSqrt((SK_Scalar1 + dot)/2))));
+        SkAssertResult(normalB.setLength(fRadius / SkScalarSqrt((SK_Scalar1 + dot)/2)));
 
         fOuter.quadTo(  pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
                         pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
@@ -761,11 +760,9 @@
         normalC = *unitNormalCD + unitBC;
 
         SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
-        SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
-                                    SkScalarSqrt((SK_Scalar1 + dot)/2))));
+        SkAssertResult(normalB.setLength(fRadius / SkScalarSqrt((SK_Scalar1 + dot)/2)));
         dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
-        SkAssertResult(normalC.setLength(SkScalarDiv(fRadius,
-                                    SkScalarSqrt((SK_Scalar1 + dot)/2))));
+        SkAssertResult(normalC.setLength(fRadius / SkScalarSqrt((SK_Scalar1 + dot)/2)));
 
         fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
                         pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
@@ -1554,7 +1551,7 @@
 };
 
 void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
-    SkASSERT(&src != NULL && dst != NULL);
+    SkASSERT(dst);
 
     SkScalar radius = SkScalarHalf(fWidth);
 
diff --git a/src/core/SkStrokerPriv.cpp b/src/core/SkStrokerPriv.cpp
index fbd552a..7fc773a 100644
--- a/src/core/SkStrokerPriv.cpp
+++ b/src/core/SkStrokerPriv.cpp
@@ -22,21 +22,6 @@
                         const SkVector& normal, const SkPoint& stop,
                         SkPath*)
 {
-#ifdef SK_SUPPORT_LEGACY_ARCTO_QUADS
-    SkScalar    px = pivot.fX;
-    SkScalar    py = pivot.fY;
-    SkScalar    nx = normal.fX;
-    SkScalar    ny = normal.fY;
-    SkScalar    sx = SkScalarMul(nx, CUBIC_ARC_FACTOR);
-    SkScalar    sy = SkScalarMul(ny, CUBIC_ARC_FACTOR);
-
-    path->cubicTo(px + nx + CWX(sx, sy), py + ny + CWY(sx, sy),
-                  px + CWX(nx, ny) + sx, py + CWY(nx, ny) + sy,
-                  px + CWX(nx, ny), py + CWY(nx, ny));
-    path->cubicTo(px + CWX(nx, ny) - sx, py + CWY(nx, ny) - sy,
-                  px - nx + CWX(sx, sy), py - ny + CWY(sx, sy),
-                  stop.fX, stop.fY);
-#else
     SkVector parallel;
     normal.rotateCW(&parallel);
 
@@ -44,7 +29,6 @@
 
     path->conicTo(projectedCenter + normal, projectedCenter, SK_ScalarRoot2Over2);
     path->conicTo(projectedCenter - normal, stop, SK_ScalarRoot2Over2);
-#endif
 }
 
 static void SquareCapper(SkPath* path, const SkPoint& pivot,
@@ -149,18 +133,6 @@
     SkMatrix    matrix;
     matrix.setScale(radius, radius);
     matrix.postTranslate(pivot.fX, pivot.fY);
-#ifdef SK_SUPPORT_LEGACY_ARCTO_QUADS
-    SkPoint     pts[kSkBuildQuadArcStorage];
-    int count = SkBuildQuadArc(before, after, dir, &matrix, pts);
-    SkASSERT((count & 1) == 1);
-    if (count > 1) {
-        for (int i = 1; i < count; i += 2) {
-            outer->quadTo(pts[i].fX, pts[i].fY, pts[i+1].fX, pts[i+1].fY);
-        }
-        after.scale(radius);
-        HandleInnerJoin(inner, pivot, after);
-    }
-#else
     SkConic conics[SkConic::kMaxConicsForArc];
     int count = SkConic::BuildUnitArc(before, after, dir, &matrix, conics);
     if (count > 0) {
@@ -170,7 +142,6 @@
         after.scale(radius);
         HandleInnerJoin(inner, pivot, after);
     }
-#endif
 }
 
 #define kOneOverSqrt2   (0.707106781f)
@@ -243,7 +214,7 @@
     else
         mid.set(before.fX + after.fX, before.fY + after.fY);
 
-    mid.setLength(SkScalarDiv(radius, sinHalfAngle));
+    mid.setLength(radius / sinHalfAngle);
 DO_MITER:
     if (prevIsLine)
         outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY);
diff --git a/src/core/SkTDynamicHash.h b/src/core/SkTDynamicHash.h
index 23544c8..8def89b 100644
--- a/src/core/SkTDynamicHash.h
+++ b/src/core/SkTDynamicHash.h
@@ -161,7 +161,7 @@
     static T* Deleted() { return reinterpret_cast<T*>(1); }  // Also an invalid pointer.
 
     bool validate() const {
-        #define SKTDYNAMICHASH_CHECK(x) SkASSERT((x)); if (!(x)) return false
+        #define SKTDYNAMICHASH_CHECK(x) SkASSERT(x); if (!(x)) return false
         static const int kLarge = 50;  // Arbitrary, tweak to suit your patience.
 
         // O(1) checks, always done.
diff --git a/src/core/SkTHash.h b/src/core/SkTHash.h
index b47f8fa..ffcdea5 100644
--- a/src/core/SkTHash.h
+++ b/src/core/SkTHash.h
@@ -1,6 +1,14 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #ifndef SkTHash_DEFINED
 #define SkTHash_DEFINED
 
+#include "SkChecksum.h"
 #include "SkTypes.h"
 #include "SkTemplates.h"
 
@@ -16,7 +24,7 @@
 template <typename T, typename K, typename Traits = T>
 class SkTHashTable : SkNoncopyable {
 public:
-    SkTHashTable() : fCount(0), fCapacity(0) {}
+    SkTHashTable() : fCount(0), fRemoved(0), fCapacity(0) {}
 
     // Clear the table.
     void reset() {
@@ -40,7 +48,7 @@
     // Copy val into the hash table, returning a pointer to the copy now in the table.
     // If there already is an entry in the table with the same key, we overwrite it.
     T* set(const T& val) {
-        if (4 * fCount >= 3 * fCapacity) {
+        if (4 * (fCount+fRemoved) >= 3 * fCapacity) {
             this->resize(fCapacity > 0 ? fCapacity * 2 : 4);
         }
         return this->uncheckedSet(val);
@@ -55,7 +63,7 @@
             if (s.empty()) {
                 return NULL;
             }
-            if (hash == s.hash && key == Traits::GetKey(s.val)) {
+            if (!s.removed() && hash == s.hash && key == Traits::GetKey(s.val)) {
                 return &s.val;
             }
             index = this->next(index, n);
@@ -64,13 +72,42 @@
         return NULL;
     }
 
+    // Remove the value with this key from the hash table.
+    void remove(const K& key) {
+        SkASSERT(this->find(key));
+
+        uint32_t hash = Hash(key);
+        int index = hash & (fCapacity-1);
+        for (int n = 0; n < fCapacity; n++) {
+            Slot& s = fSlots[index];
+            SkASSERT(!s.empty());
+            if (!s.removed() && hash == s.hash && key == Traits::GetKey(s.val)) {
+                fRemoved++;
+                fCount--;
+                s.markRemoved();
+                return;
+            }
+            index = this->next(index, n);
+        }
+        SkASSERT(fCapacity == 0);
+    }
+
     // Call fn on every entry in the table.  You may mutate the entries, but be very careful.
-    template <typename Arg>
-    void foreach(void(*fn)(T*, Arg), Arg arg) {
+    template <typename Fn>  // f(T*)
+    void foreach(Fn&& fn) {
         for (int i = 0; i < fCapacity; i++) {
-            Slot& s = fSlots[i];
-            if (!s.empty()) {
-                fn(&s.val, arg);
+            if (!fSlots[i].empty() && !fSlots[i].removed()) {
+                fn(&fSlots[i].val);
+            }
+        }
+    }
+
+    // Call fn on every entry in the table.  You may not mutate anything.
+    template <typename Fn>  // f(T) or f(const T&)
+    void foreach(Fn&& fn) const {
+        for (int i = 0; i < fCapacity; i++) {
+            if (!fSlots[i].empty() && !fSlots[i].removed()) {
+                fn(fSlots[i].val);
             }
         }
     }
@@ -82,8 +119,11 @@
         int index = hash & (fCapacity-1);
         for (int n = 0; n < fCapacity; n++) {
             Slot& s = fSlots[index];
-            if (s.empty()) {
+            if (s.empty() || s.removed()) {
                 // New entry.
+                if (s.removed()) {
+                    fRemoved--;
+                }
                 s.val  = val;
                 s.hash = hash;
                 fCount++;
@@ -105,14 +145,14 @@
         int oldCapacity = fCapacity;
         SkDEBUGCODE(int oldCount = fCount);
 
-        fCount = 0;
+        fCount = fRemoved = 0;
         fCapacity = capacity;
         SkAutoTArray<Slot> oldSlots(capacity);
         oldSlots.swap(fSlots);
 
         for (int i = 0; i < oldCapacity; i++) {
             const Slot& s = oldSlots[i];
-            if (!s.empty()) {
+            if (!s.empty() && !s.removed()) {
                 this->uncheckedSet(s.val);
             }
         }
@@ -128,24 +168,27 @@
 
     static uint32_t Hash(const K& key) {
         uint32_t hash = Traits::Hash(key);
-        return hash == 0 ? 1 : hash;  // We reserve hash == 0 to mark empty slots.
+        return hash < 2 ? hash+2 : hash;  // We reserve hash 0 and 1 to mark empty or removed slots.
     }
 
     struct Slot {
         Slot() : hash(0) {}
-        bool empty() const { return hash == 0; }
+        bool   empty() const { return this->hash == 0; }
+        bool removed() const { return this->hash == 1; }
+
+        void markRemoved() { this->hash = 1; }
 
         T val;
         uint32_t hash;
     };
 
-    int fCount, fCapacity;
+    int fCount, fRemoved, fCapacity;
     SkAutoTArray<Slot> fSlots;
 };
 
 // Maps K->V.  A more user-friendly wrapper around SkTHashTable, suitable for most use cases.
 // K and V are treated as ordinary copyable C++ types, with no assumed relationship between the two.
-template <typename K, typename V, uint32_t(*HashK)(const K&)>
+template <typename K, typename V, uint32_t(*HashK)(const K&) = &SkGoodHash>
 class SkTHashMap : SkNoncopyable {
 public:
     SkTHashMap() {}
@@ -175,8 +218,23 @@
         return NULL;
     }
 
+    // Remove the key/value entry in the table with this key.
+    void remove(const K& key) {
+        SkASSERT(this->find(key));
+        fTable.remove(key);
+    }
+
     // Call fn on every key/value pair in the table.  You may mutate the value but not the key.
-    void foreach(void(*fn)(K, V*)) { fTable.foreach(ForEach, fn); }
+    template <typename Fn>  // f(K, V*) or f(const K&, V*)
+    void foreach(Fn&& fn) {
+        fTable.foreach([&fn](Pair* p){ fn(p->key, &p->val); });
+    }
+
+    // Call fn on every key/value pair in the table.  You may not mutate anything.
+    template <typename Fn>  // f(K, V), f(const K&, V), f(K, const V&) or f(const K&, const V&).
+    void foreach(Fn&& fn) const {
+        fTable.foreach([&fn](const Pair& p){ fn(p.key, p.val); });
+    }
 
 private:
     struct Pair {
@@ -185,13 +243,12 @@
         static const K& GetKey(const Pair& p) { return p.key; }
         static uint32_t Hash(const K& key) { return HashK(key); }
     };
-    static void ForEach(Pair* p, void (*fn)(K, V*)) { fn(p->key, &p->val); }
 
     SkTHashTable<Pair, K> fTable;
 };
 
 // A set of T.  T is treated as an ordiary copyable C++ type.
-template <typename T, uint32_t(*HashT)(const T&)>
+template <typename T, uint32_t(*HashT)(const T&) = &SkGoodHash>
 class SkTHashSet : SkNoncopyable {
 public:
     SkTHashSet() {}
@@ -206,7 +263,23 @@
     void add(const T& item) { fTable.set(item); }
 
     // Is this item in the set?
-    bool contains(const T& item) const { return SkToBool(fTable.find(item)); }
+    bool contains(const T& item) const { return SkToBool(this->find(item)); }
+
+    // If an item equal to this is in the set, return a pointer to it, otherwise null.
+    // This pointer remains valid until the next call to add().
+    const T* find(const T& item) const { return fTable.find(item); }
+
+    // Remove the item in the set equal to this.
+    void remove(const T& item) {
+        SkASSERT(this->contains(item));
+        fTable.remove(item);
+    }
+
+    // Call fn on every item in the set.  You may not mutate anything.
+    template <typename Fn>  // f(T), f(const T&)
+    void foreach (Fn&& fn) const {
+        fTable.foreach(fn);
+    }
 
 private:
     struct Traits {
diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp
index bf62032..6ea081d 100644
--- a/src/core/SkTextBlob.cpp
+++ b/src/core/SkTextBlob.cpp
@@ -11,6 +11,87 @@
 #include "SkTypeface.h"
 #include "SkWriteBuffer.h"
 
+namespace {
+
+// TODO(fmalita): replace with SkFont.
+class RunFont : SkNoncopyable {
+public:
+    RunFont(const SkPaint& paint)
+        : fSize(paint.getTextSize())
+        , fScaleX(paint.getTextScaleX())
+        , fTypeface(SkSafeRef(paint.getTypeface()))
+        , fSkewX(paint.getTextSkewX())
+        , fHinting(paint.getHinting())
+        , fFlags(paint.getFlags() & kFlagsMask) { }
+
+    void applyToPaint(SkPaint* paint) const {
+        paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        paint->setTypeface(fTypeface.get());
+        paint->setTextSize(fSize);
+        paint->setTextScaleX(fScaleX);
+        paint->setTextSkewX(fSkewX);
+        paint->setHinting(static_cast<SkPaint::Hinting>(fHinting));
+
+        paint->setFlags((paint->getFlags() & ~kFlagsMask) | fFlags);
+    }
+
+    bool operator==(const RunFont& other) const {
+        return fTypeface == other.fTypeface
+            && fSize == other.fSize
+            && fScaleX == other.fScaleX
+            && fSkewX == other.fSkewX
+            && fHinting == other.fHinting
+            && fFlags == other.fFlags;
+    }
+
+    bool operator!=(const RunFont& other) const {
+        return !(*this == other);
+    }
+
+    uint32_t flags() const { return fFlags; }
+
+private:
+    const static uint32_t kFlagsMask =
+        SkPaint::kAntiAlias_Flag          |
+        SkPaint::kUnderlineText_Flag      |
+        SkPaint::kStrikeThruText_Flag     |
+        SkPaint::kFakeBoldText_Flag       |
+        SkPaint::kLinearText_Flag         |
+        SkPaint::kSubpixelText_Flag       |
+        SkPaint::kDevKernText_Flag        |
+        SkPaint::kLCDRenderText_Flag      |
+        SkPaint::kEmbeddedBitmapText_Flag |
+        SkPaint::kAutoHinting_Flag        |
+        SkPaint::kVerticalText_Flag       |
+        SkPaint::kGenA8FromLCD_Flag       |
+        SkPaint::kDistanceFieldTextTEMP_Flag;
+
+    SkScalar                 fSize;
+    SkScalar                 fScaleX;
+
+    // Keep this SkAutoTUnref off the first position, to avoid interfering with SkNoncopyable
+    // empty baseclass optimization (http://code.google.com/p/skia/issues/detail?id=3694).
+    SkAutoTUnref<SkTypeface> fTypeface;
+    SkScalar                 fSkewX;
+
+    SK_COMPILE_ASSERT(SkPaint::kFull_Hinting < 4, insufficient_hinting_bits);
+    uint32_t                 fHinting : 2;
+    SK_COMPILE_ASSERT((kFlagsMask & 0xffff) == kFlagsMask, insufficient_flags_bits);
+    uint32_t                 fFlags : 16;
+
+    typedef SkNoncopyable INHERITED;
+};
+
+struct RunFontStorageEquivalent {
+    SkScalar fSize, fScaleX;
+    void*    fTypeface;
+    SkScalar fSkewX;
+    uint32_t fFlags;
+};
+SK_COMPILE_ASSERT(sizeof(RunFont) == sizeof(RunFontStorageEquivalent), runfont_should_stay_packed);
+
+} // anonymous namespace
+
 //
 // Textblob data is laid out into externally-managed storage as follows:
 //
@@ -26,9 +107,9 @@
 class SkTextBlob::RunRecord {
 public:
     RunRecord(uint32_t count, const SkPoint& offset, const SkPaint& font, GlyphPositioning pos)
-        : fCount(count)
+        : fFont(font)
+        , fCount(count)
         , fOffset(offset)
-        , fFont(font)
         , fPositioning(pos) {
         SkDEBUGCODE(fMagic = kRunRecordMagic);
     }
@@ -41,7 +122,7 @@
         return fOffset;
     }
 
-    const SkPaint& font() const {
+    const RunFont& font() const {
         return fFont;
     }
 
@@ -100,17 +181,27 @@
         memmove(posBuffer(), initialPosBuffer, copySize);
     }
 
+    RunFont          fFont;
     uint32_t         fCount;
     SkPoint          fOffset;
-    SkPaint          fFont;
     GlyphPositioning fPositioning;
 
     SkDEBUGCODE(unsigned fMagic;)
 };
 
+static int32_t gNextID = 1;
+static int32_t next_id() {
+    int32_t id;
+    do {
+        id = sk_atomic_inc(&gNextID);
+    } while (id == SK_InvalidGenID);
+    return id;
+}
+
 SkTextBlob::SkTextBlob(int runCount, const SkRect& bounds)
     : fRunCount(runCount)
-    , fBounds(bounds) {
+    , fBounds(bounds)
+    , fUniqueID(next_id()) {
 }
 
 SkTextBlob::~SkTextBlob() {
@@ -123,17 +214,6 @@
     }
 }
 
-uint32_t SkTextBlob::uniqueID() const {
-    static int32_t  gTextBlobGenerationID; // = 0;
-
-    // loop in case our global wraps around, as we never want to return SK_InvalidGenID
-    while (SK_InvalidGenID == fUniqueID) {
-        fUniqueID = sk_atomic_inc(&gTextBlobGenerationID) + 1;
-    }
-
-    return fUniqueID;
-}
-
 void SkTextBlob::flatten(SkWriteBuffer& buffer) const {
     int runCount = fRunCount;
 
@@ -263,29 +343,11 @@
 void SkTextBlob::RunIterator::applyFontToPaint(SkPaint* paint) const {
     SkASSERT(!this->done());
 
-    const SkPaint& font = fCurrentRun->font();
+    fCurrentRun->font().applyToPaint(paint);
+}
 
-    paint->setTypeface(font.getTypeface());
-    paint->setTextEncoding(font.getTextEncoding());
-    paint->setTextSize(font.getTextSize());
-    paint->setTextScaleX(font.getTextScaleX());
-    paint->setTextSkewX(font.getTextSkewX());
-    paint->setHinting(font.getHinting());
-
-    uint32_t flagsMask = SkPaint::kAntiAlias_Flag
-                       | SkPaint::kUnderlineText_Flag
-                       | SkPaint::kStrikeThruText_Flag
-                       | SkPaint::kFakeBoldText_Flag
-                       | SkPaint::kLinearText_Flag
-                       | SkPaint::kSubpixelText_Flag
-                       | SkPaint::kDevKernText_Flag
-                       | SkPaint::kLCDRenderText_Flag
-                       | SkPaint::kEmbeddedBitmapText_Flag
-                       | SkPaint::kAutoHinting_Flag
-                       | SkPaint::kVerticalText_Flag
-                       | SkPaint::kGenA8FromLCD_Flag
-                       | SkPaint::kDistanceFieldTextTEMP_Flag;
-    paint->setFlags((paint->getFlags() & ~flagsMask) | (font.getFlags() & flagsMask));
+bool SkTextBlob::RunIterator::isLCD() const {
+    return SkToBool(fCurrentRun->font().flags() & SkPaint::kLCDRenderText_Flag);
 }
 
 SkTextBlobBuilder::SkTextBlobBuilder()
@@ -309,7 +371,9 @@
     SkASSERT(SkTextBlob::kDefault_Positioning == run.positioning());
 
     SkRect bounds;
-    run.font().measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds);
+    SkPaint paint;
+    run.font().applyToPaint(&paint);
+    paint.measureText(run.glyphBuffer(), run.glyphCount() * sizeof(uint16_t), &bounds);
 
     return bounds.makeOffset(run.offset().x(), run.offset().y());
 }
@@ -347,7 +411,9 @@
     }
 
     // Expand by typeface glyph bounds.
-    const SkRect fontBounds = run.font().getFontBounds();
+    SkPaint paint;
+    run.font().applyToPaint(&paint);
+    const SkRect fontBounds = paint.getFontBounds();
     bounds.fLeft   += fontBounds.left();
     bounds.fTop    += fontBounds.top();
     bounds.fRight  += fontBounds.right();
@@ -367,7 +433,6 @@
     SkASSERT(fLastRun >= sizeof(SkTextBlob));
     SkTextBlob::RunRecord* run = reinterpret_cast<SkTextBlob::RunRecord*>(fStorage.get() +
                                                                           fLastRun);
-    SkASSERT(SkPaint::kGlyphID_TextEncoding == run->font().getTextEncoding());
 
     // FIXME: we should also use conservative bounds for kDefault_Positioning.
     SkRect runBounds = SkTextBlob::kDefault_Positioning == run->positioning() ?
diff --git a/src/core/SkTime.cpp b/src/core/SkTime.cpp
new file mode 100644
index 0000000..b8e4236
--- /dev/null
+++ b/src/core/SkTime.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 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 "SkTime.h"
+
+void SkTime::DateTime::toISO8601(SkString* dst) const {
+    if (dst) {
+        int timeZoneMinutes = SkToInt(fTimeZoneMinutes);
+        char timezoneSign = timeZoneMinutes >= 0 ? '+' : '-';
+        int timeZoneHours = abs(timeZoneMinutes) / 60;
+        timeZoneMinutes = abs(timeZoneMinutes) % 60;
+        dst->printf("%04u-%02u-%02uT%02u:%02u:%02u%c%02d:%02d",
+                    static_cast<unsigned>(fYear), static_cast<unsigned>(fMonth),
+                    static_cast<unsigned>(fDay), static_cast<unsigned>(fHour),
+                    static_cast<unsigned>(fMinute),
+                    static_cast<unsigned>(fSecond), timezoneSign, timeZoneHours,
+                    timeZoneMinutes);
+    }
+}
diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp
index ad928bb..0f5d70b 100644
--- a/src/core/SkTypeface.cpp
+++ b/src/core/SkTypeface.cpp
@@ -29,36 +29,36 @@
 protected:
     SkEmptyTypeface() : SkTypeface(SkFontStyle(), 0, true) { }
 
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE { return NULL; }
-    SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE {
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override { return NULL; }
+    SkScalerContext* onCreateScalerContext(const SkDescriptor*) const override {
         return NULL;
     }
-    void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE { }
+    void onFilterRec(SkScalerContextRec*) const override { }
     virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                                SkAdvancedTypefaceMetrics::PerGlyphInfo,
-                                const uint32_t*, uint32_t) const SK_OVERRIDE { return NULL; }
-    void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE { }
+                                PerGlyphInfo,
+                                const uint32_t*, uint32_t) const override { return NULL; }
+    void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { }
     virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
-                                uint16_t glyphs[], int glyphCount) const SK_OVERRIDE {
+                                uint16_t glyphs[], int glyphCount) const override {
         if (glyphs && glyphCount > 0) {
             sk_bzero(glyphs, glyphCount * sizeof(glyphs[0]));
         }
         return 0;
     }
-    int onCountGlyphs() const SK_OVERRIDE { return 0; };
-    int onGetUPEM() const SK_OVERRIDE { return 0; };
+    int onCountGlyphs() const override { return 0; };
+    int onGetUPEM() const override { return 0; };
     class EmptyLocalizedStrings : public SkTypeface::LocalizedStrings {
     public:
-        bool next(SkTypeface::LocalizedString*) SK_OVERRIDE { return false; }
+        bool next(SkTypeface::LocalizedString*) override { return false; }
     };
-    void onGetFamilyName(SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(SkString* familyName) const override {
         familyName->reset();
     }
-    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE {
+    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override {
         return SkNEW(EmptyLocalizedStrings);
     };
-    int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE { return 0; }
-    size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const SK_OVERRIDE {
+    int onGetTableTags(SkFontTableTag tags[]) const override { return 0; }
+    size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override {
         return 0;
     }
 };
@@ -76,7 +76,7 @@
     SkAutoMutexAcquire lock(&gCreateDefaultMutex);
 
     SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
-    SkTypeface* t = fm->legacyCreateTypeface(NULL, style);;
+    SkTypeface* t = fm->legacyCreateTypeface(NULL, style);
     return t ? t : SkEmptyTypeface::Create();
 }
 
@@ -265,7 +265,7 @@
 }
 
 SkAdvancedTypefaceMetrics* SkTypeface::getAdvancedTypefaceMetrics(
-                                SkAdvancedTypefaceMetrics::PerGlyphInfo info,
+                                PerGlyphInfo info,
                                 const uint32_t* glyphIDs,
                                 uint32_t glyphIDsCount) const {
     SkAdvancedTypefaceMetrics* result =
diff --git a/src/core/SkTypefaceCache.cpp b/src/core/SkTypefaceCache.cpp
index 8adffe6..f253b60 100644
--- a/src/core/SkTypefaceCache.cpp
+++ b/src/core/SkTypefaceCache.cpp
@@ -34,18 +34,6 @@
     rec->fRequestedStyle = requestedStyle;
 }
 
-SkTypeface* SkTypefaceCache::findByID(SkFontID fontID) const {
-    const Rec* curr = fArray.begin();
-    const Rec* stop = fArray.end();
-    while (curr < stop) {
-        if (curr->fFace->uniqueID() == fontID) {
-            return curr->fFace;
-        }
-        curr += 1;
-    }
-    return NULL;
-}
-
 SkTypeface* SkTypefaceCache::findByProcAndRef(FindProc proc, void* ctx) const {
     const Rec* curr = fArray.begin();
     const Rec* stop = fArray.end();
@@ -100,11 +88,6 @@
     Get().add(face, requestedStyle);
 }
 
-SkTypeface* SkTypefaceCache::FindByID(SkFontID fontID) {
-    SkAutoMutexAcquire ama(gMutex);
-    return Get().findByID(fontID);
-}
-
 SkTypeface* SkTypefaceCache::FindByProcAndRef(FindProc proc, void* ctx) {
     SkAutoMutexAcquire ama(gMutex);
     SkTypeface* typeface = Get().findByProcAndRef(proc, ctx);
diff --git a/src/core/SkTypefaceCache.h b/src/core/SkTypefaceCache.h
index c6b433d..7929586 100644
--- a/src/core/SkTypefaceCache.h
+++ b/src/core/SkTypefaceCache.h
@@ -42,14 +42,6 @@
     void add(SkTypeface*, const SkFontStyle& requested);
 
     /**
-     *  Search the cache for a typeface with the specified fontID (uniqueID).
-     *  If one is found, return it (its reference count is unmodified). If none
-     *  is found, return NULL. The reference count is unmodified as it is
-     *  assumed that the stack will contain a ref to the typeface.
-     */
-    SkTypeface* findByID(SkFontID findID) const;
-
-    /**
      *  Iterate through the cache, calling proc(typeface, ctx) with each
      *  typeface. If proc returns true, then we return that typeface (this
      *  ref()s the typeface). If it never returns true, we return NULL.
@@ -73,7 +65,6 @@
     // These are static wrappers around a global instance of a cache.
 
     static void Add(SkTypeface*, const SkFontStyle& requested);
-    static SkTypeface* FindByID(SkFontID fontID);
     static SkTypeface* FindByProcAndRef(FindProc proc, void* ctx);
     static void PurgeAll();
 
diff --git a/src/core/SkUtils.cpp b/src/core/SkUtils.cpp
index b063071..33ea4db 100644
--- a/src/core/SkUtils.cpp
+++ b/src/core/SkUtils.cpp
@@ -134,12 +134,12 @@
 
 }  // namespace
 
-void sk_memset16(uint16_t dst[], uint16_t value, int count) {
+void sk_memset16_large(uint16_t dst[], uint16_t value, int count) {
     SK_DECLARE_STATIC_LAZY_FN_PTR(SkMemset16Proc, proc, choose_memset16);
     proc.get()(dst, value, count);
 }
 
-void sk_memset32(uint32_t dst[], uint32_t value, int count) {
+void sk_memset32_large(uint32_t dst[], uint32_t value, int count) {
     SK_DECLARE_STATIC_LAZY_FN_PTR(SkMemset32Proc, proc, choose_memset32);
     proc.get()(dst, value, count);
 }
diff --git a/src/core/SkUtilsArm.h b/src/core/SkUtilsArm.h
index f156481..51ae7e4 100644
--- a/src/core/SkUtilsArm.h
+++ b/src/core/SkUtilsArm.h
@@ -21,9 +21,9 @@
 #define SK_ARM_NEON_MODE_ALWAYS   1
 #define SK_ARM_NEON_MODE_DYNAMIC  2
 
-#if defined(SK_CPU_ARM32) && defined(SK_ARM_HAS_OPTIONAL_NEON)
+#if defined(SK_ARM_HAS_OPTIONAL_NEON)
 #  define SK_ARM_NEON_MODE  SK_ARM_NEON_MODE_DYNAMIC
-#elif defined(SK_CPU_ARM32) && defined(SK_ARM_HAS_NEON) || defined(SK_CPU_ARM64)
+#elif defined(SK_ARM_HAS_NEON)
 #  define SK_ARM_NEON_MODE  SK_ARM_NEON_MODE_ALWAYS
 #else
 #  define SK_ARM_NEON_MODE  SK_ARM_NEON_MODE_NONE
diff --git a/src/core/SkValidatingReadBuffer.h b/src/core/SkValidatingReadBuffer.h
index 916bed4..bcdcba5 100644
--- a/src/core/SkValidatingReadBuffer.h
+++ b/src/core/SkValidatingReadBuffer.h
@@ -23,48 +23,48 @@
     SkValidatingReadBuffer(const void* data, size_t size);
     virtual ~SkValidatingReadBuffer();
 
-    const void* skip(size_t size) SK_OVERRIDE;
+    const void* skip(size_t size) override;
 
     // primitives
-    bool readBool() SK_OVERRIDE;
-    SkColor readColor() SK_OVERRIDE;
-    SkFixed readFixed() SK_OVERRIDE;
-    int32_t readInt() SK_OVERRIDE;
-    SkScalar readScalar() SK_OVERRIDE;
-    uint32_t readUInt() SK_OVERRIDE;
-    int32_t read32() SK_OVERRIDE;
+    bool readBool() override;
+    SkColor readColor() override;
+    SkFixed readFixed() override;
+    int32_t readInt() override;
+    SkScalar readScalar() override;
+    uint32_t readUInt() override;
+    int32_t read32() override;
 
     // strings -- the caller is responsible for freeing the string contents
-    void readString(SkString* string) SK_OVERRIDE;
-    void* readEncodedString(size_t* length, SkPaint::TextEncoding encoding) SK_OVERRIDE;
+    void readString(SkString* string) override;
+    void* readEncodedString(size_t* length, SkPaint::TextEncoding encoding) override;
 
     // common data structures
-    SkFlattenable* readFlattenable(SkFlattenable::Type type) SK_OVERRIDE;
-    void skipFlattenable() SK_OVERRIDE;
-    void readPoint(SkPoint* point) SK_OVERRIDE;
-    void readMatrix(SkMatrix* matrix) SK_OVERRIDE;
-    void readIRect(SkIRect* rect) SK_OVERRIDE;
-    void readRect(SkRect* rect) SK_OVERRIDE;
-    void readRegion(SkRegion* region) SK_OVERRIDE;
-    void readPath(SkPath* path) SK_OVERRIDE;
+    SkFlattenable* readFlattenable(SkFlattenable::Type type) override;
+    void skipFlattenable() override;
+    void readPoint(SkPoint* point) override;
+    void readMatrix(SkMatrix* matrix) override;
+    void readIRect(SkIRect* rect) override;
+    void readRect(SkRect* rect) override;
+    void readRegion(SkRegion* region) override;
+    void readPath(SkPath* path) override;
 
     // binary data and arrays
-    bool readByteArray(void* value, size_t size) SK_OVERRIDE;
-    bool readColorArray(SkColor* colors, size_t size) SK_OVERRIDE;
-    bool readIntArray(int32_t* values, size_t size) SK_OVERRIDE;
-    bool readPointArray(SkPoint* points, size_t size) SK_OVERRIDE;
-    bool readScalarArray(SkScalar* values, size_t size) SK_OVERRIDE;
+    bool readByteArray(void* value, size_t size) override;
+    bool readColorArray(SkColor* colors, size_t size) override;
+    bool readIntArray(int32_t* values, size_t size) override;
+    bool readPointArray(SkPoint* points, size_t size) override;
+    bool readScalarArray(SkScalar* values, size_t size) override;
 
     // helpers to get info about arrays and binary data
-    uint32_t getArrayCount() SK_OVERRIDE;
+    uint32_t getArrayCount() override;
 
     // TODO: Implement this (securely) when needed
-    SkTypeface* readTypeface() SK_OVERRIDE;
+    SkTypeface* readTypeface() override;
 
-    bool validate(bool isValid) SK_OVERRIDE;
-    bool isValid() const SK_OVERRIDE;
+    bool validate(bool isValid) override;
+    bool isValid() const override;
 
-    bool validateAvailable(size_t size) SK_OVERRIDE;
+    bool validateAvailable(size_t size) override;
 
 private:
     bool readArray(void* value, size_t size, size_t elementSize);
diff --git a/src/core/SkVarAlloc.cpp b/src/core/SkVarAlloc.cpp
index ba5d6f5..5d395d4 100644
--- a/src/core/SkVarAlloc.cpp
+++ b/src/core/SkVarAlloc.cpp
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "SkVarAlloc.h"
 
 // We use non-standard malloc diagnostic methods to make sure our allocations are sized well.
@@ -20,11 +27,19 @@
 };
 
 SkVarAlloc::SkVarAlloc(size_t minLgSize)
-    : fByte(NULL)
+    : fBytesAllocated(0)
+    , fByte(NULL)
     , fRemaining(0)
     , fLgSize(minLgSize)
     , fBlock(NULL) {}
 
+SkVarAlloc::SkVarAlloc(size_t minLgSize, char* storage, size_t len)
+    : fBytesAllocated(0)
+    , fByte(storage)
+    , fRemaining(len)
+    , fLgSize(minLgSize)
+    , fBlock(NULL) {}
+
 SkVarAlloc::~SkVarAlloc() {
     Block* b = fBlock;
     while (b) {
@@ -41,34 +56,15 @@
     while (alloc < bytes + sizeof(Block)) {
         alloc *= 2;
     }
+    fBytesAllocated += alloc;
     fBlock = Block::Alloc(fBlock, alloc, flags);
     fByte = fBlock->data();
     fRemaining = alloc - sizeof(Block);
 
 #if defined(SK_BUILD_FOR_MAC)
     SkASSERT(alloc == malloc_good_size(alloc));
-#elif defined(SK_BUILD_FOR_UNIX)
+#elif defined(SK_BUILD_FOR_UNIX) && !defined(__UCLIBC__)
     // TODO(mtklein): tune so we can assert something like this
     //SkASSERT(alloc == malloc_usable_size(fBlock));
 #endif
 }
-
-static size_t heap_size(void* p) {
-#if defined(SK_BUILD_FOR_MAC)
-    return malloc_size(p);
-#elif defined(SK_BUILD_FOR_UNIX)
-    return malloc_usable_size(p);
-#elif defined(SK_BUILD_FOR_WIN32)
-    return _msize(p);
-#else
-    return 0;  // Tough luck.
-#endif
-}
-
-size_t SkVarAlloc::approxBytesAllocated() const {
-    size_t sum = 0;
-    for (Block* b = fBlock; b; b = b->prev) {
-        sum += heap_size(b);
-    }
-    return sum;
-}
diff --git a/src/core/SkVarAlloc.h b/src/core/SkVarAlloc.h
index fb55192..e8236cf 100644
--- a/src/core/SkVarAlloc.h
+++ b/src/core/SkVarAlloc.h
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #ifndef SkVarAlloc_DEFINED
 #define SkVarAlloc_DEFINED
 
@@ -7,6 +14,9 @@
 public:
     // Smallest block we'll allocate is 2**N bytes.
     explicit SkVarAlloc(size_t minLgSize);
+    // Same as above, but first uses up to len bytes from storage.
+    SkVarAlloc(size_t minLgSize, char* storage, size_t len);
+
     ~SkVarAlloc();
 
     // Returns contiguous bytes aligned at least for pointers.  You may pass SK_MALLOC_THROW, etc.
@@ -25,12 +35,14 @@
     }
 
     // Returns our best estimate of the number of bytes we've allocated.
-    // (We intentionally do not track this precisely to save space.)
-    size_t approxBytesAllocated() const;
+    // (We may not track this precisely to save space.)
+    size_t approxBytesAllocated() const { return fBytesAllocated; }
 
 private:
     void makeSpace(size_t bytes, unsigned flags);
 
+    size_t fBytesAllocated;
+
     char* fByte;
     unsigned fRemaining;
     unsigned fLgSize;
@@ -38,6 +50,6 @@
     struct Block;
     Block* fBlock;
 };
-SK_COMPILE_ASSERT(sizeof(SkVarAlloc) <= 24, SkVarAllocSize);
+SK_COMPILE_ASSERT(sizeof(SkVarAlloc) <= 32, SkVarAllocSize);
 
 #endif//SkVarAlloc_DEFINED
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
index 08f760d..8824a88 100644
--- a/src/core/SkXfermode.cpp
+++ b/src/core/SkXfermode.cpp
@@ -9,35 +9,31 @@
 #include "SkXfermode.h"
 #include "SkXfermode_opts_SSE2.h"
 #include "SkXfermode_proccoeff.h"
+#include "Sk4px.h"
 #include "SkColorPriv.h"
 #include "SkLazyPtr.h"
 #include "SkMathPriv.h"
+#include "SkPMFloat.h"
 #include "SkReadBuffer.h"
 #include "SkString.h"
 #include "SkUtilsArm.h"
 #include "SkWriteBuffer.h"
 
+// When implemented, the Sk4f and Sk4px xfermodes beat src/opts/SkXfermodes_opts_SSE2's.
+// When implemented, the Sk4px, but not Sk4f, xfermodes beat src/opts/SkXfermodes_arm_neon's.
+#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
+    #define SK_4F_XFERMODES_ARE_FAST
+    #define SK_4PX_XFERMODES_ARE_FAST
+#elif defined(SK_ARM_HAS_NEON)
+    #define SK_4PX_XFERMODES_ARE_FAST
+#endif
+
 #if !SK_ARM_NEON_IS_NONE
-#include "SkXfermode_opts_arm_neon.h"
+    #include "SkXfermode_opts_arm_neon.h"
 #endif
 
 #define SkAlphaMulAlpha(a, b)   SkMulDiv255Round(a, b)
 
-#if 0
-// idea for higher precision blends in xfer procs (and slightly faster)
-// see DstATop as a probable caller
-static U8CPU mulmuldiv255round(U8CPU a, U8CPU b, U8CPU c, U8CPU d) {
-    SkASSERT(a <= 255);
-    SkASSERT(b <= 255);
-    SkASSERT(c <= 255);
-    SkASSERT(d <= 255);
-    unsigned prod = SkMulS16(a, b) + SkMulS16(c, d) + 128;
-    unsigned result = (prod + (prod >> 8)) >> 8;
-    SkASSERT(result <= 255);
-    return result;
-}
-#endif
-
 static inline unsigned saturated_add(unsigned a, unsigned b) {
     SkASSERT(a <= 255);
     SkASSERT(b <= 255);
@@ -828,12 +824,12 @@
     if (CANNOT_USE_COEFF == fSrcCoeff) {
         return false;
     }
-   
+
     if (SkXfermode::kDA_Coeff == fSrcCoeff || SkXfermode::kDC_Coeff == fSrcCoeff ||
         SkXfermode::kIDA_Coeff == fSrcCoeff || SkXfermode::kIDC_Coeff == fSrcCoeff) {
         return false;
     }
-        
+
     switch (fDstCoeff) {
         case SkXfermode::kZero_Coeff:
             return true;
@@ -1020,8 +1016,8 @@
         return SkNEW_ARGS(SkClearXfermode, (rec));
     }
 
-    void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
-    void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
+    void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const override;
+    void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const override;
 
     SK_TO_STRING_OVERRIDE()
 
@@ -1082,8 +1078,8 @@
         return SkNEW_ARGS(SkSrcXfermode, (rec));
     }
 
-    void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
-    void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
+    void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const override;
+    void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const override;
 
     SK_TO_STRING_OVERRIDE()
 
@@ -1148,7 +1144,7 @@
         return SkNEW_ARGS(SkDstInXfermode, (rec));
     }
 
-    void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
+    void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const override;
 
     SK_TO_STRING_OVERRIDE()
 
@@ -1186,13 +1182,271 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+/* These modes can merge coverage into src-alpha
+ *
+{ dst_modeproc,     SkXfermode::kZero_Coeff,    SkXfermode::kOne_Coeff },
+{ srcover_modeproc, SkXfermode::kOne_Coeff,     SkXfermode::kISA_Coeff },
+{ dstover_modeproc, SkXfermode::kIDA_Coeff,     SkXfermode::kOne_Coeff },
+{ dstout_modeproc,  SkXfermode::kZero_Coeff,    SkXfermode::kISA_Coeff },
+{ srcatop_modeproc, SkXfermode::kDA_Coeff,      SkXfermode::kISA_Coeff },
+{ xor_modeproc,     SkXfermode::kIDA_Coeff,     SkXfermode::kISA_Coeff },
+{ plus_modeproc,    SkXfermode::kOne_Coeff,     SkXfermode::kOne_Coeff },
+{ screen_modeproc,  SkXfermode::kOne_Coeff,     SkXfermode::kISC_Coeff },
+*/
+
+static const float gInv255 = 0.0039215683f; //  (1.0f / 255) - ULP == SkBits2Float(0x3B808080)
+
+static Sk4f ramp(const Sk4f& v0, const Sk4f& v1, const Sk4f& t) {
+    return v0 + (v1 - v0) * t;
+}
+
+static Sk4f clamp_255(const Sk4f& value) {
+    return Sk4f::Min(Sk4f(255), value);
+}
+
+static Sk4f clamp_0_255(const Sk4f& value) {
+    return Sk4f::Max(Sk4f(0), Sk4f::Min(Sk4f(255), value));
+}
+
+/**
+ *  Some modes can, due to very slight numerical error, generate "invalid" pmcolors...
+ *
+ *  e.g.
+ *      alpha = 100.9999
+ *      red   = 101
+ *
+ *  or
+ *      alpha = 255.0001
+ *
+ *  If we know we're going to write-out the values as bytes, we can relax these somewhat,
+ *  since we only really need to enforce that the bytes are valid premul...
+ *
+ *  To that end, this method asserts that the resulting pmcolor will be valid, but does not call
+ *  SkPMFloat::isValid(), as that would fire sometimes, but not result in a bad pixel.
+ */
+static inline SkPMFloat check_as_pmfloat(const Sk4f& value) {
+    SkPMFloat pm = value;
+#ifdef SK_DEBUG
+    (void)pm.round();
+#endif
+    return pm;
+}
+
+//  kSrcATop_Mode,  //!< [Da, Sc * Da + (1 - Sa) * Dc]
+struct SrcATop4f {
+    static SkPMFloat Xfer(const SkPMFloat& src, const SkPMFloat& dst) {
+        const Sk4f inv255(gInv255);
+        return check_as_pmfloat(dst + (src * Sk4f(dst.a()) - dst * Sk4f(src.a())) * inv255);
+    }
+    static Sk4px Xfer(const Sk4px& src, const Sk4px& dst) {
+        return Sk4px::Wide(src.mulWiden(dst.alphas()) + dst.mulWiden(src.alphas().inv()))
+            .div255RoundNarrow();
+    }
+    static const bool kFoldCoverageIntoSrcAlpha = true;
+    static const SkXfermode::Mode kMode = SkXfermode::kSrcATop_Mode;
+};
+
+//  kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]
+struct DstATop4f {
+    static SkPMFloat Xfer(const SkPMFloat& src, const SkPMFloat& dst) {
+        return SrcATop4f::Xfer(dst, src);
+    }
+    static Sk4px Xfer(const Sk4px& src, const Sk4px& dst) {
+        return SrcATop4f::Xfer(dst, src);
+    }
+    static const bool kFoldCoverageIntoSrcAlpha = false;
+    static const SkXfermode::Mode kMode = SkXfermode::kDstATop_Mode;
+};
+
+//  kXor_Mode   [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
+struct Xor4f {
+    static SkPMFloat Xfer(const SkPMFloat& src, const SkPMFloat& dst) {
+        const Sk4f inv255(gInv255);
+        return check_as_pmfloat(src + dst - (src * Sk4f(dst.a()) + dst * Sk4f(src.a())) * inv255);
+    }
+    static Sk4px Xfer(const Sk4px& src, const Sk4px& dst) {
+        return Sk4px::Wide(src.mulWiden(dst.alphas().inv()) + dst.mulWiden(src.alphas().inv()))
+            .div255RoundNarrow();
+    }
+    static const bool kFoldCoverageIntoSrcAlpha = true;
+    static const SkXfermode::Mode kMode = SkXfermode::kXor_Mode;
+};
+
+//  kPlus_Mode   [Sa + Da, Sc + Dc]
+struct Plus4f {
+    static SkPMFloat Xfer(const SkPMFloat& src, const SkPMFloat& dst) {
+        return check_as_pmfloat(clamp_255(src + dst));
+    }
+    static Sk4px Xfer(const Sk4px& src, const Sk4px& dst) {
+        return src.saturatedAdd(dst);
+    }
+    static const bool kFoldCoverageIntoSrcAlpha = false;
+    static const SkXfermode::Mode kMode = SkXfermode::kPlus_Mode;
+};
+
+//  kModulate_Mode   [Sa * Da, Sc * Dc]
+struct Modulate4f {
+    static SkPMFloat Xfer(const SkPMFloat& src, const SkPMFloat& dst) {
+        const Sk4f inv255(gInv255);
+        return check_as_pmfloat(src * dst * inv255);
+    }
+    static Sk4px Xfer(const Sk4px& src, const Sk4px& dst) {
+        return src.mulWiden(dst).div255RoundNarrow();
+    }
+    static const bool kFoldCoverageIntoSrcAlpha = false;
+    static const SkXfermode::Mode kMode = SkXfermode::kModulate_Mode;
+};
+
+//  kScreen_Mode   [S + D - S * D]
+struct Screen4f {
+    static SkPMFloat Xfer(const SkPMFloat& src, const SkPMFloat& dst) {
+        const Sk4f inv255(gInv255);
+        return check_as_pmfloat(src + dst - src * dst * inv255);
+    }
+    static Sk4px Xfer(const Sk4px& src, const Sk4px& dst) {
+        // Doing the math as S + (1-S)*D or S + (D - S*D) means the add and subtract can be done
+        // in 8-bit space without overflow.  S + (1-S)*D is a touch faster because inv() is cheap.
+        return src + src.inv().mulWiden(dst).div255RoundNarrow();
+    }
+    static const bool kFoldCoverageIntoSrcAlpha = true;
+    static const SkXfermode::Mode kMode = SkXfermode::kScreen_Mode;
+};
+
+struct Multiply4f {
+    static SkPMFloat Xfer(const SkPMFloat& src, const SkPMFloat& dst) {
+        const Sk4f inv255(gInv255);
+        Sk4f sa = Sk4f(src.a());
+        Sk4f da = Sk4f(dst.a());
+        Sk4f sc = src;
+        Sk4f dc = dst;
+        Sk4f rc = sc + dc + (sc * (dc - da) - dc * sa) * inv255;
+        // ra = srcover(sa, da), but the calc for rc happens to accomplish this for us
+        return check_as_pmfloat(clamp_0_255(rc));
+    }
+    static Sk4px Xfer(const Sk4px& src, const Sk4px& dst) {
+        return Sk4px::Wide(src.mulWiden(dst.alphas().inv()) +
+                           dst.mulWiden(src.alphas().inv()) +
+                           src.mulWiden(dst))
+            .div255RoundNarrow();
+    }
+    static const bool kFoldCoverageIntoSrcAlpha = false;
+    static const SkXfermode::Mode kMode = SkXfermode::kMultiply_Mode;
+};
+
+// [ sa + da - sa*da, sc + dc - 2*min(sc*da, dc*sa) ]  (And notice sa*da == min(sa*da, da*sa).)
+struct Difference4f {
+    static SkPMFloat Xfer(const SkPMFloat& src, const SkPMFloat& dst) {
+        const Sk4f inv255(gInv255);
+        Sk4f sa = Sk4f(src.a());
+        Sk4f da = Sk4f(dst.a());
+        Sk4f sc = src;
+        Sk4f dc = dst;
+        Sk4f min = Sk4f::Min(sc * da, dc * sa) * inv255;
+        Sk4f ra = sc + dc - min;
+        return check_as_pmfloat(ra - min * SkPMFloat(0, 1, 1, 1));
+    }
+    static Sk4px Xfer(const Sk4px& src, const Sk4px& dst) {
+        auto m = Sk4px::Wide(Sk16h::Min(src.mulWiden(dst.alphas()), dst.mulWiden(src.alphas())))
+            .div255RoundNarrow();
+        // There's no chance of underflow, and if we subtract m before adding src+dst, no overflow.
+        return (src - m) + (dst - m.zeroAlphas());
+    }
+    static const bool kFoldCoverageIntoSrcAlpha = false;
+    static const SkXfermode::Mode kMode = SkXfermode::kDifference_Mode;
+};
+
+// [ sa + da - sa*da, sc + dc - 2*sc*dc ]
+struct Exclusion4f {
+    static SkPMFloat Xfer(const SkPMFloat& src, const SkPMFloat& dst) {
+        const Sk4f inv255(gInv255);
+        Sk4f sc = src;
+        Sk4f dc = dst;
+        Sk4f prod = sc * dc * inv255;
+        Sk4f ra = sc + dc - prod;
+        return check_as_pmfloat(ra - prod * SkPMFloat(0, 1, 1, 1));
+    }
+    static Sk4px Xfer(const Sk4px& src, const Sk4px& dst) {
+        auto p = src.mulWiden(dst).div255RoundNarrow();
+        // There's no chance of underflow, and if we subtract p before adding src+dst, no overflow.
+        return (src - p) + (dst - p.zeroAlphas());
+    }
+    static const bool kFoldCoverageIntoSrcAlpha = false;
+    static const SkXfermode::Mode kMode = SkXfermode::kExclusion_Mode;
+};
+
+template <typename ProcType>
+class SkT4fXfermode : public SkProcCoeffXfermode {
+public:
+    static SkXfermode* Create(const ProcCoeff& rec) {
+        return SkNEW_ARGS(SkT4fXfermode, (rec));
+    }
+
+    void xfer32(SkPMColor dst[], const SkPMColor src[], int n, const SkAlpha aa[]) const override {
+        if (NULL == aa) {
+            for (int i = 0; i < n; ++i) {
+                dst[i] = ProcType::Xfer(SkPMFloat(src[i]), SkPMFloat(dst[i])).round();
+            }
+        } else {
+            for (int i = 0; i < n; ++i) {
+                const Sk4f aa4 = Sk4f(aa[i] * gInv255);
+                SkPMFloat dstF(dst[i]);
+                SkPMFloat srcF(src[i]);
+                Sk4f res;
+                if (ProcType::kFoldCoverageIntoSrcAlpha) {
+                    Sk4f src4 = srcF;
+                    res = ProcType::Xfer(src4 * aa4, dstF);
+                } else {
+                    res = ramp(dstF, ProcType::Xfer(srcF, dstF), aa4);
+                }
+                dst[i] = SkPMFloat(res).round();
+            }
+        }
+    }
+
+private:
+    SkT4fXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, ProcType::kMode) {}
+
+    typedef SkProcCoeffXfermode INHERITED;
+};
+
+template <typename ProcType>
+class SkT4pxXfermode : public SkProcCoeffXfermode {
+public:
+    static SkXfermode* Create(const ProcCoeff& rec) {
+        return SkNEW_ARGS(SkT4pxXfermode, (rec));
+    }
+
+    void xfer32(SkPMColor dst[], const SkPMColor src[], int n, const SkAlpha aa[]) const override {
+        if (NULL == aa) {
+            Sk4px::MapDstSrc(n, dst, src, [&](const Sk4px& dst4, const Sk4px& src4) {
+                return ProcType::Xfer(src4, dst4);
+            });
+        } else {
+            Sk4px::MapDstSrcAlpha(n, dst, src, aa,
+                    [&](const Sk4px& dst4, const Sk4px& src4, const Sk16b& alpha) {
+                // We can't exploit kFoldCoverageIntoSrcAlpha. That requires >=24-bit intermediates.
+                Sk4px res4 = ProcType::Xfer(src4, dst4);
+                return Sk4px::Wide(res4.mulWiden(alpha) + dst4.mulWiden(Sk4px(alpha).inv()))
+                           .div255RoundNarrow();
+            });
+        }
+    }
+
+private:
+    SkT4pxXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, ProcType::kMode) {}
+
+    typedef SkProcCoeffXfermode INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
 class SkDstOutXfermode : public SkProcCoeffXfermode {
 public:
     static SkDstOutXfermode* Create(const ProcCoeff& rec) {
         return SkNEW_ARGS(SkDstOutXfermode, (rec));
     }
 
-    void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
+    void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const override;
 
     SK_TO_STRING_OVERRIDE()
 
@@ -1244,7 +1498,38 @@
         rec.fProc = pp;
     }
 
+#if defined(SK_4PX_XFERMODES_ARE_FAST) && !defined(SK_PREFER_LEGACY_FLOAT_XFERMODES)
+    switch (mode) {
+        case SkXfermode::kSrcATop_Mode:    return SkT4pxXfermode<SrcATop4f>::Create(rec);
+        case SkXfermode::kDstATop_Mode:    return SkT4pxXfermode<DstATop4f>::Create(rec);
+        case SkXfermode::kXor_Mode:        return SkT4pxXfermode<Xor4f>::Create(rec);
+        case SkXfermode::kPlus_Mode:       return SkT4pxXfermode<Plus4f>::Create(rec);
+        case SkXfermode::kModulate_Mode:   return SkT4pxXfermode<Modulate4f>::Create(rec);
+        case SkXfermode::kScreen_Mode:     return SkT4pxXfermode<Screen4f>::Create(rec);
+        case SkXfermode::kMultiply_Mode:   return SkT4pxXfermode<Multiply4f>::Create(rec);
+        case SkXfermode::kDifference_Mode: return SkT4pxXfermode<Difference4f>::Create(rec);
+        case SkXfermode::kExclusion_Mode:  return SkT4pxXfermode<Exclusion4f>::Create(rec);
+        default: break;
+    }
+#endif
+
+#if defined(SK_4F_XFERMODES_ARE_FAST)
+    switch (mode) {
+        case SkXfermode::kSrcATop_Mode:    return SkT4fXfermode<SrcATop4f>::Create(rec);
+        case SkXfermode::kDstATop_Mode:    return SkT4fXfermode<DstATop4f>::Create(rec);
+        case SkXfermode::kXor_Mode:        return SkT4fXfermode<Xor4f>::Create(rec);
+        case SkXfermode::kPlus_Mode:       return SkT4fXfermode<Plus4f>::Create(rec);
+        case SkXfermode::kModulate_Mode:   return SkT4fXfermode<Modulate4f>::Create(rec);
+        case SkXfermode::kScreen_Mode:     return SkT4fXfermode<Screen4f>::Create(rec);
+        case SkXfermode::kMultiply_Mode:   return SkT4fXfermode<Multiply4f>::Create(rec);
+        case SkXfermode::kDifference_Mode: return SkT4fXfermode<Difference4f>::Create(rec);
+        case SkXfermode::kExclusion_Mode:  return SkT4fXfermode<Exclusion4f>::Create(rec);
+        default: break;
+    }
+#endif
+
     SkXfermode* xfer = NULL;
+
     // check if we have a platform optim for that
     SkProcCoeffXfermode* xfm = SkPlatformXfermodeFactory(rec, mode);
     if (xfm != NULL) {
diff --git a/src/core/SkXfermode_proccoeff.h b/src/core/SkXfermode_proccoeff.h
index 8bd0811..6a3a244 100644
--- a/src/core/SkXfermode_proccoeff.h
+++ b/src/core/SkXfermode_proccoeff.h
@@ -24,30 +24,30 @@
     }
 
     virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) const SK_OVERRIDE;
+                        const SkAlpha aa[]) const override;
     virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) const SK_OVERRIDE;
+                        const SkAlpha aa[]) const override;
     virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) const SK_OVERRIDE;
+                        const SkAlpha aa[]) const override;
 
-    bool asMode(Mode* mode) const SK_OVERRIDE;
+    bool asMode(Mode* mode) const override;
 
-    bool supportsCoverageAsAlpha() const SK_OVERRIDE;
+    bool supportsCoverageAsAlpha() const override;
 
-    bool isOpaque(SkXfermode::SrcColorOpacity opacityType) const SK_OVERRIDE;
+    bool isOpaque(SkXfermode::SrcColorOpacity opacityType) const override;
 
 #if SK_SUPPORT_GPU
     virtual bool asFragmentProcessor(GrFragmentProcessor**,
-                                     GrTexture* background) const SK_OVERRIDE;
+                                     GrTexture* background) const override;
 
-    virtual bool asXPFactory(GrXPFactory**) const SK_OVERRIDE;
+    virtual bool asXPFactory(GrXPFactory**) const override;
 #endif
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkProcCoeffXfermode)
 
 protected:
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer& buffer) const override;
 
     Mode getMode() const { return fMode; }
 
diff --git a/src/core/SkYUVPlanesCache.cpp b/src/core/SkYUVPlanesCache.cpp
index 5ef89b6..79136db 100644
--- a/src/core/SkYUVPlanesCache.cpp
+++ b/src/core/SkYUVPlanesCache.cpp
@@ -46,8 +46,8 @@
     YUVPlanesKey  fKey;
     YUVValue      fValue;
 
-    const Key& getKey() const SK_OVERRIDE { return fKey; }
-    size_t bytesUsed() const SK_OVERRIDE { return sizeof(*this) + fValue.fData->size(); }
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override { return sizeof(*this) + fValue.fData->size(); }
 
     static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextData) {
         const YUVPlanesRec& rec = static_cast<const YUVPlanesRec&>(baseRec);
diff --git a/src/device/xps/SkXPSDevice.cpp b/src/device/xps/SkXPSDevice.cpp
index 2ca799a..1c06235 100644
--- a/src/device/xps/SkXPSDevice.cpp
+++ b/src/device/xps/SkXPSDevice.cpp
@@ -512,7 +512,7 @@
         SkScalar startToStop = (stopTransformed.fX - startTransformed.fX)
                              + (stopTransformed.fY - startTransformed.fY);
         //Percentage along transformed line.
-        stopOffsets[i] = SkScalarDiv(startToStop, startToEnd);
+        stopOffsets[i] = startToStop / startToEnd;
     }
 }
 
@@ -1044,8 +1044,7 @@
             return S_OK;
         }
 
-        if (SkShader::kRadial2_GradientType == gradientType ||
-            SkShader::kConical_GradientType == gradientType) {
+        if (SkShader::kConical_GradientType == gradientType) {
             //simple if affine and one is 0, otherwise will have to fake
         }
 
@@ -1061,8 +1060,6 @@
                                                         &outMatrix,
                                                         xy);
     switch (bitmapType) {
-        case SkShader::kNone_BitmapType:
-            break;
         case SkShader::kDefault_BitmapType: {
             //TODO: outMatrix??
             SkMatrix localMatrix = shader->getLocalMatrix();
@@ -1081,9 +1078,6 @@
 
             return S_OK;
         }
-        case SkShader::kRadial_BitmapType:
-        case SkShader::kSweep_BitmapType:
-        case SkShader::kTwoPointRadial_BitmapType:
         default:
             break;
     }
@@ -1402,10 +1396,8 @@
                                SkVector* ppuScale,
                                const SkIRect& clip, SkIRect* clipIRect) {
     //This action is in unit space, but the ppm is specified in physical space.
-    ppuScale->fX = SkScalarDiv(this->fCurrentPixelsPerMeter.fX,
-                               this->fCurrentUnitsPerMeter.fX);
-    ppuScale->fY = SkScalarDiv(this->fCurrentPixelsPerMeter.fY,
-                               this->fCurrentUnitsPerMeter.fY);
+    ppuScale->set(fCurrentPixelsPerMeter.fX / fCurrentUnitsPerMeter.fX,
+                  fCurrentPixelsPerMeter.fY / fCurrentUnitsPerMeter.fY);
 
     matrix->postScale(ppuScale->fX, ppuScale->fY);
 
@@ -2251,7 +2243,7 @@
          "Could not add layer to current visuals.");
 }
 
-SkBaseDevice* SkXPSDevice::onCreateCompatibleDevice(const CreateInfo& info) {
+SkBaseDevice* SkXPSDevice::onCreateDevice(const CreateInfo& info, const SkPaint*) {
 //Conditional for bug compatibility with PDF device.
 #if 0
     if (SkBaseDevice::kGeneral_Usage == info.fUsage) {
@@ -2282,6 +2274,3 @@
          "Could not create canvas for layer.");
 }
 
-bool SkXPSDevice::allowImageFilter(const SkImageFilter*) {
-    return false;
-}
diff --git a/src/doc/SkDocument_PDF.cpp b/src/doc/SkDocument_PDF.cpp
index e486737..eeb8aa8 100644
--- a/src/doc/SkDocument_PDF.cpp
+++ b/src/doc/SkDocument_PDF.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Google Inc.
+ * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
@@ -7,16 +7,287 @@
 
 #include "SkDocument.h"
 #include "SkPDFCanon.h"
-#include "SkPDFDocument.h"
 #include "SkPDFDevice.h"
+#include "SkPDFFont.h"
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkStream.h"
 
+static void emit_pdf_header(SkWStream* stream) {
+    stream->writeText("%PDF-1.4\n%");
+    // The PDF spec recommends including a comment with four bytes, all
+    // with their high bits set.  This is "Skia" with the high bits set.
+    stream->write32(0xD3EBE9E1);
+    stream->writeText("\n");
+}
+
+static void emit_pdf_footer(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes,
+                            SkPDFObject* docCatalog,
+                            int64_t objCount,
+                            int32_t xRefFileOffset) {
+    SkPDFDict trailerDict;
+    // TODO(vandebo): Linearized format will take a Prev entry too.
+    // TODO(vandebo): PDF/A requires an ID entry.
+    trailerDict.insertInt("Size", int(objCount));
+    trailerDict.insertObjRef("Root", SkRef(docCatalog));
+
+    stream->writeText("trailer\n");
+    trailerDict.emitObject(stream, objNumMap, substitutes);
+    stream->writeText("\nstartxref\n");
+    stream->writeBigDecAsText(xRefFileOffset);
+    stream->writeText("\n%%EOF");
+}
+
+static void perform_font_subsetting(
+        const SkTDArray<const SkPDFDevice*>& pageDevices,
+        SkPDFSubstituteMap* substituteMap) {
+    SkASSERT(substituteMap);
+
+    SkPDFGlyphSetMap usage;
+    for (int i = 0; i < pageDevices.count(); ++i) {
+        usage.merge(pageDevices[i]->getFontGlyphUsage());
+    }
+    SkPDFGlyphSetMap::F2BIter iterator(usage);
+    const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
+    while (entry) {
+        SkAutoTUnref<SkPDFFont> subsetFont(
+                entry->fFont->getFontSubset(entry->fGlyphSet));
+        if (subsetFont) {
+            substituteMap->setSubstitute(entry->fFont, subsetFont.get());
+        }
+        entry = iterator.next();
+    }
+}
+
+static SkPDFObject* create_pdf_page_content(const SkPDFDevice* pageDevice) {
+    SkAutoTDelete<SkStreamAsset> content(pageDevice->content());
+    return SkNEW_ARGS(SkPDFStream, (content.get()));
+}
+
+static SkPDFDict* create_pdf_page(const SkPDFDevice* pageDevice) {
+    SkAutoTUnref<SkPDFDict> page(SkNEW_ARGS(SkPDFDict, ("Page")));
+    page->insertObject("Resources", pageDevice->createResourceDict());
+    page->insertObject("MediaBox", pageDevice->copyMediaBox());
+    if (SkPDFArray* annots = pageDevice->getAnnotations()) {
+        SkASSERT(annots->size() > 0);
+        page->insertObject("Annots", SkRef(annots));
+    }
+    page->insertObjRef("Contents", create_pdf_page_content(pageDevice));
+    return page.detach();
+}
+
+static void generate_page_tree(const SkTDArray<SkPDFDict*>& pages,
+                               SkTDArray<SkPDFDict*>* pageTree,
+                               SkPDFDict** rootNode) {
+    // PDF wants a tree describing all the pages in the document.  We arbitrary
+    // choose 8 (kNodeSize) as the number of allowed children.  The internal
+    // nodes have type "Pages" with an array of children, a parent pointer, and
+    // the number of leaves below the node as "Count."  The leaves are passed
+    // into the method, have type "Page" and need a parent pointer. This method
+    // builds the tree bottom up, skipping internal nodes that would have only
+    // one child.
+    static const int kNodeSize = 8;
+
+    // curNodes takes a reference to its items, which it passes to pageTree.
+    SkTDArray<SkPDFDict*> curNodes;
+    curNodes.setReserve(pages.count());
+    for (int i = 0; i < pages.count(); i++) {
+        SkSafeRef(pages[i]);
+        curNodes.push(pages[i]);
+    }
+
+    // nextRoundNodes passes its references to nodes on to curNodes.
+    SkTDArray<SkPDFDict*> nextRoundNodes;
+    nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
+
+    int treeCapacity = kNodeSize;
+    do {
+        for (int i = 0; i < curNodes.count(); ) {
+            if (i > 0 && i + 1 == curNodes.count()) {
+                nextRoundNodes.push(curNodes[i]);
+                break;
+            }
+
+            SkAutoTUnref<SkPDFDict> newNode(new SkPDFDict("Pages"));
+            SkAutoTUnref<SkPDFArray> kids(new SkPDFArray);
+            kids->reserve(kNodeSize);
+
+            int count = 0;
+            for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
+                curNodes[i]->insertObjRef("Parent", SkRef(newNode.get()));
+                kids->appendObjRef(SkRef(curNodes[i]));
+
+                // TODO(vandebo): put the objects in strict access order.
+                // Probably doesn't matter because they are so small.
+                if (curNodes[i] != pages[0]) {
+                    pageTree->push(curNodes[i]);  // Transfer reference.
+                } else {
+                    SkSafeUnref(curNodes[i]);
+                }
+            }
+
+            // 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 (i == curNodes.count()) {
+                pageCount = ((pages.count() - 1) % treeCapacity) + 1;
+            }
+            newNode->insertInt("Count", pageCount);
+            newNode->insertObject("Kids", kids.detach());
+            nextRoundNodes.push(newNode.detach());  // Transfer reference.
+        }
+
+        curNodes = nextRoundNodes;
+        nextRoundNodes.rewind();
+        treeCapacity *= kNodeSize;
+    } while (curNodes.count() > 1);
+
+    pageTree->push(curNodes[0]);  // Transfer reference.
+    if (rootNode) {
+        *rootNode = curNodes[0];
+    }
+}
+
+static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices,
+                              SkWStream* stream) {
+    if (pageDevices.isEmpty()) {
+        return false;
+    }
+
+    SkTDArray<SkPDFDict*> pages;
+    SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict));
+
+    for (int i = 0; i < pageDevices.count(); i++) {
+        SkASSERT(pageDevices[i]);
+        SkASSERT(i == 0 ||
+                 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon());
+        SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i]));
+        pageDevices[i]->appendDestinations(dests, page.get());
+        pages.push(page.detach());
+    }
+
+    SkTDArray<SkPDFDict*> pageTree;
+    SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog")));
+
+    SkPDFDict* pageTreeRoot;
+    generate_page_tree(pages, &pageTree, &pageTreeRoot);
+    docCatalog->insertObjRef("Pages", SkRef(pageTreeRoot));
+
+    if (dests->size() > 0) {
+        docCatalog->insertObjRef("Dests", dests.detach());
+    }
+
+    /* TODO(vandebo): output intent
+    SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
+    outputIntent->insertName("S", "GTS_PDFA1");
+    outputIntent->insertString("OutputConditionIdentifier", "sRGB");
+    SkAutoTUnref<SkPDFArray> intentArray(new SkPDFArray);
+    intentArray->appendObject(SkRef(outputIntent.get()));
+    docCatalog->insertObject("OutputIntent", intentArray.detach());
+    */
+
+    // Build font subsetting info before proceeding.
+    SkPDFSubstituteMap substitutes;
+    perform_font_subsetting(pageDevices, &substitutes);
+
+    SkPDFObjNumMap objNumMap;
+    if (objNumMap.addObject(docCatalog.get())) {
+        docCatalog->addResources(&objNumMap, substitutes);
+    }
+    size_t baseOffset = SkToOffT(stream->bytesWritten());
+    emit_pdf_header(stream);
+    SkTDArray<int32_t> offsets;
+    for (int i = 0; i < objNumMap.objects().count(); ++i) {
+        SkPDFObject* object = objNumMap.objects()[i];
+        offsets.push(SkToS32(stream->bytesWritten() - baseOffset));
+        SkASSERT(object == substitutes.getSubstitute(object));
+        SkASSERT(objNumMap.getObjectNumber(object) == i + 1);
+        stream->writeDecAsText(i + 1);
+        stream->writeText(" 0 obj\n");  // Generation number is always 0.
+        object->emitObject(stream, objNumMap, substitutes);
+        stream->writeText("\nendobj\n");
+    }
+    int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset);
+
+    // Include the zeroth object in the count.
+    int32_t objCount = SkToS32(offsets.count() + 1);
+
+    stream->writeText("xref\n0 ");
+    stream->writeDecAsText(objCount);
+    stream->writeText("\n0000000000 65535 f \n");
+    for (int i = 0; i < offsets.count(); i++) {
+        SkASSERT(offsets[i] > 0);
+        stream->writeBigDecAsText(offsets[i], 10);
+        stream->writeText(" 00000 n \n");
+    }
+    emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount,
+                    xRefFileOffset);
+
+    // The page tree has both child and parent pointers, so it creates a
+    // reference cycle.  We must clear that cycle to properly reclaim memory.
+    for (int i = 0; i < pageTree.count(); i++) {
+        pageTree[i]->clear();
+    }
+    pageTree.safeUnrefAll();
+    pages.unrefAll();
+    return true;
+}
+
+#if 0
+// TODO(halcanary): expose notEmbeddableCount in SkDocument
+void GetCountOfFontTypes(
+        const SkTDArray<SkPDFDevice*>& pageDevices,
+        int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
+        int* notSubsettableCount,
+        int* notEmbeddableCount) {
+    sk_bzero(counts, sizeof(int) *
+                     (SkAdvancedTypefaceMetrics::kOther_Font + 1));
+    SkTDArray<SkFontID> seenFonts;
+    int notSubsettable = 0;
+    int notEmbeddable = 0;
+
+    for (int pageNumber = 0; pageNumber < pageDevices.count(); pageNumber++) {
+        const SkTDArray<SkPDFFont*>& fontResources =
+                pageDevices[pageNumber]->getFontResources();
+        for (int font = 0; font < fontResources.count(); font++) {
+            SkFontID fontID = fontResources[font]->typeface()->uniqueID();
+            if (seenFonts.find(fontID) == -1) {
+                counts[fontResources[font]->getType()]++;
+                seenFonts.push(fontID);
+                if (!fontResources[font]->canSubset()) {
+                    notSubsettable++;
+                }
+                if (!fontResources[font]->canEmbed()) {
+                    notEmbeddable++;
+                }
+            }
+        }
+    }
+    if (notSubsettableCount) {
+        *notSubsettableCount = notSubsettable;
+
+    }
+    if (notEmbeddableCount) {
+        *notEmbeddableCount = notEmbeddable;
+    }
+}
+#endif
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
 class SkDocument_PDF : public SkDocument {
 public:
     SkDocument_PDF(SkWStream* stream,
                    void (*doneProc)(SkWStream*, bool),
                    SkScalar rasterDpi)
         : SkDocument(stream, doneProc)
-        , fDoc(SkNEW(SkPDFDocument))
         , fRasterDpi(rasterDpi) {}
 
     virtual ~SkDocument_PDF() {
@@ -25,69 +296,60 @@
     }
 
 protected:
-    virtual SkCanvas* onBeginPage(SkScalar width, SkScalar height,
-                                  const SkRect& trimBox) SK_OVERRIDE {
+    SkCanvas* onBeginPage(SkScalar width, SkScalar height,
+                          const SkRect& trimBox) override {
         SkASSERT(!fCanvas.get());
-        SkASSERT(!fDevice.get());
 
         SkISize pageSize = SkISize::Make(
                 SkScalarRoundToInt(width), SkScalarRoundToInt(height));
-        fDevice.reset(SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon));
-        fCanvas.reset(SkNEW_ARGS(SkCanvas, (fDevice)));
+        SkAutoTUnref<SkPDFDevice> device(
+                SkPDFDevice::Create(pageSize, fRasterDpi, &fCanon));
+        fCanvas.reset(SkNEW_ARGS(SkCanvas, (device.get())));
+        fPageDevices.push(device.detach());
         fCanvas->clipRect(trimBox);
         fCanvas->translate(trimBox.x(), trimBox.y());
         return fCanvas.get();
     }
 
-    void onEndPage() SK_OVERRIDE {
+    void onEndPage() override {
         SkASSERT(fCanvas.get());
-        SkASSERT(fDevice.get());
-
         fCanvas->flush();
-        fDoc->appendPage(fDevice.get());
-
         fCanvas.reset(NULL);
-        fDevice.reset(NULL);
     }
 
-    bool onClose(SkWStream* stream) SK_OVERRIDE {
+    bool onClose(SkWStream* stream) override {
         SkASSERT(!fCanvas.get());
-        SkASSERT(!fDevice.get());
 
-        bool success = fDoc->emitPDF(stream);
-        fDoc.free();
+        bool success = emit_pdf_document(fPageDevices, stream);
+        fPageDevices.unrefAll();
         fCanon.reset();
         return success;
     }
 
-    void onAbort() SK_OVERRIDE {
-        fDoc.free();
+    void onAbort() override {
+        fPageDevices.unrefAll();
         fCanon.reset();
     }
 
 private:
     SkPDFCanon fCanon;
-    SkAutoTDelete<SkPDFDocument> fDoc;
-    SkAutoTUnref<SkPDFDevice> fDevice;
+    SkTDArray<const SkPDFDevice*> fPageDevices;
     SkAutoTUnref<SkCanvas> fCanvas;
     SkScalar fRasterDpi;
 };
-
+}  // namespace
 ///////////////////////////////////////////////////////////////////////////////
 
 SkDocument* SkDocument::CreatePDF(SkWStream* stream, SkScalar dpi) {
     return stream ? SkNEW_ARGS(SkDocument_PDF, (stream, NULL, dpi)) : NULL;
 }
 
-static void delete_wstream(SkWStream* stream, bool aborted) {
-    SkDELETE(stream);
-}
-
 SkDocument* SkDocument::CreatePDF(const char path[], SkScalar dpi) {
     SkFILEWStream* stream = SkNEW_ARGS(SkFILEWStream, (path));
     if (!stream->isValid()) {
         SkDELETE(stream);
         return NULL;
     }
+    auto delete_wstream = [](SkWStream* stream, bool) { SkDELETE(stream); };
     return SkNEW_ARGS(SkDocument_PDF, (stream, delete_wstream, dpi));
 }
diff --git a/src/doc/SkDocument_XPS.cpp b/src/doc/SkDocument_XPS.cpp
index db14a70..884d9a5 100644
--- a/src/doc/SkDocument_XPS.cpp
+++ b/src/doc/SkDocument_XPS.cpp
@@ -28,9 +28,9 @@
     }
 
 protected:
-    virtual SkCanvas* onBeginPage(SkScalar width,
-                                  SkScalar height,
-                                  const SkRect& trimBox) SK_OVERRIDE {
+    SkCanvas* onBeginPage(SkScalar width,
+                          SkScalar height,
+                          const SkRect& trimBox) override {
         fDevice.beginSheet(fUnitsPerMeter, fPixelsPerMeter,
                            SkSize::Make(width, height));
         fCanvas.reset(SkNEW_ARGS(SkCanvas, (&fDevice)));
@@ -39,19 +39,19 @@
         return fCanvas.get();
     }
 
-    void onEndPage() SK_OVERRIDE {
+    void onEndPage() override {
         SkASSERT(fCanvas.get());
         fCanvas->flush();
         fCanvas.reset(NULL);
         fDevice.endSheet();
     }
 
-    bool onClose(SkWStream*) SK_OVERRIDE {
+    bool onClose(SkWStream*) override {
         SkASSERT(!fCanvas.get());
         return fDevice.endPortfolio();
     }
 
-    void onAbort() SK_OVERRIDE {}
+    void onAbort() override {}
 
 private:
     SkXPSDevice fDevice;
diff --git a/src/effects/SkAlphaThresholdFilter.cpp b/src/effects/SkAlphaThresholdFilter.cpp
index 09dc742..70d195b 100644
--- a/src/effects/SkAlphaThresholdFilter.cpp
+++ b/src/effects/SkAlphaThresholdFilter.cpp
@@ -20,13 +20,13 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAlphaThresholdFilterImpl)
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                               SkBitmap* result, SkIPoint* offset) const override;
 #if SK_SUPPORT_GPU
     virtual bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
-                                     const SkIRect& bounds) const SK_OVERRIDE;
+                                     const SkIRect& bounds) const override;
 #endif
 
 private:
@@ -71,14 +71,14 @@
 
     virtual ~AlphaThresholdEffect() {};
 
-    const char* name() const SK_OVERRIDE { return "Alpha Threshold"; }
+    const char* name() const override { return "Alpha Threshold"; }
 
     float innerThreshold() const { return fInnerThreshold; }
     float outerThreshold() const { return fOuterThreshold; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
 private:
     AlphaThresholdEffect(GrTexture* texture,
@@ -102,9 +102,9 @@
         this->addTextureAccess(&fMaskTextureAccess);
     }
 
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
@@ -127,9 +127,9 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
 
@@ -154,7 +154,7 @@
         kFloat_GrSLType, kDefault_GrSLPrecision,
         "outer_threshold");
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
     SkString maskCoords2D = fsBuilder->ensureFSCoords2D(coords, 1);
 
@@ -214,7 +214,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void AlphaThresholdEffect::getGLProcessorKey(const GrGLCaps& caps,
+void AlphaThresholdEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                              GrProcessorKeyBuilder* b) const {
     GrGLAlphaThresholdEffect::GenKey(*this, caps, b);
 }
@@ -278,8 +278,8 @@
         // the outside.
         maskDesc.fWidth = texture->width();
         maskDesc.fHeight = texture->height();
-        SkAutoTUnref<GrTexture> maskTexture(
-            context->refScratchTexture(maskDesc, GrContext::kApprox_ScratchTexMatch));
+        SkAutoTUnref<GrTexture> maskTexture(context->textureProvider()->refScratchTexture(
+            maskDesc, GrTextureProvider::kApprox_ScratchTexMatch));
         if (!maskTexture) {
             return false;
         }
diff --git a/src/effects/SkArithmeticMode.cpp b/src/effects/SkArithmeticMode.cpp
index ae18c27..4914c69 100644
--- a/src/effects/SkArithmeticMode.cpp
+++ b/src/effects/SkArithmeticMode.cpp
@@ -25,15 +25,15 @@
     }
 
     virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) const SK_OVERRIDE;
+                        const SkAlpha aa[]) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkArithmeticMode_scalar)
 
 #if SK_SUPPORT_GPU
-    bool asFragmentProcessor(GrFragmentProcessor**, GrTexture* background) const SK_OVERRIDE;
+    bool asFragmentProcessor(GrFragmentProcessor**, GrTexture* background) const override;
 
-    bool asXPFactory(GrXPFactory**) const SK_OVERRIDE;
+    bool asXPFactory(GrXPFactory**) const override;
 #endif
 
 private:
@@ -45,7 +45,7 @@
         fEnforcePMColor = enforcePMColor;
     }
 
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
+    void flatten(SkWriteBuffer& buffer) const override {
         buffer.writeScalar(fK[0]);
         buffer.writeScalar(fK[1]);
         buffer.writeScalar(fK[2]);
diff --git a/src/effects/SkArithmeticMode_gpu.cpp b/src/effects/SkArithmeticMode_gpu.cpp
index e068c9d..69c2d7c 100644
--- a/src/effects/SkArithmeticMode_gpu.cpp
+++ b/src/effects/SkArithmeticMode_gpu.cpp
@@ -20,7 +20,7 @@
 
 static const bool gUseUnpremul = false;
 
-static void add_arithmetic_code(GrGLFPFragmentBuilder* fsBuilder,
+static void add_arithmetic_code(GrGLFragmentBuilder* fsBuilder,
                                 const char* inputColor,
                                 const char* dstColor,
                                 const char* outputColor,
@@ -58,15 +58,15 @@
         : fEnforcePMColor(true) {
     }
 
-    ~GLArithmeticFP() SK_OVERRIDE {}
+    ~GLArithmeticFP() override {}
 
     void emitCode(GrGLFPBuilder* builder,
                   const GrFragmentProcessor& fp,
                   const char* outputColor,
                   const char* inputColor,
                   const TransformedCoordsArray& coords,
-                  const TextureSamplerArray& samplers) SK_OVERRIDE {
-        GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+                  const TextureSamplerArray& samplers) override {
+        GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
         fsBuilder->codeAppend("vec4 bgColor = ");
         fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0].getType());
         fsBuilder->codeAppendf(";");
@@ -80,13 +80,13 @@
         add_arithmetic_code(fsBuilder, inputColor, dstColor, outputColor, kUni, fEnforcePMColor);
     }
 
-    void setData(const GrGLProgramDataManager& pdman, const GrProcessor& proc) SK_OVERRIDE {
+    void setData(const GrGLProgramDataManager& pdman, const GrProcessor& proc) override {
         const GrArithmeticFP& arith = proc.cast<GrArithmeticFP>();
         pdman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4());
         fEnforcePMColor = arith.enforcePMColor();
     }
 
-    static void GenKey(const GrProcessor& proc, const GrGLCaps& caps, GrProcessorKeyBuilder* b) {
+    static void GenKey(const GrProcessor& proc, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {
         const GrArithmeticFP& arith = proc.cast<GrArithmeticFP>();
         uint32_t key = arith.enforcePMColor() ? 1 : 0;
         b->add32(key);
@@ -115,7 +115,7 @@
     this->addTextureAccess(&fBackgroundAccess);
 }
 
-void GrArithmeticFP::getGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const {
+void GrArithmeticFP::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
     GLArithmeticFP::GenKey(*this, caps, b);
 }
 
@@ -167,25 +167,13 @@
                                          willReadDstColor));
     }
 
-    ~ArithmeticXP() SK_OVERRIDE {};
+    ~ArithmeticXP() override {};
 
-    const char* name() const SK_OVERRIDE { return "Arithmetic"; }
+    const char* name() const override { return "Arithmetic"; }
 
-    GrGLXferProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLXferProcessor* createGLInstance() const override;
 
-    bool hasSecondaryOutput() const SK_OVERRIDE { return false; }
-
-    GrXferProcessor::OptFlags getOptimizations(const GrProcOptInfo& colorPOI,
-                                               const GrProcOptInfo& coveragePOI,
-                                               bool doesStencilWrite,
-                                               GrColor* overrideColor,
-                                               const GrDrawTargetCaps& caps) SK_OVERRIDE;
-
-    void getBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const SK_OVERRIDE {
-        blendInfo->fSrcBlend = kOne_GrBlendCoeff;
-        blendInfo->fDstBlend = kZero_GrBlendCoeff;
-        blendInfo->fBlendConstant = 0;
-    }
+    bool hasSecondaryOutput() const override { return false; }
 
     float k1() const { return fK1; }
     float k2() const { return fK2; }
@@ -197,9 +185,15 @@
     ArithmeticXP(float k1, float k2, float k3, float k4, bool enforcePMColor,
                    const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
 
-    void onGetGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+    GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
+                                                 const GrProcOptInfo& coveragePOI,
+                                                 bool doesStencilWrite,
+                                                 GrColor* overrideColor,
+                                                 const GrDrawTargetCaps& caps) override;
 
-    bool onIsEqual(const GrXferProcessor& xpBase) const SK_OVERRIDE {
+    void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
+
+    bool onIsEqual(const GrXferProcessor& xpBase) const override {
         const ArithmeticXP& xp = xpBase.cast<ArithmeticXP>();
         if (fK1 != xp.fK1 ||
             fK2 != xp.fK2 ||
@@ -225,9 +219,9 @@
         : fEnforcePMColor(true) {
     }
 
-    ~GLArithmeticXP() SK_OVERRIDE {}
+    ~GLArithmeticXP() override {}
 
-    static void GenKey(const GrProcessor& processor, const GrGLCaps& caps,
+    static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps,
                        GrProcessorKeyBuilder* b) {
         const ArithmeticXP& arith = processor.cast<ArithmeticXP>();
         uint32_t key = arith.enforcePMColor() ? 1 : 0;
@@ -235,8 +229,8 @@
     }
 
 private:
-    void onEmitCode(const EmitArgs& args) SK_OVERRIDE {
-        GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+    void onEmitCode(const EmitArgs& args) override {
+        GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
 
         const char* dstColor = fsBuilder->dstColor();
 
@@ -254,7 +248,7 @@
     }
 
     void onSetData(const GrGLProgramDataManager& pdman,
-                   const GrXferProcessor& processor) SK_OVERRIDE {
+                   const GrXferProcessor& processor) override {
         const ArithmeticXP& arith = processor.cast<ArithmeticXP>();
         pdman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4());
         fEnforcePMColor = arith.enforcePMColor();
@@ -279,7 +273,7 @@
     this->initClassID<ArithmeticXP>();
 }
 
-void ArithmeticXP::onGetGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const {
+void ArithmeticXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
     GLArithmeticXP::GenKey(*this, caps, b);
 }
 
@@ -287,11 +281,11 @@
     return SkNEW_ARGS(GLArithmeticXP, (*this));
 }
 
-GrXferProcessor::OptFlags ArithmeticXP::getOptimizations(const GrProcOptInfo& colorPOI,
-                                                         const GrProcOptInfo& coveragePOI,
-                                                         bool doesStencilWrite,
-                                                         GrColor* overrideColor,
-                                                         const GrDrawTargetCaps& caps) {
+GrXferProcessor::OptFlags ArithmeticXP::onGetOptimizations(const GrProcOptInfo& colorPOI,
+                                                           const GrProcOptInfo& coveragePOI,
+                                                           bool doesStencilWrite,
+                                                           GrColor* overrideColor,
+                                                           const GrDrawTargetCaps& caps) {
    return GrXferProcessor::kNone_Opt;
 }
 
diff --git a/src/effects/SkArithmeticMode_gpu.h b/src/effects/SkArithmeticMode_gpu.h
index 8fb58a1..9f6953e 100644
--- a/src/effects/SkArithmeticMode_gpu.h
+++ b/src/effects/SkArithmeticMode_gpu.h
@@ -36,13 +36,13 @@
         return SkNEW_ARGS(GrArithmeticFP, (k1, k2, k3, k4, enforcePMColor, background));
     }
 
-    ~GrArithmeticFP() SK_OVERRIDE {};
+    ~GrArithmeticFP() override {};
 
-    const char* name() const SK_OVERRIDE { return "Arithmetic"; }
+    const char* name() const override { return "Arithmetic"; }
 
-    void getGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     float k1() const { return fK1; }
     float k2() const { return fK2; }
@@ -51,9 +51,9 @@
     bool enforcePMColor() const { return fEnforcePMColor; }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     GrArithmeticFP(float k1, float k2, float k3, float k4, bool enforcePMColor,
                    GrTexture* background);
@@ -77,16 +77,12 @@
         return SkNEW_ARGS(GrArithmeticXPFactory, (k1, k2, k3, k4, enforcePMColor));
     }
 
-    bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const SK_OVERRIDE {
+    bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const override {
         return true;
     }
 
-    bool canTweakAlphaForCoverage() const SK_OVERRIDE {
-        return false;
-    }
-
     void getInvariantOutput(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI,
-                            GrXPFactory::InvariantOutput*) const SK_OVERRIDE;
+                            GrXPFactory::InvariantOutput*) const override;
 
 private:
     GrArithmeticXPFactory(float k1, float k2, float k3, float k4, bool enforcePMColor); 
@@ -94,15 +90,15 @@
     GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
                                            const GrProcOptInfo& colorPOI,
                                            const GrProcOptInfo& coveragePOI,
-                                           const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE; 
+                                           const GrDeviceCoordTexture* dstCopy) const override; 
 
     bool willReadDstColor(const GrDrawTargetCaps& caps,
                           const GrProcOptInfo& colorPOI,
-                          const GrProcOptInfo& coveragePOI) const SK_OVERRIDE {
+                          const GrProcOptInfo& coveragePOI) const override {
         return true;
     }
 
-    bool onIsEqual(const GrXPFactory& xpfBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrXPFactory& xpfBase) const override {
         const GrArithmeticXPFactory& xpf = xpfBase.cast<GrArithmeticXPFactory>();
         if (fK1 != xpf.fK1 ||
             fK2 != xpf.fK2 ||
diff --git a/src/effects/SkBitmapSource.cpp b/src/effects/SkBitmapSource.cpp
index fc86f19..ed9521f 100644
--- a/src/effects/SkBitmapSource.cpp
+++ b/src/effects/SkBitmapSource.cpp
@@ -18,15 +18,26 @@
   , fSrcRect(SkRect::MakeWH(SkIntToScalar(bitmap.width()),
                             SkIntToScalar(bitmap.height())))
   , fDstRect(fSrcRect)
-{}
+  , fFilterQuality(kHigh_SkFilterQuality) {
+}
 
-SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap, const SkRect& srcRect, const SkRect& dstRect)
+SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap, 
+                               const SkRect& srcRect, const SkRect& dstRect,
+                               SkFilterQuality filterQuality)
   : INHERITED(0, 0)
   , fBitmap(bitmap)
   , fSrcRect(srcRect)
-  , fDstRect(dstRect) {}
+  , fDstRect(dstRect)
+  , fFilterQuality(filterQuality) {
+}
 
 SkFlattenable* SkBitmapSource::CreateProc(SkReadBuffer& buffer) {
+    SkFilterQuality filterQuality;
+    if (buffer.isVersionLT(SkReadBuffer::kBitmapSourceFilterQuality_Version)) {
+        filterQuality = kHigh_SkFilterQuality;
+    } else {
+        filterQuality = (SkFilterQuality)buffer.readInt();
+    }
     SkRect src, dst;
     buffer.readRect(&src);
     buffer.readRect(&dst);
@@ -34,10 +45,11 @@
     if (!buffer.readBitmap(&bitmap)) {
         return NULL;
     }
-    return SkBitmapSource::Create(bitmap, src, dst);
+    return SkBitmapSource::Create(bitmap, src, dst, filterQuality);
 }
 
 void SkBitmapSource::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeInt(fFilterQuality);
     buffer.writeRect(fSrcRect);
     buffer.writeRect(fDstRect);
     buffer.writeBitmap(fBitmap);
@@ -69,14 +81,15 @@
     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
     // FIXME: this probably shouldn't be necessary, but drawBitmapRectToRect asserts
     // None filtering when it's translate-only
-    paint.setFilterLevel(
+    paint.setFilterQuality(
         fSrcRect.width() == dstRect.width() && fSrcRect.height() == dstRect.height() ?
-        SkPaint::kNone_FilterLevel : SkPaint::kHigh_FilterLevel);
+               kNone_SkFilterQuality : fFilterQuality);
     canvas.drawBitmapRectToRect(fBitmap, &fSrcRect, dstRect, &paint);
 
     *result = device.get()->accessBitmap(false);
     offset->fX = dstIRect.fLeft;
     offset->fY = dstIRect.fTop;
+
     return true;
 }
 
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
index 4dcc05e..38d3d9d 100644
--- a/src/effects/SkBlurImageFilter.cpp
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -34,16 +34,15 @@
 SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX,
                                      SkScalar sigmaY,
                                      SkImageFilter* input,
-                                     const CropRect* cropRect,
-                                     uint32_t uniqueID)
-    : INHERITED(1, &input, cropRect, uniqueID), fSigma(SkSize::Make(sigmaX, sigmaY)) {
+                                     const CropRect* cropRect)
+    : INHERITED(1, &input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) {
 }
 
 SkFlattenable* SkBlurImageFilter::CreateProc(SkReadBuffer& buffer) {
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
     SkScalar sigmaX = buffer.readScalar();
     SkScalar sigmaY = buffer.readScalar();
-    return Create(sigmaX, sigmaY, common.getInput(0), &common.cropRect(), common.uniqueID());
+    return Create(sigmaX, sigmaY, common.getInput(0), &common.cropRect());
 }
 
 void SkBlurImageFilter::flatten(SkWriteBuffer& buffer) const {
@@ -204,10 +203,9 @@
     SkPMColor* d = dst->getAddr32(0, 0);
     int w = dstBounds.width(), h = dstBounds.height();
     int sw = src.rowBytesAsPixels();
-    SkBoxBlurProc boxBlurX, boxBlurY, boxBlurXY, boxBlurYX;
-    if (!SkBoxBlurGetPlatformProcs(&boxBlurX, &boxBlurY, &boxBlurXY, &boxBlurYX)) {
+    SkBoxBlurProc boxBlurX, boxBlurXY, boxBlurYX;
+    if (!SkBoxBlurGetPlatformProcs(&boxBlurX, &boxBlurXY, &boxBlurYX)) {
         boxBlurX = boxBlur<kX, kX>;
-        boxBlurY = boxBlur<kY, kY>;
         boxBlurXY = boxBlur<kX, kY>;
         boxBlurYX = boxBlur<kY, kX>;
     }
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index e363f7b..574a9af 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -38,39 +38,39 @@
     SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle, uint32_t flags);
 
     // overrides from SkMaskFilter
-    SkMask::Format getFormat() const SK_OVERRIDE;
+    SkMask::Format getFormat() const override;
     virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
-                            SkIPoint* margin) const SK_OVERRIDE;
+                            SkIPoint* margin) const override;
 
 #if SK_SUPPORT_GPU
     virtual bool canFilterMaskGPU(const SkRect& devBounds,
                                   const SkIRect& clipBounds,
                                   const SkMatrix& ctm,
-                                  SkRect* maskRect) const SK_OVERRIDE;
+                                  SkRect* maskRect) const override;
     virtual bool directFilterMaskGPU(GrContext* context,
                                      GrRenderTarget* rt,
                                      GrPaint* grp,
                                      const GrClip&,
                                      const SkMatrix& viewMatrix,
                                      const SkStrokeRec& strokeRec,
-                                     const SkPath& path) const SK_OVERRIDE;
+                                     const SkPath& path) const override;
     virtual bool directFilterRRectMaskGPU(GrContext* context,
                                           GrRenderTarget* rt,
                                           GrPaint* grp,
                                           const GrClip&,
                                           const SkMatrix& viewMatrix,
                                           const SkStrokeRec& strokeRec,
-                                          const SkRRect& rrect) const SK_OVERRIDE;
+                                          const SkRRect& rrect) const override;
 
     virtual bool filterMaskGPU(GrTexture* src,
                                const SkMatrix& ctm,
                                const SkRect& maskRect,
                                GrTexture** result,
-                               bool canOverwriteSrc) const SK_OVERRIDE;
+                               bool canOverwriteSrc) const override;
 #endif
 
-    void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
-    bool asABlur(BlurRec*) const SK_OVERRIDE;
+    void computeFastBounds(const SkRect&, SkRect*) const override;
+    bool asABlur(BlurRec*) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
@@ -78,11 +78,11 @@
 protected:
     virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
                                            const SkIRect& clipBounds,
-                                           NinePatch*) const SK_OVERRIDE;
+                                           NinePatch*) const override;
 
     virtual FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&,
                                            const SkIRect& clipBounds,
-                                           NinePatch*) const SK_OVERRIDE;
+                                           NinePatch*) const override;
 
     bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
                         SkIPoint* margin, SkMask::CreateMode createMode) const;
@@ -105,7 +105,7 @@
     }
 
     SkBlurMaskFilterImpl(SkReadBuffer&);
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
     SkScalar computeXformedSigma(const SkMatrix& ctm) const {
         bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
@@ -607,17 +607,18 @@
 public:
     virtual ~GrRectBlurEffect();
 
-    const char* name() const SK_OVERRIDE { return "RectBlur"; }
+    const char* name() const override { return "RectBlur"; }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     /**
      * Create a simple filter effect with custom bicubic coefficients.
      */
-    static GrFragmentProcessor* Create(GrContext *context, const SkRect& rect, float sigma) {
+    static GrFragmentProcessor* Create(GrTextureProvider *textureProvider, const SkRect& rect,
+                                       float sigma) {
         GrTexture *blurProfileTexture = NULL;
         int doubleProfileSize = SkScalarCeilToInt(12*sigma);
 
@@ -628,7 +629,8 @@
             return NULL;
         }
 
-        bool createdBlurProfileTexture = CreateBlurProfileTexture(context, sigma, &blurProfileTexture);
+        bool createdBlurProfileTexture = CreateBlurProfileTexture(
+            textureProvider, sigma, &blurProfileTexture);
         SkAutoTUnref<GrTexture> hunref(blurProfileTexture);
         if (!createdBlurProfileTexture) {
            return NULL;
@@ -641,11 +643,11 @@
 
 private:
     GrRectBlurEffect(const SkRect& rect, float sigma, GrTexture *blur_profile);
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
-    static bool CreateBlurProfileTexture(GrContext *context, float sigma,
+    static bool CreateBlurProfileTexture(GrTextureProvider*, float sigma,
                                          GrTexture **blurProfileTexture);
 
     SkRect          fRect;
@@ -665,9 +667,9 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     typedef GrGLProgramDataManager::UniformHandle UniformHandle;
@@ -678,7 +680,7 @@
     typedef GrGLFragmentProcessor INHERITED;
 };
 
-void OutputRectBlurProfileLookup(GrGLFPFragmentBuilder* fsBuilder,
+void OutputRectBlurProfileLookup(GrGLFragmentBuilder* fsBuilder,
                                  const GrGLShaderBuilder::TextureSampler& sampler,
                                  const char *output,
                                  const char *profileSize, const char *loc,
@@ -715,7 +717,7 @@
                                             "profileSize",
                                             &profileSizeName);
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     const char *fragmentPos = fsBuilder->fragmentPosition();
 
     if (inputColor) {
@@ -748,7 +750,7 @@
     pdman.set1f(fProfileSizeUniform, SkScalarCeilToScalar(6*rbe.getSigma()));
 }
 
-bool GrRectBlurEffect::CreateBlurProfileTexture(GrContext *context, float sigma,
+bool GrRectBlurEffect::CreateBlurProfileTexture(GrTextureProvider* textureProvider, float sigma,
                                                 GrTexture **blurProfileTexture) {
     GrSurfaceDesc texDesc;
 
@@ -767,19 +769,19 @@
     uint8_t *profile = NULL;
     SkAutoTDeleteArray<uint8_t> ada(NULL);
 
-    *blurProfileTexture = context->findAndRefCachedTexture(key);
+    *blurProfileTexture = textureProvider->findAndRefTextureByUniqueKey(key);
 
     if (NULL == *blurProfileTexture) {
 
         SkBlurMask::ComputeBlurProfile(sigma, &profile);
         ada.reset(profile);
 
-        *blurProfileTexture = context->createTexture(texDesc, true, profile, 0);
+        *blurProfileTexture = textureProvider->createTexture(texDesc, true, profile, 0);
 
         if (NULL == *blurProfileTexture) {
             return false;
         }
-        context->addResourceToCache(key, *blurProfileTexture);
+        textureProvider->assignUniqueKeyToTexture(key, *blurProfileTexture);
     }
 
     return true;
@@ -798,7 +800,7 @@
 GrRectBlurEffect::~GrRectBlurEffect() {
 }
 
-void GrRectBlurEffect::getGLProcessorKey(const GrGLCaps& caps,
+void GrRectBlurEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                          GrProcessorKeyBuilder* b) const {
     GrGLRectBlurEffect::GenKey(*this, caps, b);
 }
@@ -825,7 +827,8 @@
     float sigma = random->nextRangeF(3,8);
     float width = random->nextRangeF(200,300);
     float height = random->nextRangeF(200,300);
-    return GrRectBlurEffect::Create(context, SkRect::MakeWH(width, height), sigma);
+    return GrRectBlurEffect::Create(context->textureProvider(), SkRect::MakeWH(width, height),
+                                    sigma);
 }
 
 
@@ -855,7 +858,8 @@
     int pad=SkScalarCeilToInt(6*xformedSigma)/2;
     rect.outset(SkIntToScalar(pad), SkIntToScalar(pad));
 
-    SkAutoTUnref<GrFragmentProcessor> fp(GrRectBlurEffect::Create(context, rect, xformedSigma));
+    SkAutoTUnref<GrFragmentProcessor> fp(GrRectBlurEffect::Create(
+        context->textureProvider(), rect, xformedSigma));
     if (!fp) {
         return false;
     }
@@ -876,22 +880,22 @@
     static GrFragmentProcessor* Create(GrContext* context, float sigma, const SkRRect&);
 
     virtual ~GrRRectBlurEffect() {};
-    const char* name() const SK_OVERRIDE { return "GrRRectBlur"; }
+    const char* name() const override { return "GrRRectBlur"; }
 
     const SkRRect& getRRect() const { return fRRect; }
     float getSigma() const { return fSigma; }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
 private:
     GrRRectBlurEffect(float sigma, const SkRRect&, GrTexture* profileTexture);
 
-    bool onIsEqual(const GrFragmentProcessor& other) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor& other) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     SkRRect             fRRect;
     float               fSigma;
@@ -927,7 +931,8 @@
     builder[1] = cornerRadius;
     builder.finish();
 
-    SkAutoTUnref<GrTexture> blurNinePatchTexture(context->findAndRefCachedTexture(key));
+    SkAutoTUnref<GrTexture> blurNinePatchTexture(
+        context->textureProvider()->findAndRefTextureByUniqueKey(key));
 
     if (!blurNinePatchTexture) {
         SkMask mask;
@@ -964,12 +969,13 @@
         texDesc.fHeight = texSide;
         texDesc.fConfig = kAlpha_8_GrPixelConfig;
 
-        blurNinePatchTexture.reset(context->createTexture(texDesc, true, blurredMask.fImage, 0));
+        blurNinePatchTexture.reset(
+            context->textureProvider()->createTexture(texDesc, true, blurredMask.fImage, 0));
         SkMask::FreeImage(blurredMask.fImage);
         if (!blurNinePatchTexture) {
             return NULL;
         }
-        context->addResourceToCache(key, blurNinePatchTexture);
+        context->textureProvider()->assignUniqueKeyToTexture(key, blurNinePatchTexture);
     }
     return SkNEW_ARGS(GrRRectBlurEffect, (sigma, rrect, blurNinePatchTexture));
 }
@@ -1020,9 +1026,9 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     GrGLProgramDataManager::UniformHandle fProxyRectUniform;
@@ -1060,7 +1066,7 @@
                                               "blurRadius",
                                               &blurRadiusName);
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     const char* fragmentPos = fsBuilder->fragmentPosition();
 
     // warp the fragment position to the appropriate part of the 9patch blur texture
@@ -1108,7 +1114,7 @@
     pdman.set1f(fCornerRadiusUniform, radius);
 }
 
-void GrRRectBlurEffect::getGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const {
+void GrRRectBlurEffect::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
     GrGLRRectBlurEffect::GenKey(*this, caps, b);
 }
 
diff --git a/src/effects/SkColorCubeFilter.cpp b/src/effects/SkColorCubeFilter.cpp
index e2aade7..253d287 100644
--- a/src/effects/SkColorCubeFilter.cpp
+++ b/src/effects/SkColorCubeFilter.cpp
@@ -192,16 +192,16 @@
 
     virtual ~GrColorCubeEffect();
 
-    const char* name() const SK_OVERRIDE { return "ColorCube"; }
+    const char* name() const override { return "ColorCube"; }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
     int colorCubeSize() const { return fColorCubeAccess.getTexture()->width(); }
 
 
-    void onComputeInvariantOutput(GrInvariantOutput*) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput*) const override;
 
     class GLProcessor : public GrGLFragmentProcessor {
     public:
@@ -213,11 +213,11 @@
                               const char* outputColor,
                               const char* inputColor,
                               const TransformedCoordsArray&,
-                              const TextureSamplerArray&) SK_OVERRIDE;
+                              const TextureSamplerArray&) override;
 
-        static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+        static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
 
-        void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+        void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
     private:
         GrGLProgramDataManager::UniformHandle fColorCubeSizeUni;
@@ -227,7 +227,7 @@
     };
 
 private:
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE { return true; }
+    bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
 
     GrColorCubeEffect(GrTexture* colorCube);
 
@@ -247,7 +247,7 @@
 GrColorCubeEffect::~GrColorCubeEffect() {
 }
 
-void GrColorCubeEffect::getGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const {
+void GrColorCubeEffect::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
     GLProcessor::GenKey(*this, caps, b);
 }
 
@@ -295,7 +295,7 @@
     // Note: if implemented using texture3D in OpenGL ES older than OpenGL ES 3.0,
     //       the shader might need "#extension GL_OES_texture_3D : enable".
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
 
     // Unpremultiply color
     fsBuilder->codeAppendf("\tfloat %s = max(%s.a, 0.00001);\n", nonZeroAlpha, inputColor);
@@ -334,7 +334,7 @@
 }
 
 void GrColorCubeEffect::GLProcessor::GenKey(const GrProcessor& proc,
-                                            const GrGLCaps&, GrProcessorKeyBuilder* b) {
+                                            const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
 }
 
 bool SkColorCubeFilter::asFragmentProcessors(GrContext* context,
@@ -351,11 +351,13 @@
     desc.fHeight = fCache.cubeDimension() * fCache.cubeDimension();
     desc.fConfig = kRGBA_8888_GrPixelConfig;
 
-    SkAutoTUnref<GrTexture> textureCube(context->findAndRefCachedTexture(key));
+    SkAutoTUnref<GrTexture> textureCube(
+        context->textureProvider()->findAndRefTextureByUniqueKey(key));
     if (!textureCube) {
-        textureCube.reset(context->createTexture(desc, true, fCubeData->data(), 0));
+        textureCube.reset(context->textureProvider()->createTexture(
+            desc, true, fCubeData->data(), 0));
         if (textureCube) {
-            context->addResourceToCache(key, textureCube);
+            context->textureProvider()->assignUniqueKeyToTexture(key, textureCube);
         }
     }
 
diff --git a/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp
index 210fb57..2eb720e 100755
--- a/src/effects/SkColorFilterImageFilter.cpp
+++ b/src/effects/SkColorFilterImageFilter.cpp
@@ -16,7 +16,7 @@
 #include "SkWriteBuffer.h"
 
 SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
-        SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID) {
+        SkImageFilter* input, const CropRect* cropRect) {
     if (NULL == cf) {
         return NULL;
     }
@@ -28,22 +28,22 @@
         SkAutoUnref autoUnref(inputCF);
         SkAutoTUnref<SkColorFilter> newCF(SkColorFilter::CreateComposeFilter(cf, inputCF));
         if (newCF) {
-            return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0), cropRect, 0));
+            return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0), cropRect));
         }
     }
 
-    return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input, cropRect, uniqueID));
+    return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input, cropRect));
 }
 
 SkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf,
-        SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID)
-    : INHERITED(1, &input, cropRect, uniqueID), fColorFilter(SkRef(cf)) {
+        SkImageFilter* input, const CropRect* cropRect)
+    : INHERITED(1, &input, cropRect), fColorFilter(SkRef(cf)) {
 }
 
 SkFlattenable* SkColorFilterImageFilter::CreateProc(SkReadBuffer& buffer) {
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
     SkAutoTUnref<SkColorFilter> cf(buffer.readColorFilter());
-    return Create(cf, common.getInput(0), &common.cropRect(), common.uniqueID());
+    return Create(cf, common.getInput(0), &common.cropRect());
 }
 
 void SkColorFilterImageFilter::flatten(SkWriteBuffer& buffer) const {
diff --git a/src/effects/SkColorFilters.cpp b/src/effects/SkColorFilters.cpp
index 8bcd0eb..63016f0 100644
--- a/src/effects/SkColorFilters.cpp
+++ b/src/effects/SkColorFilters.cpp
@@ -27,28 +27,25 @@
 }
 
 uint32_t SkModeColorFilter::getFlags() const {
-    return fProc16 ? (kAlphaUnchanged_Flag | kHasFilter16_Flag) : 0;
+    switch (fMode) {
+        case SkXfermode::kDst_Mode:      //!< [Da, Dc]
+        case SkXfermode::kSrcATop_Mode:  //!< [Da, Sc * Da + (1 - Sa) * Dc]
+            return kAlphaUnchanged_Flag;
+        default:
+            break;
+    }
+    return 0;
 }
 
 void SkModeColorFilter::filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) const {
     SkPMColor       color = fPMColor;
     SkXfermodeProc  proc = fProc;
-    
+
     for (int i = 0; i < count; i++) {
         result[i] = proc(color, shader[i]);
     }
 }
 
-void SkModeColorFilter::filterSpan16(const uint16_t shader[], int count, uint16_t result[]) const {
-    SkASSERT(this->getFlags() & kHasFilter16_Flag);
-    
-    SkPMColor        color = fPMColor;
-    SkXfermodeProc16 proc16 = fProc16;
-    
-    for (int i = 0; i < count; i++) {
-        result[i] = proc16(color, shader[i]);
-    }
-}
 void SkModeColorFilter::flatten(SkWriteBuffer& buffer) const {
     buffer.writeColor(fColor);
     buffer.writeUInt(fMode);
@@ -57,7 +54,6 @@
 void SkModeColorFilter::updateCache() {
     fPMColor = SkPreMultiplyColor(fColor);
     fProc = SkXfermode::GetProc(fMode);
-    fProc16 = SkXfermode::GetProc16(fMode, fColor);
 }
 
 SkFlattenable* SkModeColorFilter::CreateProc(SkReadBuffer& buffer) {
@@ -151,16 +147,16 @@
         return true;
     }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GLProcessor::GenKey(*this, caps, b);
     }
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
+    GrGLFragmentProcessor* createGLInstance() const override {
         return SkNEW_ARGS(GLProcessor, (*this));
     }
 
-    const char* name() const SK_OVERRIDE { return "ModeColorFilterEffect"; }
+    const char* name() const override { return "ModeColorFilterEffect"; }
 
     SkXfermode::Mode mode() const { return fMode; }
     GrColor color() const { return fColor; }
@@ -175,7 +171,7 @@
                               const char* outputColor,
                               const char* inputColor,
                               const TransformedCoordsArray&,
-                              const TextureSamplerArray&) SK_OVERRIDE {
+                              const TextureSamplerArray&) override {
             SkXfermode::Mode mode = fp.cast<ModeColorFilterEffect>().mode();
 
             SkASSERT(SkXfermode::kDst_Mode != mode);
@@ -195,7 +191,7 @@
                     codeAppendf("\t%s = %s;\n", outputColor, filter.c_str());
         }
 
-        static void GenKey(const GrProcessor& fp, const GrGLCaps&,
+        static void GenKey(const GrProcessor& fp, const GrGLSLCaps&,
                            GrProcessorKeyBuilder* b) {
             const ModeColorFilterEffect& colorModeFilter = fp.cast<ModeColorFilterEffect>();
             // The SL code does not depend on filter color at the moment, so no need to represent it
@@ -204,7 +200,7 @@
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
-                             const GrProcessor& fp) SK_OVERRIDE {
+                             const GrProcessor& fp) override {
             if (fFilterColorUni.isValid()) {
                 const ModeColorFilterEffect& colorModeFilter = fp.cast<ModeColorFilterEffect>();
                 GrGLfloat c[4];
@@ -228,12 +224,12 @@
         this->initClassID<ModeColorFilterEffect>();
     }
 
-    bool onIsEqual(const GrFragmentProcessor& other) const SK_OVERRIDE {
+    bool onIsEqual(const GrFragmentProcessor& other) const override {
         const ModeColorFilterEffect& s = other.cast<ModeColorFilterEffect>();
         return fMode == s.fMode && fColor == s.fColor;
     }
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     SkXfermode::Mode fMode;
     GrColor fColor;
@@ -388,59 +384,23 @@
 public:
     Src_SkModeColorFilter(SkColor color) : INHERITED(color, SkXfermode::kSrc_Mode) {}
 
-    uint32_t getFlags() const SK_OVERRIDE {
-        if (SkGetPackedA32(this->getPMColor()) == 0xFF) {
-            return kAlphaUnchanged_Flag | kHasFilter16_Flag;
-        } else {
-            return 0;
-        }
-    }
-
-    virtual void filterSpan(const SkPMColor shader[], int count,
-                            SkPMColor result[]) const SK_OVERRIDE {
+    void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) const override {
         sk_memset32(result, this->getPMColor(), count);
     }
 
-    virtual void filterSpan16(const uint16_t shader[], int count,
-                              uint16_t result[]) const SK_OVERRIDE {
-        SkASSERT(this->getFlags() & kHasFilter16_Flag);
-        sk_memset16(result, SkPixel32ToPixel16(this->getPMColor()), count);
-    }
-
 private:
     typedef SkModeColorFilter INHERITED;
 };
 
 class SrcOver_SkModeColorFilter : public SkModeColorFilter {
 public:
-    SrcOver_SkModeColorFilter(SkColor color)
-            : INHERITED(color, SkXfermode::kSrcOver_Mode) {
-        fColor32Proc = SkBlitRow::ColorProcFactory();
-    }
+    SrcOver_SkModeColorFilter(SkColor color) : INHERITED(color, SkXfermode::kSrcOver_Mode) { }
 
-    uint32_t getFlags() const SK_OVERRIDE {
-        if (SkGetPackedA32(this->getPMColor()) == 0xFF) {
-            return kAlphaUnchanged_Flag | kHasFilter16_Flag;
-        } else {
-            return 0;
-        }
-    }
-
-    virtual void filterSpan(const SkPMColor shader[], int count,
-                            SkPMColor result[]) const SK_OVERRIDE {
-        fColor32Proc(result, shader, count, this->getPMColor());
-    }
-
-    virtual void filterSpan16(const uint16_t shader[], int count,
-                              uint16_t result[]) const SK_OVERRIDE {
-        SkASSERT(this->getFlags() & kHasFilter16_Flag);
-        sk_memset16(result, SkPixel32ToPixel16(this->getPMColor()), count);
+    void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) const override {
+        SkBlitRow::Color32(result, shader, count, this->getPMColor());
     }
 
 private:
-
-    SkBlitRow::ColorProc fColor32Proc;
-
     typedef SkModeColorFilter INHERITED;
 };
 
diff --git a/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp
index bb3b6dd..58f074c 100644
--- a/src/effects/SkColorMatrixFilter.cpp
+++ b/src/effects/SkColorMatrixFilter.cpp
@@ -1,18 +1,38 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "SkColorMatrixFilter.h"
 #include "SkColorMatrix.h"
 #include "SkColorPriv.h"
+#include "SkPMFloat.h"
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
 #include "SkUnPreMultiply.h"
 #include "SkString.h"
 
+#define SK_PMORDER_INDEX_A  (SK_A32_SHIFT / 8)
+#define SK_PMORDER_INDEX_R  (SK_R32_SHIFT / 8)
+#define SK_PMORDER_INDEX_G  (SK_G32_SHIFT / 8)
+#define SK_PMORDER_INDEX_B  (SK_B32_SHIFT / 8)
+
+static void transpose_to_pmorder(float dst[20], const float src[20]) {
+    const float* srcR = src + 0;
+    const float* srcG = src + 5;
+    const float* srcB = src + 10;
+    const float* srcA = src + 15;
+
+    for (int i = 0; i < 20; i += 4) {
+        dst[i + SK_PMORDER_INDEX_A] = *srcA++;
+        dst[i + SK_PMORDER_INDEX_R] = *srcR++;
+        dst[i + SK_PMORDER_INDEX_G] = *srcG++;
+        dst[i + SK_PMORDER_INDEX_B] = *srcB++;
+    }
+}
+
 static int32_t rowmul4(const int32_t array[], unsigned r, unsigned g,
                           unsigned b, unsigned a) {
     return array[0] * r + array[1] * g  + array[2] * b + array[3] * a + array[4];
@@ -117,12 +137,11 @@
     result[3] = a;
 }
 
-#define kNO_ALPHA_FLAGS (SkColorFilter::kAlphaUnchanged_Flag |  \
-                         SkColorFilter::kHasFilter16_Flag)
-
 // src is [20] but some compilers won't accept __restrict__ on anything
 // but an raw pointer or reference
 void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) {
+    transpose_to_pmorder(fTranspose, src);
+
     int32_t* array = fState.fArray;
     SkFixed max = 0;
     for (int i = 0; i < 20; i++) {
@@ -159,7 +178,7 @@
         fProc = shiftIs16 ? General16 : General;
         fFlags = changesAlpha ? 0 : SkColorFilter::kAlphaUnchanged_Flag;
     } else {
-        fFlags = kNO_ALPHA_FLAGS;
+        fFlags = SkColorFilter::kAlphaUnchanged_Flag;
 
         int32_t needsScale = (array[SkColorMatrix::kR_Scale] - one) |
                              (array[SkColorMatrix::kG_Scale] - one) |
@@ -220,12 +239,45 @@
     return this->INHERITED::getFlags() | fFlags;
 }
 
-void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count,
-                                     SkPMColor dst[]) const {
-    Proc proc = fProc;
-    const State& state = fState;
-    int32_t result[4];
+/**
+ *  Need inv255 = 1 / 255 as a constant, so when we premul a SkPMFloat, we can do this
+ *
+ *      new_red = old_red * alpha * inv255
+ *
+ *  instead of (much slower)
+ *
+ *      new_red = old_red * alpha / 255
+ *
+ *  However, 1.0f/255 comes to (in hex) 0x3B808081, which is slightly bigger than the "actual"
+ *  value of 0x3B808080(repeat 80)... This slightly too-big value can cause us to compute
+ *  new_red > alpha, which is a problem (for valid premul). To fix this, we use a
+ *  hand-computed value of 0x3B808080, 1 ULP smaller. This keeps our colors valid.
+ */
+static const float gInv255 = 0.0039215683f; //  (1.0f / 255) - ULP == SkBits2Float(0x3B808080)
 
+static Sk4f premul(const Sk4f& x) {
+    float scale = SkPMFloat(x).a() * gInv255;
+    Sk4f pm = x * Sk4f(scale, scale, scale, 1);
+
+#ifdef SK_DEBUG
+    SkPMFloat pmf(pm);
+    SkASSERT(pmf.isValid());
+#endif
+
+    return pm;
+}
+
+static Sk4f unpremul(const SkPMFloat& pm) {
+    float scale = 255 / pm.a(); // candidate for fast/approx invert?
+    return pm * Sk4f(scale, scale, scale, 1);
+}
+
+static Sk4f clamp_0_255(const Sk4f& value) {
+    return Sk4f::Max(Sk4f::Min(value, Sk4f(255)), Sk4f(0));
+}
+
+void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const {
+    Proc proc = fProc;
     if (NULL == proc) {
         if (src != dst) {
             memcpy(dst, src, count * sizeof(SkPMColor));
@@ -233,70 +285,80 @@
         return;
     }
 
-    const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
+#ifdef SK_SUPPORT_LEGACY_INT_COLORMATRIX
+    const bool use_floats = false;
+#else
+    const bool use_floats = true;
+#endif
 
-    for (int i = 0; i < count; i++) {
-        SkPMColor c = src[i];
+    if (use_floats) {
+        const Sk4f c0 = Sk4f::Load(fTranspose + 0);
+        const Sk4f c1 = Sk4f::Load(fTranspose + 4);
+        const Sk4f c2 = Sk4f::Load(fTranspose + 8);
+        const Sk4f c3 = Sk4f::Load(fTranspose + 12);
+        const Sk4f c4 = Sk4f::Load(fTranspose + 16);  // translates
 
-        unsigned r = SkGetPackedR32(c);
-        unsigned g = SkGetPackedG32(c);
-        unsigned b = SkGetPackedB32(c);
-        unsigned a = SkGetPackedA32(c);
+        // todo: we could cache this in the constructor...
+        SkPMColor matrix_translate_pmcolor = SkPMFloat(premul(clamp_0_255(c4))).roundClamp();
 
-        // need our components to be un-premultiplied
-        if (255 != a) {
-            SkUnPreMultiply::Scale scale = table[a];
-            r = SkUnPreMultiply::ApplyScale(scale, r);
-            g = SkUnPreMultiply::ApplyScale(scale, g);
-            b = SkUnPreMultiply::ApplyScale(scale, b);
+        for (int i = 0; i < count; i++) {
+            const SkPMColor src_c = src[i];
+            if (0 == src_c) {
+                dst[i] = matrix_translate_pmcolor;
+                continue;
+            }
 
-            SkASSERT(r <= 255);
-            SkASSERT(g <= 255);
-            SkASSERT(b <= 255);
+            SkPMFloat srcf(src_c);
+
+            if (0xFF != SkGetPackedA32(src_c)) {
+                srcf = unpremul(srcf);
+            }
+
+            Sk4f r4 = Sk4f(srcf.r());
+            Sk4f g4 = Sk4f(srcf.g());
+            Sk4f b4 = Sk4f(srcf.b());
+            Sk4f a4 = Sk4f(srcf.a());
+
+            // apply matrix
+            Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4;
+
+            // clamp, re-premul, and write
+            dst[i] = SkPMFloat(premul(clamp_0_255(dst4))).round();
         }
+    } else {
+        const State& state = fState;
+        int32_t result[4];
+        const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
 
-        proc(state, r, g, b, a, result);
+        for (int i = 0; i < count; i++) {
+            SkPMColor c = src[i];
 
-        r = pin(result[0], SK_R32_MASK);
-        g = pin(result[1], SK_G32_MASK);
-        b = pin(result[2], SK_B32_MASK);
-        a = pin(result[3], SK_A32_MASK);
-        // re-prepremultiply if needed
-        dst[i] = SkPremultiplyARGBInline(a, r, g, b);
-    }
-}
+            unsigned r = SkGetPackedR32(c);
+            unsigned g = SkGetPackedG32(c);
+            unsigned b = SkGetPackedB32(c);
+            unsigned a = SkGetPackedA32(c);
 
-void SkColorMatrixFilter::filterSpan16(const uint16_t src[], int count,
-                                       uint16_t dst[]) const {
-    SkASSERT(fFlags & SkColorFilter::kHasFilter16_Flag);
+            // need our components to be un-premultiplied
+            if (255 != a) {
+                SkUnPreMultiply::Scale scale = table[a];
+                r = SkUnPreMultiply::ApplyScale(scale, r);
+                g = SkUnPreMultiply::ApplyScale(scale, g);
+                b = SkUnPreMultiply::ApplyScale(scale, b);
 
-    Proc   proc = fProc;
-    const State& state = fState;
-    int32_t result[4];
+                SkASSERT(r <= 255);
+                SkASSERT(g <= 255);
+                SkASSERT(b <= 255);
+            }
 
-    if (NULL == proc) {
-        if (src != dst) {
-            memcpy(dst, src, count * sizeof(uint16_t));
+            proc(state, r, g, b, a, result);
+
+            r = pin(result[0], SK_R32_MASK);
+            g = pin(result[1], SK_G32_MASK);
+            b = pin(result[2], SK_B32_MASK);
+            a = pin(result[3], SK_A32_MASK);
+            // re-prepremultiply if needed
+            dst[i] = SkPremultiplyARGBInline(a, r, g, b);
         }
-        return;
-    }
-
-    for (int i = 0; i < count; i++) {
-        uint16_t c = src[i];
-
-        // expand to 8bit components (since our matrix translate is 8bit biased
-        unsigned r = SkPacked16ToR32(c);
-        unsigned g = SkPacked16ToG32(c);
-        unsigned b = SkPacked16ToB32(c);
-
-        proc(state, r, g, b, 0, result);
-
-        r = pin(result[0], SK_R32_MASK);
-        g = pin(result[1], SK_G32_MASK);
-        b = pin(result[2], SK_B32_MASK);
-
-        // now packed it back down to 16bits (hmmm, could dither...)
-        dst[i] = SkPack888ToRGB16(r, g, b);
     }
 }
 
@@ -344,14 +406,14 @@
         return SkNEW_ARGS(ColorMatrixEffect, (matrix));
     }
 
-    const char* name() const SK_OVERRIDE { return "Color Matrix"; }
+    const char* name() const override { return "Color Matrix"; }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GLProcessor::GenKey(*this, caps, b);
     }
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
+    GrGLFragmentProcessor* createGLInstance() const override {
         return SkNEW_ARGS(GLProcessor, (*this));
     }
 
@@ -361,7 +423,7 @@
     class GLProcessor : public GrGLFragmentProcessor {
     public:
         // this class always generates the same code.
-        static void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder* b) {}
+        static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {}
 
         GLProcessor(const GrProcessor&) {}
 
@@ -370,7 +432,7 @@
                               const char* outputColor,
                               const char* inputColor,
                               const TransformedCoordsArray&,
-                              const TextureSamplerArray&) SK_OVERRIDE {
+                              const TextureSamplerArray&) override {
             fMatrixHandle = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
                                                 kMat44f_GrSLType, kDefault_GrSLPrecision,
                                                 "ColorMatrix");
@@ -382,7 +444,7 @@
                 // could optimize this case, but we aren't for now.
                 inputColor = "vec4(1)";
             }
-            GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+            GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
             // The max() is to guard against 0 / 0 during unpremul when the incoming color is
             // transparent black.
             fsBuilder->codeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", inputColor);
@@ -396,7 +458,7 @@
         }
 
         virtual void setData(const GrGLProgramDataManager& uniManager,
-                             const GrProcessor& proc) SK_OVERRIDE {
+                             const GrProcessor& proc) override {
             const ColorMatrixEffect& cme = proc.cast<ColorMatrixEffect>();
             const float* m = cme.fMatrix.fMat;
             // The GL matrix is transposed from SkColorMatrix.
@@ -426,12 +488,12 @@
         this->initClassID<ColorMatrixEffect>();
     }
 
-    bool onIsEqual(const GrFragmentProcessor& s) const SK_OVERRIDE {
+    bool onIsEqual(const GrFragmentProcessor& s) const override {
         const ColorMatrixEffect& cme = s.cast<ColorMatrixEffect>();
         return cme.fMatrix == fMatrix;
     }
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE {
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const 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.
 
diff --git a/src/effects/SkCornerPathEffect.cpp b/src/effects/SkCornerPathEffect.cpp
index 79d4c4c..4b81f26 100644
--- a/src/effects/SkCornerPathEffect.cpp
+++ b/src/effects/SkCornerPathEffect.cpp
@@ -20,13 +20,12 @@
                         SkPoint* step) {
     SkScalar dist = SkPoint::Distance(a, b);
 
-    step->set(b.fX - a.fX, b.fY - a.fY);
-
+    *step = b - a;
     if (dist <= radius * 2) {
-        step->scale(SK_ScalarHalf);
+        *step *= SK_ScalarHalf;
         return false;
     } else {
-        step->scale(SkScalarDiv(radius, dist));
+        *step *= radius / dist;
         return true;
     }
 }
@@ -47,6 +46,7 @@
     bool        prevIsValid = true;
 
     // to avoid warnings
+    step.set(0, 0);
     moveTo.set(0, 0);
     firstStep.set(0, 0);
     lastCorner.set(0, 0);
diff --git a/src/effects/SkDashPathEffect.cpp b/src/effects/SkDashPathEffect.cpp
index 5296f97..62e6771 100644
--- a/src/effects/SkDashPathEffect.cpp
+++ b/src/effects/SkDashPathEffect.cpp
@@ -248,7 +248,7 @@
                 len2 -= clampedInitialDashLength; // skip initial partial empty
             }
         }
-        int numMidPoints = SkScalarFloorToInt(SkScalarDiv(len2, fIntervalLength));
+        int numMidPoints = SkScalarFloorToInt(len2 / fIntervalLength);
         results->fNumPoints += numMidPoints;
         len2 -= numMidPoints * fIntervalLength;
         bool partialLast = false;
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index 25339e6..2b9665c 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -14,6 +14,7 @@
 #include "GrContext.h"
 #include "GrCoordTransform.h"
 #include "GrInvariantOutput.h"
+#include "effects/GrTextureDomain.h"
 #include "gl/GrGLProcessor.h"
 #include "gl/builders/GrGLProgramBuilder.h"
 #endif
@@ -55,7 +56,7 @@
                          SkBitmap* src,
                          const SkIRect& bounds)
 {
-    static const SkScalar Inv8bit = SkScalarDiv(SK_Scalar1, 255.0f);
+    static const SkScalar Inv8bit = SkScalarInvert(255);
     const int srcW = src->width();
     const int srcH = src->height();
     const SkVector scaleForColor = SkVector::Make(SkScalarMul(scale.fX, Inv8bit),
@@ -164,7 +165,7 @@
                                                          SkScalar scale,
                                                          SkImageFilter* displacement,
                                                          SkImageFilter* color,
-                                                         const CropRect* cropRect, uint32_t uniqueID) {
+                                                         const CropRect* cropRect) {
     if (!channel_selector_type_is_valid(xChannelSelector) ||
         !channel_selector_type_is_valid(yChannelSelector)) {
         return NULL;
@@ -172,16 +173,15 @@
 
     SkImageFilter* inputs[2] = { displacement, color };
     return SkNEW_ARGS(SkDisplacementMapEffect, (xChannelSelector, yChannelSelector, scale,
-                                                inputs, cropRect, uniqueID));
+                                                inputs, cropRect));
 }
 
 SkDisplacementMapEffect::SkDisplacementMapEffect(ChannelSelectorType xChannelSelector,
                                                  ChannelSelectorType yChannelSelector,
                                                  SkScalar scale,
                                                  SkImageFilter* inputs[2],
-                                                 const CropRect* cropRect,
-                                                 uint32_t uniqueID)
-  : INHERITED(2, inputs, cropRect, uniqueID)
+                                                 const CropRect* cropRect)
+  : INHERITED(2, inputs, cropRect)
   , fXChannelSelector(xChannelSelector)
   , fYChannelSelector(yChannelSelector)
   , fScale(scale)
@@ -196,7 +196,7 @@
     ChannelSelectorType xsel = (ChannelSelectorType)buffer.readInt();
     ChannelSelectorType ysel = (ChannelSelectorType)buffer.readInt();
     SkScalar scale = buffer.readScalar();
-    return Create(xsel, ysel, scale, common.getInput(0), common.getInput(1), &common.cropRect(), common.uniqueID());
+    return Create(xsel, ysel, scale, common.getInput(0), common.getInput(1), &common.cropRect());
 }
 
 void SkDisplacementMapEffect::flatten(SkWriteBuffer& buffer) const {
@@ -310,16 +310,18 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
+    const GrTextureDomain::GLDomain& glDomain() const { return fGLDomain; }
 
 private:
     SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
     SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
     GrGLProgramDataManager::UniformHandle fScaleUni;
+    GrTextureDomain::GLDomain fGLDomain;
 
     typedef GrGLFragmentProcessor INHERITED;
 };
@@ -331,23 +333,25 @@
     static GrFragmentProcessor* Create(
             SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
             SkDisplacementMapEffect::ChannelSelectorType yChannelSelector, SkVector scale,
-            GrTexture* displacement, const SkMatrix& offsetMatrix, GrTexture* color) {
+            GrTexture* displacement, const SkMatrix& offsetMatrix, GrTexture* color,
+            const SkISize& colorDimensions) {
         return SkNEW_ARGS(GrDisplacementMapEffect, (xChannelSelector,
                                                     yChannelSelector,
                                                     scale,
                                                     displacement,
                                                     offsetMatrix,
-                                                    color));
+                                                    color,
+                                                    colorDimensions));
     }
 
     virtual ~GrDisplacementMapEffect();
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GrGLDisplacementMapEffect::GenKey(*this, caps, b);
     }
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
+    GrGLFragmentProcessor* createGLInstance() const override {
         return SkNEW_ARGS(GrGLDisplacementMapEffect, (*this));
     }
 
@@ -357,24 +361,27 @@
         { return fYChannelSelector; }
     const SkVector& scale() const { return fScale; }
 
-    const char* name() const SK_OVERRIDE { return "DisplacementMap"; }
+    const char* name() const override { return "DisplacementMap"; }
+    const GrTextureDomain& domain() const { return fDomain; }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     GrDisplacementMapEffect(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
                             SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
                             const SkVector& scale,
                             GrTexture* displacement, const SkMatrix& offsetMatrix,
-                            GrTexture* color);
+                            GrTexture* color,
+                            const SkISize& colorDimensions);
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
     GrCoordTransform            fDisplacementTransform;
     GrTextureAccess             fDisplacementAccess;
     GrCoordTransform            fColorTransform;
+    GrTextureDomain             fDomain;
     GrTextureAccess             fColorAccess;
     SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
     SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
@@ -422,8 +429,8 @@
     desc.fHeight = bounds.height();
     desc.fConfig = kSkia8888_GrPixelConfig;
 
-    SkAutoTUnref<GrTexture> dst(
-        context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch));
+    SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture(desc,
+        GrTextureProvider::kApprox_ScratchTexMatch));
 
     if (!dst) {
         return false;
@@ -443,7 +450,8 @@
                                         scale,
                                         displacement,
                                         offsetMatrix,
-                                        color))->unref();
+                                        color,
+                                        colorBM.dimensions()))->unref();
     SkIRect colorBounds = bounds;
     colorBounds.offset(-colorOffset);
     SkMatrix matrix;
@@ -465,11 +473,14 @@
                              const SkVector& scale,
                              GrTexture* displacement,
                              const SkMatrix& offsetMatrix,
-                             GrTexture* color)
+                             GrTexture* color,
+                             const SkISize& colorDimensions)
     : fDisplacementTransform(kLocal_GrCoordSet, offsetMatrix, displacement,
                              GrTextureParams::kNone_FilterMode)
     , fDisplacementAccess(displacement)
     , fColorTransform(kLocal_GrCoordSet, color, GrTextureParams::kNone_FilterMode)
+    , fDomain(GrTextureDomain::MakeTexelDomain(color, SkIRect::MakeSize(colorDimensions)),
+              GrTextureDomain::kDecal_Mode)
     , fColorAccess(color)
     , fXChannelSelector(xChannelSelector)
     , fYChannelSelector(yChannelSelector)
@@ -521,10 +532,12 @@
         random->nextRangeU(1, kMaxComponent));
     SkVector scale = SkVector::Make(random->nextRangeScalar(0, 100.0f),
                                     random->nextRangeScalar(0, 100.0f));
-
+    SkISize colorDimensions;
+    colorDimensions.fWidth = random->nextRangeU(0, textures[texIdxColor]->width());
+    colorDimensions.fHeight = random->nextRangeU(0, textures[texIdxColor]->height());
     return GrDisplacementMapEffect::Create(xChannelSelector, yChannelSelector, scale,
                                            textures[texIdxDispl], SkMatrix::I(),
-                                           textures[texIdxColor]);
+                                           textures[texIdxColor], colorDimensions);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -538,11 +551,12 @@
 }
 
 void GrGLDisplacementMapEffect::emitCode(GrGLFPBuilder* builder,
-                                         const GrFragmentProcessor&,
+                                         const GrFragmentProcessor& fp,
                                          const char* outputColor,
                                          const char* inputColor,
                                          const TransformedCoordsArray& coords,
                                          const TextureSamplerArray& samplers) {
+    const GrTextureDomain& domain = fp.cast<GrDisplacementMapEffect>().domain();
     sk_ignore_unused_variable(inputColor);
 
     fScaleUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
@@ -550,12 +564,11 @@
     const char* scaleUni = builder->getUniformCStr(fScaleUni);
     const char* dColor = "dColor";
     const char* cCoords = "cCoords";
-    const char* outOfBounds = "outOfBounds";
     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.
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     fsBuilder->codeAppendf("\t\tvec4 %s = ", dColor);
     fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0].getType());
     fsBuilder->codeAppend(";\n");
@@ -604,15 +617,7 @@
     }
     fsBuilder->codeAppend("-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.
-    fsBuilder->codeAppendf(
-        "bool %s = (%s.x < 0.0) || (%s.y < 0.0) || (%s.x > 1.0) || (%s.y > 1.0);\t\t",
-        outOfBounds, cCoords, cCoords, cCoords, cCoords);
-    fsBuilder->codeAppendf("%s = %s ? vec4(0.0) : ", outputColor, outOfBounds);
-
-    // cCoords is always a vec2f
-    fsBuilder->appendTextureLookup(samplers[1], cCoords, kVec2f_GrSLType);
+    fGLDomain.sampleTexture(fsBuilder, domain, outputColor, SkString(cCoords), samplers[1]);
     fsBuilder->codeAppend(";\n");
 }
 
@@ -620,15 +625,16 @@
                                         const GrProcessor& proc) {
     const GrDisplacementMapEffect& displacementMap = proc.cast<GrDisplacementMapEffect>();
     GrTexture* colorTex = displacementMap.texture(1);
-    SkScalar scaleX = SkScalarDiv(displacementMap.scale().fX, SkIntToScalar(colorTex->width()));
-    SkScalar scaleY = SkScalarDiv(displacementMap.scale().fY, SkIntToScalar(colorTex->height()));
+    SkScalar scaleX = displacementMap.scale().fX / colorTex->width();
+    SkScalar scaleY = displacementMap.scale().fY / colorTex->height();
     pdman.set2f(fScaleUni, SkScalarToFloat(scaleX),
                 colorTex->origin() == kTopLeft_GrSurfaceOrigin ?
                 SkScalarToFloat(scaleY) : SkScalarToFloat(-scaleY));
+    fGLDomain.setData(pdman, displacementMap.domain(), colorTex->origin());
 }
 
 void GrGLDisplacementMapEffect::GenKey(const GrProcessor& proc,
-                                       const GrGLCaps&, GrProcessorKeyBuilder* b) {
+                                       const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
     const GrDisplacementMapEffect& displacementMap = proc.cast<GrDisplacementMapEffect>();
 
     uint32_t xKey = displacementMap.xChannelSelector();
diff --git a/src/effects/SkDropShadowImageFilter.cpp b/src/effects/SkDropShadowImageFilter.cpp
index 22bee9d..c453411 100644
--- a/src/effects/SkDropShadowImageFilter.cpp
+++ b/src/effects/SkDropShadowImageFilter.cpp
@@ -18,8 +18,8 @@
 SkDropShadowImageFilter::SkDropShadowImageFilter(SkScalar dx, SkScalar dy,
                                                  SkScalar sigmaX, SkScalar sigmaY, SkColor color,
                                                  ShadowMode shadowMode, SkImageFilter* input,
-                                                 const CropRect* cropRect, uint32_t uniqueID)
-    : INHERITED(1, &input, cropRect, uniqueID)
+                                                 const CropRect* cropRect)
+    : INHERITED(1, &input, cropRect)
     , fDx(dx)
     , fDy(dy)
     , fSigmaX(sigmaX)
@@ -40,7 +40,7 @@
                             kDrawShadowAndForeground_ShadowMode :
                             static_cast<ShadowMode>(buffer.readInt());
     return Create(dx, dy, sigmaX, sigmaY, color, shadowMode, common.getInput(0),
-                  &common.cropRect(), common.uniqueID());
+                  &common.cropRect());
 }
 
 void SkDropShadowImageFilter::flatten(SkWriteBuffer& buffer) const {
diff --git a/src/effects/SkEmbossMaskFilter.cpp b/src/effects/SkEmbossMaskFilter.cpp
index 8fcd272..59d2978 100644
--- a/src/effects/SkEmbossMaskFilter.cpp
+++ b/src/effects/SkEmbossMaskFilter.cpp
@@ -61,7 +61,7 @@
     mag = SkScalarSqrt(mag);
 
     for (int i = 0; i < 3; i++) {
-        v[i] = SkScalarDiv(v[i], mag);
+        v[i] /= mag;
     }
 }
 
diff --git a/src/effects/SkGpuBlurUtils.cpp b/src/effects/SkGpuBlurUtils.cpp
index 832e48a..6cb349e 100644
--- a/src/effects/SkGpuBlurUtils.cpp
+++ b/src/effects/SkGpuBlurUtils.cpp
@@ -180,12 +180,14 @@
     GrTexture* tempTexture;
     SkAutoTUnref<GrTexture> temp1, temp2;
 
-    temp1.reset(context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch));
+    temp1.reset(context->textureProvider()->refScratchTexture(
+        desc, GrTextureProvider::kApprox_ScratchTexMatch));
     dstTexture = temp1.get();
     if (canClobberSrc) {
         tempTexture = srcTexture;
     } else {
-        temp2.reset(context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch));
+        temp2.reset(context->textureProvider()->refScratchTexture(
+            desc, GrTextureProvider::kApprox_ScratchTexMatch));
         tempTexture = temp2.get();
     }
 
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index 2454dc1..b324cc4 100644
--- a/src/effects/SkLightingImageFilter.cpp
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -30,8 +30,8 @@
 
 namespace {
 
-const SkScalar gOneThird = SkScalarInvert(SkIntToScalar(3));
-const SkScalar gTwoThirds = SkScalarDiv(SkIntToScalar(2), SkIntToScalar(3));
+const SkScalar gOneThird = SkIntToScalar(1) / 3;
+const SkScalar gTwoThirds = SkIntToScalar(2) / 3;
 const SkScalar gOneHalf = 0.5f;
 const SkScalar gOneQuarter = 0.25f;
 
@@ -277,10 +277,139 @@
     buffer.writeScalar(point.fZ);
 };
 
-class SkDiffuseLightingImageFilter : public SkLightingImageFilter {
+enum BoundaryMode {
+    kTopLeft_BoundaryMode,
+    kTop_BoundaryMode,
+    kTopRight_BoundaryMode,
+    kLeft_BoundaryMode,
+    kInterior_BoundaryMode,
+    kRight_BoundaryMode,
+    kBottomLeft_BoundaryMode,
+    kBottom_BoundaryMode,
+    kBottomRight_BoundaryMode,
+
+    kBoundaryModeCount,
+};
+
+class SkLightingImageFilterInternal : public SkLightingImageFilter {
+protected:
+    SkLightingImageFilterInternal(SkLight* light,
+                                  SkScalar surfaceScale,
+                                  SkImageFilter* input,
+                                  const CropRect* cropRect)
+      : INHERITED(light, surfaceScale, input, cropRect) {}
+
+#if SK_SUPPORT_GPU
+    bool canFilterImageGPU() const override { return true; }
+    bool filterImageGPU(Proxy*, const SkBitmap& src, const Context&,
+                        SkBitmap* result, SkIPoint* offset) const override;
+    virtual GrFragmentProcessor* getFragmentProcessor(GrTexture*,
+                                                      const SkMatrix&,
+                                                      const SkIRect& bounds,
+                                                      BoundaryMode boundaryMode) const = 0;
+#endif
+private:
+#if SK_SUPPORT_GPU
+    void drawRect(GrContext* context,
+                  GrTexture* src,
+                  GrTexture* dst,
+                  const SkMatrix& matrix,
+                  const GrClip& clip,
+                  const SkRect& dstRect,
+                  BoundaryMode boundaryMode,
+                  const SkIRect& bounds) const;
+#endif
+    typedef SkLightingImageFilter INHERITED;
+};
+
+#if SK_SUPPORT_GPU
+void SkLightingImageFilterInternal::drawRect(GrContext* context,
+                                             GrTexture* src,
+                                             GrTexture* dst,
+                                             const SkMatrix& matrix,
+                                             const GrClip& clip,
+                                             const SkRect& dstRect,
+                                             BoundaryMode boundaryMode,
+                                             const SkIRect& bounds) const {
+    SkRect srcRect = dstRect.makeOffset(SkIntToScalar(bounds.x()), SkIntToScalar(bounds.y()));
+    GrFragmentProcessor* fp = this->getFragmentProcessor(src, matrix, bounds, boundaryMode);
+    GrPaint paint;
+    paint.addColorProcessor(fp)->unref();
+    context->drawNonAARectToRect(dst->asRenderTarget(), clip, paint, SkMatrix::I(),
+                                 dstRect, srcRect);
+}
+
+bool SkLightingImageFilterInternal::filterImageGPU(Proxy* proxy,
+                                                   const SkBitmap& src,
+                                                   const Context& ctx,
+                                                   SkBitmap* result,
+                                                   SkIPoint* offset) const {
+    SkBitmap input = src;
+    SkIPoint srcOffset = SkIPoint::Make(0, 0);
+    if (this->getInput(0) &&
+        !this->getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
+        return false;
+    }
+    SkIRect bounds;
+    if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
+        return false;
+    }
+    SkRect dstRect = SkRect::MakeWH(SkIntToScalar(bounds.width()),
+                                    SkIntToScalar(bounds.height()));
+    GrTexture* srcTexture = input.getTexture();
+    GrContext* context = srcTexture->getContext();
+
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag,
+    desc.fWidth = bounds.width();
+    desc.fHeight = bounds.height();
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture(desc,
+        GrTextureProvider::kApprox_ScratchTexMatch));
+    if (!dst) {
+        return false;
+    }
+
+    // setup new clip
+    GrClip clip(dstRect);
+
+    offset->fX = bounds.left();
+    offset->fY = bounds.top();
+    SkMatrix matrix(ctx.ctm());
+    matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
+    bounds.offset(-srcOffset);
+    SkRect topLeft = SkRect::MakeXYWH(0, 0, 1, 1);
+    SkRect top = SkRect::MakeXYWH(1, 0, dstRect.width() - 2, 1);
+    SkRect topRight = SkRect::MakeXYWH(dstRect.width() - 1, 0, 1, 1);
+    SkRect left = SkRect::MakeXYWH(0, 1, 1, dstRect.height() - 2);
+    SkRect interior = dstRect.makeInset(1, 1);
+    SkRect right = SkRect::MakeXYWH(dstRect.width() - 1, 1, 1, dstRect.height() - 2);
+    SkRect bottomLeft = SkRect::MakeXYWH(0, dstRect.height() - 1, 1, 1);
+    SkRect bottom = SkRect::MakeXYWH(1, dstRect.height() - 1, dstRect.width() - 2, 1);
+    SkRect bottomRight = SkRect::MakeXYWH(dstRect.width() - 1, dstRect.height() - 1, 1, 1);
+    this->drawRect(context, srcTexture, dst, matrix, clip, topLeft, kTopLeft_BoundaryMode, bounds);
+    this->drawRect(context, srcTexture, dst, matrix, clip, top, kTop_BoundaryMode, bounds);
+    this->drawRect(context, srcTexture, dst, matrix, clip, topRight, kTopRight_BoundaryMode,
+                   bounds);
+    this->drawRect(context, srcTexture, dst, matrix, clip, left, kLeft_BoundaryMode, bounds);
+    this->drawRect(context, srcTexture, dst, matrix, clip, interior, kInterior_BoundaryMode,
+                   bounds);
+    this->drawRect(context, srcTexture, dst, matrix, clip, right, kRight_BoundaryMode, bounds);
+    this->drawRect(context, srcTexture, dst, matrix, clip, bottomLeft, kBottomLeft_BoundaryMode,
+                   bounds);
+    this->drawRect(context, srcTexture, dst, matrix, clip, bottom, kBottom_BoundaryMode, bounds);
+    this->drawRect(context, srcTexture, dst, matrix, clip, bottomRight, kBottomRight_BoundaryMode,
+                   bounds);
+    WrapTexture(dst, bounds.width(), bounds.height(), result);
+    return true;
+}
+#endif
+
+class SkDiffuseLightingImageFilter : public SkLightingImageFilterInternal {
 public:
     static SkImageFilter* Create(SkLight* light, SkScalar surfaceScale, SkScalar kd, SkImageFilter*,
-                                 const CropRect*, uint32_t uniqueID = 0);
+                                 const CropRect*);
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiffuseLightingImageFilter)
@@ -288,27 +417,25 @@
 
 protected:
     SkDiffuseLightingImageFilter(SkLight* light, SkScalar surfaceScale,
-                                 SkScalar kd, SkImageFilter* input, const CropRect* cropRect,
-                                 uint32_t uniqueID);
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                                 SkScalar kd, SkImageFilter* input, const CropRect* cropRect);
+    void flatten(SkWriteBuffer& buffer) const override;
+    bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
+                       SkBitmap* result, SkIPoint* offset) const override;
 #if SK_SUPPORT_GPU
-    virtual bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
-                                     const SkIRect& bounds) const SK_OVERRIDE;
+    GrFragmentProcessor* getFragmentProcessor(GrTexture*, const SkMatrix&,
+                                              const SkIRect& bounds, BoundaryMode) const override;
 #endif
 
 private:
     friend class SkLightingImageFilter;
-    typedef SkLightingImageFilter INHERITED;
+    typedef SkLightingImageFilterInternal INHERITED;
     SkScalar fKD;
 };
 
-class SkSpecularLightingImageFilter : public SkLightingImageFilter {
+class SkSpecularLightingImageFilter : public SkLightingImageFilterInternal {
 public:
     static SkImageFilter* Create(SkLight* light, SkScalar surfaceScale,
-                                 SkScalar ks, SkScalar shininess, SkImageFilter*, const CropRect*,
-                                 uint32_t uniqueID = 0);
+                                 SkScalar ks, SkScalar shininess, SkImageFilter*, const CropRect*);
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpecularLightingImageFilter)
@@ -318,38 +445,39 @@
 
 protected:
     SkSpecularLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar ks,
-                                  SkScalar shininess, SkImageFilter* input, const CropRect*,
-                                  uint32_t uniqueID);
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
+                                  SkScalar shininess, SkImageFilter* input, const CropRect*);
+    void flatten(SkWriteBuffer& buffer) const override;
+    bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
+                       SkBitmap* result, SkIPoint* offset) const override;
 #if SK_SUPPORT_GPU
-    virtual bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
-                                     const SkIRect& bounds) const SK_OVERRIDE;
+    GrFragmentProcessor* getFragmentProcessor(GrTexture*, const SkMatrix&,
+                                              const SkIRect& bounds, BoundaryMode) const override;
 #endif
 
 private:
     SkScalar fKS;
     SkScalar fShininess;
     friend class SkLightingImageFilter;
-    typedef SkLightingImageFilter INHERITED;
+    typedef SkLightingImageFilterInternal INHERITED;
 };
 
 #if SK_SUPPORT_GPU
 
 class GrLightingEffect : public GrSingleTextureEffect {
 public:
-    GrLightingEffect(GrTexture* texture, const SkLight* light, SkScalar surfaceScale, const SkMatrix& matrix);
+    GrLightingEffect(GrTexture* texture, const SkLight* light, SkScalar surfaceScale,
+                     const SkMatrix& matrix, BoundaryMode boundaryMode);
     virtual ~GrLightingEffect();
 
     const SkLight* light() const { return fLight; }
     SkScalar surfaceScale() const { return fSurfaceScale; }
     const SkMatrix& filterMatrix() const { return fFilterMatrix; }
+    BoundaryMode boundaryMode() const { return fBoundaryMode; }
 
 protected:
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE {
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
         // lighting shaders are complicated. We just throw up our hands.
         inout->mulByUnknownFourComponents();
     }
@@ -359,6 +487,7 @@
     const SkLight* fLight;
     SkScalar fSurfaceScale;
     SkMatrix fFilterMatrix;
+    BoundaryMode fBoundaryMode;
 };
 
 class GrDiffuseLightingEffect : public GrLightingEffect {
@@ -367,30 +496,33 @@
                                        const SkLight* light,
                                        SkScalar surfaceScale,
                                        const SkMatrix& matrix,
-                                       SkScalar kd) {
+                                       SkScalar kd,
+                                       BoundaryMode boundaryMode) {
         return SkNEW_ARGS(GrDiffuseLightingEffect, (texture,
                                                     light,
                                                     surfaceScale,
                                                     matrix,
-                                                    kd));
+                                                    kd,
+                                                    boundaryMode));
     }
 
-    const char* name() const SK_OVERRIDE { return "DiffuseLighting"; }
+    const char* name() const override { return "DiffuseLighting"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     SkScalar kd() const { return fKD; }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
     GrDiffuseLightingEffect(GrTexture* texture,
                             const SkLight* light,
                             SkScalar surfaceScale,
                             const SkMatrix& matrix,
-                            SkScalar kd);
+                            SkScalar kd,
+                            BoundaryMode boundaryMode);
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
     typedef GrLightingEffect INHERITED;
@@ -404,33 +536,36 @@
                                        SkScalar surfaceScale,
                                        const SkMatrix& matrix,
                                        SkScalar ks,
-                                       SkScalar shininess) {
+                                       SkScalar shininess,
+                                       BoundaryMode boundaryMode) {
         return SkNEW_ARGS(GrSpecularLightingEffect, (texture,
                                                      light,
                                                      surfaceScale,
                                                      matrix,
                                                      ks,
-                                                     shininess));
+                                                     shininess,
+                                                     boundaryMode));
     }
 
-    const char* name() const SK_OVERRIDE { return "SpecularLighting"; }
+    const char* name() const override { return "SpecularLighting"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     SkScalar ks() const { return fKS; }
     SkScalar shininess() const { return fShininess; }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
     GrSpecularLightingEffect(GrTexture* texture,
                              const SkLight* light,
                              SkScalar surfaceScale,
                              const SkMatrix& matrix,
                              SkScalar ks,
-                             SkScalar shininess);
+                             SkScalar shininess,
+                             BoundaryMode boundaryMode);
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
     typedef GrLightingEffect INHERITED;
@@ -484,9 +619,8 @@
 class GrGLDistantLight : public GrGLLight {
 public:
     virtual ~GrGLDistantLight() {}
-    virtual void setData(const GrGLProgramDataManager&,
-                         const SkLight* light) const SK_OVERRIDE;
-    void emitSurfaceToLight(GrGLFPBuilder*, const char* z) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const SkLight* light) const override;
+    void emitSurfaceToLight(GrGLFPBuilder*, const char* z) override;
 
 private:
     typedef GrGLLight INHERITED;
@@ -498,9 +632,8 @@
 class GrGLPointLight : public GrGLLight {
 public:
     virtual ~GrGLPointLight() {}
-    virtual void setData(const GrGLProgramDataManager&,
-                         const SkLight* light) const SK_OVERRIDE;
-    void emitSurfaceToLight(GrGLFPBuilder*, const char* z) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const SkLight* light) const override;
+    void emitSurfaceToLight(GrGLFPBuilder*, const char* z) override;
 
 private:
     typedef GrGLLight INHERITED;
@@ -512,10 +645,9 @@
 class GrGLSpotLight : public GrGLLight {
 public:
     virtual ~GrGLSpotLight() {}
-    virtual void setData(const GrGLProgramDataManager&,
-                         const SkLight* light) const SK_OVERRIDE;
-    void emitSurfaceToLight(GrGLFPBuilder*, const char* z) SK_OVERRIDE;
-    void emitLightColor(GrGLFPBuilder*, const char *surfaceToLight) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const SkLight* light) const override;
+    void emitSurfaceToLight(GrGLFPBuilder*, const char* z) override;
+    void emitLightColor(GrGLFPBuilder*, const char *surfaceToLight) override;
 
 private:
     typedef GrGLLight INHERITED;
@@ -592,9 +724,9 @@
         return fDirection;
     };
     SkPoint3 lightColor(const SkPoint3&) const { return color(); }
-    LightType type() const SK_OVERRIDE { return kDistant_LightType; }
+    LightType type() const override { return kDistant_LightType; }
     const SkPoint3& direction() const { return fDirection; }
-    GrGLLight* createGLLight() const SK_OVERRIDE {
+    GrGLLight* createGLLight() const override {
 #if SK_SUPPORT_GPU
         return SkNEW(GrGLDistantLight);
 #else
@@ -602,9 +734,9 @@
         return NULL;
 #endif
     }
-    bool requiresFragmentPosition() const SK_OVERRIDE { return false; }
+    bool requiresFragmentPosition() const override { return false; }
 
-    bool isEqual(const SkLight& other) const SK_OVERRIDE {
+    bool isEqual(const SkLight& other) const override {
         if (other.type() != kDistant_LightType) {
             return false;
         }
@@ -622,10 +754,10 @@
     SkDistantLight(const SkPoint3& direction, const SkPoint3& color)
       : INHERITED(color), fDirection(direction) {
     }
-    SkLight* transform(const SkMatrix& matrix) const SK_OVERRIDE {
+    SkLight* transform(const SkMatrix& matrix) const override {
         return new SkDistantLight(direction(), color());
     }
-    void onFlattenLight(SkWriteBuffer& buffer) const SK_OVERRIDE {
+    void onFlattenLight(SkWriteBuffer& buffer) const override {
         writePoint3(fDirection, buffer);
     }
 
@@ -649,9 +781,9 @@
         return direction;
     };
     SkPoint3 lightColor(const SkPoint3&) const { return color(); }
-    LightType type() const SK_OVERRIDE { return kPoint_LightType; }
+    LightType type() const override { return kPoint_LightType; }
     const SkPoint3& location() const { return fLocation; }
-    GrGLLight* createGLLight() const SK_OVERRIDE {
+    GrGLLight* createGLLight() const override {
 #if SK_SUPPORT_GPU
         return SkNEW(GrGLPointLight);
 #else
@@ -659,8 +791,8 @@
         return NULL;
 #endif
     }
-    bool requiresFragmentPosition() const SK_OVERRIDE { return true; }
-    bool isEqual(const SkLight& other) const SK_OVERRIDE {
+    bool requiresFragmentPosition() const override { return true; }
+    bool isEqual(const SkLight& other) const override {
         if (other.type() != kPoint_LightType) {
             return false;
         }
@@ -668,7 +800,7 @@
         return INHERITED::isEqual(other) &&
                fLocation == o.fLocation;
     }
-    SkLight* transform(const SkMatrix& matrix) const SK_OVERRIDE {
+    SkLight* transform(const SkMatrix& matrix) const override {
         SkPoint location2 = SkPoint::Make(fLocation.fX, fLocation.fY);
         matrix.mapPoints(&location2, 1);
         // Use X scale and Y scale on Z and average the result
@@ -685,7 +817,7 @@
 protected:
     SkPointLight(const SkPoint3& location, const SkPoint3& color)
      : INHERITED(color), fLocation(location) {}
-    void onFlattenLight(SkWriteBuffer& buffer) const SK_OVERRIDE {
+    void onFlattenLight(SkWriteBuffer& buffer) const override {
         writePoint3(fLocation, buffer);
     }
 
@@ -698,7 +830,11 @@
 
 class SkSpotLight : public SkLight {
 public:
-    SkSpotLight(const SkPoint3& location, const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle, SkColor color)
+    SkSpotLight(const SkPoint3& location,
+                const SkPoint3& target,
+                SkScalar specularExponent,
+                SkScalar cutoffAngle,
+                SkColor color)
      : INHERITED(color),
        fLocation(location),
        fTarget(target),
@@ -712,7 +848,7 @@
        fConeScale = SkScalarInvert(antiAliasThreshold);
     }
 
-    SkLight* transform(const SkMatrix& matrix) const SK_OVERRIDE {
+    SkLight* transform(const SkMatrix& matrix) const override {
         SkPoint location2 = SkPoint::Make(fLocation.fX, fLocation.fY);
         matrix.mapPoints(&location2, 1);
         // Use X scale and Y scale on Z and average the result
@@ -726,7 +862,14 @@
         SkPoint3 target(target2.fX, target2.fY, SkScalarAve(targetZ.fX, targetZ.fY));
         SkPoint3 s = target - location;
         s.normalize();
-        return new SkSpotLight(location, target, fSpecularExponent, fCosOuterConeAngle, fCosInnerConeAngle, fConeScale, s, color());
+        return new SkSpotLight(location,
+                               target,
+                               fSpecularExponent,
+                               fCosOuterConeAngle,
+                               fCosInnerConeAngle,
+                               fConeScale,
+                               s,
+                               color());
     }
 
     SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const {
@@ -748,7 +891,7 @@
         }
         return color() * scale;
     }
-    GrGLLight* createGLLight() const SK_OVERRIDE {
+    GrGLLight* createGLLight() const override {
 #if SK_SUPPORT_GPU
         return SkNEW(GrGLSpotLight);
 #else
@@ -756,8 +899,8 @@
         return NULL;
 #endif
     }
-    bool requiresFragmentPosition() const SK_OVERRIDE { return true; }
-    LightType type() const SK_OVERRIDE { return kSpot_LightType; }
+    bool requiresFragmentPosition() const override { return true; }
+    LightType type() const override { return kSpot_LightType; }
     const SkPoint3& location() const { return fLocation; }
     const SkPoint3& target() const { return fTarget; }
     SkScalar specularExponent() const { return fSpecularExponent; }
@@ -780,7 +923,14 @@
                         SkScalarIsFinite(fConeScale));
     }
 protected:
-    SkSpotLight(const SkPoint3& location, const SkPoint3& target, SkScalar specularExponent, SkScalar cosOuterConeAngle, SkScalar cosInnerConeAngle, SkScalar coneScale, const SkPoint3& s, const SkPoint3& color)
+    SkSpotLight(const SkPoint3& location,
+                const SkPoint3& target,
+                SkScalar specularExponent,
+                SkScalar cosOuterConeAngle,
+                SkScalar cosInnerConeAngle,
+                SkScalar coneScale,
+                const SkPoint3& s,
+                const SkPoint3& color)
      : INHERITED(color),
        fLocation(location),
        fTarget(target),
@@ -791,7 +941,7 @@
        fS(s)
     {
     }
-    void onFlattenLight(SkWriteBuffer& buffer) const SK_OVERRIDE {
+    void onFlattenLight(SkWriteBuffer& buffer) const override {
         writePoint3(fLocation, buffer);
         writePoint3(fTarget, buffer);
         buffer.writeScalar(fSpecularExponent);
@@ -801,7 +951,7 @@
         writePoint3(fS, buffer);
     }
 
-    bool isEqual(const SkLight& other) const SK_OVERRIDE {
+    bool isEqual(const SkLight& other) const override {
         if (other.type() != kSpot_LightType) {
             return false;
         }
@@ -860,9 +1010,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 SkLightingImageFilter::SkLightingImageFilter(SkLight* light, SkScalar surfaceScale,
-                                             SkImageFilter* input, const CropRect* cropRect,
-                                             uint32_t uniqueID)
-  : INHERITED(1, &input, cropRect, uniqueID)
+                                             SkImageFilter* input, const CropRect* cropRect)
+  : INHERITED(1, &input, cropRect)
   , fLight(SkRef(light))
   , fSurfaceScale(surfaceScale / 255)
 {}
@@ -949,7 +1098,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 SkImageFilter* SkDiffuseLightingImageFilter::Create(SkLight* light, SkScalar surfaceScale,
-                                    SkScalar kd, SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID) {
+                                    SkScalar kd, SkImageFilter* input, const CropRect* cropRect) {
     if (NULL == light) {
         return NULL;
     }
@@ -961,11 +1110,15 @@
     if (kd < 0) {
         return NULL;
     }
-    return SkNEW_ARGS(SkDiffuseLightingImageFilter, (light, surfaceScale, kd, input, cropRect, uniqueID));
+    return SkNEW_ARGS(SkDiffuseLightingImageFilter, (light, surfaceScale, kd, input, cropRect));
 }
 
-SkDiffuseLightingImageFilter::SkDiffuseLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar kd, SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID)
-  : SkLightingImageFilter(light, surfaceScale, input, cropRect, uniqueID),
+SkDiffuseLightingImageFilter::SkDiffuseLightingImageFilter(SkLight* light,
+                                                           SkScalar surfaceScale,
+                                                           SkScalar kd,
+                                                           SkImageFilter* input,
+                                                           const CropRect* cropRect)
+  : INHERITED(light, surfaceScale, input, cropRect),
     fKD(kd)
 {
 }
@@ -975,7 +1128,7 @@
     SkAutoTUnref<SkLight> light(SkLight::UnflattenLight(buffer));
     SkScalar surfaceScale = buffer.readScalar();
     SkScalar kd = buffer.readScalar();
-    return Create(light, surfaceScale, kd, common.getInput(0), &common.cropRect(), common.uniqueID());
+    return Create(light, surfaceScale, kd, common.getInput(0), &common.cropRect());
 }
 
 void SkDiffuseLightingImageFilter::flatten(SkWriteBuffer& buffer) const {
@@ -1024,13 +1177,28 @@
     bounds.offset(-srcOffset);
     switch (transformedLight->type()) {
         case SkLight::kDistant_LightType:
-            lightBitmap<DiffuseLightingType, SkDistantLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
+            lightBitmap<DiffuseLightingType, SkDistantLight>(lightingType,
+                                                             transformedLight,
+                                                             src,
+                                                             dst,
+                                                             surfaceScale(),
+                                                             bounds);
             break;
         case SkLight::kPoint_LightType:
-            lightBitmap<DiffuseLightingType, SkPointLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
+            lightBitmap<DiffuseLightingType, SkPointLight>(lightingType,
+                                                           transformedLight,
+                                                           src,
+                                                           dst,
+                                                           surfaceScale(),
+                                                           bounds);
             break;
         case SkLight::kSpot_LightType:
-            lightBitmap<DiffuseLightingType, SkSpotLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
+            lightBitmap<DiffuseLightingType, SkSpotLight>(lightingType,
+                                                          transformedLight,
+                                                          src,
+                                                          dst,
+                                                          surfaceScale(),
+                                                          bounds);
             break;
     }
 
@@ -1046,22 +1214,21 @@
 #endif
 
 #if SK_SUPPORT_GPU
-bool SkDiffuseLightingImageFilter::asFragmentProcessor(GrFragmentProcessor** fp,
+GrFragmentProcessor* SkDiffuseLightingImageFilter::getFragmentProcessor(
                                                        GrTexture* texture,
                                                        const SkMatrix& matrix,
-                                                       const SkIRect&) const {
-    if (fp) {
-        SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
-        *fp = GrDiffuseLightingEffect::Create(texture, light(), scale, matrix, kd());
-    }
-    return true;
+                                                       const SkIRect&,
+                                                       BoundaryMode boundaryMode
+) const {
+    SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
+    return GrDiffuseLightingEffect::Create(texture, light(), scale, matrix, kd(), boundaryMode);
 }
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
 SkImageFilter* SkSpecularLightingImageFilter::Create(SkLight* light, SkScalar surfaceScale,
-                SkScalar ks, SkScalar shininess, SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID) {
+                SkScalar ks, SkScalar shininess, SkImageFilter* input, const CropRect* cropRect) {
     if (NULL == light) {
         return NULL;
     }
@@ -1074,11 +1241,16 @@
         return NULL;
     }
     return SkNEW_ARGS(SkSpecularLightingImageFilter,
-                      (light, surfaceScale, ks, shininess, input, cropRect, uniqueID));
+                      (light, surfaceScale, ks, shininess, input, cropRect));
 }
 
-SkSpecularLightingImageFilter::SkSpecularLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, SkImageFilter* input, const CropRect* cropRect, uint32_t uniqueID)
-  : SkLightingImageFilter(light, surfaceScale, input, cropRect, uniqueID),
+SkSpecularLightingImageFilter::SkSpecularLightingImageFilter(SkLight* light,
+                                                             SkScalar surfaceScale,
+                                                             SkScalar ks,
+                                                             SkScalar shininess,
+                                                             SkImageFilter* input,
+                                                             const CropRect* cropRect)
+  : INHERITED(light, surfaceScale, input, cropRect),
     fKS(ks),
     fShininess(shininess)
 {
@@ -1090,7 +1262,7 @@
     SkScalar surfaceScale = buffer.readScalar();
     SkScalar ks = buffer.readScalar();
     SkScalar shine = buffer.readScalar();
-    return Create(light, surfaceScale, ks, shine, common.getInput(0), &common.cropRect(), common.uniqueID());
+    return Create(light, surfaceScale, ks, shine, common.getInput(0), &common.cropRect());
 }
 
 void SkSpecularLightingImageFilter::flatten(SkWriteBuffer& buffer) const {
@@ -1140,13 +1312,28 @@
     SkAutoTUnref<SkLight> transformedLight(light()->transform(ctx.ctm()));
     switch (transformedLight->type()) {
         case SkLight::kDistant_LightType:
-            lightBitmap<SpecularLightingType, SkDistantLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
+            lightBitmap<SpecularLightingType, SkDistantLight>(lightingType,
+                                                              transformedLight,
+                                                              src,
+                                                              dst,
+                                                              surfaceScale(),
+                                                              bounds);
             break;
         case SkLight::kPoint_LightType:
-            lightBitmap<SpecularLightingType, SkPointLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
+            lightBitmap<SpecularLightingType, SkPointLight>(lightingType,
+                                                            transformedLight,
+                                                            src,
+                                                            dst,
+                                                            surfaceScale(),
+                                                            bounds);
             break;
         case SkLight::kSpot_LightType:
-            lightBitmap<SpecularLightingType, SkSpotLight>(lightingType, transformedLight, src, dst, surfaceScale(), bounds);
+            lightBitmap<SpecularLightingType, SkSpotLight>(lightingType,
+                                                           transformedLight,
+                                                           src,
+                                                           dst,
+                                                           surfaceScale(),
+                                                           bounds);
             break;
     }
     return true;
@@ -1161,15 +1348,14 @@
 #endif
 
 #if SK_SUPPORT_GPU
-bool SkSpecularLightingImageFilter::asFragmentProcessor(GrFragmentProcessor** fp,
-                                                        GrTexture* texture,
-                                                        const SkMatrix& matrix,
-                                                        const SkIRect&) const {
-    if (fp) {
-        SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
-        *fp = GrSpecularLightingEffect::Create(texture, light(), scale, matrix, ks(), shininess());
-    }
-    return true;
+GrFragmentProcessor* SkSpecularLightingImageFilter::getFragmentProcessor(
+                                                         GrTexture* texture,
+                                                         const SkMatrix& matrix,
+                                                         const SkIRect&,
+                                                         BoundaryMode boundaryMode) const {
+    SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
+    return GrSpecularLightingEffect::Create(texture, light(), scale, matrix, ks(), shininess(),
+                                            boundaryMode);
 }
 #endif
 
@@ -1206,6 +1392,81 @@
     }
 }
 
+SkString emitNormalFunc(BoundaryMode mode,
+                        const char* pointToNormalName,
+                        const char* sobelFuncName) {
+    SkString result;
+    switch (mode) {
+    case kTopLeft_BoundaryMode:
+        result.printf("\treturn %s(%s(0.0, 0.0, m[4], m[5], m[7], m[8], %g),\n"
+                      "\t          %s(0.0, 0.0, m[4], m[7], m[5], m[8], %g),\n"
+                      "\t          surfaceScale);\n",
+                      pointToNormalName, sobelFuncName, gTwoThirds,
+                                         sobelFuncName, gTwoThirds);
+        break;
+    case kTop_BoundaryMode:
+        result.printf("\treturn %s(%s(0.0, 0.0, m[3], m[5], m[6], m[8], %g),\n"
+                      "\t          %s(0.0, 0.0, m[4], m[7], m[5], m[8], %g),\n"
+                      "\t          surfaceScale);\n",
+                      pointToNormalName, sobelFuncName, gOneThird,
+                                         sobelFuncName, gOneHalf);
+        break;
+    case kTopRight_BoundaryMode:
+        result.printf("\treturn %s(%s( 0.0,  0.0, m[3], m[4], m[6], m[7], %g),\n"
+                      "\t          %s(m[3], m[6], m[4], m[7],  0.0,  0.0, %g),\n"
+                      "\t          surfaceScale);\n",
+                      pointToNormalName, sobelFuncName, gTwoThirds,
+                                         sobelFuncName, gTwoThirds);
+        break;
+    case kLeft_BoundaryMode:
+        result.printf("\treturn %s(%s(m[1], m[2], m[4], m[5], m[7], m[8], %g),\n"
+                      "\t          %s( 0.0,  0.0, m[1], m[7], m[2], m[8], %g),\n"
+                      "\t          surfaceScale);\n",
+                      pointToNormalName, sobelFuncName, gOneHalf,
+                                         sobelFuncName, gOneThird);
+        break;
+    case kInterior_BoundaryMode:
+        result.printf("\treturn %s(%s(m[0], m[2], m[3], m[5], m[6], m[8], %g),\n"
+                      "\t          %s(m[0], m[6], m[1], m[7], m[2], m[8], %g),\n"
+                      "\t          surfaceScale);\n",
+                      pointToNormalName, sobelFuncName, gOneQuarter,
+                                         sobelFuncName, gOneQuarter);
+        break;
+    case kRight_BoundaryMode:
+        result.printf("\treturn %s(%s(m[0], m[1], m[3], m[4], m[6], m[7], %g),\n"
+                      "\t          %s(m[0], m[6], m[1], m[7],  0.0,  0.0, %g),\n"
+                      "\t          surfaceScale);\n",
+                      pointToNormalName, sobelFuncName, gOneHalf,
+                                         sobelFuncName, gOneThird);
+        break;
+    case kBottomLeft_BoundaryMode:
+        result.printf("\treturn %s(%s(m[1], m[2], m[4], m[5],  0.0,  0.0, %g),\n"
+                      "\t          %s( 0.0,  0.0, m[1], m[4], m[2], m[5], %g),\n"
+                      "\t          surfaceScale);\n",
+                      pointToNormalName, sobelFuncName, gTwoThirds,
+                                         sobelFuncName, gTwoThirds);
+        break;
+    case kBottom_BoundaryMode:
+        result.printf("\treturn %s(%s(m[0], m[2], m[3], m[5],  0.0,  0.0, %g),\n"
+                      "\t          %s(m[0], m[3], m[1], m[4], m[2], m[5], %g),\n"
+                      "\t          surfaceScale);\n",
+                      pointToNormalName, sobelFuncName, gOneThird,
+                                         sobelFuncName, gOneHalf);
+        break;
+    case kBottomRight_BoundaryMode:
+        result.printf("\treturn %s(%s(m[0], m[1], m[3], m[4],  0.0,  0.0, %g),\n"
+                      "\t          %s(m[0], m[3], m[1], m[4],  0.0,  0.0, %g),\n"
+                      "\t          surfaceScale);\n",
+                      pointToNormalName, sobelFuncName, gTwoThirds,
+                                         sobelFuncName, gTwoThirds);
+        break;
+    default:
+        SkASSERT(false);
+        break;
+    }
+    return result;
+}
+
 }
 
 class GrGLLightingEffect  : public GrGLFragmentProcessor {
@@ -1213,19 +1474,19 @@
     GrGLLightingEffect(const GrProcessor&);
     virtual ~GrGLLightingEffect();
 
-    virtual void emitCode(GrGLFPBuilder*,
-                          const GrFragmentProcessor&,
-                          const char* outputColor,
-                          const char* inputColor,
-                          const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+    void emitCode(GrGLFPBuilder*,
+                  const GrFragmentProcessor&,
+                  const char* outputColor,
+                  const char* inputColor,
+                  const TransformedCoordsArray&,
+                  const TextureSamplerArray&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder* b);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b);
 
     /**
      * Subclasses of GrGLLightingEffect must call INHERITED::setData();
      */
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 protected:
     virtual void emitLightFunc(GrGLFPBuilder*, SkString* funcName) = 0;
@@ -1236,6 +1497,7 @@
     UniformHandle       fImageIncrementUni;
     UniformHandle       fSurfaceScaleUni;
     GrGLLight*          fLight;
+    BoundaryMode        fBoundaryMode;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1243,8 +1505,8 @@
 class GrGLDiffuseLightingEffect  : public GrGLLightingEffect {
 public:
     GrGLDiffuseLightingEffect(const GrProcessor&);
-    void emitLightFunc(GrGLFPBuilder*, SkString* funcName) SK_OVERRIDE;
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void emitLightFunc(GrGLFPBuilder*, SkString* funcName) override;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     typedef GrGLLightingEffect INHERITED;
@@ -1257,8 +1519,8 @@
 class GrGLSpecularLightingEffect  : public GrGLLightingEffect {
 public:
     GrGLSpecularLightingEffect(const GrProcessor&);
-    void emitLightFunc(GrGLFPBuilder*, SkString* funcName) SK_OVERRIDE;
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void emitLightFunc(GrGLFPBuilder*, SkString* funcName) override;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     typedef GrGLLightingEffect INHERITED;
@@ -1272,11 +1534,13 @@
 GrLightingEffect::GrLightingEffect(GrTexture* texture,
                                    const SkLight* light,
                                    SkScalar surfaceScale,
-                                   const SkMatrix& matrix)
+                                   const SkMatrix& matrix,
+                                   BoundaryMode boundaryMode)
     : INHERITED(texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture))
     , fLight(light)
     , fSurfaceScale(surfaceScale)
-    , fFilterMatrix(matrix) {
+    , fFilterMatrix(matrix)
+    , fBoundaryMode(boundaryMode) {
     fLight->ref();
     if (light->requiresFragmentPosition()) {
         this->setWillReadFragmentPosition();
@@ -1290,7 +1554,8 @@
 bool GrLightingEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
     const GrLightingEffect& s = sBase.cast<GrLightingEffect>();
     return fLight->isEqual(*s.fLight) &&
-           fSurfaceScale == s.fSurfaceScale;
+           fSurfaceScale == s.fSurfaceScale &&
+           fBoundaryMode == s.fBoundaryMode;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1299,8 +1564,9 @@
                                                  const SkLight* light,
                                                  SkScalar surfaceScale,
                                                  const SkMatrix& matrix,
-                                                 SkScalar kd)
-    : INHERITED(texture, light, surfaceScale, matrix), fKD(kd) {
+                                                 SkScalar kd,
+                                                 BoundaryMode boundaryMode)
+    : INHERITED(texture, light, surfaceScale, matrix, boundaryMode), fKD(kd) {
     this->initClassID<GrDiffuseLightingEffect>();
 }
 
@@ -1310,7 +1576,7 @@
             this->kd() == s.kd();
 }
 
-void GrDiffuseLightingEffect::getGLProcessorKey(const GrGLCaps& caps,
+void GrDiffuseLightingEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                                 GrProcessorKeyBuilder* b) const {
     GrGLDiffuseLightingEffect::GenKey(*this, caps, b);
 }
@@ -1332,8 +1598,9 @@
     for (int i = 0; i < 9; i++) {
         matrix[i] = random->nextUScalar1();
     }
+    BoundaryMode mode = static_cast<BoundaryMode>(random->nextU() % kBoundaryModeCount);
     return GrDiffuseLightingEffect::Create(textures[GrProcessorUnitTest::kAlphaTextureIdx],
-                                           light, surfaceScale, matrix, kd);
+                                           light, surfaceScale, matrix, kd, mode);
 }
 
 
@@ -1342,6 +1609,7 @@
 GrGLLightingEffect::GrGLLightingEffect(const GrProcessor& fp) {
     const GrLightingEffect& m = fp.cast<GrLightingEffect>();
     fLight = m.light()->createGLLight();
+    fBoundaryMode = m.boundaryMode();
 }
 
 GrGLLightingEffect::~GrGLLightingEffect() {
@@ -1373,7 +1641,7 @@
         GrGLShaderVar("scale", kFloat_GrSLType),
     };
     SkString sobelFuncName;
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
 
     fsBuilder->emitFunction(kFloat_GrSLType,
@@ -1392,27 +1660,23 @@
                             "pointToNormal",
                             SK_ARRAY_COUNT(gPointToNormalArgs),
                             gPointToNormalArgs,
-                            "\treturn normalize(vec3(-x * scale, y * scale, 1));\n",
+                            "\treturn normalize(vec3(-x * scale, -y * scale, 1));\n",
                             &pointToNormalName);
 
     static const GrGLShaderVar gInteriorNormalArgs[] =  {
         GrGLShaderVar("m", kFloat_GrSLType, 9),
         GrGLShaderVar("surfaceScale", kFloat_GrSLType),
     };
-    SkString interiorNormalBody;
-    interiorNormalBody.appendf("\treturn %s(%s(m[0], m[2], m[3], m[5], m[6], m[8], 0.25),\n"
-                               "\t       %s(m[0], m[6], m[1], m[7], m[2], m[8], 0.25),\n"
-                               "\t       surfaceScale);\n",
-                                pointToNormalName.c_str(),
-                                sobelFuncName.c_str(),
-                                sobelFuncName.c_str());
-    SkString interiorNormalName;
+    SkString normalBody = emitNormalFunc(fBoundaryMode,
+                                         pointToNormalName.c_str(),
+                                         sobelFuncName.c_str());
+    SkString normalName;
     fsBuilder->emitFunction(kVec3f_GrSLType,
-                            "interiorNormal",
+                            "normal",
                             SK_ARRAY_COUNT(gInteriorNormalArgs),
                             gInteriorNormalArgs,
-                            interiorNormalBody.c_str(),
-                            &interiorNormalName);
+                            normalBody.c_str(),
+                            &normalName);
 
     fsBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str());
     fsBuilder->codeAppend("\t\tfloat m[9];\n");
@@ -1421,7 +1685,7 @@
     const char* surfScale = builder->getUniformCStr(fSurfaceScaleUni);
 
     int index = 0;
-    for (int dy = -1; dy <= 1; dy++) {
+    for (int dy = 1; dy >= -1; dy--) {
         for (int dx = -1; dx <= 1; dx++) {
             SkString texCoords;
             texCoords.appendf("coord + vec2(%d, %d) * %s", dx, dy, imgInc);
@@ -1436,7 +1700,7 @@
     fLight->emitSurfaceToLight(builder, arg.c_str());
     fsBuilder->codeAppend(";\n");
     fsBuilder->codeAppendf("\t\t%s = %s(%s(m, %s), surfaceToLight, ",
-                           outputColor, lightFunc.c_str(), interiorNormalName.c_str(), surfScale);
+                           outputColor, lightFunc.c_str(), normalName.c_str(), surfScale);
     fLight->emitLightColor(builder, "surfaceToLight");
     fsBuilder->codeAppend(");\n");
     SkString modulate;
@@ -1445,8 +1709,9 @@
 }
 
 void GrGLLightingEffect::GenKey(const GrProcessor& proc,
-                                const GrGLCaps& caps, GrProcessorKeyBuilder* b) {
-    b->add32(proc.cast<GrLightingEffect>().light()->type());
+                                const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {
+    const GrLightingEffect& lighting = proc.cast<GrLightingEffect>();
+    b->add32(lighting.boundaryMode() << 2 | lighting.light()->type());
 }
 
 void GrGLLightingEffect::setData(const GrGLProgramDataManager& pdman,
@@ -1504,8 +1769,9 @@
                                                    SkScalar surfaceScale,
                                                    const SkMatrix& matrix,
                                                    SkScalar ks,
-                                                   SkScalar shininess)
-    : INHERITED(texture, light, surfaceScale, matrix),
+                                                   SkScalar shininess,
+                                                   BoundaryMode boundaryMode)
+    : INHERITED(texture, light, surfaceScale, matrix, boundaryMode),
       fKS(ks),
       fShininess(shininess) {
     this->initClassID<GrSpecularLightingEffect>();
@@ -1518,7 +1784,7 @@
            this->shininess() == s.shininess();
 }
 
-void GrSpecularLightingEffect::getGLProcessorKey(const GrGLCaps& caps,
+void GrSpecularLightingEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                                 GrProcessorKeyBuilder* b) const {
     GrGLSpecularLightingEffect::GenKey(*this, caps, b);
 }
@@ -1541,8 +1807,9 @@
     for (int i = 0; i < 9; i++) {
         matrix[i] = random->nextUScalar1();
     }
+    BoundaryMode mode = static_cast<BoundaryMode>(random->nextU() % kBoundaryModeCount);
     return GrSpecularLightingEffect::Create(textures[GrProcessorUnitTest::kAlphaTextureIdx],
-                                            light, surfaceScale, matrix, ks, shininess);
+                                            light, surfaceScale, matrix, ks, shininess, mode);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1558,7 +1825,10 @@
     fKSUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
                                  kFloat_GrSLType, kDefault_GrSLPrecision, "KS", &ks);
     fShininessUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
-                                        kFloat_GrSLType, kDefault_GrSLPrecision, "Shininess", &shininess);
+                                        kFloat_GrSLType,
+                                        kDefault_GrSLPrecision,
+                                        "Shininess",
+                                        &shininess);
 
     static const GrGLShaderVar gLightArgs[] = {
         GrGLShaderVar("normal", kVec3f_GrSLType),
@@ -1636,7 +1906,7 @@
     fLocationUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
                                        kVec3f_GrSLType, kDefault_GrSLPrecision,
                                        "LightLocation", &loc);
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     fsBuilder->codeAppendf("normalize(%s - vec3(%s.xy, %s))",
             loc, fsBuilder->fragmentPosition(), z);
 }
@@ -1662,7 +1932,7 @@
                                        kVec3f_GrSLType, kDefault_GrSLPrecision,
                                        "LightLocation", &location);
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     fsBuilder->codeAppendf("normalize(%s - vec3(%s.xy, %s))",
             location, fsBuilder->fragmentPosition(), z);
 }
@@ -1706,7 +1976,7 @@
                            color, cosOuter, coneScale);
     lightColorBody.appendf("\t}\n");
     lightColorBody.appendf("\treturn %s;\n", color);
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     fsBuilder->emitFunction(kVec3f_GrSLType,
                             "lightColor",
                             SK_ARRAY_COUNT(gLightColorArgs),
diff --git a/src/effects/SkLumaColorFilter.cpp b/src/effects/SkLumaColorFilter.cpp
index b7860e0..0000541 100644
--- a/src/effects/SkLumaColorFilter.cpp
+++ b/src/effects/SkLumaColorFilter.cpp
@@ -63,14 +63,14 @@
         return SkRef(gLumaEffect);
     }
 
-    const char* name() const SK_OVERRIDE { return "Luminance-to-Alpha"; }
+    const char* name() const override { return "Luminance-to-Alpha"; }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GLProcessor::GenKey(*this, caps, b);
     }
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
+    GrGLFragmentProcessor* createGLInstance() const override {
         return SkNEW_ARGS(GLProcessor, (*this));
     }
 
@@ -78,19 +78,19 @@
     public:
         GLProcessor(const GrProcessor&) {}
 
-        static void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder* b) {}
+        static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {}
 
         virtual void emitCode(GrGLFPBuilder* builder,
                               const GrFragmentProcessor&,
                               const char* outputColor,
                               const char* inputColor,
                               const TransformedCoordsArray&,
-                              const TextureSamplerArray&) SK_OVERRIDE {
+                              const TextureSamplerArray&) override {
             if (NULL == inputColor) {
                 inputColor = "vec4(1)";
             }
 
-            GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+            GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
             fsBuilder->codeAppendf("\tfloat luma = dot(vec3(%f, %f, %f), %s.rgb);\n",
                                    SK_ITU_BT709_LUM_COEFF_R,
                                    SK_ITU_BT709_LUM_COEFF_G,
@@ -110,9 +110,9 @@
         this->initClassID<LumaColorFilterEffect>();
     }
 
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE { return true; }
+    bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE {
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
         // The output is always black. The alpha value for the color passed in is arbitrary.
         inout->setToOther(kRGB_GrColorComponentFlags, GrColorPackRGBA(0, 0, 0, 0),
                           GrInvariantOutput::kWill_ReadInput);
diff --git a/src/effects/SkMagnifierImageFilter.cpp b/src/effects/SkMagnifierImageFilter.cpp
index eaac30c..48fad8e 100644
--- a/src/effects/SkMagnifierImageFilter.cpp
+++ b/src/effects/SkMagnifierImageFilter.cpp
@@ -25,6 +25,7 @@
 
 public:
     static GrFragmentProcessor* Create(GrTexture* texture,
+                                       const SkRect& bounds,
                                        float xOffset,
                                        float yOffset,
                                        float xInvZoom,
@@ -32,6 +33,7 @@
                                        float xInvInset,
                                        float yInvInset) {
         return SkNEW_ARGS(GrMagnifierEffect, (texture,
+                                              bounds,
                                               xOffset,
                                               yOffset,
                                               xInvZoom,
@@ -42,21 +44,28 @@
 
     virtual ~GrMagnifierEffect() {};
 
-    const char* name() const SK_OVERRIDE { return "Magnifier"; }
+    const char* name() const override { return "Magnifier"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
+    const SkRect& bounds() const { return fBounds; }    // Bounds of source image.
+    // Offset to apply to zoomed pixels, (srcRect position / texture size).
     float x_offset() const { return fXOffset; }
     float y_offset() const { return fYOffset; }
+
+    // Scale to apply to zoomed pixels (srcRect size / bounds size).
     float x_inv_zoom() const { return fXInvZoom; }
     float y_inv_zoom() const { return fYInvZoom; }
+
+    // 1/radius over which to transition from unzoomed to zoomed pixels (bounds size / inset).
     float x_inv_inset() const { return fXInvInset; }
     float y_inv_inset() const { return fYInvInset; }
 
 private:
     GrMagnifierEffect(GrTexture* texture,
+                      const SkRect& bounds,
                       float xOffset,
                       float yOffset,
                       float xInvZoom,
@@ -64,6 +73,7 @@
                       float xInvInset,
                       float yInvInset)
         : GrSingleTextureEffect(texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture))
+        , fBounds(bounds)
         , fXOffset(xOffset)
         , fYOffset(yOffset)
         , fXInvZoom(xInvZoom)
@@ -73,12 +83,13 @@
         this->initClassID<GrMagnifierEffect>();
     }
 
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
+    SkRect fBounds;
     float fXOffset;
     float fYOffset;
     float fXInvZoom;
@@ -101,14 +112,15 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     UniformHandle       fOffsetVar;
     UniformHandle       fInvZoomVar;
     UniformHandle       fInvInsetVar;
+    UniformHandle       fBoundsVar;
 
     typedef GrGLFragmentProcessor INHERITED;
 };
@@ -134,17 +146,21 @@
         GrGLProgramBuilder::kFragment_Visibility |
         GrGLProgramBuilder::kVertex_Visibility,
         kVec2f_GrSLType, kDefault_GrSLPrecision, "InvInset");
+    fBoundsVar = builder->addUniform(
+        GrGLProgramBuilder::kFragment_Visibility |
+        GrGLProgramBuilder::kVertex_Visibility,
+        kVec4f_GrSLType, kDefault_GrSLPrecision, "Bounds");
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
     fsBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str());
     fsBuilder->codeAppendf("\t\tvec2 zoom_coord = %s + %s * %s;\n",
                            builder->getUniformCStr(fOffsetVar),
                            coords2D.c_str(),
                            builder->getUniformCStr(fInvZoomVar));
-
-    fsBuilder->codeAppend("\t\tvec2 delta = min(coord, vec2(1.0, 1.0) - coord);\n");
-
+    const char* bounds = builder->getUniformCStr(fBoundsVar);
+    fsBuilder->codeAppendf("\t\tvec2 delta = (coord - %s.xy) * %s.zw;\n", bounds, bounds);
+    fsBuilder->codeAppendf("\t\tdelta = min(delta, vec2(1.0, 1.0) - delta);\n");
     fsBuilder->codeAppendf("\t\tdelta = delta * %s;\n", builder->getUniformCStr(fInvInsetVar));
 
     fsBuilder->codeAppend("\t\tfloat weight = 0.0;\n");
@@ -175,11 +191,13 @@
     pdman.set2f(fOffsetVar, zoom.x_offset(), zoom.y_offset());
     pdman.set2f(fInvZoomVar, zoom.x_inv_zoom(), zoom.y_inv_zoom());
     pdman.set2f(fInvInsetVar, zoom.x_inv_inset(), zoom.y_inv_inset());
+    pdman.set4f(fBoundsVar, zoom.bounds().x(), zoom.bounds().y(),
+                            zoom.bounds().width(), zoom.bounds().height());
 }
 
 /////////////////////////////////////////////////////////////////////
 
-void GrMagnifierEffect::getGLProcessorKey(const GrGLCaps& caps,
+void GrMagnifierEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                           GrProcessorKeyBuilder* b) const {
     GrGLMagnifierEffect::GenKey(*this, caps, b);
 }
@@ -206,6 +224,7 @@
 
     GrFragmentProcessor* effect = GrMagnifierEffect::Create(
         texture,
+        SkRect::MakeWH(SkIntToScalar(kMaxWidth), SkIntToScalar(kMaxHeight)),
         (float) width / texture->width(),
         (float) height / texture->height(),
         texture->width() / (float) x,
@@ -220,7 +239,8 @@
 
 bool GrMagnifierEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
     const GrMagnifierEffect& s = sBase.cast<GrMagnifierEffect>();
-    return (this->fXOffset == s.fXOffset &&
+    return (this->fBounds == s.fBounds &&
+            this->fXOffset == s.fXOffset &&
             this->fYOffset == s.fYOffset &&
             this->fXInvZoom == s.fXInvZoom &&
             this->fYInvZoom == s.fYInvZoom &&
@@ -258,18 +278,27 @@
 
 #if SK_SUPPORT_GPU
 bool SkMagnifierImageFilter::asFragmentProcessor(GrFragmentProcessor** fp, GrTexture* texture,
-                                                 const SkMatrix&, const SkIRect&) const {
+                                                 const SkMatrix&, const SkIRect&bounds) const {
     if (fp) {
-        SkScalar yOffset = (texture->origin() == kTopLeft_GrSurfaceOrigin) ? fSrcRect.y() :
-                           (texture->height() - (fSrcRect.y() + fSrcRect.height()));
+        SkScalar yOffset = texture->origin() == kTopLeft_GrSurfaceOrigin ? fSrcRect.y() :
+           texture->height() - fSrcRect.height() * texture->height() / bounds.height()
+                             - fSrcRect.y();
+        int boundsY = (texture->origin() == kTopLeft_GrSurfaceOrigin) ? bounds.y() :
+                      (texture->height() - bounds.height());
+        SkRect effectBounds = SkRect::MakeXYWH(
+            SkIntToScalar(bounds.x()) / texture->width(),
+            SkIntToScalar(boundsY) / texture->height(),
+            SkIntToScalar(texture->width()) / bounds.width(),
+            SkIntToScalar(texture->height()) / bounds.height());
         SkScalar invInset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1;
         *fp = GrMagnifierEffect::Create(texture,
+                                        effectBounds,
                                         fSrcRect.x() / texture->width(),
                                         yOffset / texture->height(),
-                                        fSrcRect.width() / texture->width(),
-                                        fSrcRect.height() / texture->height(),
-                                        texture->width() * invInset,
-                                        texture->height() * invInset);
+                                        fSrcRect.width() / bounds.width(),
+                                        fSrcRect.height() / bounds.height(),
+                                        bounds.width() * invInset,
+                                        bounds.height() * invInset);
     }
     return true;
 }
diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp
index e9dd878..4cba043 100644
--- a/src/effects/SkMatrixConvolutionImageFilter.cpp
+++ b/src/effects/SkMatrixConvolutionImageFilter.cpp
@@ -30,9 +30,8 @@
     TileMode tileMode,
     bool convolveAlpha,
     SkImageFilter* input,
-    const CropRect* cropRect,
-    uint32_t uniqueID)
-  : INHERITED(1, &input, cropRect, uniqueID),
+    const CropRect* cropRect)
+  : INHERITED(1, &input, cropRect),
     fKernelSize(kernelSize),
     fGain(gain),
     fBias(bias),
@@ -56,8 +55,7 @@
     TileMode tileMode,
     bool convolveAlpha,
     SkImageFilter* input,
-    const CropRect* cropRect,
-    uint32_t uniqueID) {
+    const CropRect* cropRect) {
     if (kernelSize.width() < 1 || kernelSize.height() < 1) {
         return NULL;
     }
@@ -73,7 +71,7 @@
     }
     return SkNEW_ARGS(SkMatrixConvolutionImageFilter, (kernelSize, kernel, gain, bias,
                                                        kernelOffset, tileMode, convolveAlpha,
-                                                       input, cropRect, uniqueID));
+                                                       input, cropRect));
 }
 
 SkFlattenable* SkMatrixConvolutionImageFilter::CreateProc(SkReadBuffer& buffer) {
@@ -99,7 +97,7 @@
     TileMode tileMode = (TileMode)buffer.readInt();
     bool convolveAlpha = buffer.readBool();
     return Create(kernelSize, kernel.get(), gain, bias, kernelOffset, tileMode, convolveAlpha,
-                  common.getInput(0), &common.cropRect(), common.uniqueID());
+                  common.getInput(0), &common.cropRect());
 }
 
 void SkMatrixConvolutionImageFilter::flatten(SkWriteBuffer& buffer) const {
diff --git a/src/effects/SkMergeImageFilter.cpp b/src/effects/SkMergeImageFilter.cpp
index 7034e15..f68d913 100755
--- a/src/effects/SkMergeImageFilter.cpp
+++ b/src/effects/SkMergeImageFilter.cpp
@@ -42,9 +42,8 @@
 
 SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* filters[], int count,
                                        const SkXfermode::Mode modes[],
-                                       const CropRect* cropRect,
-                                       uint32_t uniqueID)
-  : INHERITED(count, filters, cropRect, uniqueID) {
+                                       const CropRect* cropRect)
+  : INHERITED(count, filters, cropRect) {
     SkASSERT(count >= 0);
     this->initModes(modes);
 }
@@ -128,9 +127,9 @@
         if (!buffer.isValid()) {
             return NULL;
         }
-        return Create(common.inputs(), count, modes.get(), &common.cropRect(), common.uniqueID());
+        return Create(common.inputs(), count, modes.get(), &common.cropRect());
     }
-    return Create(common.inputs(), count, NULL, &common.cropRect(), common.uniqueID());
+    return Create(common.inputs(), count, NULL, &common.cropRect());
 }
 
 void SkMergeImageFilter::flatten(SkWriteBuffer& buffer) const {
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index d2ec036..9ecf0c2 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -24,9 +24,8 @@
 SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX,
                                                  int radiusY,
                                                  SkImageFilter* input,
-                                                 const CropRect* cropRect,
-                                                 uint32_t uniqueID)
-    : INHERITED(1, &input, cropRect, uniqueID), fRadius(SkISize::Make(radiusX, radiusY)) {
+                                                 const CropRect* cropRect)
+    : INHERITED(1, &input, cropRect), fRadius(SkISize::Make(radiusX, radiusY)) {
 }
 
 void SkMorphologyImageFilter::flatten(SkWriteBuffer& buffer) const {
@@ -254,14 +253,14 @@
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
     const int width = buffer.readInt();
     const int height = buffer.readInt();
-    return Create(width, height, common.getInput(0), &common.cropRect(), common.uniqueID());
+    return Create(width, height, common.getInput(0), &common.cropRect());
 }
 
 SkFlattenable* SkDilateImageFilter::CreateProc(SkReadBuffer& buffer) {
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
     const int width = buffer.readInt();
     const int height = buffer.readInt();
-    return Create(width, height, common.getInput(0), &common.cropRect(), common.uniqueID());
+    return Create(width, height, common.getInput(0), &common.cropRect());
 }
 
 #ifndef SK_IGNORE_TO_STRING
@@ -314,11 +313,11 @@
     bool useRange() const { return fUseRange; }
     const float* range() const { return fRange; }
 
-    const char* name() const SK_OVERRIDE { return "Morphology"; }
+    const char* name() const override { return "Morphology"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
 protected:
 
@@ -327,9 +326,9 @@
     float fRange[2];
 
 private:
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
     GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType, float bounds[2]);
@@ -350,11 +349,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder* b);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b);
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
@@ -392,7 +391,7 @@
                                             "Range");
     const char* range = builder->getUniformCStr(fRangeUni);
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
     const char* func;
     switch (fType) {
@@ -451,7 +450,7 @@
 }
 
 void GrGLMorphologyEffect::GenKey(const GrProcessor& proc,
-                                  const GrGLCaps&, GrProcessorKeyBuilder* b) {
+                                  const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
     const GrMorphologyEffect& m = proc.cast<GrMorphologyEffect>();
     uint32_t key = static_cast<uint32_t>(m.radius());
     key |= (m.type() << 8);
@@ -518,7 +517,7 @@
 GrMorphologyEffect::~GrMorphologyEffect() {
 }
 
-void GrMorphologyEffect::getGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const {
+void GrMorphologyEffect::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
     GrGLMorphologyEffect::GenKey(*this, caps, b);
 }
 
@@ -668,7 +667,8 @@
     SkIRect srcRect = rect;
 
     if (radius.fWidth > 0) {
-        GrTexture* texture = context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch);
+        GrTexture* texture = context->textureProvider()->refScratchTexture(
+            desc, GrTextureProvider::kApprox_ScratchTexMatch);
         if (NULL == texture) {
             return false;
         }
@@ -685,7 +685,8 @@
         srcRect = dstRect;
     }
     if (radius.fHeight > 0) {
-        GrTexture* texture = context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch);
+        GrTexture* texture = context->textureProvider()->refScratchTexture(desc,
+            GrTextureProvider::kApprox_ScratchTexMatch);
         if (NULL == texture) {
             return false;
         }
diff --git a/src/effects/SkOffsetImageFilter.cpp b/src/effects/SkOffsetImageFilter.cpp
index 8a17ce3..1944e58 100644
--- a/src/effects/SkOffsetImageFilter.cpp
+++ b/src/effects/SkOffsetImageFilter.cpp
@@ -95,7 +95,7 @@
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
     SkPoint offset;
     buffer.readPoint(&offset);
-    return Create(offset.x(), offset.y(), common.getInput(0), &common.cropRect(), common.uniqueID());
+    return Create(offset.x(), offset.y(), common.getInput(0), &common.cropRect());
 }
 
 void SkOffsetImageFilter::flatten(SkWriteBuffer& buffer) const {
@@ -104,8 +104,8 @@
 }
 
 SkOffsetImageFilter::SkOffsetImageFilter(SkScalar dx, SkScalar dy, SkImageFilter* input,
-                                         const CropRect* cropRect, uint32_t uniqueID)
-  : INHERITED(1, &input, cropRect, uniqueID) {
+                                         const CropRect* cropRect)
+  : INHERITED(1, &input, cropRect) {
     fOffset.set(dx, dy);
 }
 
diff --git a/src/effects/SkPerlinNoiseShader.cpp b/src/effects/SkPerlinNoiseShader.cpp
index 3c1bf2f..c59bd8c 100644
--- a/src/effects/SkPerlinNoiseShader.cpp
+++ b/src/effects/SkPerlinNoiseShader.cpp
@@ -78,16 +78,14 @@
                  SkScalar baseFrequencyX, SkScalar baseFrequencyY,
                  const SkMatrix& matrix)
     {
-        SkVector wavelength = SkVector::Make(SkScalarInvert(baseFrequencyX),
-                                             SkScalarInvert(baseFrequencyY));
-        matrix.mapVectors(&wavelength, 1);
-        fBaseFrequency.fX = SkScalarInvert(wavelength.fX);
-        fBaseFrequency.fY = SkScalarInvert(wavelength.fY);
-        SkVector sizeVec = SkVector::Make(SkIntToScalar(tileSize.fWidth),
-                                          SkIntToScalar(tileSize.fHeight));
-        matrix.mapVectors(&sizeVec, 1);
-        fTileSize.fWidth = SkScalarRoundToInt(sizeVec.fX);
-        fTileSize.fHeight = SkScalarRoundToInt(sizeVec.fY);
+        SkVector vec[2] = {
+            { SkScalarInvert(baseFrequencyX),   SkScalarInvert(baseFrequencyY)  },
+            { SkIntToScalar(tileSize.fWidth),   SkIntToScalar(tileSize.fHeight) },
+        };
+        matrix.mapVectors(vec, 2);
+
+        fBaseFrequency.set(SkScalarInvert(vec[0].fX), SkScalarInvert(vec[0].fY));
+        fTileSize.set(SkScalarRoundToInt(vec[1].fX), SkScalarRoundToInt(vec[1].fY));
         this->init(seed);
         if (!fTileSize.isEmpty()) {
             this->stitch();
@@ -214,8 +212,7 @@
             SkScalar highFrequencx =
                 SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
             // BaseFrequency should be non-negative according to the standard.
-            if (SkScalarDiv(fBaseFrequency.fX, lowFrequencx) <
-                SkScalarDiv(highFrequencx, fBaseFrequency.fX)) {
+            if (fBaseFrequency.fX / lowFrequencx < highFrequencx / fBaseFrequency.fX) {
                 fBaseFrequency.fX = lowFrequencx;
             } else {
                 fBaseFrequency.fX = highFrequencx;
@@ -226,8 +223,7 @@
                 SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
             SkScalar highFrequency =
                 SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
-            if (SkScalarDiv(fBaseFrequency.fY, lowFrequency) <
-                SkScalarDiv(highFrequency, fBaseFrequency.fY)) {
+            if (fBaseFrequency.fY / lowFrequency < highFrequency / fBaseFrequency.fY) {
                 fBaseFrequency.fY = lowFrequency;
             } else {
                 fBaseFrequency.fY = highFrequency;
@@ -386,8 +382,9 @@
     SkScalar ratio = SK_Scalar1;
     for (int octave = 0; octave < perlinNoiseShader.fNumOctaves; ++octave) {
         SkScalar noise = noise2D(channel, stitchData, noiseVector);
-        turbulenceFunctionResult += SkScalarDiv(
-            (perlinNoiseShader.fType == kFractalNoise_Type) ? noise : SkScalarAbs(noise), ratio);
+        SkScalar numer = (perlinNoiseShader.fType == kFractalNoise_Type) ?
+                            noise : SkScalarAbs(noise);
+        turbulenceFunctionResult += numer / ratio;
         noiseVector.fX *= 2;
         noiseVector.fY *= 2;
         ratio *= 2;
@@ -408,8 +405,7 @@
     }
 
     if (channel == 3) { // Scale alpha by paint value
-        turbulenceFunctionResult = SkScalarMul(turbulenceFunctionResult,
-            SkScalarDiv(SkIntToScalar(getPaintAlpha()), SkIntToScalar(255)));
+        turbulenceFunctionResult *= SkIntToScalar(getPaintAlpha()) / 255;
     }
 
     // Clamp result
@@ -496,11 +492,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder* b);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b);
 
 private:
 
@@ -532,14 +528,14 @@
         SkDELETE(fPaintingData);
     }
 
-    const char* name() const SK_OVERRIDE { return "PerlinNoise"; }
+    const char* name() const override { return "PerlinNoise"; }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GrGLPerlinNoise::GenKey(*this, caps, b);
     }
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
+    GrGLFragmentProcessor* createGLInstance() const override {
         return SkNEW_ARGS(GrGLPerlinNoise, (*this));
     }
 
@@ -553,7 +549,7 @@
     uint8_t alpha() const { return fAlpha; }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor& sBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
         const GrPerlinNoiseEffect& s = sBase.cast<GrPerlinNoiseEffect>();
         return fType == s.fType &&
                fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency &&
@@ -563,7 +559,7 @@
                fPaintingData->fStitchDataInit == s.fPaintingData->fStitchDataInit;
     }
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE {
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
         inout->setToUnknown(GrInvariantOutput::kWillNot_ReadInput);
     }
 
@@ -627,7 +623,7 @@
     GrColor paintColor;
     GrFragmentProcessor* effect;
     SkAssertResult(shader->asFragmentProcessor(context, paint,
-                                               GrProcessorUnitTest::TestMatrix(random), NULL,
+                                               GrTest::TestMatrix(random), NULL,
                                                &paintColor, &effect));
 
     SkDELETE(shader);
@@ -649,7 +645,7 @@
                                const TextureSamplerArray& samplers) {
     sk_ignore_unused_variable(inputColor);
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString vCoords = fsBuilder->ensureFSCoords2D(coords, 0);
 
     fBaseFrequencyUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
@@ -899,7 +895,7 @@
                   outputColor, outputColor, outputColor, outputColor);
 }
 
-void GrGLPerlinNoise::GenKey(const GrProcessor& processor, const GrGLCaps&,
+void GrGLPerlinNoise::GenKey(const GrProcessor& processor, const GrGLSLCaps&,
                              GrProcessorKeyBuilder* b) {
     const GrPerlinNoiseEffect& turbulence = processor.cast<GrPerlinNoiseEffect>();
 
@@ -933,7 +929,7 @@
 
     const SkVector& baseFrequency = turbulence.baseFrequency();
     pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY);
-    pdman.set1f(fAlphaUni, SkScalarDiv(SkIntToScalar(turbulence.alpha()), SkIntToScalar(255)));
+    pdman.set1f(fAlphaUni, SkIntToScalar(turbulence.alpha()) / 255);
 
     if (turbulence.stitchTiles()) {
         const SkPerlinNoiseShader::StitchData& stitchData = turbulence.stitchData();
diff --git a/src/effects/SkPictureImageFilter.cpp b/src/effects/SkPictureImageFilter.cpp
index 391af11..baae6f8 100644
--- a/src/effects/SkPictureImageFilter.cpp
+++ b/src/effects/SkPictureImageFilter.cpp
@@ -13,22 +13,22 @@
 #include "SkWriteBuffer.h"
 #include "SkValidationUtils.h"
 
-SkPictureImageFilter::SkPictureImageFilter(const SkPicture* picture, uint32_t uniqueID)
-    : INHERITED(0, 0, NULL, uniqueID)
+SkPictureImageFilter::SkPictureImageFilter(const SkPicture* picture)
+    : INHERITED(0, 0, NULL)
     , fPicture(SkSafeRef(picture))
     , fCropRect(picture ? picture->cullRect() : SkRect::MakeEmpty())
     , fPictureResolution(kDeviceSpace_PictureResolution) 
-    , fFilterLevel(SkPaint::kLow_FilterLevel) {
+    , fFilterQuality(kLow_SkFilterQuality) {
 }
 
 SkPictureImageFilter::SkPictureImageFilter(const SkPicture* picture, const SkRect& cropRect,
-                                           uint32_t uniqueID, PictureResolution pictureResolution,
-                                           SkPaint::FilterLevel filterLevel)
-    : INHERITED(0, 0, NULL, uniqueID)
+                                           PictureResolution pictureResolution,
+                                           SkFilterQuality filterQuality)
+    : INHERITED(0, 0, NULL)
     , fPicture(SkSafeRef(picture))
     , fCropRect(cropRect)
     , fPictureResolution(pictureResolution)
-    , fFilterLevel(filterLevel) {
+    , fFilterQuality(filterQuality) {
 }
 
 SkPictureImageFilter::~SkPictureImageFilter() {
@@ -59,13 +59,13 @@
 
     if (kLocalSpace_PictureResolution == pictureResolution) {
         //filterLevel is only serialized if pictureResolution is LocalSpace
-        SkPaint::FilterLevel filterLevel;
+        SkFilterQuality filterQuality;
         if (buffer.isVersionLT(SkReadBuffer::kPictureImageFilterLevel_Version)) {
-            filterLevel = SkPaint::kLow_FilterLevel;
+            filterQuality = kLow_SkFilterQuality;
         } else {
-            filterLevel = (SkPaint::FilterLevel)buffer.readInt();
+            filterQuality = (SkFilterQuality)buffer.readInt();
         }
-        return CreateForLocalSpace(picture, cropRect, filterLevel);    
+        return CreateForLocalSpace(picture, cropRect, filterQuality);
     }
     return Create(picture, cropRect);
 }
@@ -86,7 +86,7 @@
     buffer.writeRect(fCropRect);
     buffer.writeInt(fPictureResolution);
     if (kLocalSpace_PictureResolution == fPictureResolution) {
-        buffer.writeInt(fFilterLevel);
+        buffer.writeInt(fFilterQuality);
     }
 }
 
@@ -168,7 +168,7 @@
     canvas.translate(-SkIntToScalar(deviceBounds.fLeft), -SkIntToScalar(deviceBounds.fTop));
     canvas.concat(ctx.ctm());
     SkPaint paint;
-    paint.setFilterLevel(fFilterLevel);
+    paint.setFilterQuality(fFilterQuality);
     canvas.drawBitmap(localDevice.get()->accessBitmap(false), SkIntToScalar(localIBounds.fLeft),
                       SkIntToScalar(localIBounds.fTop), &paint);
     //canvas.drawPicture(fPicture);
diff --git a/src/effects/SkRectShaderImageFilter.cpp b/src/effects/SkRectShaderImageFilter.cpp
index a00994e..cdf0313 100644
--- a/src/effects/SkRectShaderImageFilter.cpp
+++ b/src/effects/SkRectShaderImageFilter.cpp
@@ -23,21 +23,20 @@
     return s ? SkNEW_ARGS(SkRectShaderImageFilter, (s, &cropRect)) : NULL;
 }
 
-SkRectShaderImageFilter* SkRectShaderImageFilter::Create(SkShader* s, const CropRect* cropRect, uint32_t uniqueID) {
+SkRectShaderImageFilter* SkRectShaderImageFilter::Create(SkShader* s, const CropRect* cropRect) {
     SkASSERT(s);
-    return s ? SkNEW_ARGS(SkRectShaderImageFilter, (s, cropRect, uniqueID)) : NULL;
+    return s ? SkNEW_ARGS(SkRectShaderImageFilter, (s, cropRect)) : NULL;
 }
 
-SkRectShaderImageFilter::SkRectShaderImageFilter(SkShader* s, const CropRect* cropRect,
-                                                 uint32_t uniqueID)
-  : INHERITED(0, NULL, cropRect, uniqueID)
+SkRectShaderImageFilter::SkRectShaderImageFilter(SkShader* s, const CropRect* cropRect)
+  : INHERITED(0, NULL, cropRect)
   , fShader(SkRef(s)) {
 }
 
 SkFlattenable* SkRectShaderImageFilter::CreateProc(SkReadBuffer& buffer) {
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
     SkAutoTUnref<SkShader> shader(buffer.readShader());
-    return Create(shader.get(), &common.cropRect(), common.uniqueID());
+    return Create(shader.get(), &common.cropRect());
 }
 
 void SkRectShaderImageFilter::flatten(SkWriteBuffer& buffer) const {
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index 073bc18..7298960 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -1,3 +1,9 @@
+/*
+* Copyright 2015 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
 
 #include "SkBitmap.h"
 #include "SkTableColorFilter.h"
@@ -40,14 +46,14 @@
         SkDELETE(fBitmap);
     }
 
-    bool asComponentTable(SkBitmap* table) const SK_OVERRIDE;
-    SkColorFilter* newComposed(const SkColorFilter* inner) const SK_OVERRIDE;
+    bool asComponentTable(SkBitmap* table) const override;
+    SkColorFilter* newComposed(const SkColorFilter* inner) const override;
 
 #if SK_SUPPORT_GPU
-    bool asFragmentProcessors(GrContext*, SkTDArray<GrFragmentProcessor*>*) const SK_OVERRIDE;
+    bool asFragmentProcessors(GrContext*, SkTDArray<GrFragmentProcessor*>*) const override;
 #endif
 
-    void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const SK_OVERRIDE;
+    void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override;
 
     SK_TO_STRING_OVERRIDE()
 
@@ -61,7 +67,7 @@
     };
 
 protected:
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
 
 private:
     mutable const SkBitmap* fBitmap; // lazily allocated
@@ -338,19 +344,19 @@
 
     virtual ~ColorTableEffect();
 
-    const char* name() const SK_OVERRIDE { return "ColorTable"; }
+    const char* name() const override { return "ColorTable"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     const GrTextureStripAtlas* atlas() const { return fAtlas; }
     int atlasRow() const { return fRow; }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     ColorTableEffect(GrTexture* texture, GrTextureStripAtlas* atlas, int row, unsigned flags);
 
@@ -376,11 +382,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
-    static void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder* b) {}
+    static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {}
 
 private:
     UniformHandle fRGBAYValuesUni;
@@ -421,7 +427,7 @@
                                           "yoffsets", &yoffsets);
     static const float kColorScaleFactor = 255.0f / 256.0f;
     static const float kColorOffsetFactor = 1.0f / 512.0f;
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     if (NULL == inputColor) {
         // the input color is solid white (all ones).
         static const float kMaxValue = kColorScaleFactor + kColorOffsetFactor;
@@ -501,7 +507,7 @@
     }
 }
 
-void ColorTableEffect::getGLProcessorKey(const GrGLCaps& caps,
+void ColorTableEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                          GrProcessorKeyBuilder* b) const {
     GLColorTableEffect::GenKey(*this, caps, b);
 }
diff --git a/src/effects/SkTestImageFilters.cpp b/src/effects/SkTestImageFilters.cpp
index f0f8ae0..4858eb2 100755
--- a/src/effects/SkTestImageFilters.cpp
+++ b/src/effects/SkTestImageFilters.cpp
@@ -49,7 +49,7 @@
         OwnDeviceCanvas canvas(dev);
         SkPaint paint;
 
-        paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        paint.setFilterQuality(kLow_SkFilterQuality);
         canvas.scale(scale, scale);
         canvas.drawBitmap(src, 0, 0, &paint);
         tmp = dev->accessBitmap(false);
diff --git a/src/effects/SkTileImageFilter.cpp b/src/effects/SkTileImageFilter.cpp
index 669c234..c5cf518 100644
--- a/src/effects/SkTileImageFilter.cpp
+++ b/src/effects/SkTileImageFilter.cpp
@@ -17,11 +17,11 @@
 #include "SkValidationUtils.h"
 
 SkTileImageFilter* SkTileImageFilter::Create(const SkRect& srcRect, const SkRect& dstRect,
-                                             SkImageFilter* input, uint32_t uniqueID) {
+                                             SkImageFilter* input) {
     if (!SkIsValidRect(srcRect) || !SkIsValidRect(dstRect)) {
         return NULL;
     }
-    return SkNEW_ARGS(SkTileImageFilter, (srcRect, dstRect, input, uniqueID));
+    return SkNEW_ARGS(SkTileImageFilter, (srcRect, dstRect, input));
 }
 
 bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
@@ -98,7 +98,7 @@
     SkRect src, dst;
     buffer.readRect(&src);
     buffer.readRect(&dst);
-    return Create(src, dst, common.getInput(0), common.uniqueID());
+    return Create(src, dst, common.getInput(0));
 }
 
 void SkTileImageFilter::flatten(SkWriteBuffer& buffer) const {
diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp
index 2ba5ec5..76c4629 100644
--- a/src/effects/SkXfermodeImageFilter.cpp
+++ b/src/effects/SkXfermodeImageFilter.cpp
@@ -14,7 +14,7 @@
 #include "SkXfermode.h"
 #if SK_SUPPORT_GPU
 #include "GrContext.h"
-#include "effects/GrSimpleTextureEffect.h"
+#include "effects/GrTextureDomain.h"
 #include "SkGr.h"
 #endif
 
@@ -22,9 +22,8 @@
 
 SkXfermodeImageFilter::SkXfermodeImageFilter(SkXfermode* mode,
                                              SkImageFilter* inputs[2],
-                                             const CropRect* cropRect,
-                                             uint32_t uniqueID)
-  : INHERITED(2, inputs, cropRect, uniqueID), fMode(mode) {
+                                             const CropRect* cropRect)
+  : INHERITED(2, inputs, cropRect), fMode(mode) {
     SkSafeRef(fMode);
 }
 
@@ -35,7 +34,7 @@
 SkFlattenable* SkXfermodeImageFilter::CreateProc(SkReadBuffer& buffer) {
     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
     SkAutoTUnref<SkXfermode> mode(buffer.readXfermode());
-    return Create(mode, common.getInput(0), common.getInput(1), &common.cropRect(), common.uniqueID());
+    return Create(mode, common.getInput(0), common.getInput(1), &common.cropRect());
 }
 
 void SkXfermodeImageFilter::flatten(SkWriteBuffer& buffer) const {
@@ -149,8 +148,8 @@
     desc.fWidth = src.width();
     desc.fHeight = src.height();
     desc.fConfig = kSkia8888_GrPixelConfig;
-    SkAutoTUnref<GrTexture> dst(
-        context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch));
+    SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture(
+        desc, GrTextureProvider::kApprox_ScratchTexMatch));
     if (!dst) {
         return false;
     }
@@ -170,7 +169,14 @@
     src.getBounds(&srcRect);
 
     GrPaint paint;
-    paint.addColorTextureProcessor(foregroundTex, foregroundMatrix);
+    SkAutoTUnref<GrFragmentProcessor> foregroundDomain(GrTextureDomainEffect::Create(
+        foregroundTex, foregroundMatrix,
+        GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()),
+        GrTextureDomain::kDecal_Mode,
+        GrTextureParams::kNone_FilterMode)
+    );
+
+    paint.addColorProcessor(foregroundDomain.get());
     paint.addColorProcessor(xferProcessor)->unref();
     context->drawRect(dst->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), srcRect);
 
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index 0f6599d..52731b3 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -8,7 +8,6 @@
 #include "SkGradientShaderPriv.h"
 #include "SkLinearGradient.h"
 #include "SkRadialGradient.h"
-#include "SkTwoPointRadialGradient.h"
 #include "SkTwoPointConicalGradient.h"
 #include "SkSweepGradient.h"
 
@@ -802,30 +801,6 @@
     return SkNEW_ARGS(SkRadialGradient, (center, radius, desc));
 }
 
-SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
-                                                 SkScalar startRadius,
-                                                 const SkPoint& end,
-                                                 SkScalar endRadius,
-                                                 const SkColor colors[],
-                                                 const SkScalar pos[],
-                                                 int colorCount,
-                                                 SkShader::TileMode mode,
-                                                 uint32_t flags,
-                                                 const SkMatrix* localMatrix) {
-    if (startRadius < 0 || endRadius < 0) {
-        return NULL;
-    }
-    if (!valid_grad(colors, pos, colorCount, mode)) {
-        return NULL;
-    }
-    EXPAND_1_COLOR(colorCount);
-
-    SkGradientShaderBase::Descriptor desc;
-    desc_init(&desc, colors, pos, colorCount, mode, flags, localMatrix);
-    return SkNEW_ARGS(SkTwoPointRadialGradient,
-                      (start, startRadius, end, endRadius, desc));
-}
-
 SkShader* SkGradientShader::CreateTwoPointConical(const SkPoint& start,
                                                   SkScalar startRadius,
                                                   const SkPoint& end,
@@ -897,7 +872,6 @@
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLinearGradient)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRadialGradient)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSweepGradient)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTwoPointRadialGradient)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTwoPointConicalGradient)
 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
 
@@ -1028,7 +1002,7 @@
                                    const char* outputColor,
                                    const char* inputColor,
                                    const TextureSamplerArray& samplers) {
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     if (SkGradientShaderBase::kTwo_GpuColorType == ge.getColorType()){
         fsBuilder->codeAppendf("\tvec4 colorTemp = mix(%s, %s, clamp(%s, 0.0, 1.0));\n",
                                builder->getUniformVariable(fColorStartUni).c_str(),
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
index 99ad70c..dff6983 100644
--- a/src/effects/gradients/SkGradientShaderPriv.h
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -166,7 +166,7 @@
     public:
         GradientShaderBaseContext(const SkGradientShaderBase& shader, const ContextRec&);
 
-        uint32_t getFlags() const SK_OVERRIDE { return fFlags; }
+        uint32_t getFlags() const override { return fFlags; }
 
     protected:
         SkMatrix    fDstToIndex;
@@ -180,7 +180,7 @@
         typedef SkShader::Context INHERITED;
     };
 
-    bool isOpaque() const SK_OVERRIDE;
+    bool isOpaque() const override;
 
     void getGradientTableBitmap(SkBitmap*) const;
 
@@ -220,7 +220,7 @@
 
 protected:
     SkGradientShaderBase(SkReadBuffer& );
-    void flatten(SkWriteBuffer&) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer&) const override;
     SK_TO_STRING_OVERRIDE()
 
     const SkMatrix fPtsToUnit;
@@ -237,7 +237,7 @@
 
     void commonAsAGradient(GradientInfo*, bool flipGrad = false) const;
 
-    bool onAsLuminanceColor(SkColor*) const SK_OVERRIDE;
+    bool onAsLuminanceColor(SkColor*) const override;
 
     /*
      * Takes in pointers to gradient color and Rec info as colorSrc and recSrc respectively.
@@ -368,9 +368,9 @@
                                     SkScalar** stops,
                                     SkShader::TileMode* tm);
 
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     const GrCoordTransform& getCoordTransform() const { return fCoordTransform; }
 
@@ -399,7 +399,7 @@
     GrGLGradientEffect();
     virtual ~GrGLGradientEffect();
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 protected:
     /**
diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp
index c7d845a..da4c20d 100644
--- a/src/effects/gradients/SkLinearGradient.cpp
+++ b/src/effects/gradients/SkLinearGradient.cpp
@@ -472,9 +472,9 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) {
+    static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
         b->add32(GenBaseGradientKey(processor));
     }
 
@@ -497,14 +497,14 @@
 
     virtual ~GrLinearGradient() { }
 
-    const char* name() const SK_OVERRIDE { return "Linear Gradient"; }
+    const char* name() const override { return "Linear Gradient"; }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GrGLLinearGradient::GenKey(*this, caps, b);
     }
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
+    GrGLFragmentProcessor* createGLInstance() const override {
         return SkNEW_ARGS(GrGLLinearGradient, (*this));
     }
 
@@ -544,7 +544,7 @@
     GrColor paintColor;
     GrFragmentProcessor* fp;
     SkAssertResult(shader->asFragmentProcessor(context, paint,
-                                               GrProcessorUnitTest::TestMatrix(random), NULL,
+                                               GrTest::TestMatrix(random), NULL,
                                                &paintColor, &fp));
     return fp;
 }
diff --git a/src/effects/gradients/SkLinearGradient.h b/src/effects/gradients/SkLinearGradient.h
index e63a1b8..3f4f36f 100644
--- a/src/effects/gradients/SkLinearGradient.h
+++ b/src/effects/gradients/SkLinearGradient.h
@@ -14,33 +14,33 @@
 public:
     SkLinearGradient(const SkPoint pts[2], const Descriptor&);
 
-    size_t contextSize() const SK_OVERRIDE;
+    size_t contextSize() const override;
 
     class LinearGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
     public:
         LinearGradientContext(const SkLinearGradient&, const ContextRec&);
         ~LinearGradientContext() {}
 
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
-        void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
+        void shadeSpan16(int x, int y, uint16_t dstC[], int count) override;
 
     private:
         typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
     };
 
-    BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const SK_OVERRIDE;
-    GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
+    BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const override;
+    GradientType asAGradient(GradientInfo* info) const override;
     virtual bool asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix& viewM,
                                      const SkMatrix*,
-                                     GrColor*, GrFragmentProcessor**) const SK_OVERRIDE;
+                                     GrColor*, GrFragmentProcessor**) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLinearGradient)
 
 protected:
     SkLinearGradient(SkReadBuffer& buffer);
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer& buffer) const override;
+    Context* onCreateContext(const ContextRec&, void* storage) const override;
 
 private:
     friend class SkGradientShader;
diff --git a/src/effects/gradients/SkRadialGradient.cpp b/src/effects/gradients/SkRadialGradient.cpp
index 5ea2e72..bc2d15a 100644
--- a/src/effects/gradients/SkRadialGradient.cpp
+++ b/src/effects/gradients/SkRadialGradient.cpp
@@ -8,6 +8,7 @@
 
 #include "SkRadialGradient.h"
 #include "SkRadialGradient_Table.h"
+#include "SkNx.h"
 
 #define kSQRT_TABLE_BITS    11
 #define kSQRT_TABLE_SIZE    (1 << kSQRT_TABLE_BITS)
@@ -269,106 +270,77 @@
 
 namespace {
 
-inline bool radial_completely_pinned(int fx, int dx, int fy, int dy) {
-    // fast, overly-conservative test: checks unit square instead
-    // of unit circle
-    bool xClamped = (fx >= SK_FixedHalf && dx >= 0) ||
-                    (fx <= -SK_FixedHalf && dx <= 0);
-    bool yClamped = (fy >= SK_FixedHalf && dy >= 0) ||
-                    (fy <= -SK_FixedHalf && dy <= 0);
-
+inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) {
+    // fast, overly-conservative test: checks unit square instead of unit circle
+    bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0);
+    bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0);
     return xClamped || yClamped;
 }
 
-// Return true if (fx * fy) is always inside the unit circle
-// SkPin32 is expensive, but so are all the SkFixedMul in this test,
-// so it shouldn't be run if count is small.
-inline bool no_need_for_radial_pin(int fx, int dx,
-                                          int fy, int dy, int count) {
-    SkASSERT(count > 0);
-    if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
-        return false;
-    }
-    if (fx*fx + fy*fy > 0x7FFF*0x7FFF) {
-        return false;
-    }
-    fx += (count - 1) * dx;
-    fy += (count - 1) * dy;
-    if (SkAbs32(fx) > 0x7FFF || SkAbs32(fy) > 0x7FFF) {
-        return false;
-    }
-    return fx*fx + fy*fy <= 0x7FFF*0x7FFF;
-}
-
-#define UNPINNED_RADIAL_STEP \
-    fi = (fx * fx + fy * fy) >> (14 + 16 - kSQRT_TABLE_BITS); \
-    *dstC++ = cache[toggle + \
-                    (sqrt_table[fi] >> SkGradientShaderBase::kSqrt32Shift)]; \
-    toggle = next_dither_toggle(toggle); \
-    fx += dx; \
-    fy += dy;
-
 typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
         SkScalar sfy, SkScalar sdy,
         SkPMColor* dstC, const SkPMColor* cache,
         int count, int toggle);
 
-// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
-void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
-        SkScalar sfy, SkScalar sdy,
-        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
-        int count, int toggle) {
-    // Floating point seems to be slower than fixed point,
-    // even when we have float hardware.
-    const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
-    SkFixed fx = SkScalarToFixed(sfx) >> 1;
-    SkFixed dx = SkScalarToFixed(sdx) >> 1;
-    SkFixed fy = SkScalarToFixed(sfy) >> 1;
-    SkFixed dy = SkScalarToFixed(sdy) >> 1;
-    if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
+static inline Sk4f fast_sqrt(const Sk4f& R) {
+    // R * R.rsqrt0() is much faster, but it's non-monotonic, which isn't so pretty for gradients.
+    return R * R.rsqrt1();
+}
+
+static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) {
+    return a * a + b * b;
+}
+
+void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy,
+                             SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
+                             int count, int toggle) {
+    if (radial_completely_pinned(sfx, sdx, sfy, sdy)) {
         unsigned fi = SkGradientShaderBase::kCache32Count - 1;
         sk_memset32_dither(dstC,
-            cache[toggle + fi],
-            cache[next_dither_toggle(toggle) + fi],
-            count);
-    } else if ((count > 4) &&
-               no_need_for_radial_pin(fx, dx, fy, dy, count)) {
-        unsigned fi;
-        // 4x unroll appears to be no faster than 2x unroll on Linux
-        while (count > 1) {
-            UNPINNED_RADIAL_STEP;
-            UNPINNED_RADIAL_STEP;
-            count -= 2;
+                           cache[toggle + fi],
+                           cache[next_dither_toggle(toggle) + fi],
+                           count);
+    } else {
+        const Sk4f max(255);
+        const float scale = 255;
+        sfx *= scale;
+        sfy *= scale;
+        sdx *= scale;
+        sdy *= scale;
+        const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx);
+        const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy);
+        const Sk4f dx4(sdx * 4);
+        const Sk4f dy4(sdy * 4);
+
+        Sk4f tmpxy = fx4 * dx4 + fy4 * dy4;
+        Sk4f tmpdxdy = sum_squares(dx4, dy4);
+        Sk4f R = sum_squares(fx4, fy4);
+        Sk4f dR = tmpxy + tmpxy + tmpdxdy;
+        const Sk4f ddR = tmpdxdy + tmpdxdy;
+
+        for (int i = 0; i < (count >> 2); ++i) {
+            Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
+            R += dR;
+            dR += ddR;
+
+            int fi[4];
+            dist.castTrunc().store(fi);
+
+            for (int i = 0; i < 4; i++) {
+                *dstC++ = cache[toggle + fi[i]];
+                toggle = next_dither_toggle(toggle);
+            }
         }
+        count &= 3;
         if (count) {
-            UNPINNED_RADIAL_STEP;
-        }
-    } else  {
-        // Specializing for dy == 0 gains us 25% on Skia benchmarks
-        if (dy == 0) {
-            unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
-            yy *= yy;
-            do {
-                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
-                unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
-                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
-                *dstC++ = cache[toggle + (sqrt_table[fi] >>
-                    SkGradientShaderBase::kSqrt32Shift)];
+            Sk4f dist = Sk4f::Min(fast_sqrt(R), max);
+
+            int fi[4];
+            dist.castTrunc().store(fi);
+            for (int i = 0; i < count; i++) {
+                *dstC++ = cache[toggle + fi[i]];
                 toggle = next_dither_toggle(toggle);
-                fx += dx;
-            } while (--count != 0);
-        } else {
-            do {
-                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
-                unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
-                fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
-                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
-                *dstC++ = cache[toggle + (sqrt_table[fi] >>
-                    SkGradientShaderBase::kSqrt32Shift)];
-                toggle = next_dither_toggle(toggle);
-                fx += dx;
-                fy += dy;
-            } while (--count != 0);
+            }
         }
     }
 }
@@ -435,7 +407,7 @@
 
         RadialShadeProc shadeProc = shadeSpan_radial_repeat;
         if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
-            shadeProc = shadeSpan_radial_clamp;
+            shadeProc = shadeSpan_radial_clamp2;
         } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
             shadeProc = shadeSpan_radial_mirror;
         } else {
@@ -473,9 +445,9 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) {
+    static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
         b->add32(GenBaseGradientKey(processor));
     }
 
@@ -498,14 +470,14 @@
 
     virtual ~GrRadialGradient() { }
 
-    const char* name() const SK_OVERRIDE { return "Radial Gradient"; }
+    const char* name() const override { return "Radial Gradient"; }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GrGLRadialGradient::GenKey(*this, caps, b);
     }
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
+    GrGLFragmentProcessor* createGLInstance() const override {
         return SkNEW_ARGS(GrGLRadialGradient, (*this));
     }
 
@@ -546,7 +518,7 @@
     GrColor paintColor;
     GrFragmentProcessor* fp;
     SkAssertResult(shader->asFragmentProcessor(context, paint,
-                                               GrProcessorUnitTest::TestMatrix(random), NULL,
+                                               GrTest::TestMatrix(random), NULL,
                                                &paintColor, &fp));
     return fp;
 }
diff --git a/src/effects/gradients/SkRadialGradient.h b/src/effects/gradients/SkRadialGradient.h
index a690bb5..f80a8e8 100644
--- a/src/effects/gradients/SkRadialGradient.h
+++ b/src/effects/gradients/SkRadialGradient.h
@@ -15,14 +15,14 @@
 public:
     SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor&);
 
-    size_t contextSize() const SK_OVERRIDE;
+    size_t contextSize() const override;
 
     class RadialGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
     public:
         RadialGradientContext(const SkRadialGradient&, const ContextRec&);
 
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
-        void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
+        void shadeSpan16(int x, int y, uint16_t dstC[], int count) override;
 
     private:
         typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
@@ -30,19 +30,19 @@
 
     virtual BitmapType asABitmap(SkBitmap* bitmap,
                                  SkMatrix* matrix,
-                                 TileMode* xy) const SK_OVERRIDE;
-    GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
+                                 TileMode* xy) const override;
+    GradientType asAGradient(GradientInfo* info) const override;
     virtual bool asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix& viewM,
                                      const SkMatrix*, GrColor*,
-                                     GrFragmentProcessor**) const SK_OVERRIDE;
+                                     GrFragmentProcessor**) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRadialGradient)
 
 protected:
     SkRadialGradient(SkReadBuffer& buffer);
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer& buffer) const override;
+    Context* onCreateContext(const ContextRec&, void* storage) const override;
 
 private:
     friend class SkGradientShader;
diff --git a/src/effects/gradients/SkSweepGradient.cpp b/src/effects/gradients/SkSweepGradient.cpp
index f8ee458..03b41a4 100644
--- a/src/effects/gradients/SkSweepGradient.cpp
+++ b/src/effects/gradients/SkSweepGradient.cpp
@@ -195,9 +195,9 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) {
+    static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
         b->add32(GenBaseGradientKey(processor));
     }
 
@@ -217,14 +217,14 @@
     }
     virtual ~GrSweepGradient() { }
 
-    const char* name() const SK_OVERRIDE { return "Sweep Gradient"; }
+    const char* name() const override { return "Sweep Gradient"; }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GrGLSweepGradient::GenKey(*this, caps, b);
     }
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
+    GrGLFragmentProcessor* createGLInstance() const override {
         return SkNEW_ARGS(GrGLSweepGradient, (*this));
     }
 
@@ -261,7 +261,7 @@
     GrFragmentProcessor* fp;
     GrColor paintColor;
     SkAssertResult(shader->asFragmentProcessor(context, paint,
-                                               GrProcessorUnitTest::TestMatrix(random), NULL,
+                                               GrTest::TestMatrix(random), NULL,
                                                &paintColor, &fp));
     return fp;
 }
diff --git a/src/effects/gradients/SkSweepGradient.h b/src/effects/gradients/SkSweepGradient.h
index b72a08d..7e08d97 100644
--- a/src/effects/gradients/SkSweepGradient.h
+++ b/src/effects/gradients/SkSweepGradient.h
@@ -15,14 +15,14 @@
 public:
     SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor&);
 
-    size_t contextSize() const SK_OVERRIDE;
+    size_t contextSize() const override;
 
     class SweepGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
     public:
         SweepGradientContext(const SkSweepGradient& shader, const ContextRec&);
 
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
-        void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
+        void shadeSpan16(int x, int y, uint16_t dstC[], int count) override;
 
     private:
         typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
@@ -30,20 +30,20 @@
 
     virtual BitmapType asABitmap(SkBitmap* bitmap,
                                  SkMatrix* matrix,
-                                 TileMode* xy) const SK_OVERRIDE;
+                                 TileMode* xy) const override;
 
-    GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
+    GradientType asAGradient(GradientInfo* info) const override;
 
     virtual bool asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix& viewM,
                                      const SkMatrix*, GrColor*,
-                                     GrFragmentProcessor**) const SK_OVERRIDE;
+                                     GrFragmentProcessor**) const override;
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSweepGradient)
 
 protected:
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer& buffer) const override;
+    Context* onCreateContext(const ContextRec&, void* storage) const override;
 
 private:
     const SkPoint fCenter;
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.h b/src/effects/gradients/SkTwoPointConicalGradient.h
index ff12236..0a3cd90 100644
--- a/src/effects/gradients/SkTwoPointConicalGradient.h
+++ b/src/effects/gradients/SkTwoPointConicalGradient.h
@@ -45,14 +45,14 @@
                               bool flippedGrad, const Descriptor&);
 
 
-    size_t contextSize() const SK_OVERRIDE;
+    size_t contextSize() const override;
 
     class TwoPointConicalGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
     public:
         TwoPointConicalGradientContext(const SkTwoPointConicalGradient&, const ContextRec&);
         ~TwoPointConicalGradientContext() {}
 
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+        void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
 
     private:
         typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
@@ -60,11 +60,11 @@
 
     virtual BitmapType asABitmap(SkBitmap* bitmap,
                                  SkMatrix* matrix,
-                                 TileMode* xy) const SK_OVERRIDE;
-    SkShader::GradientType asAGradient(GradientInfo* info) const  SK_OVERRIDE;
+                                 TileMode* xy) const override;
+    SkShader::GradientType asAGradient(GradientInfo* info) const  override;
     virtual bool asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&, const SkMatrix*,
-                                     GrColor*, GrFragmentProcessor**) const SK_OVERRIDE;
-    bool isOpaque() const SK_OVERRIDE;
+                                     GrColor*, GrFragmentProcessor**) const override;
+    bool isOpaque() const override;
 
     SkScalar getCenterX1() const { return SkPoint::Distance(fCenter1, fCenter2); }
     SkScalar getStartRadius() const { return fRadius1; }
@@ -79,8 +79,8 @@
 
 protected:
     SkTwoPointConicalGradient(SkReadBuffer& buffer);
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
+    void flatten(SkWriteBuffer& buffer) const override;
+    Context* onCreateContext(const ContextRec&, void* storage) const override;
 
 private:
     SkPoint fCenter1;
diff --git a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp
index c9f2d2f..f7b3eb8 100644
--- a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp
+++ b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp
@@ -66,13 +66,13 @@
 
     virtual ~Edge2PtConicalEffect() {}
 
-    const char* name() const SK_OVERRIDE {
+    const char* name() const override {
         return "Two-Point Conical Gradient Edge Touching";
     }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
     SkScalar center() const { return fCenterX1; }
@@ -80,7 +80,7 @@
     SkScalar radius() const { return fRadius0; }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor& sBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
         const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>();
         return (INHERITED::onIsEqual(sBase) &&
                 this->fCenterX1 == s.fCenterX1 &&
@@ -146,10 +146,10 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
-    static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
+    static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
 
 protected:
     UniformHandle fParamUni;
@@ -170,7 +170,7 @@
 
 };
 
-void Edge2PtConicalEffect::getGLProcessorKey(const GrGLCaps& caps,
+void Edge2PtConicalEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                              GrProcessorKeyBuilder* b) const {
     GLEdge2PtConicalEffect::GenKey(*this, caps, b);
 }
@@ -217,7 +217,7 @@
     GrFragmentProcessor* fp;
     GrColor paintColor;
     SkAssertResult(shader->asFragmentProcessor(context, paint,
-                                               GrProcessorUnitTest::TestMatrix(random), NULL,
+                                               GrTest::TestMatrix(random), NULL,
                                                &paintColor, &fp));
     return fp;
 }
@@ -254,7 +254,7 @@
     SkASSERT(coords[0].getType() == coords[1].getType());
     const char* coords2D;
     SkString bVar;
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     if (kVec3f_GrSLType == coords[0].getType()) {
         fsBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
                                coords[0].c_str(), coords[0].c_str(), coords[1].c_str(),
@@ -309,7 +309,7 @@
 }
 
 void GLEdge2PtConicalEffect::GenKey(const GrProcessor& processor,
-                                    const GrGLCaps&, GrProcessorKeyBuilder* b) {
+                                    const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
     b->add32(GenBaseGradientKey(processor));
 }
 
@@ -357,7 +357,7 @@
 
     // Scale factor 1 / (1 - focalX * focalX)
     SkScalar oneMinusF2 = 1.f - SkScalarMul(*focalX, *focalX);
-    SkScalar s = SkScalarDiv(1.f, oneMinusF2);
+    SkScalar s = SkScalarInvert(oneMinusF2);
 
 
     if (s >= 0.f) {
@@ -388,19 +388,19 @@
 
     virtual ~FocalOutside2PtConicalEffect() { }
 
-    const char* name() const SK_OVERRIDE {
+    const char* name() const override {
         return "Two-Point Conical Gradient Focal Outside";
     }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     bool isFlipped() const { return fIsFlipped; }
     SkScalar focal() const { return fFocalX; }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor& sBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
         const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
         return (INHERITED::onIsEqual(sBase) &&
                 this->fFocalX == s.fFocalX &&
@@ -434,10 +434,10 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
-    static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
+    static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
 
 protected:
     UniformHandle fParamUni;
@@ -459,7 +459,7 @@
 
 };
 
-void FocalOutside2PtConicalEffect::getGLProcessorKey(const GrGLCaps& caps,
+void FocalOutside2PtConicalEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                                      GrProcessorKeyBuilder* b) const {
     GLFocalOutside2PtConicalEffect::GenKey(*this, caps, b);
 }
@@ -504,7 +504,7 @@
     GrColor paintColor;
 
     SkAssertResult(shader->asFragmentProcessor(context, paint,
-                                               GrProcessorUnitTest::TestMatrix(random), NULL,
+                                               GrTest::TestMatrix(random), NULL,
                                                &paintColor, &effect));
     return effect;
 }
@@ -536,7 +536,7 @@
     builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
 
     // if we have a vec3 from being in perspective, convert it to a vec2 first
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2DString = fsBuilder->ensureFSCoords2D(coords, 0);
     const char* coords2D = coords2DString.c_str();
 
@@ -587,7 +587,7 @@
 }
 
 void GLFocalOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
-                                            const GrGLCaps&, GrProcessorKeyBuilder* b) {
+                                            const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
     uint32_t* key = b->add32n(2);
     key[0] = GenBaseGradientKey(processor);
     key[1] = processor.cast<FocalOutside2PtConicalEffect>().isFlipped();
@@ -610,20 +610,20 @@
 
     virtual ~FocalInside2PtConicalEffect() {}
 
-    const char* name() const SK_OVERRIDE {
+    const char* name() const override {
         return "Two-Point Conical Gradient Focal Inside";
     }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     SkScalar focal() const { return fFocalX; }
 
     typedef GLFocalInside2PtConicalEffect GLProcessor;
 
 private:
-    bool onIsEqual(const GrFragmentProcessor& sBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
         const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
         return (INHERITED::onIsEqual(sBase) &&
                 this->fFocalX == s.fFocalX);
@@ -655,10 +655,10 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
-    static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
+    static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
 
 protected:
     UniformHandle fFocalUni;
@@ -678,7 +678,7 @@
 
 };
 
-void FocalInside2PtConicalEffect::getGLProcessorKey(const GrGLCaps& caps,
+void FocalInside2PtConicalEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                GrProcessorKeyBuilder* b) const {
     GLFocalInside2PtConicalEffect::GenKey(*this, caps, b);
 }
@@ -724,7 +724,7 @@
     GrColor paintColor;
     GrFragmentProcessor* fp;
     SkAssertResult(shader->asFragmentProcessor(context, paint,
-                                               GrProcessorUnitTest::TestMatrix(random), NULL,
+                                               GrTest::TestMatrix(random), NULL,
                                                &paintColor, &fp));
     return fp;
 }
@@ -752,7 +752,7 @@
     GrGLShaderVar focal = builder->getUniformVariable(fFocalUni);
 
     // if we have a vec3 from being in perspective, convert it to a vec2 first
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2DString = fsBuilder->ensureFSCoords2D(coords, 0);
     const char* coords2D = coords2DString.c_str();
 
@@ -776,7 +776,7 @@
 }
 
 void GLFocalInside2PtConicalEffect::GenKey(const GrProcessor& processor,
-                                           const GrGLCaps&, GrProcessorKeyBuilder* b) {
+                                           const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
     b->add32(GenBaseGradientKey(processor));
 }
 
@@ -857,12 +857,12 @@
 
     virtual ~CircleInside2PtConicalEffect() {}
 
-    const char* name() const SK_OVERRIDE { return "Two-Point Conical Gradient Inside"; }
+    const char* name() const override { return "Two-Point Conical Gradient Inside"; }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
     SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
@@ -871,7 +871,7 @@
     SkScalar C() const { return fInfo.fC; }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor& sBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
         const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
         return (INHERITED::onIsEqual(sBase) &&
                 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
@@ -906,10 +906,10 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
-    static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
+    static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
 
 protected:
     UniformHandle fCenterUni;
@@ -934,7 +934,7 @@
 
 };
 
-void CircleInside2PtConicalEffect::getGLProcessorKey(const GrGLCaps& caps,
+void CircleInside2PtConicalEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                                      GrProcessorKeyBuilder* b) const {
     GLCircleInside2PtConicalEffect::GenKey(*this, caps, b);
 }
@@ -979,7 +979,7 @@
     GrColor paintColor;
     GrFragmentProcessor* processor;
     SkAssertResult(shader->asFragmentProcessor(context, paint,
-                                               GrProcessorUnitTest::TestMatrix(random), NULL,
+                                               GrTest::TestMatrix(random), NULL,
                                                &paintColor, &processor));
     return processor;
 }
@@ -1016,7 +1016,7 @@
     GrGLShaderVar params = builder->getUniformVariable(fParamUni);
 
     // if we have a vec3 from being in perspective, convert it to a vec2 first
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2DString = fsBuilder->ensureFSCoords2D(coords, 0);
     const char* coords2D = coords2DString.c_str();
 
@@ -1062,7 +1062,7 @@
 }
 
 void GLCircleInside2PtConicalEffect::GenKey(const GrProcessor& processor,
-                                            const GrGLCaps&, GrProcessorKeyBuilder* b) {
+                                            const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
     b->add32(GenBaseGradientKey(processor));
 }
 
@@ -1081,11 +1081,11 @@
 
     virtual ~CircleOutside2PtConicalEffect() {}
 
-    const char* name() const SK_OVERRIDE { return "Two-Point Conical Gradient Outside"; }
+    const char* name() const override { return "Two-Point Conical Gradient Outside"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
     SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
@@ -1096,7 +1096,7 @@
     bool isFlipped() const { return fIsFlipped; }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor& sBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
         const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>();
         return (INHERITED::onIsEqual(sBase) &&
                 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
@@ -1115,8 +1115,7 @@
         : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
         this->initClassID<CircleOutside2PtConicalEffect>();
         if (shader.getStartRadius() != shader.getEndRadius()) {
-            fTLimit = SkScalarDiv(shader.getStartRadius(),
-                                  (shader.getStartRadius() - shader.getEndRadius()));
+            fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius());
         } else {
             fTLimit = SK_ScalarMin;
         }
@@ -1143,10 +1142,10 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
-    static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
+    static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
 
 protected:
     UniformHandle fCenterUni;
@@ -1174,7 +1173,7 @@
 
 };
 
-void CircleOutside2PtConicalEffect::getGLProcessorKey(const GrGLCaps& caps,
+void CircleOutside2PtConicalEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                                       GrProcessorKeyBuilder* b) const {
     GLCircleOutside2PtConicalEffect::GenKey(*this, caps, b);
 }
@@ -1221,7 +1220,7 @@
     GrFragmentProcessor* processor;
 
     SkAssertResult(shader->asFragmentProcessor(context, paint,
-                                               GrProcessorUnitTest::TestMatrix(random), NULL,
+                                               GrTest::TestMatrix(random), NULL,
                                                &paintColor, &processor));
     return processor;
 }
@@ -1262,7 +1261,7 @@
     GrGLShaderVar params = builder->getUniformVariable(fParamUni);
 
     // if we have a vec3 from being in perspective, convert it to a vec2 first
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2DString = fsBuilder->ensureFSCoords2D(coords, 0);
     const char* coords2D = coords2DString.c_str();
 
@@ -1328,7 +1327,7 @@
 }
 
 void GLCircleOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
-                                             const GrGLCaps&, GrProcessorKeyBuilder* b) {
+                                             const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
     uint32_t* key = b->add32n(2);
     key[0] = GenBaseGradientKey(processor);
     key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped();
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.cpp b/src/effects/gradients/SkTwoPointRadialGradient.cpp
deleted file mode 100644
index e0ea35e..0000000
--- a/src/effects/gradients/SkTwoPointRadialGradient.cpp
+++ /dev/null
@@ -1,724 +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.
- */
-
-#include "SkTwoPointRadialGradient.h"
-
-/* Two-point radial gradients are specified by two circles, each with a center
-   point and radius.  The gradient can be considered to be a series of
-   concentric circles, with the color interpolated from the start circle
-   (at t=0) to the end circle (at t=1).
-
-   For each point (x, y) in the span, we want to find the
-   interpolated circle that intersects that point.  The center
-   of the desired circle (Cx, Cy) falls at some distance t
-   along the line segment between the start point (Sx, Sy) and
-   end point (Ex, Ey):
-
-      Cx = (1 - t) * Sx + t * Ex        (0 <= t <= 1)
-      Cy = (1 - t) * Sy + t * Ey
-
-   The radius of the desired circle (r) is also a linear interpolation t
-   between the start and end radii (Sr and Er):
-
-      r = (1 - t) * Sr + t * Er
-
-   But
-
-      (x - Cx)^2 + (y - Cy)^2 = r^2
-
-   so
-
-     (x - ((1 - t) * Sx + t * Ex))^2
-   + (y - ((1 - t) * Sy + t * Ey))^2
-   = ((1 - t) * Sr + t * Er)^2
-
-   Solving for t yields
-
-     [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
-   + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
-   + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
-
-   To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
-
-     [Dx^2 + Dy^2 - Dr^2)] * t^2
-   + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
-   + [dx^2 + dy^2 - Sr^2] = 0
-
-   A quadratic in t.  The two roots of the quadratic reflect the two
-   possible circles on which the point may fall.  Solving for t yields
-   the gradient value to use.
-
-   If a<0, the start circle is entirely contained in the
-   end circle, and one of the roots will be <0 or >1 (off the line
-   segment).  If a>0, the start circle falls at least partially
-   outside the end circle (or vice versa), and the gradient
-   defines a "tube" where a point may be on one circle (on the
-   inside of the tube) or the other (outside of the tube).  We choose
-   one arbitrarily.
-
-   In order to keep the math to within the limits of fixed point,
-   we divide the entire quadratic by Dr^2, and replace
-   (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
-
-   [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
-   + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
-   + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
-
-   (x' and y' are computed by appending the subtract and scale to the
-   fDstToIndex matrix in the constructor).
-
-   Since the 'A' component of the quadratic is independent of x' and y', it
-   is precomputed in the constructor.  Since the 'B' component is linear in
-   x' and y', if x and y are linear in the span, 'B' can be computed
-   incrementally with a simple delta (db below).  If it is not (e.g.,
-   a perspective projection), it must be computed in the loop.
-
-*/
-
-namespace {
-
-inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
-                                SkScalar sr2d2, SkScalar foura,
-                                SkScalar oneOverTwoA, bool posRoot) {
-    SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
-    if (0 == foura) {
-        return SkScalarToFixed(SkScalarDiv(-c, b));
-    }
-
-    SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
-    if (discrim < 0) {
-        discrim = -discrim;
-    }
-    SkScalar rootDiscrim = SkScalarSqrt(discrim);
-    SkScalar result;
-    if (posRoot) {
-        result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
-    } else {
-        result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
-    }
-    return SkScalarToFixed(result);
-}
-
-typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
-        SkScalar fy, SkScalar dy,
-        SkScalar b, SkScalar db,
-        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
-        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
-        int count);
-
-void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
-        SkScalar fy, SkScalar dy,
-        SkScalar b, SkScalar db,
-        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
-        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
-        int count) {
-    for (; count > 0; --count) {
-        SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
-                                     fOneOverTwoA, posRoot);
-        SkFixed index = SkClampMax(t, 0xFFFF);
-        SkASSERT(index <= 0xFFFF);
-        *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
-        fx += dx;
-        fy += dy;
-        b += db;
-    }
-}
-void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
-        SkScalar fy, SkScalar dy,
-        SkScalar b, SkScalar db,
-        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
-        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
-        int count) {
-    for (; count > 0; --count) {
-        SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
-                                     fOneOverTwoA, posRoot);
-        SkFixed index = mirror_tileproc(t);
-        SkASSERT(index <= 0xFFFF);
-        *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
-        fx += dx;
-        fy += dy;
-        b += db;
-    }
-}
-
-void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
-        SkScalar fy, SkScalar dy,
-        SkScalar b, SkScalar db,
-        SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
-        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
-        int count) {
-    for (; count > 0; --count) {
-        SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
-                                     fOneOverTwoA, posRoot);
-        SkFixed index = repeat_tileproc(t);
-        SkASSERT(index <= 0xFFFF);
-        *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
-        fx += dx;
-        fy += dy;
-        b += db;
-    }
-}
-}
-
-/////////////////////////////////////////////////////////////////////
-
-static SkMatrix pts_to_unit(const SkPoint& start, SkScalar diffRadius) {
-    SkScalar inv = diffRadius ? SkScalarInvert(diffRadius) : 0;
-    SkMatrix matrix;
-    matrix.setTranslate(-start.fX, -start.fY);
-    matrix.postScale(inv, inv);
-    return matrix;
-}
-
-SkTwoPointRadialGradient::SkTwoPointRadialGradient(const SkPoint& start, SkScalar startRadius,
-                                                   const SkPoint& end, SkScalar endRadius,
-                                                   const Descriptor& desc)
-    : SkGradientShaderBase(desc, pts_to_unit(start, endRadius - startRadius))
-    , fCenter1(start)
-    , fCenter2(end)
-    , fRadius1(startRadius)
-    , fRadius2(endRadius)
-{
-    fDiff = fCenter1 - fCenter2;
-    fDiffRadius = fRadius2 - fRadius1;
-    // hack to avoid zero-divide for now
-    SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
-    fDiff.fX = SkScalarMul(fDiff.fX, inv);
-    fDiff.fY = SkScalarMul(fDiff.fY, inv);
-    fStartRadius = SkScalarMul(fRadius1, inv);
-    fSr2D2 = SkScalarSquare(fStartRadius);
-    fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
-    fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
-}
-
-SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
-    SkBitmap* bitmap,
-    SkMatrix* matrix,
-    SkShader::TileMode* xy) const {
-    if (bitmap) {
-        this->getGradientTableBitmap(bitmap);
-    }
-    SkScalar diffL = 0; // just to avoid gcc warning
-    if (matrix) {
-        diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
-                             SkScalarSquare(fDiff.fY));
-    }
-    if (matrix) {
-        if (diffL) {
-            SkScalar invDiffL = SkScalarInvert(diffL);
-            matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
-                              SkScalarMul(invDiffL, fDiff.fX));
-        } else {
-            matrix->reset();
-        }
-        matrix->preConcat(fPtsToUnit);
-    }
-    if (xy) {
-        xy[0] = fTileMode;
-        xy[1] = kClamp_TileMode;
-    }
-    return kTwoPointRadial_BitmapType;
-}
-
-SkShader::GradientType SkTwoPointRadialGradient::asAGradient(
-    SkShader::GradientInfo* info) const {
-    if (info) {
-        commonAsAGradient(info);
-        info->fPoint[0] = fCenter1;
-        info->fPoint[1] = fCenter2;
-        info->fRadius[0] = fRadius1;
-        info->fRadius[1] = fRadius2;
-    }
-    return kRadial2_GradientType;
-}
-
-size_t SkTwoPointRadialGradient::contextSize() const {
-    return sizeof(TwoPointRadialGradientContext);
-}
-
-SkShader::Context* SkTwoPointRadialGradient::onCreateContext(const ContextRec& rec,
-                                                             void* storage) const {
-    // For now, we might have divided by zero, so detect that.
-    if (0 == fDiffRadius) {
-        return NULL;
-    }
-    return SkNEW_PLACEMENT_ARGS(storage, TwoPointRadialGradientContext, (*this, rec));
-}
-
-SkTwoPointRadialGradient::TwoPointRadialGradientContext::TwoPointRadialGradientContext(
-        const SkTwoPointRadialGradient& shader, const ContextRec& rec)
-    : INHERITED(shader, rec)
-{
-    // we don't have a span16 proc
-    fFlags &= ~kHasSpan16_Flag;
-}
-
-void SkTwoPointRadialGradient::TwoPointRadialGradientContext::shadeSpan(
-        int x, int y, SkPMColor* dstCParam, int count) {
-    SkASSERT(count > 0);
-
-    const SkTwoPointRadialGradient& twoPointRadialGradient =
-            static_cast<const SkTwoPointRadialGradient&>(fShader);
-
-    SkPMColor* SK_RESTRICT dstC = dstCParam;
-
-    // Zero difference between radii:  fill with transparent black.
-    if (twoPointRadialGradient.fDiffRadius == 0) {
-      sk_bzero(dstC, count * sizeof(*dstC));
-      return;
-    }
-    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-    TileProc            proc = twoPointRadialGradient.fTileProc;
-    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
-
-    SkScalar foura = twoPointRadialGradient.fA * 4;
-    bool posRoot = twoPointRadialGradient.fDiffRadius < 0;
-    if (fDstToIndexClass != kPerspective_MatrixClass) {
-        SkPoint srcPt;
-        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
-                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkScalar dx, fx = srcPt.fX;
-        SkScalar dy, fy = srcPt.fY;
-
-        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
-            SkFixed fixedX, fixedY;
-            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
-            dx = SkFixedToScalar(fixedX);
-            dy = SkFixedToScalar(fixedY);
-        } else {
-            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-            dx = fDstToIndex.getScaleX();
-            dy = fDstToIndex.getSkewY();
-        }
-        SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
-                     SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
-                     twoPointRadialGradient.fStartRadius) * 2;
-        SkScalar db = (SkScalarMul(twoPointRadialGradient.fDiff.fX, dx) +
-                      SkScalarMul(twoPointRadialGradient.fDiff.fY, dy)) * 2;
-
-        TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
-        if (SkShader::kClamp_TileMode == twoPointRadialGradient.fTileMode) {
-            shadeProc = shadeSpan_twopoint_clamp;
-        } else if (SkShader::kMirror_TileMode == twoPointRadialGradient.fTileMode) {
-            shadeProc = shadeSpan_twopoint_mirror;
-        } else {
-            SkASSERT(SkShader::kRepeat_TileMode == twoPointRadialGradient.fTileMode);
-        }
-        (*shadeProc)(fx, dx, fy, dy, b, db,
-                     twoPointRadialGradient.fSr2D2, foura,
-                     twoPointRadialGradient.fOneOverTwoA, posRoot,
-                     dstC, cache, count);
-    } else {    // perspective case
-        SkScalar dstX = SkIntToScalar(x);
-        SkScalar dstY = SkIntToScalar(y);
-        for (; count > 0; --count) {
-            SkPoint             srcPt;
-            dstProc(fDstToIndex, dstX, dstY, &srcPt);
-            SkScalar fx = srcPt.fX;
-            SkScalar fy = srcPt.fY;
-            SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
-                         SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
-                         twoPointRadialGradient.fStartRadius) * 2;
-            SkFixed t = two_point_radial(b, fx, fy, twoPointRadialGradient.fSr2D2, foura,
-                                         twoPointRadialGradient.fOneOverTwoA, posRoot);
-            SkFixed index = proc(t);
-            SkASSERT(index <= 0xFFFF);
-            *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
-            dstX += SK_Scalar1;
-        }
-    }
-}
-
-#ifndef SK_IGNORE_TO_STRING
-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
-
-SkFlattenable* SkTwoPointRadialGradient::CreateProc(SkReadBuffer& buffer) {
-    DescriptorScope desc;
-    if (!desc.unflatten(buffer)) {
-        return NULL;
-    }
-    const SkPoint c1 = buffer.readPoint();
-    const SkPoint c2 = buffer.readPoint();
-    const SkScalar r1 = buffer.readScalar();
-    const SkScalar r2 = buffer.readScalar();
-    return SkGradientShader::CreateTwoPointRadial(c1, r1, c2, r2, desc.fColors, desc.fPos,
-                                                  desc.fCount, desc.fTileMode, desc.fGradFlags,
-                                                  desc.fLocalMatrix);
-}
-
-void SkTwoPointRadialGradient::flatten(
-    SkWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-    buffer.writePoint(fCenter1);
-    buffer.writePoint(fCenter2);
-    buffer.writeScalar(fRadius1);
-    buffer.writeScalar(fRadius2);
-}
-
-/////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-#include "SkGr.h"
-#include "gl/builders/GrGLProgramBuilder.h"
-
-// For brevity
-typedef GrGLProgramDataManager::UniformHandle UniformHandle;
-
-class GrGLRadial2Gradient : public GrGLGradientEffect {
-
-public:
-
-    GrGLRadial2Gradient(const GrProcessor&);
-    virtual ~GrGLRadial2Gradient() { }
-
-    virtual void emitCode(GrGLFPBuilder*,
-                          const GrFragmentProcessor&,
-                          const char* outputColor,
-                          const char* inputColor,
-                          const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
-
-    static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
-
-protected:
-
-    UniformHandle fParamUni;
-
-    const char* fVSVaryingName;
-    const char* fFSVaryingName;
-
-    bool fIsDegenerate;
-
-    // @{
-    /// Values last uploaded as uniforms
-
-    SkScalar fCachedCenter;
-    SkScalar fCachedRadius;
-    bool     fCachedPosRoot;
-
-    // @}
-
-private:
-
-    typedef GrGLGradientEffect INHERITED;
-
-};
-
-/////////////////////////////////////////////////////////////////////
-
-class GrRadial2Gradient : public GrGradientEffect {
-public:
-    static GrFragmentProcessor* Create(GrContext* ctx,
-                                       const SkTwoPointRadialGradient& shader,
-                                       const SkMatrix& matrix,
-                                       SkShader::TileMode tm) {
-        return SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm));
-    }
-
-    virtual ~GrRadial2Gradient() { }
-
-    const char* name() const SK_OVERRIDE { return "Two-Point Radial Gradient"; }
-
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
-        GrGLRadial2Gradient::GenKey(*this, caps, b);
-    }
-
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
-        return SkNEW_ARGS(GrGLRadial2Gradient, (*this));
-    }
-
-    // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
-    bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
-    SkScalar center() const { return fCenterX1; }
-    SkScalar radius() const { return fRadius0; }
-    bool isPosRoot() const { return SkToBool(fPosRoot); }
-
-private:
-    bool onIsEqual(const GrFragmentProcessor& sBase) const SK_OVERRIDE {
-        const GrRadial2Gradient& s = sBase.cast<GrRadial2Gradient>();
-        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) {
-        this->initClassID<GrRadial2Gradient>();
-        // We pass the linear part of the quadratic as a varying.
-        //    float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
-        fBTransform = this->getCoordTransform();
-        SkMatrix& bMatrix = *fBTransform.accessMatrix();
-        bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
-                                           SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
-        bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
-                                          SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
-        bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
-                                           SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
-        this->addCoordTransform(&fBTransform);
-    }
-
-    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
-
-    // @{
-    // Cache of values - these can change arbitrarily, EXCEPT
-    // we shouldn't change between degenerate and non-degenerate?!
-
-    GrCoordTransform fBTransform;
-    SkScalar         fCenterX1;
-    SkScalar         fRadius0;
-    SkBool8          fPosRoot;
-
-    // @}
-
-    typedef GrGradientEffect INHERITED;
-};
-
-/////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadial2Gradient);
-
-GrFragmentProcessor* GrRadial2Gradient::TestCreate(SkRandom* random,
-                                                   GrContext* context,
-                                                   const GrDrawTargetCaps&,
-                                                   GrTexture**) {
-    SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
-    SkScalar radius1 = random->nextUScalar1();
-    SkPoint center2;
-    SkScalar radius2;
-    do {
-        center2.set(random->nextUScalar1(), random->nextUScalar1());
-        radius2 = random->nextUScalar1 ();
-        // There is a bug in two point radial gradients with identical radii
-    } while (radius1 == radius2);
-
-    SkColor colors[kMaxRandomGradientColors];
-    SkScalar stopsArray[kMaxRandomGradientColors];
-    SkScalar* stops = stopsArray;
-    SkShader::TileMode tm;
-    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
-    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
-                                                                         center2, radius2,
-                                                                         colors, stops, colorCount,
-                                                                         tm));
-    SkPaint paint;
-    GrFragmentProcessor* fp;
-    GrColor paintColor;
-    SkAssertResult(shader->asFragmentProcessor(context, paint,
-                                               GrProcessorUnitTest::TestMatrix(random), NULL,
-                                               &paintColor, &fp));
-    return fp;
-}
-
-/////////////////////////////////////////////////////////////////////
-
-GrGLRadial2Gradient::GrGLRadial2Gradient(const GrProcessor& processor)
-    : fVSVaryingName(NULL)
-    , fFSVaryingName(NULL)
-    , fCachedCenter(SK_ScalarMax)
-    , fCachedRadius(-SK_ScalarMax)
-    , fCachedPosRoot(0) {
-
-    const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
-    fIsDegenerate = data.isDegenerate();
-}
-
-void GrGLRadial2Gradient::emitCode(GrGLFPBuilder* builder,
-                                   const GrFragmentProcessor& fp,
-                                   const char* outputColor,
-                                   const char* inputColor,
-                                   const TransformedCoordsArray& coords,
-                                   const TextureSamplerArray& samplers) {
-    const GrRadial2Gradient& ge = fp.cast<GrRadial2Gradient>();
-    this->emitUniforms(builder, ge);
-    fParamUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility,
-                                         kFloat_GrSLType, kDefault_GrSLPrecision,
-                                         "Radial2FSParams", 6);
-
-    SkString cName("c");
-    SkString ac4Name("ac4");
-    SkString rootName("root");
-    SkString t;
-    SkString p0;
-    SkString p1;
-    SkString p2;
-    SkString p3;
-    SkString p4;
-    SkString p5;
-    builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
-    builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
-    builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
-    builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
-    builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
-    builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
-
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
-    // We interpolate the linear component in coords[1].
-    SkASSERT(coords[0].getType() == coords[1].getType());
-    const char* coords2D;
-    SkString bVar;
-    if (kVec3f_GrSLType == coords[0].getType()) {
-        fsBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
-                               coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
-        coords2D = "interpolants.xy";
-        bVar = "interpolants.z";
-    } else {
-        coords2D = coords[0].c_str();
-        bVar.printf("%s.x", coords[1].c_str());
-    }
-
-    // c = (x^2)+(y^2) - params[4]
-    fsBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
-                           cName.c_str(), coords2D, coords2D, p4.c_str());
-
-    // If we aren't degenerate, emit some extra code, and accept a slightly
-    // more complex coord.
-    if (!fIsDegenerate) {
-
-        // ac4 = 4.0 * params[0] * c
-        fsBuilder->codeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
-                               ac4Name.c_str(), p0.c_str(),
-                               cName.c_str());
-
-        // root = sqrt(b^2-4ac)
-        // (abs to avoid exception due to fp precision)
-        fsBuilder->codeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
-                               rootName.c_str(), bVar.c_str(), bVar.c_str(),
-                               ac4Name.c_str());
-
-        // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
-        t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
-                 rootName.c_str(), p1.c_str());
-    } else {
-        // t is: -c/b
-        t.printf("-%s / %s", cName.c_str(), bVar.c_str());
-    }
-
-    this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
-}
-
-void GrGLRadial2Gradient::setData(const GrGLProgramDataManager& pdman,
-                                  const GrProcessor& processor) {
-    INHERITED::setData(pdman, processor);
-    const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
-    SkASSERT(data.isDegenerate() == fIsDegenerate);
-    SkScalar centerX1 = data.center();
-    SkScalar radius0 = data.radius();
-    if (fCachedCenter != centerX1 ||
-        fCachedRadius != radius0 ||
-        fCachedPosRoot != data.isPosRoot()) {
-
-        SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
-
-        // When we're in the degenerate (linear) case, the second
-        // value will be INF but the program doesn't read it. (We
-        // use the same 6 uniforms even though we don't need them
-        // all in the linear case just to keep the code complexity
-        // down).
-        float values[6] = {
-            SkScalarToFloat(a),
-            1 / (2.f * SkScalarToFloat(a)),
-            SkScalarToFloat(centerX1),
-            SkScalarToFloat(radius0),
-            SkScalarToFloat(SkScalarMul(radius0, radius0)),
-            data.isPosRoot() ? 1.f : -1.f
-        };
-
-        pdman.set1fv(fParamUni, 6, values);
-        fCachedCenter = centerX1;
-        fCachedRadius = radius0;
-        fCachedPosRoot = data.isPosRoot();
-    }
-}
-
-void GrGLRadial2Gradient::GenKey(const GrProcessor& processor,
-                                 const GrGLCaps&, GrProcessorKeyBuilder* b) {
-    uint32_t* key = b->add32n(2);
-    key[0] = GenBaseGradientKey(processor);
-    key[1] = processor.cast<GrRadial2Gradient>().isDegenerate();
-}
-
-/////////////////////////////////////////////////////////////////////
-
-bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
-                                                   const SkMatrix&,
-                                                   const SkMatrix* localMatrix, GrColor* paintColor,
-                                                   GrFragmentProcessor** fp)  const {
-    SkASSERT(context);
-
-    // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
-    SkMatrix matrix;
-    if (!this->getLocalMatrix().invert(&matrix)) {
-        return false;
-    }
-    if (localMatrix) {
-        SkMatrix inv;
-        if (!localMatrix->invert(&inv)) {
-            return false;
-        }
-        matrix.postConcat(inv);
-    }
-    matrix.postConcat(fPtsToUnit);
-
-    SkScalar diffLen = fDiff.length();
-    if (0 != diffLen) {
-        SkScalar invDiffLen = SkScalarInvert(diffLen);
-        SkMatrix rot;
-        rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
-                       SkScalarMul(invDiffLen, fDiff.fX));
-        matrix.postConcat(rot);
-    }
-
-    *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
-    *fp = GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
-
-    return true;
-}
-
-#else
-
-bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&,
-                                                   const SkMatrix*,
-                                                   GrColor*, GrFragmentProcessor**)  const {
-    SkDEBUGFAIL("Should not call in GPU-less build");
-    return false;
-}
-
-#endif
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.h b/src/effects/gradients/SkTwoPointRadialGradient.h
deleted file mode 100644
index ece3858..0000000
--- a/src/effects/gradients/SkTwoPointRadialGradient.h
+++ /dev/null
@@ -1,64 +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 SkTwoPointRadialGradient_DEFINED
- #define SkTwoPointRadialGradient_DEFINED
-
- #include "SkGradientShaderPriv.h"
-
-class SkTwoPointRadialGradient : public SkGradientShaderBase {
-public:
-    SkTwoPointRadialGradient(const SkPoint& start, SkScalar startRadius,
-                             const SkPoint& end, SkScalar endRadius,
-                             const Descriptor&);
-
-    virtual BitmapType asABitmap(SkBitmap* bitmap,
-                                 SkMatrix* matrix,
-                                 TileMode* xy) const SK_OVERRIDE;
-    GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
-    virtual bool asFragmentProcessor(GrContext* context, const SkPaint&, const SkMatrix& viewM,
-                                     const SkMatrix*, GrColor*,
-                                     GrFragmentProcessor**)  const SK_OVERRIDE;
-
-    size_t contextSize() const SK_OVERRIDE;
-
-    class TwoPointRadialGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
-    public:
-        TwoPointRadialGradientContext(const SkTwoPointRadialGradient&, const ContextRec&);
-
-        void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
-
-    private:
-        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
-    };
-
-    SkScalar getCenterX1() const { return fDiff.length(); }
-    SkScalar getStartRadius() const { return fStartRadius; }
-    SkScalar getDiffRadius() const { return fDiffRadius; }
-
-    SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTwoPointRadialGradient)
-
-protected:
-    SkTwoPointRadialGradient(SkReadBuffer& buffer);
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
-    Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
-
-private:
-    const SkPoint fCenter1;
-    const SkPoint fCenter2;
-    const SkScalar fRadius1;
-    const SkScalar fRadius2;
-    SkPoint fDiff;
-    SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
-
-    friend class SkGradientShader;
-    typedef SkGradientShaderBase INHERITED;
-};
-
-#endif
diff --git a/src/fonts/SkFontMgr_fontconfig.cpp b/src/fonts/SkFontMgr_fontconfig.cpp
index 9f43681..9bdc927 100644
--- a/src/fonts/SkFontMgr_fontconfig.cpp
+++ b/src/fonts/SkFontMgr_fontconfig.cpp
@@ -142,10 +142,10 @@
     SkFontStyleSet_FC(FcPattern** matches, int count);
     virtual ~SkFontStyleSet_FC();
 
-    int count() SK_OVERRIDE { return fRecCount; }
-    void getStyle(int index, SkFontStyle*, SkString* style) SK_OVERRIDE;
-    SkTypeface* createTypeface(int index) SK_OVERRIDE;
-    SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE;
+    int count() override { return fRecCount; }
+    void getStyle(int index, SkFontStyle*, SkString* style) override;
+    SkTypeface* createTypeface(int index) override;
+    SkTypeface* matchStyle(const SkFontStyle& pattern) override;
 
 private:
     struct Rec {
@@ -226,19 +226,19 @@
     }
 
 protected:
-    int onCountFamilies() const SK_OVERRIDE {
+    int onCountFamilies() const override {
         return fFamilyNames->count();
     }
 
-    void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(int index, SkString* familyName) const override {
         familyName->set(fFamilyNames->atStr(index));
     }
 
-    SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
+    SkFontStyleSet* onCreateStyleSet(int index) const override {
         return this->onMatchFamily(fFamilyNames->atStr(index));
     }
 
-    SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE {
+    SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
         FCLocker lock;
 
         FcPattern* pattern = FcPatternCreate();
@@ -283,19 +283,19 @@
         return sset;
     }
 
-    virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
-                                           const SkFontStyle&) const SK_OVERRIDE { return NULL; }
-    virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
-                                                    const char* bcp47[], int bcp47Count,
-                                                    SkUnichar character) const SK_OVERRIDE {
+    SkTypeface* onMatchFamilyStyle(const char familyName[],
+                                   const SkFontStyle&) const override { return NULL; }
+    SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
+                                            const char* bcp47[], int bcp47Count,
+                                            SkUnichar character) const override {
         return NULL;
     }
-    virtual SkTypeface* onMatchFaceStyle(const SkTypeface*,
-                                         const SkFontStyle&) const SK_OVERRIDE { return NULL; }
+    SkTypeface* onMatchFaceStyle(const SkTypeface*,
+                                 const SkFontStyle&) const override { return NULL; }
 
-    SkTypeface* onCreateFromData(SkData*, int ttcIndex) const SK_OVERRIDE { return NULL; }
+    SkTypeface* onCreateFromData(SkData*, int ttcIndex) const override { return NULL; }
 
-    SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override {
         SkAutoTDelete<SkStreamAsset> stream(bareStream);
         const size_t length = stream->getLength();
         if (!length) {
@@ -316,13 +316,13 @@
         return face;
     }
 
-    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
         SkAutoTDelete<SkStreamAsset> stream(SkStream::NewFromFile(path));
         return stream.get() ? this->createFromStream(stream.detach(), ttcIndex) : NULL;
     }
 
-    virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
-                                               unsigned styleBits) const SK_OVERRIDE {
+    SkTypeface* onLegacyCreateTypeface(const char familyName[],
+                                       unsigned styleBits) const override {
         FCLocker lock;
         return FontConfigTypeface::LegacyCreateTypeface(familyName, (SkTypeface::Style)styleBits);
     }
diff --git a/src/fonts/SkFontMgr_indirect.cpp b/src/fonts/SkFontMgr_indirect.cpp
index 5615a78..172d541 100644
--- a/src/fonts/SkFontMgr_indirect.cpp
+++ b/src/fonts/SkFontMgr_indirect.cpp
@@ -25,9 +25,9 @@
         : fOwner(SkRef(owner)), fFamilyIndex(familyIndex), fData(data)
     { }
 
-    int count() SK_OVERRIDE { return fData->count(); }
+    int count() override { return fData->count(); }
 
-    void getStyle(int index, SkFontStyle* fs, SkString* style) SK_OVERRIDE {
+    void getStyle(int index, SkFontStyle* fs, SkString* style) override {
         if (fs) {
             *fs = fData->at(index).fFontStyle;
         }
@@ -37,11 +37,11 @@
         }
     }
 
-    SkTypeface* createTypeface(int index) SK_OVERRIDE {
+    SkTypeface* createTypeface(int index) override {
         return fOwner->createTypefaceFromFontId(fData->at(index));
     }
 
-    SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
+    SkTypeface* matchStyle(const SkFontStyle& pattern) override {
         if (fFamilyIndex >= 0) {
             SkFontIdentity id = fOwner->fProxy->matchIndexStyle(fFamilyIndex, pattern);
             return fOwner->createTypefaceFromFontId(id);
diff --git a/src/fonts/SkGScalerContext.cpp b/src/fonts/SkGScalerContext.cpp
index cd5db5f..5787478 100644
--- a/src/fonts/SkGScalerContext.cpp
+++ b/src/fonts/SkGScalerContext.cpp
@@ -16,13 +16,13 @@
     virtual ~SkGScalerContext();
 
 protected:
-    unsigned generateGlyphCount() SK_OVERRIDE;
-    uint16_t generateCharToGlyph(SkUnichar) SK_OVERRIDE;
-    void generateAdvance(SkGlyph*) SK_OVERRIDE;
-    void generateMetrics(SkGlyph*) SK_OVERRIDE;
-    void generateImage(const SkGlyph&) SK_OVERRIDE;
-    void generatePath(const SkGlyph&, SkPath*) SK_OVERRIDE;
-    void generateFontMetrics(SkPaint::FontMetrics*) SK_OVERRIDE;
+    unsigned generateGlyphCount() override;
+    uint16_t generateCharToGlyph(SkUnichar) override;
+    void generateAdvance(SkGlyph*) override;
+    void generateMetrics(SkGlyph*) override;
+    void generateImage(const SkGlyph&) override;
+    void generatePath(const SkGlyph&, SkPath*) override;
+    void generateFontMetrics(SkPaint::FontMetrics*) override;
 
 private:
     SkGTypeface*     fFace;
@@ -178,7 +178,7 @@
 }
 
 SkAdvancedTypefaceMetrics* SkGTypeface::onGetAdvancedTypefaceMetrics(
-                                SkAdvancedTypefaceMetrics::PerGlyphInfo info,
+                                PerGlyphInfo info,
                                 const uint32_t* glyphIDs,
                                 uint32_t glyphIDsCount) const {
     return fProxy->getAdvancedTypefaceMetrics(info, glyphIDs, glyphIDsCount);
diff --git a/src/fonts/SkGScalerContext.h b/src/fonts/SkGScalerContext.h
index 05dc483..75f3ebe 100644
--- a/src/fonts/SkGScalerContext.h
+++ b/src/fonts/SkGScalerContext.h
@@ -20,26 +20,26 @@
     const SkPaint& paint() const { return fPaint; }
 
 protected:
-    SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
-    void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
-    virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                                    SkAdvancedTypefaceMetrics::PerGlyphInfo,
-                                    const uint32_t* glyphIDs,
-                                    uint32_t glyphIDsCount) const SK_OVERRIDE;
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
-    void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const SK_OVERRIDE;
+    SkScalerContext* onCreateScalerContext(const SkDescriptor*) const override;
+    void onFilterRec(SkScalerContextRec*) const override;
+    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
+        PerGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) const override;
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override;
+    void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const override;
 
-    virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
-                                uint16_t glyphs[], int glyphCount) const SK_OVERRIDE;
-    int onCountGlyphs() const SK_OVERRIDE;
-    int onGetUPEM() const SK_OVERRIDE;
+    int onCharsToGlyphs(const void* chars, Encoding encoding,
+                        uint16_t glyphs[], int glyphCount) const override;
+    int onCountGlyphs() const override;
+    int onGetUPEM() const override;
 
-    void onGetFamilyName(SkString* familyName) const SK_OVERRIDE;
-    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE;
+    void onGetFamilyName(SkString* familyName) const override;
+    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
 
-    int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
-    virtual size_t onGetTableData(SkFontTableTag, size_t offset,
-                                  size_t length, void* data) const SK_OVERRIDE;
+    int onGetTableTags(SkFontTableTag tags[]) const override;
+    size_t onGetTableData(SkFontTableTag, size_t offset,
+                          size_t length, void* data) const override;
 
 private:
     SkTypeface* fProxy;
diff --git a/src/fonts/SkTestScalerContext.cpp b/src/fonts/SkTestScalerContext.cpp
index fc1f945..8ff9f43 100644
--- a/src/fonts/SkTestScalerContext.cpp
+++ b/src/fonts/SkTestScalerContext.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkAdvancedTypefaceMetrics.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkDescriptor.h"
@@ -140,7 +141,7 @@
 }
 
 SkAdvancedTypefaceMetrics* SkTestTypeface::onGetAdvancedTypefaceMetrics(
-                                SkAdvancedTypefaceMetrics::PerGlyphInfo ,
+                                PerGlyphInfo ,
                                 const uint32_t* glyphIDs,
                                 uint32_t glyphIDsCount) const {
 // pdf only
@@ -161,7 +162,6 @@
 
 void SkTestTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const {
     desc->setFamilyName(fTestFont->fName);
-    desc->setFontFileName(fTestFont->fName);
     *isLocal = false;
 }
 
@@ -201,32 +201,30 @@
     }
 
 protected:
-    unsigned generateGlyphCount() SK_OVERRIDE {
+    unsigned generateGlyphCount() override {
         return fFace->onCountGlyphs();
     }
 
-    uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE {
+    uint16_t generateCharToGlyph(SkUnichar uni) override {
         uint16_t glyph;
         (void) fFace->onCharsToGlyphs((const void *) &uni, SkTypeface::kUTF16_Encoding, &glyph, 1);
         return glyph;
     }
 
-    void generateAdvance(SkGlyph* glyph) SK_OVERRIDE {
+    void generateAdvance(SkGlyph* glyph) override {
         fFace->getAdvance(glyph);
 
-        SkVector advance;
-        fMatrix.mapXY(SkFixedToScalar(glyph->fAdvanceX),
-                      SkFixedToScalar(glyph->fAdvanceY), &advance);
+        const SkVector advance = fMatrix.mapXY(SkFixedToScalar(glyph->fAdvanceX),
+                                               SkFixedToScalar(glyph->fAdvanceY));
         glyph->fAdvanceX = SkScalarToFixed(advance.fX);
         glyph->fAdvanceY = SkScalarToFixed(advance.fY);
     }
 
-    void generateMetrics(SkGlyph* glyph) SK_OVERRIDE {
+    void generateMetrics(SkGlyph* glyph) override {
         fFace->getMetrics(glyph);
 
-        SkVector advance;
-        fMatrix.mapXY(SkFixedToScalar(glyph->fAdvanceX),
-                      SkFixedToScalar(glyph->fAdvanceY), &advance);
+        const SkVector advance = fMatrix.mapXY(SkFixedToScalar(glyph->fAdvanceX),
+                                               SkFixedToScalar(glyph->fAdvanceY));
         glyph->fAdvanceX = SkScalarToFixed(advance.fX);
         glyph->fAdvanceY = SkScalarToFixed(advance.fY);
 
@@ -248,7 +246,7 @@
         glyph->fMaskFormat = SkMask::kARGB32_Format;
     }
 
-    void generateImage(const SkGlyph& glyph) SK_OVERRIDE {
+    void generateImage(const SkGlyph& glyph) override {
         SkPath path;
         fFace->getPath(glyph, &path);
 
@@ -266,12 +264,12 @@
         canvas.drawPath(path, paint);
     }
 
-    void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE {
+    void generatePath(const SkGlyph& glyph, SkPath* path) override {
         fFace->getPath(glyph, path);
         path->transform(fMatrix);
     }
 
-    void generateFontMetrics(SkPaint::FontMetrics* metrics) SK_OVERRIDE {
+    void generateFontMetrics(SkPaint::FontMetrics* metrics) override {
         fFace->getFontMetrics(metrics);
         if (metrics) {
             SkScalar scale = fMatrix.getScaleY();
diff --git a/src/fonts/SkTestScalerContext.h b/src/fonts/SkTestScalerContext.h
index 49e9b77..1d4b89a 100644
--- a/src/fonts/SkTestScalerContext.h
+++ b/src/fonts/SkTestScalerContext.h
@@ -66,41 +66,41 @@
     void getMetrics(SkGlyph* glyph);
     void getPath(const SkGlyph& glyph, SkPath* path);
 protected:
-    SkScalerContext* onCreateScalerContext(const SkDescriptor* desc) const SK_OVERRIDE;
-    void onFilterRec(SkScalerContextRec* rec) const SK_OVERRIDE;
-    virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                                    SkAdvancedTypefaceMetrics::PerGlyphInfo ,
-                                    const uint32_t* glyphIDs,
-                                    uint32_t glyphIDsCount) const SK_OVERRIDE;
+    SkScalerContext* onCreateScalerContext(const SkDescriptor* desc) const override;
+    void onFilterRec(SkScalerContextRec* rec) const override;
+    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
+        PerGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) const override;
 
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override {
         SkASSERT(0);  // don't expect to get here
         return NULL;
     }
 
-    void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const SK_OVERRIDE;
+    void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override;
 
-    virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
-                                uint16_t glyphs[], int glyphCount) const SK_OVERRIDE;
+    int onCharsToGlyphs(const void* chars, Encoding encoding,
+                        uint16_t glyphs[], int glyphCount) const override;
 
-    int onCountGlyphs() const SK_OVERRIDE {
+    int onCountGlyphs() const override {
         return (int) fTestFont->fCharCodesCount;
     }
 
-    int onGetUPEM() const SK_OVERRIDE {
+    int onGetUPEM() const override {
         SkASSERT(0);  // don't expect to get here
         return 1;
     }
 
-    void onGetFamilyName(SkString* familyName) const SK_OVERRIDE;
-    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE;
+    void onGetFamilyName(SkString* familyName) const override;
+    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
 
-    int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE {
+    int onGetTableTags(SkFontTableTag tags[]) const override {
         return 0;
     }
 
-    virtual size_t onGetTableData(SkFontTableTag tag, size_t offset,
-                                  size_t length, void* data) const SK_OVERRIDE {
+    size_t onGetTableData(SkFontTableTag tag, size_t offset,
+                          size_t length, void* data) const override {
         return 0;
     }
 private:
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index 317f9a0..0d909b1 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -8,19 +8,21 @@
 
 #include "GrAAConvexPathRenderer.h"
 
+#include "GrAAConvexTessellator.h"
 #include "GrBatch.h"
 #include "GrBatchTarget.h"
-#include "GrBufferAllocPool.h"
+#include "GrBatchTest.h"
 #include "GrContext.h"
+#include "GrDefaultGeoProcFactory.h"
 #include "GrDrawTargetCaps.h"
 #include "GrGeometryProcessor.h"
 #include "GrInvariantOutput.h"
 #include "GrPathUtils.h"
 #include "GrProcessor.h"
 #include "GrPipelineBuilder.h"
+#include "GrStrokeInfo.h"
 #include "SkGeometry.h"
 #include "SkString.h"
-#include "SkStrokeRec.h"
 #include "SkTraceEvent.h"
 #include "gl/GrGLProcessor.h"
 #include "gl/GrGLSL.h"
@@ -103,7 +105,7 @@
         *c = avg;
     } else {
         area *= 3;
-        area = SkScalarDiv(SK_Scalar1, area);
+        area = SkScalarInvert(area);
         center.fX = SkScalarMul(center.fX, area);
         center.fY = SkScalarMul(center.fY, area);
         // undo the translate of p0 to the origin.
@@ -425,20 +427,28 @@
             verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
             verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
 
-            idxs[*i + 0] = *v + 0;
-            idxs[*i + 1] = *v + 2;
-            idxs[*i + 2] = *v + 1;
+            idxs[*i + 0] = *v + 3;
+            idxs[*i + 1] = *v + 1;
+            idxs[*i + 2] = *v + 2;
 
-            idxs[*i + 3] = *v + 3;
-            idxs[*i + 4] = *v + 1;
+            idxs[*i + 3] = *v + 4;
+            idxs[*i + 4] = *v + 3;
             idxs[*i + 5] = *v + 2;
 
-            idxs[*i + 6] = *v + 4;
-            idxs[*i + 7] = *v + 3;
-            idxs[*i + 8] = *v + 2;
+            *i += 6;
+
+            // Draw the interior fan if it exists.
+            // TODO: Detect and combine colinear segments. This will ensure we catch every case
+            // with no interior, and that the resulting shared edge uses the same endpoints.
+            if (count >= 3) {
+                idxs[*i + 0] = *v + 0;
+                idxs[*i + 1] = *v + 2;
+                idxs[*i + 2] = *v + 1;
+
+                *i += 3;
+            }
 
             *v += 5;
-            *i += 9;
         } else {
             SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
 
@@ -482,12 +492,20 @@
             idxs[*i + 7] = *v + 3;
             idxs[*i + 8] = *v + 4;
 
-            idxs[*i +  9] = *v + 0;
-            idxs[*i + 10] = *v + 2;
-            idxs[*i + 11] = *v + 1;
+            *i += 9;
+
+            // Draw the interior fan if it exists.
+            // TODO: Detect and combine colinear segments. This will ensure we catch every case
+            // with no interior, and that the resulting shared edge uses the same endpoints.
+            if (count >= 3) {
+                idxs[*i + 0] = *v + 0;
+                idxs[*i + 1] = *v + 2;
+                idxs[*i + 2] = *v + 1;
+
+                *i += 3;
+            }
 
             *v += 6;
-            *i += 12;
         }
     }
 }
@@ -513,10 +531,12 @@
 
     virtual ~QuadEdgeEffect() {}
 
-    const char* name() const SK_OVERRIDE { return "QuadEdge"; }
+    const char* name() const override { return "QuadEdge"; }
 
     const Attribute* inPosition() const { return fInPosition; }
     const Attribute* inQuadEdge() const { return fInQuadEdge; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& localMatrix() const { return fLocalMatrix; }
 
     class GLProcessor : public GrGLGeometryProcessor {
     public:
@@ -524,7 +544,7 @@
                     const GrBatchTracker&)
             : fColor(GrColor_ILLEGAL) {}
 
-        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE {
+        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
             const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
             GrGLGPBuilder* pb = args.fPB;
             GrGLVertexBuilder* vsBuilder = pb->getVertexShaderBuilder();
@@ -543,13 +563,13 @@
                                         &fColorUniform);
 
             // Setup position
-            this->setupPosition(pb, gpArgs, qe.inPosition()->fName, qe.viewMatrix());
+            this->setupPosition(pb, gpArgs, qe.inPosition()->fName);
 
             // emit transforms
             this->emitTransforms(args.fPB, gpArgs->fPositionVar, qe.inPosition()->fName,
                                  qe.localMatrix(), args.fTransformsIn, args.fTransformsOut);
 
-            GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+            GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
 
             SkAssertResult(fsBuilder->enableFeature(
                     GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
@@ -576,20 +596,18 @@
 
         static inline void GenKey(const GrGeometryProcessor& gp,
                                   const GrBatchTracker& bt,
-                                  const GrGLCaps&,
+                                  const GrGLSLCaps&,
                                   GrProcessorKeyBuilder* b) {
             const BatchTracker& local = bt.cast<BatchTracker>();
+            const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
             uint32_t key = local.fInputColorType << 16;
-            key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 : 0x0;
-            key |= ComputePosKey(gp.viewMatrix()) << 1;
+            key |= local.fUsesLocalCoords && qee.localMatrix().hasPerspective() ? 0x1 : 0x0;
             b->add32(key);
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
                              const GrPrimitiveProcessor& gp,
-                             const GrBatchTracker& bt) SK_OVERRIDE {
-            this->setUniformViewMatrix(pdman, gp.viewMatrix());
-
+                             const GrBatchTracker& bt) override {
             const BatchTracker& local = bt.cast<BatchTracker>();
             if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
                 GrGLfloat c[4];
@@ -599,6 +617,13 @@
             }
         }
 
+        void setTransformData(const GrPrimitiveProcessor& primProc,
+                              const GrGLProgramDataManager& pdman,
+                              int index,
+                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
+            this->setTransformDataHelper<QuadEdgeEffect>(primProc, pdman, index, transforms);
+        }
+
     private:
         GrColor fColor;
         UniformHandle fColorUniform;
@@ -607,49 +632,31 @@
     };
 
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GLProcessor::GenKey(*this, bt, caps, b);
     }
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE {
+                                                     const GrGLSLCaps&) const override {
         return SkNEW_ARGS(GLProcessor, (*this, bt));
     }
 
-    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
+    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
         BatchTracker* local = bt->cast<BatchTracker>();
         local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
         local->fUsesLocalCoords = init.fUsesLocalCoords;
     }
 
-    bool onCanMakeEqual(const GrBatchTracker& m,
-                        const GrGeometryProcessor& that,
-                        const GrBatchTracker& t) const SK_OVERRIDE {
-        const BatchTracker& mine = m.cast<BatchTracker>();
-        const BatchTracker& theirs = t.cast<BatchTracker>();
-        return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                       that, theirs.fUsesLocalCoords) &&
-               CanCombineOutput(mine.fInputColorType, mine.fColor,
-                                theirs.fInputColorType, theirs.fColor);
-    }
-
 private:
     QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix)
-        : INHERITED(color, SkMatrix::I(), localMatrix) {
+        : fColor(color)
+        , fLocalMatrix(localMatrix) {
         this->initClassID<QuadEdgeEffect>();
         fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
         fInQuadEdge = &this->addVertexAttrib(Attribute("inQuadEdge", kVec4f_GrVertexAttribType));
     }
 
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
-        return true;
-    }
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
-        out->setUnknownSingleComponent();
-    }
-
     struct BatchTracker {
         GrGPInput fInputColorType;
         GrColor fColor;
@@ -658,6 +665,8 @@
 
     const Attribute* fInPosition;
     const Attribute* fInQuadEdge;
+    GrColor          fColor;
+    SkMatrix         fLocalMatrix;
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
 
@@ -671,9 +680,9 @@
                                                 const GrDrawTargetCaps& caps,
                                                 GrTexture*[]) {
     // Doesn't work without derivative instructions.
-    return caps.shaderDerivativeSupport() ?
+    return caps.shaderCaps()->shaderDerivativeSupport() ?
            QuadEdgeEffect::Create(GrRandomColor(random),
-                                  GrProcessorUnitTest::TestMatrix(random)) : NULL;
+                                  GrTest::TestMatrix(random)) : NULL;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -682,12 +691,56 @@
                                          const GrPipelineBuilder*,
                                          const SkMatrix& viewMatrix,
                                          const SkPath& path,
-                                         const SkStrokeRec& stroke,
+                                         const GrStrokeInfo& stroke,
                                          bool antiAlias) const {
-    return (target->caps()->shaderDerivativeSupport() && antiAlias &&
+    return (target->caps()->shaderCaps()->shaderDerivativeSupport() && antiAlias &&
             stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
 }
 
+// extract the result vertices and indices from the GrAAConvexTessellator
+static void extract_verts(const GrAAConvexTessellator& tess,
+                          void* vertices,
+                          size_t vertexStride,
+                          GrColor color,
+                          uint16_t* idxs,
+                          bool tweakAlphaForCoverage) {
+    intptr_t verts = reinterpret_cast<intptr_t>(vertices);
+
+    for (int i = 0; i < tess.numPts(); ++i) {
+        *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
+    }
+
+    // Make 'verts' point to the colors
+    verts += sizeof(SkPoint);
+    for (int i = 0; i < tess.numPts(); ++i) {
+        SkASSERT(tess.depth(i) >= -0.5f && tess.depth(i) <= 0.5f);
+        if (tweakAlphaForCoverage) {
+            SkASSERT(SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f)) <= 255);
+            unsigned scale = SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f));
+            GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
+            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
+        } else {
+            *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
+            *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 
+                                                                    tess.depth(i) + 0.5f;
+        }
+    }
+
+    for (int i = 0; i < tess.numIndices(); ++i) {
+        idxs[i] = tess.index(i);
+    }
+}
+
+static const GrGeometryProcessor* create_fill_gp(bool tweakAlphaForCoverage,
+                                                 const SkMatrix& localMatrix) {
+    uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
+    if (!tweakAlphaForCoverage) {
+        flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
+    }
+
+    return GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix);
+}
+
 class AAConvexPathBatch : public GrBatch {
 public:
     struct Geometry {
@@ -700,17 +753,17 @@
         return SkNEW_ARGS(AAConvexPathBatch, (geometry));
     }
 
-    const char* name() const SK_OVERRIDE { return "AAConvexBatch"; }
+    const char* name() const override { return "AAConvexBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -723,9 +776,91 @@
         fBatch.fColor = fGeoData[0].fColor;
         fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
         fBatch.fCoverageIgnored = init.fCoverageIgnored;
+        fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSegmentMasks();
+        fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometryLinesOnly(GrBatchTarget* batchTarget, const GrPipeline* pipeline) {
+        bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
+
+        SkMatrix invert;
+        if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
+            SkDebugf("Could not invert viewmatrix\n");
+            return;
+        }
+
+        // Setup GrGeometryProcessor
+        SkAutoTUnref<const GrGeometryProcessor> gp(
+                                                create_fill_gp(canTweakAlphaForCoverage, invert));
+
+        batchTarget->initDraw(gp, pipeline);
+
+        // TODO remove this when batch is everywhere
+        GrPipelineInfo init;
+        init.fColorIgnored = fBatch.fColorIgnored;
+        init.fOverrideColor = GrColor_ILLEGAL;
+        init.fCoverageIgnored = fBatch.fCoverageIgnored;
+        init.fUsesLocalCoords = this->usesLocalCoords();
+        gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
+
+        size_t vertexStride = gp->getVertexStride();
+
+        SkASSERT(canTweakAlphaForCoverage ?
+                 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
+                 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
+
+        GrAAConvexTessellator tess;
+
+        int instanceCount = fGeoData.count();
+
+        for (int i = 0; i < instanceCount; i++) {
+            tess.rewind();
+
+            Geometry& args = fGeoData[i];
+
+            if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
+                continue;
+            }
+
+            const GrVertexBuffer* vertexBuffer;
+            int firstVertex;
+
+            void* verts = batchTarget->makeVertSpace(vertexStride, tess.numPts(),
+                                                     &vertexBuffer, &firstVertex);
+            if (!verts) {
+                SkDebugf("Could not allocate vertices\n");
+                return;
+            }
+
+            const GrIndexBuffer* indexBuffer;
+            int firstIndex;
+
+            uint16_t* idxs = batchTarget->makeIndexSpace(tess.numIndices(),
+                                                        &indexBuffer, &firstIndex);
+            if (!idxs) {
+                SkDebugf("Could not allocate indices\n");
+                return;
+            }
+
+            extract_verts(tess, verts, vertexStride, args.fColor, idxs, canTweakAlphaForCoverage);
+
+            GrVertices info;
+            info.initIndexed(kTriangles_GrPrimitiveType,
+                             vertexBuffer, indexBuffer,
+                             firstVertex, firstIndex,
+                             tess.numPts(), tess.numIndices());
+            batchTarget->draw(info);
+        }
+    }
+
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
+#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
+        if (this->linesOnly()) {
+            this->generateGeometryLinesOnly(batchTarget, pipeline);
+            return;
+        }
+#endif
+
         int instanceCount = fGeoData.count();
 
         SkMatrix invert;
@@ -779,12 +914,10 @@
             int firstVertex;
 
             size_t vertexStride = quadProcessor->getVertexStride();
-            void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                                  vertexCount,
-                                                                  &vertexBuffer,
-                                                                  &firstVertex);
+            QuadVertex* verts = reinterpret_cast<QuadVertex*>(batchTarget->makeVertSpace(
+                vertexStride, vertexCount, &vertexBuffer, &firstVertex));
 
-            if (!vertices) {
+            if (!verts) {
                 SkDebugf("Could not allocate vertices\n");
                 return;
             }
@@ -792,35 +925,24 @@
             const GrIndexBuffer* indexBuffer;
             int firstIndex;
 
-            void *indices = batchTarget->indexPool()->makeSpace(indexCount,
-                                                                &indexBuffer,
-                                                                &firstIndex);
-
-            if (!indices) {
+            uint16_t *idxs = batchTarget->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
+            if (!idxs) {
                 SkDebugf("Could not allocate indices\n");
                 return;
             }
 
-            QuadVertex* verts = reinterpret_cast<QuadVertex*>(vertices);
-            uint16_t* idxs = reinterpret_cast<uint16_t*>(indices);
-
             SkSTArray<kPreallocDrawCnt, Draw, true> draws;
             create_vertices(segments, fanPt, &draws, verts, idxs);
 
-            GrDrawTarget::DrawInfo info;
-            info.setVertexBuffer(vertexBuffer);
-            info.setIndexBuffer(indexBuffer);
-            info.setPrimitiveType(kTriangles_GrPrimitiveType);
-            info.setStartIndex(firstIndex);
+            GrVertices vertices;
 
-            int vOffset = 0;
             for (int i = 0; i < draws.count(); ++i) {
                 const Draw& draw = draws[i];
-                info.setStartVertex(vOffset + firstVertex);
-                info.setVertexCount(draw.fVertexCnt);
-                info.setIndexCount(draw.fIndexCnt);
-                batchTarget->draw(info);
-                vOffset += draw.fVertexCnt;
+                vertices.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer,
+                                     firstVertex, firstIndex, draw.fVertexCnt, draw.fIndexCnt);
+                batchTarget->draw(vertices);
+                firstVertex += draw.fVertexCnt;
+                firstIndex += draw.fIndexCnt;
             }
         }
     }
@@ -831,9 +953,13 @@
     AAConvexPathBatch(const Geometry& geometry) {
         this->initClassID<AAConvexPathBatch>();
         fGeoData.push_back(geometry);
+
+        // compute bounds
+        fBounds = geometry.fPath.getBounds();
+        geometry.fViewMatrix.mapRect(&fBounds);
     }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         AAConvexPathBatch* that = t->cast<AAConvexPathBatch>();
 
         if (this->color() != that->color()) {
@@ -845,12 +971,25 @@
             return false;
         }
 
+        if (this->linesOnly() != that->linesOnly()) {
+            return false;
+        }
+
+        // In the event of two batches, one who can tweak, one who cannot, we just fall back to
+        // not tweaking
+        if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
+            fBatch.fCanTweakAlphaForCoverage = false;
+        }
+
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
         return true;
     }
 
     GrColor color() const { return fBatch.fColor; }
+    bool linesOnly() const { return fBatch.fLinesOnly; }
     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+    bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
 
     struct BatchTracker {
@@ -858,6 +997,8 @@
         bool fUsesLocalCoords;
         bool fColorIgnored;
         bool fCoverageIgnored;
+        bool fLinesOnly;
+        bool fCanTweakAlphaForCoverage;
     };
 
     BatchTracker fBatch;
@@ -869,26 +1010,35 @@
                                         GrColor color,
                                         const SkMatrix& vm,
                                         const SkPath& path,
-                                        const SkStrokeRec&,
+                                        const GrStrokeInfo&,
                                         bool antiAlias) {
     if (path.isEmpty()) {
         return true;
     }
 
-    // We outset our vertices one pixel and add one more pixel for precision.
-    // TODO create tighter bounds when we start reordering.
-    SkRect devRect = path.getBounds();
-    vm.mapRect(&devRect);
-    devRect.outset(2, 2);
-
     AAConvexPathBatch::Geometry geometry;
     geometry.fColor = color;
     geometry.fViewMatrix = vm;
     geometry.fPath = path;
 
     SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry));
-    target->drawBatch(pipelineBuilder, batch, &devRect);
+    target->drawBatch(pipelineBuilder, batch);
 
     return true;
 
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef GR_TEST_UTILS
+
+BATCH_TEST_DEFINE(AAConvexPathBatch) {
+    AAConvexPathBatch::Geometry geometry;
+    geometry.fColor = GrRandomColor(random);
+    geometry.fViewMatrix = GrTest::TestMatrixInvertible(random);
+    geometry.fPath = GrTest::TestPathConvex(random);
+
+    return AAConvexPathBatch::Create(geometry);
+}
+
+#endif
diff --git a/src/gpu/GrAAConvexPathRenderer.h b/src/gpu/GrAAConvexPathRenderer.h
index bb2d3f0..3abf1ab 100644
--- a/src/gpu/GrAAConvexPathRenderer.h
+++ b/src/gpu/GrAAConvexPathRenderer.h
@@ -19,8 +19,8 @@
                              const GrPipelineBuilder*,
                              const SkMatrix& viewMatrix,
                              const SkPath&,
-                             const SkStrokeRec&,
-                             bool antiAlias) const SK_OVERRIDE;
+                             const GrStrokeInfo&,
+                             bool antiAlias) const override;
 
 protected:
     virtual bool onDrawPath(GrDrawTarget*,
@@ -28,8 +28,8 @@
                             GrColor,
                             const SkMatrix& viewMatrix,
                             const SkPath&,
-                            const SkStrokeRec&,
-                            bool antiAlias) SK_OVERRIDE;
+                            const GrStrokeInfo&,
+                            bool antiAlias) override;
 };
 
 #endif
diff --git a/src/gpu/GrAAConvexTessellator.cpp b/src/gpu/GrAAConvexTessellator.cpp
new file mode 100644
index 0000000..b2269c5
--- /dev/null
+++ b/src/gpu/GrAAConvexTessellator.cpp
@@ -0,0 +1,874 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrAAConvexTessellator.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkString.h"
+
+// Next steps:
+//  use in AAConvexPathRenderer
+//  add an interactive sample app slide
+//  add debug check that all points are suitably far apart
+//  test more degenerate cases
+
+// The tolerance for fusing vertices and eliminating colinear lines (It is in device space).
+static const SkScalar kClose = (SK_Scalar1 / 16);
+static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose);
+
+static SkScalar intersect(const SkPoint& p0, const SkPoint& n0,
+                          const SkPoint& p1, const SkPoint& n1) {
+    const SkPoint v = p1 - p0;
+
+    SkScalar perpDot = n0.fX * n1.fY - n0.fY * n1.fX;
+    return (v.fX * n1.fY - v.fY * n1.fX) / perpDot;
+}
+
+// This is a special case version of intersect where we have the vector 
+// perpendicular to the second line rather than the vector parallel to it.
+static SkScalar perp_intersect(const SkPoint& p0, const SkPoint& n0,
+                               const SkPoint& p1, const SkPoint& perp) {
+    const SkPoint v = p1 - p0;
+    SkScalar perpDot = n0.dot(perp);
+    return v.dot(perp) / perpDot;
+}
+
+static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
+    SkScalar distSq = p0.distanceToSqd(p1);
+    return distSq < kCloseSqd;
+}
+
+static SkScalar abs_dist_from_line(const SkPoint& p0, const SkVector& v, const SkPoint& test) {
+    SkPoint testV = test - p0;
+    SkScalar dist = testV.fX * v.fY - testV.fY * v.fX;
+    return SkScalarAbs(dist);
+}
+
+int GrAAConvexTessellator::addPt(const SkPoint& pt,
+                                 SkScalar depth,
+                                 bool movable) {
+    this->validate();
+
+    int index = fPts.count();
+    *fPts.push() = pt;
+    *fDepths.push() = depth;
+    *fMovable.push() = movable;
+
+    this->validate();
+    return index;
+}
+
+void GrAAConvexTessellator::popLastPt() {
+    this->validate();
+
+    fPts.pop();
+    fDepths.pop();
+    fMovable.pop();
+
+    this->validate();
+}
+
+void GrAAConvexTessellator::popFirstPtShuffle() {
+    this->validate();
+
+    fPts.removeShuffle(0);
+    fDepths.removeShuffle(0);
+    fMovable.removeShuffle(0);
+
+    this->validate();
+}
+
+void GrAAConvexTessellator::updatePt(int index,
+                                     const SkPoint& pt,
+                                     SkScalar depth) {
+    this->validate();
+    SkASSERT(fMovable[index]);
+
+    fPts[index] = pt;
+    fDepths[index] = depth;
+}
+
+void GrAAConvexTessellator::addTri(int i0, int i1, int i2) {
+    if (i0 == i1 || i1 == i2 || i2 == i0) {
+        return;
+    }
+
+    *fIndices.push() = i0;
+    *fIndices.push() = i1;
+    *fIndices.push() = i2;
+}
+
+void GrAAConvexTessellator::rewind() {
+    fPts.rewind();
+    fDepths.rewind();
+    fMovable.rewind();
+    fIndices.rewind();
+    fNorms.rewind();
+    fInitialRing.rewind();
+    fCandidateVerts.rewind();
+#if GR_AA_CONVEX_TESSELLATOR_VIZ
+    fRings.rewind();        // TODO: leak in this case!
+#else
+    fRings[0].rewind();
+    fRings[1].rewind();
+#endif
+}
+
+void GrAAConvexTessellator::computeBisectors() {
+    fBisectors.setCount(fNorms.count());
+
+    int prev = fBisectors.count() - 1;
+    for (int cur = 0; cur < fBisectors.count(); prev = cur, ++cur) {
+        fBisectors[cur] = fNorms[cur] + fNorms[prev];
+        fBisectors[cur].normalize();
+        fBisectors[cur].negate();      // make the bisector face in
+
+        SkASSERT(SkScalarNearlyEqual(1.0f, fBisectors[cur].length()));
+    }
+}
+
+// The general idea here is to, conceptually, start with the original polygon and slide
+// the vertices along the bisectors until the first intersection. At that
+// point two of the edges collapse and the process repeats on the new polygon.
+// The polygon state is captured in the Ring class while the GrAAConvexTessellator
+// controls the iteration. The CandidateVerts holds the formative points for the
+// next ring.
+bool GrAAConvexTessellator::tessellate(const SkMatrix& m, const SkPath& path) {
+    static const int kMaxNumRings = 8;
+
+    SkDEBUGCODE(fShouldCheckDepths = true;)
+
+    if (!this->extractFromPath(m, path)) {
+        return false;
+    }
+
+    this->createOuterRing();
+
+    // the bisectors are only needed for the computation of the outer ring
+    fBisectors.rewind();
+
+    Ring* lastRing = &fInitialRing;
+    int i;
+    for (i = 0; i < kMaxNumRings; ++i) {
+        Ring* nextRing = this->getNextRing(lastRing);
+
+        if (this->createInsetRing(*lastRing, nextRing)) {
+            break;
+        }
+
+        nextRing->init(*this);
+        lastRing = nextRing;
+    }
+
+    if (kMaxNumRings == i) {
+        // If we've exceeded the amount of time we want to throw at this, set
+        // the depth of all points in the final ring to 'fTargetDepth' and
+        // create a fan.
+        this->terminate(*lastRing);
+        SkDEBUGCODE(fShouldCheckDepths = false;)
+    }
+
+#ifdef SK_DEBUG
+    this->validate();
+    if (fShouldCheckDepths) {
+        SkDEBUGCODE(this->checkAllDepths();)
+    }
+#endif
+    return true;
+}
+
+SkScalar GrAAConvexTessellator::computeDepthFromEdge(int edgeIdx, const SkPoint& p) const {
+    SkASSERT(edgeIdx < fNorms.count());
+
+    SkPoint v = p - fPts[edgeIdx];
+    SkScalar depth = -fNorms[edgeIdx].dot(v);
+    SkASSERT(depth >= 0.0f);
+    return depth;
+}
+
+// Find a point that is 'desiredDepth' away from the 'edgeIdx'-th edge and lies
+// along the 'bisector' from the 'startIdx'-th point.
+bool GrAAConvexTessellator::computePtAlongBisector(int startIdx,
+                                                   const SkVector& bisector,
+                                                   int edgeIdx,
+                                                   SkScalar desiredDepth,
+                                                   SkPoint* result) const {
+    const SkPoint& norm = fNorms[edgeIdx];
+
+    // First find the point where the edge and the bisector intersect
+    SkPoint newP;
+    SkScalar t = perp_intersect(fPts[startIdx], bisector, fPts[edgeIdx], norm);
+    if (SkScalarNearlyEqual(t, 0.0f)) {
+        // the start point was one of the original ring points
+        SkASSERT(startIdx < fNorms.count());
+        newP = fPts[startIdx];
+    } else if (t > 0.0f) {
+        SkASSERT(t < 0.0f);
+        newP = bisector;
+        newP.scale(t);
+        newP += fPts[startIdx];
+    } else {
+        return false;
+    }
+
+    // Then offset along the bisector from that point the correct distance
+    t = -desiredDepth / bisector.dot(norm);
+    SkASSERT(t > 0.0f);
+    *result = bisector;
+    result->scale(t);
+    *result += newP;
+
+
+    return true;
+}
+
+bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& path) {
+    SkASSERT(SkPath::kLine_SegmentMask == path.getSegmentMasks());
+    SkASSERT(SkPath::kConvex_Convexity == path.getConvexity());
+
+    // Outer ring: 3*numPts
+    // Middle ring: numPts
+    // Presumptive inner ring: numPts
+    this->reservePts(5*path.countPoints());
+    // Outer ring: 12*numPts
+    // Middle ring: 0
+    // Presumptive inner ring: 6*numPts + 6
+    fIndices.setReserve(18*path.countPoints() + 6);
+
+    fNorms.setReserve(path.countPoints());
+
+    SkScalar minCross = SK_ScalarMax, maxCross = -SK_ScalarMax;
+
+    // TODO: is there a faster way to extract the points from the path? Perhaps
+    // get all the points via a new entry point, transform them all in bulk
+    // and then walk them to find duplicates?
+    SkPath::Iter iter(path, true);
+    SkPoint pts[4];
+    SkPath::Verb verb;
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kLine_Verb:
+                m.mapPoints(&pts[1], 1);
+                if (this->numPts() > 0 && duplicate_pt(pts[1], this->lastPoint())) {
+                    continue;
+                }
+
+                SkASSERT(fPts.count() <= 1 || fPts.count() == fNorms.count()+1);
+                if (this->numPts() >= 2 && 
+                    abs_dist_from_line(fPts.top(), fNorms.top(), pts[1]) < kClose) {
+                    // The old last point is on the line from the second to last to the new point
+                    this->popLastPt();
+                    fNorms.pop();
+                }
+
+                this->addPt(pts[1], 0.0f, false);
+                if (this->numPts() > 1) {
+                    *fNorms.push() = fPts.top() - fPts[fPts.count()-2];
+                    SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms.top());
+                    SkASSERT(len > 0.0f);
+                    SkASSERT(SkScalarNearlyEqual(1.0f, fNorms.top().length()));
+                }
+
+                if (this->numPts() >= 3) {
+                    int cur = this->numPts()-1;
+                    SkScalar cross = SkPoint::CrossProduct(fNorms[cur-1], fNorms[cur-2]);
+                    maxCross = SkTMax(maxCross, cross);
+                    minCross = SkTMin(minCross, cross);
+                }
+                break;
+            case SkPath::kQuad_Verb:
+            case SkPath::kConic_Verb:
+            case SkPath::kCubic_Verb:
+                SkASSERT(false);
+                break;
+            case SkPath::kMove_Verb:
+            case SkPath::kClose_Verb:
+            case SkPath::kDone_Verb:
+                break;
+        }
+    }
+
+    if (this->numPts() < 3) {
+        return false;
+    }
+
+    // check if last point is a duplicate of the first point. If so, remove it.
+    if (duplicate_pt(fPts[this->numPts()-1], fPts[0])) {
+        this->popLastPt();
+        fNorms.pop();
+    }
+
+    SkASSERT(fPts.count() == fNorms.count()+1);
+    if (this->numPts() >= 3 &&
+        abs_dist_from_line(fPts.top(), fNorms.top(), fPts[0]) < kClose) {
+        // The last point is on the line from the second to last to the first point.
+        this->popLastPt();
+        fNorms.pop();
+    }
+
+    if (this->numPts() < 3) {
+        return false;
+    }
+
+    *fNorms.push() = fPts[0] - fPts.top();
+    SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms.top());
+    SkASSERT(len > 0.0f);
+    SkASSERT(fPts.count() == fNorms.count());
+
+    if (abs_dist_from_line(fPts[0], fNorms.top(), fPts[1]) < kClose) {
+        // The first point is on the line from the last to the second.
+        this->popFirstPtShuffle();
+        fNorms.removeShuffle(0);
+        fNorms[0] = fPts[1] - fPts[0];
+        SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms[0]);
+        SkASSERT(len > 0.0f);
+        SkASSERT(SkScalarNearlyEqual(1.0f, fNorms[0].length()));
+    }
+
+    if (this->numPts() < 3) {
+        return false;
+    }
+
+    // Check the cross produce of the final trio
+    SkScalar cross = SkPoint::CrossProduct(fNorms[0], fNorms.top());
+    maxCross = SkTMax(maxCross, cross);
+    minCross = SkTMin(minCross, cross);
+
+    if (maxCross > 0.0f) {
+        SkASSERT(minCross >= 0.0f);
+        fSide = SkPoint::kRight_Side;
+    } else {
+        SkASSERT(minCross <= 0.0f);
+        fSide = SkPoint::kLeft_Side;
+    }
+
+    // Make all the normals face outwards rather than along the edge
+    for (int cur = 0; cur < fNorms.count(); ++cur) {
+        fNorms[cur].setOrthog(fNorms[cur], fSide);
+        SkASSERT(SkScalarNearlyEqual(1.0f, fNorms[cur].length()));
+    }
+
+    this->computeBisectors();
+
+    fCandidateVerts.setReserve(this->numPts());
+    fInitialRing.setReserve(this->numPts());
+    for (int i = 0; i < this->numPts(); ++i) {
+        fInitialRing.addIdx(i, i);
+    }
+    fInitialRing.init(fNorms, fBisectors);
+
+    this->validate();
+    return true;
+}
+
+GrAAConvexTessellator::Ring* GrAAConvexTessellator::getNextRing(Ring* lastRing) {
+#if GR_AA_CONVEX_TESSELLATOR_VIZ
+    Ring* ring = *fRings.push() = SkNEW(Ring);
+    ring->setReserve(fInitialRing.numPts());
+    ring->rewind();
+    return ring;
+#else
+    // Flip flop back and forth between fRings[0] & fRings[1]
+    int nextRing = (lastRing == &fRings[0]) ? 1 : 0;
+    fRings[nextRing].setReserve(fInitialRing.numPts());
+    fRings[nextRing].rewind();
+    return &fRings[nextRing];
+#endif
+}
+
+void GrAAConvexTessellator::fanRing(const Ring& ring) {
+    // fan out from point 0
+    for (int cur = 1; cur < ring.numPts()-1; ++cur) {
+        this->addTri(ring.index(0), ring.index(cur), ring.index(cur+1));
+    }
+}
+
+void GrAAConvexTessellator::createOuterRing() {
+    // For now, we're only generating one outer ring (at the start). This
+    // could be relaxed for stroking use cases.
+    SkASSERT(0 == fIndices.count());  
+    SkASSERT(fPts.count() == fNorms.count());
+
+    const int numPts = fPts.count();
+
+    // For each vertex of the original polygon we add three points to the 
+    // outset polygon - one extending perpendicular to each impinging edge
+    // and one along the bisector. Two triangles are added for each corner
+    // and two are added along each edge.
+    int prev = numPts - 1;
+    int lastPerpIdx = -1, firstPerpIdx = -1, newIdx0, newIdx1, newIdx2;
+    for (int cur = 0; cur < numPts; ++cur) {
+        // The perpendicular point for the last edge
+        SkPoint temp = fNorms[prev];
+        temp.scale(fTargetDepth);
+        temp += fPts[cur];
+
+        // We know it isn't a duplicate of the prior point (since it and this
+        // one are just perpendicular offsets from the non-merged polygon points)
+        newIdx0 = this->addPt(temp, -fTargetDepth, false);
+
+        // The bisector outset point
+        temp = fBisectors[cur];
+        temp.scale(-fTargetDepth);  // the bisectors point in
+        temp += fPts[cur];
+
+        // For very shallow angles all the corner points could fuse
+        if (duplicate_pt(temp, this->point(newIdx0))) {
+            newIdx1 = newIdx0;
+        } else {
+            newIdx1 = this->addPt(temp, -fTargetDepth, false);
+        }
+
+        // The perpendicular point for the next edge.
+        temp = fNorms[cur];
+        temp.scale(fTargetDepth);
+        temp += fPts[cur];
+
+        // For very shallow angles all the corner points could fuse.
+        if (duplicate_pt(temp, this->point(newIdx1))) {
+            newIdx2 = newIdx1;
+        } else {
+            newIdx2 = this->addPt(temp, -fTargetDepth, false);
+        }
+
+        if (0 == cur) {
+            // Store the index of the first perpendicular point to finish up
+            firstPerpIdx = newIdx0;
+            SkASSERT(-1 == lastPerpIdx);
+        } else {
+            // The triangles for the previous edge
+            this->addTri(prev, newIdx0, cur);
+            this->addTri(prev, lastPerpIdx, newIdx0);
+        }
+
+        // The two triangles for the corner
+        this->addTri(cur, newIdx0, newIdx1);
+        this->addTri(cur, newIdx1, newIdx2);
+
+        prev = cur;
+        // Track the last perpendicular outset point so we can construct the
+        // trailing edge triangles.
+        lastPerpIdx = newIdx2;
+    }
+
+    // pick up the final edge rect
+    this->addTri(numPts-1, firstPerpIdx, 0);
+    this->addTri(numPts-1, lastPerpIdx, firstPerpIdx);
+
+    this->validate();
+}
+
+// Something went wrong in the creation of the next ring. Mark the last good
+// ring as being at the desired depth and fan it.
+void GrAAConvexTessellator::terminate(const Ring& ring) {
+    for (int i = 0; i < ring.numPts(); ++i) {
+        fDepths[ring.index(i)] = fTargetDepth;
+    }
+
+    this->fanRing(ring);
+}
+
+// return true when processing is complete
+bool GrAAConvexTessellator::createInsetRing(const Ring& lastRing, Ring* nextRing) {
+    bool done = false;
+
+    fCandidateVerts.rewind();
+
+    // Loop through all the points in the ring and find the intersection with the smallest depth
+    SkScalar minDist = SK_ScalarMax, minT = 0.0f;
+    int minEdgeIdx = -1;
+
+    for (int cur = 0; cur < lastRing.numPts(); ++cur) {
+        int next = (cur + 1) % lastRing.numPts();
+
+        SkScalar t = intersect(this->point(lastRing.index(cur)),  lastRing.bisector(cur),
+                               this->point(lastRing.index(next)), lastRing.bisector(next));
+        SkScalar dist = -t * lastRing.norm(cur).dot(lastRing.bisector(cur));
+
+        if (minDist > dist) {
+            minDist = dist;
+            minT = t;
+            minEdgeIdx = cur;
+        }
+    }
+
+    SkPoint newPt = lastRing.bisector(minEdgeIdx);
+    newPt.scale(minT);
+    newPt += this->point(lastRing.index(minEdgeIdx));
+
+    SkScalar depth = this->computeDepthFromEdge(lastRing.origEdgeID(minEdgeIdx), newPt);
+    if (depth >= fTargetDepth) {
+        // None of the bisectors intersect before reaching the desired depth.
+        // Just step them all to the desired depth
+        depth = fTargetDepth;
+        done = true;
+    }
+
+    // 'dst' stores where each point in the last ring maps to/transforms into
+    // in the next ring.
+    SkTDArray<int> dst;
+    dst.setCount(lastRing.numPts());
+
+    // Create the first point (who compares with no one)
+    if (!this->computePtAlongBisector(lastRing.index(0),
+                                      lastRing.bisector(0),
+                                      lastRing.origEdgeID(0),
+                                      depth, &newPt)) {
+        this->terminate(lastRing);
+        SkDEBUGCODE(fShouldCheckDepths = false;)
+        return true;
+    }
+    dst[0] = fCandidateVerts.addNewPt(newPt,
+                                      lastRing.index(0), lastRing.origEdgeID(0),
+                                      !this->movable(lastRing.index(0)));
+
+    // Handle the middle points (who only compare with the prior point)
+    for (int cur = 1; cur < lastRing.numPts()-1; ++cur) {
+        if (!this->computePtAlongBisector(lastRing.index(cur),
+                                          lastRing.bisector(cur),
+                                          lastRing.origEdgeID(cur),
+                                          depth, &newPt)) {
+            this->terminate(lastRing);
+            SkDEBUGCODE(fShouldCheckDepths = false;)
+            return true;
+        }
+        if (!duplicate_pt(newPt, fCandidateVerts.lastPoint())) {
+            dst[cur] = fCandidateVerts.addNewPt(newPt,
+                                                lastRing.index(cur), lastRing.origEdgeID(cur),
+                                                !this->movable(lastRing.index(cur)));
+        } else {
+            dst[cur] = fCandidateVerts.fuseWithPrior(lastRing.origEdgeID(cur));
+        }
+    }
+
+    // Check on the last point (handling the wrap around)
+    int cur = lastRing.numPts()-1;
+    if  (!this->computePtAlongBisector(lastRing.index(cur),
+                                       lastRing.bisector(cur),
+                                       lastRing.origEdgeID(cur),
+                                       depth, &newPt)) {
+        this->terminate(lastRing);
+        SkDEBUGCODE(fShouldCheckDepths = false;)
+        return true;
+    }
+    bool dupPrev = duplicate_pt(newPt, fCandidateVerts.lastPoint());
+    bool dupNext = duplicate_pt(newPt, fCandidateVerts.firstPoint());
+
+    if (!dupPrev && !dupNext) {
+        dst[cur] = fCandidateVerts.addNewPt(newPt,
+                                            lastRing.index(cur), lastRing.origEdgeID(cur),
+                                            !this->movable(lastRing.index(cur)));
+    } else if (dupPrev && !dupNext) {
+        dst[cur] = fCandidateVerts.fuseWithPrior(lastRing.origEdgeID(cur));
+    } else if (!dupPrev && dupNext) {
+        dst[cur] = fCandidateVerts.fuseWithNext();
+    } else {
+        bool dupPrevVsNext = duplicate_pt(fCandidateVerts.firstPoint(), fCandidateVerts.lastPoint());
+
+        if (!dupPrevVsNext) {
+            dst[cur] = fCandidateVerts.fuseWithPrior(lastRing.origEdgeID(cur));
+        } else {
+            dst[cur] = dst[cur-1] = fCandidateVerts.fuseWithBoth();
+        }
+    }
+
+    // Fold the new ring's points into the global pool
+    for (int i = 0; i < fCandidateVerts.numPts(); ++i) {
+        int newIdx;
+        if (fCandidateVerts.needsToBeNew(i)) {
+            // if the originating index is still valid then this point wasn't 
+            // fused (and is thus movable)
+            newIdx = this->addPt(fCandidateVerts.point(i), depth,
+                                 fCandidateVerts.originatingIdx(i) != -1);
+        } else {
+            SkASSERT(fCandidateVerts.originatingIdx(i) != -1);
+            this->updatePt(fCandidateVerts.originatingIdx(i), fCandidateVerts.point(i), depth);
+            newIdx = fCandidateVerts.originatingIdx(i);
+        }
+
+        nextRing->addIdx(newIdx, fCandidateVerts.origEdge(i));
+    }
+
+    // 'dst' currently has indices into the ring. Remap these to be indices
+    // into the global pool since the triangulation operates in that space.
+    for (int i = 0; i < dst.count(); ++i) {
+        dst[i] = nextRing->index(dst[i]);
+    }
+
+    for (int cur = 0; cur < lastRing.numPts(); ++cur) {
+        int next = (cur + 1) % lastRing.numPts();
+
+        this->addTri(lastRing.index(cur), lastRing.index(next), dst[next]);
+        this->addTri(lastRing.index(cur), dst[next], dst[cur]);
+    }
+
+    if (done) {
+        this->fanRing(*nextRing);
+    }
+
+    if (nextRing->numPts() < 3) {
+        done = true;
+    }
+
+    return done;
+}
+
+void GrAAConvexTessellator::validate() const {
+    SkASSERT(fPts.count() == fDepths.count());
+    SkASSERT(fPts.count() == fMovable.count());
+    SkASSERT(0 == (fIndices.count() % 3));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+void GrAAConvexTessellator::Ring::init(const GrAAConvexTessellator& tess) {
+    this->computeNormals(tess);
+    this->computeBisectors();
+    SkASSERT(this->isConvex(tess));
+}
+
+void GrAAConvexTessellator::Ring::init(const SkTDArray<SkVector>& norms,
+                                       const SkTDArray<SkVector>& bisectors) {
+    for (int i = 0; i < fPts.count(); ++i) {
+        fPts[i].fNorm = norms[i];
+        fPts[i].fBisector = bisectors[i];
+    }
+}
+
+// Compute the outward facing normal at each vertex.
+void GrAAConvexTessellator::Ring::computeNormals(const GrAAConvexTessellator& tess) {
+    for (int cur = 0; cur < fPts.count(); ++cur) {
+        int next = (cur + 1) % fPts.count();
+
+        fPts[cur].fNorm = tess.point(fPts[next].fIndex) - tess.point(fPts[cur].fIndex);
+        SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fPts[cur].fNorm);
+        SkASSERT(len > 0.0f);
+        fPts[cur].fNorm.setOrthog(fPts[cur].fNorm, tess.side());
+
+        SkASSERT(SkScalarNearlyEqual(1.0f, fPts[cur].fNorm.length()));
+    }
+}
+
+void GrAAConvexTessellator::Ring::computeBisectors() {
+    int prev = fPts.count() - 1;
+    for (int cur = 0; cur < fPts.count(); prev = cur, ++cur) {
+        fPts[cur].fBisector = fPts[cur].fNorm + fPts[prev].fNorm;
+        fPts[cur].fBisector.normalize();
+        fPts[cur].fBisector.negate();      // make the bisector face in
+
+        SkASSERT(SkScalarNearlyEqual(1.0f, fPts[cur].fBisector.length()));
+    }    
+}
+
+//////////////////////////////////////////////////////////////////////////////
+#ifdef SK_DEBUG
+// Is this ring convex?
+bool GrAAConvexTessellator::Ring::isConvex(const GrAAConvexTessellator& tess) const {
+    if (fPts.count() < 3) {
+        return false;
+    }
+
+    SkPoint prev = tess.point(fPts[0].fIndex) - tess.point(fPts.top().fIndex);
+    SkPoint cur  = tess.point(fPts[1].fIndex) - tess.point(fPts[0].fIndex);
+    SkScalar minDot = prev.fX * cur.fY - prev.fY * cur.fX;
+    SkScalar maxDot = minDot;
+
+    prev = cur;
+    for (int i = 1; i < fPts.count(); ++i) {
+        int next = (i + 1) % fPts.count();
+
+        cur  = tess.point(fPts[next].fIndex) - tess.point(fPts[i].fIndex);
+        SkScalar dot = prev.fX * cur.fY - prev.fY * cur.fX;
+
+        minDot = SkMinScalar(minDot, dot);
+        maxDot = SkMaxScalar(maxDot, dot);
+
+        prev = cur;
+    }
+
+    return (maxDot > 0.0f) == (minDot >= 0.0f);
+}
+
+static SkScalar capsule_depth(const SkPoint& p0, const SkPoint& p1,
+                              const SkPoint& test, SkPoint::Side side,
+                              int* sign) {
+    *sign = -1;
+    SkPoint edge = p1 - p0;
+    SkScalar len = SkPoint::Normalize(&edge);
+
+    SkPoint testVec = test - p0;
+
+    SkScalar d0 = edge.dot(testVec);
+    if (d0 < 0.0f) {
+        return SkPoint::Distance(p0, test);
+    }
+    if (d0 > len) {
+        return SkPoint::Distance(p1, test);
+    }
+
+    SkScalar perpDist = testVec.fY * edge.fX - testVec.fX * edge.fY;
+    if (SkPoint::kRight_Side == side) {
+        perpDist = -perpDist;
+    }
+
+    if (perpDist < 0.0f) {
+        perpDist = -perpDist;
+    } else {
+        *sign = 1;
+    }
+    return perpDist;
+}
+
+SkScalar GrAAConvexTessellator::computeRealDepth(const SkPoint& p) const {
+    SkScalar minDist = SK_ScalarMax;
+    int closestSign, sign;
+
+    for (int edge = 0; edge < fNorms.count(); ++edge) {
+        SkScalar dist = capsule_depth(this->point(edge),
+                                      this->point((edge+1) % fNorms.count()),
+                                      p, fSide, &sign);
+        SkASSERT(dist >= 0.0f);
+
+        if (minDist > dist) {
+            minDist = dist;
+            closestSign = sign;
+        }
+    }
+
+    return closestSign * minDist;
+}
+
+// Verify that the incrementally computed depths are close to the actual depths.
+void GrAAConvexTessellator::checkAllDepths() const {
+    for (int cur = 0; cur < this->numPts(); ++cur) {
+        SkScalar realDepth = this->computeRealDepth(this->point(cur));
+        SkScalar computedDepth = this->depth(cur);
+        SkASSERT(SkScalarNearlyEqual(realDepth, computedDepth, 0.01f));
+    }
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+#if GR_AA_CONVEX_TESSELLATOR_VIZ
+static const SkScalar kPointRadius = 0.02f;
+static const SkScalar kArrowStrokeWidth = 0.0f;
+static const SkScalar kArrowLength = 0.2f;
+static const SkScalar kEdgeTextSize = 0.1f;
+static const SkScalar kPointTextSize = 0.02f;
+
+static void draw_point(SkCanvas* canvas, const SkPoint& p, SkScalar paramValue, bool stroke) {
+    SkPaint paint;
+    SkASSERT(paramValue <= 1.0f);
+    int gs = int(255*paramValue);
+    paint.setARGB(255, gs, gs, gs);
+
+    canvas->drawCircle(p.fX, p.fY, kPointRadius, paint);
+
+    if (stroke) {
+        SkPaint stroke;
+        stroke.setColor(SK_ColorYELLOW);
+        stroke.setStyle(SkPaint::kStroke_Style);
+        stroke.setStrokeWidth(kPointRadius/3.0f);
+        canvas->drawCircle(p.fX, p.fY, kPointRadius, stroke); 
+    }
+}
+
+static void draw_line(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1, SkColor color) {
+    SkPaint p;
+    p.setColor(color);
+
+    canvas->drawLine(p0.fX, p0.fY, p1.fX, p1.fY, p);
+}
+
+static void draw_arrow(SkCanvas*canvas, const SkPoint& p, const SkPoint &n,
+                       SkScalar len, SkColor color) {
+    SkPaint paint;
+    paint.setColor(color);
+    paint.setStrokeWidth(kArrowStrokeWidth);
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    canvas->drawLine(p.fX, p.fY,
+                     p.fX + len * n.fX, p.fY + len * n.fY,
+                     paint);
+}
+
+void GrAAConvexTessellator::Ring::draw(SkCanvas* canvas, const GrAAConvexTessellator& tess) const {
+    SkPaint paint;
+    paint.setTextSize(kEdgeTextSize);
+
+    for (int cur = 0; cur < fPts.count(); ++cur) {
+        int next = (cur + 1) % fPts.count();
+
+        draw_line(canvas,
+                  tess.point(fPts[cur].fIndex),
+                  tess.point(fPts[next].fIndex),
+                  SK_ColorGREEN);
+
+        SkPoint mid = tess.point(fPts[cur].fIndex) + tess.point(fPts[next].fIndex);
+        mid.scale(0.5f);
+
+        if (fPts.count()) {
+            draw_arrow(canvas, mid, fPts[cur].fNorm, kArrowLength, SK_ColorRED);
+            mid.fX += (kArrowLength/2) * fPts[cur].fNorm.fX;
+            mid.fY += (kArrowLength/2) * fPts[cur].fNorm.fY;
+        }
+
+        SkString num;
+        num.printf("%d", this->origEdgeID(cur));
+        canvas->drawText(num.c_str(), num.size(), mid.fX, mid.fY, paint);
+
+        if (fPts.count()) {
+            draw_arrow(canvas, tess.point(fPts[cur].fIndex), fPts[cur].fBisector,
+                       kArrowLength, SK_ColorBLUE);
+        }
+    }    
+}
+
+void GrAAConvexTessellator::draw(SkCanvas* canvas) const {
+    for (int i = 0; i < fIndices.count(); i += 3) {
+        SkASSERT(fIndices[i] < this->numPts()) ;
+        SkASSERT(fIndices[i+1] < this->numPts()) ;
+        SkASSERT(fIndices[i+2] < this->numPts()) ;
+
+        draw_line(canvas,
+                  this->point(this->fIndices[i]), this->point(this->fIndices[i+1]),
+                  SK_ColorBLACK);
+        draw_line(canvas,
+                  this->point(this->fIndices[i+1]), this->point(this->fIndices[i+2]),
+                  SK_ColorBLACK);
+        draw_line(canvas,
+                  this->point(this->fIndices[i+2]), this->point(this->fIndices[i]),
+                  SK_ColorBLACK);
+    }
+
+    fInitialRing.draw(canvas, *this);
+    for (int i = 0; i < fRings.count(); ++i) {
+        fRings[i]->draw(canvas, *this);
+    }
+
+    for (int i = 0; i < this->numPts(); ++i) {
+        draw_point(canvas,
+                   this->point(i), 0.5f + (this->depth(i)/(2*fTargetDepth)), 
+                   !this->movable(i));
+
+        SkPaint paint;
+        paint.setTextSize(kPointTextSize);
+        paint.setTextAlign(SkPaint::kCenter_Align);
+        if (this->depth(i) <= -fTargetDepth) {
+            paint.setColor(SK_ColorWHITE);
+        }
+
+        SkString num;
+        num.printf("%d", i);
+        canvas->drawText(num.c_str(), num.size(), 
+                         this->point(i).fX, this->point(i).fY+(kPointRadius/2.0f), 
+                         paint);
+    }
+}
+
+#endif
+
diff --git a/src/gpu/GrAAConvexTessellator.h b/src/gpu/GrAAConvexTessellator.h
new file mode 100644
index 0000000..c2b751e
--- /dev/null
+++ b/src/gpu/GrAAConvexTessellator.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrAAConvexTessellator_DEFINED
+#define GrAAConvexTessellator_DEFINED
+
+#include "SkColor.h"
+#include "SkPoint.h"
+#include "SkScalar.h"
+#include "SkTDArray.h"
+
+class SkCanvas;
+class SkMatrix;
+class SkPath;
+
+//#define GR_AA_CONVEX_TESSELLATOR_VIZ 1
+
+class GrAAConvexTessellator;
+
+// The AAConvexTessellator holds the global pool of points and the triangulation
+// that connects them. It also drives the tessellation process.
+// The outward facing normals of the original polygon are stored (in 'fNorms') to service
+// computeDepthFromEdge requests.
+class GrAAConvexTessellator {
+public:
+    GrAAConvexTessellator(SkScalar targetDepth = 0.5f)
+        : fSide(SkPoint::kOn_Side)
+        , fTargetDepth(targetDepth) {
+    }
+
+    void setTargetDepth(SkScalar targetDepth) { fTargetDepth = targetDepth; }
+    SkScalar targetDepth() const { return fTargetDepth; }
+
+    SkPoint::Side side() const { return fSide; }
+
+    bool tessellate(const SkMatrix& m, const SkPath& path);
+
+    // The next five should only be called after tessellate to extract the result
+    int numPts() const { return fPts.count(); }
+    int numIndices() const { return fIndices.count(); }
+
+    const SkPoint& lastPoint() const { return fPts.top(); }
+    const SkPoint& point(int index) const { return fPts[index]; }
+    int index(int index) const { return fIndices[index]; }
+    SkScalar depth(int index) const {return fDepths[index]; }
+
+#if GR_AA_CONVEX_TESSELLATOR_VIZ
+    void draw(SkCanvas* canvas) const;
+#endif
+
+    // The tessellator can be reused for multiple paths by rewinding in between
+    void rewind();
+
+private:
+    // CandidateVerts holds the vertices for the next ring while they are 
+    // being generated. Its main function is to de-dup the points.
+    class CandidateVerts {
+    public:
+        void setReserve(int numPts) { fPts.setReserve(numPts); }
+        void rewind() { fPts.rewind(); }
+
+        int numPts() const { return fPts.count(); }
+
+        const SkPoint& lastPoint() const { return fPts.top().fPt; }
+        const SkPoint& firstPoint() const { return fPts[0].fPt; }
+        const SkPoint& point(int index) const { return fPts[index].fPt; }
+
+        int originatingIdx(int index) const { return fPts[index].fOriginatingIdx; }
+        int origEdge(int index) const { return fPts[index].fOrigEdgeId; }
+        bool needsToBeNew(int index) const { return fPts[index].fNeedsToBeNew; }
+
+        int addNewPt(const SkPoint& newPt, int originatingIdx, int origEdge, bool needsToBeNew) {
+            struct PointData* pt = fPts.push();
+            pt->fPt = newPt;
+            pt->fOrigEdgeId = origEdge;
+            pt->fOriginatingIdx = originatingIdx;
+            pt->fNeedsToBeNew = needsToBeNew;
+            return fPts.count() - 1;
+        }
+
+        int fuseWithPrior(int origEdgeId) {
+            fPts.top().fOrigEdgeId = origEdgeId;
+            fPts.top().fOriginatingIdx = -1;
+            fPts.top().fNeedsToBeNew = true;
+            return fPts.count() - 1;
+        }
+
+        int fuseWithNext() {
+            fPts[0].fOriginatingIdx = -1;
+            fPts[0].fNeedsToBeNew = true;
+            return 0;
+        }
+
+        int fuseWithBoth() {
+            if (fPts.count() > 1) {
+                fPts.pop();
+            }
+
+            fPts[0].fOriginatingIdx = -1;
+            fPts[0].fNeedsToBeNew = true;
+            return 0;
+        }
+
+    private:
+        struct PointData {
+            SkPoint fPt;
+            int     fOriginatingIdx;
+            int     fOrigEdgeId;
+            bool    fNeedsToBeNew;
+        };
+
+        SkTDArray<struct PointData> fPts;
+    };
+
+    // The Ring holds a set of indices into the global pool that together define
+    // a single polygon inset.
+    class Ring {
+    public:
+        void setReserve(int numPts) { fPts.setReserve(numPts); }
+        void rewind() { fPts.rewind(); }
+
+        int numPts() const { return fPts.count(); }
+
+        void addIdx(int index, int origEdgeId) {
+            struct PointData* pt = fPts.push();
+            pt->fIndex = index;
+            pt->fOrigEdgeId = origEdgeId;
+        }
+
+        // init should be called after all the indices have been added (via addIdx)
+        void init(const GrAAConvexTessellator& tess);
+        void init(const SkTDArray<SkVector>& norms, const SkTDArray<SkVector>& bisectors);
+
+        const SkPoint& norm(int index) const { return fPts[index].fNorm; }
+        const SkPoint& bisector(int index) const { return fPts[index].fBisector; }
+        int index(int index) const { return fPts[index].fIndex; }
+        int origEdgeID(int index) const { return fPts[index].fOrigEdgeId; }
+
+    #if GR_AA_CONVEX_TESSELLATOR_VIZ
+        void draw(SkCanvas* canvas, const GrAAConvexTessellator& tess) const;
+    #endif
+
+    private:
+        void computeNormals(const GrAAConvexTessellator& result);
+        void computeBisectors();
+
+        SkDEBUGCODE(bool isConvex(const GrAAConvexTessellator& tess) const;)
+
+        struct PointData {
+            SkPoint fNorm;
+            SkPoint fBisector;
+            int     fIndex;
+            int     fOrigEdgeId;
+        };
+
+        SkTDArray<PointData> fPts;
+    };
+
+    bool movable(int index) const { return fMovable[index]; }
+
+    // Movable points are those that can be slid along their bisector.
+    // Basically, a point is immovable if it is part of the original
+    // polygon or it results from the fusing of two bisectors.
+    int addPt(const SkPoint& pt, SkScalar depth, bool movable);
+    void popLastPt();
+    void popFirstPtShuffle();
+
+    void updatePt(int index, const SkPoint& pt, SkScalar depth);
+
+    void addTri(int i0, int i1, int i2);
+
+    void reservePts(int count) {
+        fPts.setReserve(count);
+        fDepths.setReserve(count);  
+        fMovable.setReserve(count);
+    }
+
+    SkScalar computeDepthFromEdge(int edgeIdx, const SkPoint& p) const;
+
+    bool computePtAlongBisector(int startIdx, const SkPoint& bisector,
+                                int edgeIdx, SkScalar desiredDepth,
+                                SkPoint* result) const;
+
+    void terminate(const Ring& lastRing);
+
+    // return false on failure/degenerate path
+    bool extractFromPath(const SkMatrix& m, const SkPath& path);
+    void computeBisectors();
+
+    void fanRing(const Ring& ring);
+    void createOuterRing();
+
+    Ring* getNextRing(Ring* lastRing);
+
+    bool createInsetRing(const Ring& lastRing, Ring* nextRing);
+
+    void validate() const;
+
+
+#ifdef SK_DEBUG
+    SkScalar computeRealDepth(const SkPoint& p) const;
+    void checkAllDepths() const;
+#endif
+
+    // fPts, fWeights & fMovable should always have the same # of elements
+    SkTDArray<SkPoint>  fPts;
+    SkTDArray<SkScalar> fDepths;
+    // movable points are those that can be slid further along their bisector
+    SkTDArray<bool>     fMovable;
+
+    // The outward facing normals for the original polygon
+    SkTDArray<SkVector> fNorms;
+    // The inward facing bisector at each point in the original polygon. Only
+    // needed for exterior ring creation and then handed off to the initial ring.
+    SkTDArray<SkVector> fBisectors;
+    SkPoint::Side       fSide;    // winding of the original polygon
+
+    // The triangulation of the points
+    SkTDArray<int>      fIndices;
+
+    Ring                fInitialRing;
+#if GR_AA_CONVEX_TESSELLATOR_VIZ
+    // When visualizing save all the rings
+    SkTDArray<Ring*>    fRings;
+#else
+    Ring                fRings[2];
+#endif
+    CandidateVerts      fCandidateVerts;
+
+    SkScalar            fTargetDepth;
+
+    // If some goes wrong with the inset computation the tessellator will 
+    // truncate the creation of the inset polygon. In this case the depth
+    // check will complain.
+    SkDEBUGCODE(bool fShouldCheckDepths;)
+};
+
+
+#endif
+
diff --git a/src/gpu/GrAADistanceFieldPathRenderer.cpp b/src/gpu/GrAADistanceFieldPathRenderer.cpp
index d50af9f..c1bd885 100755
--- a/src/gpu/GrAADistanceFieldPathRenderer.cpp
+++ b/src/gpu/GrAADistanceFieldPathRenderer.cpp
@@ -8,13 +8,17 @@
 
 #include "GrAADistanceFieldPathRenderer.h"
 
-#include "GrAtlas.h"
+#include "GrBatch.h"
+#include "GrBatchTarget.h"
+#include "GrBatchTest.h"
 #include "GrContext.h"
 #include "GrPipelineBuilder.h"
+#include "GrResourceProvider.h"
 #include "GrSurfacePriv.h"
 #include "GrSWMaskHelper.h"
 #include "GrTexturePriv.h"
-#include "effects/GrDistanceFieldTextureEffect.h"
+#include "GrVertexBuffer.h"
+#include "effects/GrDistanceFieldGeoProc.h"
 
 #include "SkDistanceFieldGen.h"
 #include "SkRTConf.h"
@@ -27,9 +31,6 @@
 #define NUM_PLOTS_X   (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH)
 #define NUM_PLOTS_Y   (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
 
-SK_CONF_DECLARE(bool, c_DumpPathCache, "gpu.dumpPathCache", false,
-                "Dump the contents of the path cache before every purge.");
-
 #ifdef DF_PATH_TRACKING
 static int g_NumCachedPaths = 0;
 static int g_NumFreedPaths = 0;
@@ -40,11 +41,30 @@
 static const int kMediumMIP = 78;
 static const int kLargeMIP = 192;
 
+// Callback to clear out internal path cache when eviction occurs
+void GrAADistanceFieldPathRenderer::HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
+    GrAADistanceFieldPathRenderer* dfpr = (GrAADistanceFieldPathRenderer*)pr;
+    // remove any paths that use this plot
+    PathDataList::Iter iter;
+    iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
+    PathData* pathData;
+    while ((pathData = iter.get())) {
+        iter.next();
+        if (id == pathData->fID) {
+            dfpr->fPathCache.remove(pathData->fKey);
+            dfpr->fPathList.remove(pathData);
+            SkDELETE(pathData);
+#ifdef DF_PATH_TRACKING
+            ++g_NumFreedPaths;
+#endif
+        }
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 GrAADistanceFieldPathRenderer::GrAADistanceFieldPathRenderer(GrContext* context)
     : fContext(context)
-    , fAtlas(NULL)
-    , fEffectFlags(kInvalid_DistanceFieldEffectFlag) {
+    , fAtlas(NULL) {
 }
 
 GrAADistanceFieldPathRenderer::~GrAADistanceFieldPathRenderer() {
@@ -56,7 +76,6 @@
         fPathList.remove(pathData);
         SkDELETE(pathData);
     }
-    
     SkDELETE(fAtlas);
 
 #ifdef DF_PATH_TRACKING
@@ -69,13 +88,13 @@
                                                 const GrPipelineBuilder* pipelineBuilder,
                                                 const SkMatrix& viewMatrix,
                                                 const SkPath& path,
-                                                const SkStrokeRec& stroke,
+                                                const GrStrokeInfo& stroke,
                                                 bool antiAlias) const {
     
     // TODO: Support inverse fill
     // TODO: Support strokes
-    if (!target->caps()->shaderDerivativeSupport() || !antiAlias || path.isInverseFillType()
-        || path.isVolatile() || SkStrokeRec::kFill_Style != stroke.getStyle()) {
+    if (!target->caps()->shaderCaps()->shaderDerivativeSupport() || !antiAlias 
+        || path.isInverseFillType() || path.isVolatile() || !stroke.isFillStyle()) {
         return false;
     }
 
@@ -89,7 +108,7 @@
     SkScalar maxScale = viewMatrix.getMaxScale();
     const SkRect& bounds = path.getBounds();
     SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
-    return maxDim < 64.f && maxDim*maxScale < 256.f;
+    return maxDim < 64.f && maxDim * maxScale < 256.f;
 }
 
 
@@ -97,18 +116,471 @@
 GrAADistanceFieldPathRenderer::onGetStencilSupport(const GrDrawTarget*,
                                                    const GrPipelineBuilder*,
                                                    const SkPath&,
-                                                   const SkStrokeRec&) const {
+                                                   const GrStrokeInfo&) const {
     return GrPathRenderer::kNoSupport_StencilSupport;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
+// padding around path bounds to allow for antialiased pixels
+static const SkScalar kAntiAliasPad = 1.0f;
+
+class AADistanceFieldPathBatch : public GrBatch {
+public:
+    typedef GrAADistanceFieldPathRenderer::PathData PathData;
+    typedef SkTDynamicHash<PathData, PathData::Key> PathCache;
+    typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
+
+    struct Geometry {
+        Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {}
+        SkPath fPath;
+        SkStrokeRec fStroke;
+        bool fAntiAlias;
+        PathData* fPathData;
+    };
+
+    static GrBatch* Create(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix,
+                           GrBatchAtlas* atlas, PathCache* pathCache, PathDataList* pathList) {
+        return SkNEW_ARGS(AADistanceFieldPathBatch, (geometry, color, viewMatrix,
+                                                     atlas, pathCache, pathList));
+    }
+
+    const char* name() const override { return "AADistanceFieldPathBatch"; }
+
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
+        out->setKnownFourComponents(fBatch.fColor);
+    }
+
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
+        out->setUnknownSingleComponent();
+    }
+
+    void initBatchTracker(const GrPipelineInfo& init) override {
+        // Handle any color overrides
+        if (init.fColorIgnored) {
+            fBatch.fColor = GrColor_ILLEGAL;
+        } else if (GrColor_ILLEGAL != init.fOverrideColor) {
+            fBatch.fColor = init.fOverrideColor;
+        }
+
+        // setup batch properties
+        fBatch.fColorIgnored = init.fColorIgnored;
+        fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
+        fBatch.fCoverageIgnored = init.fCoverageIgnored;
+    }
+
+    struct FlushInfo {
+        SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
+        SkAutoTUnref<const GrIndexBuffer>  fIndexBuffer;
+        int fVertexOffset;
+        int fInstancesToFlush;
+    };
+
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
+        int instanceCount = fGeoData.count();
+
+        SkMatrix invert;
+        if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
+            SkDebugf("Could not invert viewmatrix\n");
+            return;
+        }
+
+        uint32_t flags = 0;
+        flags |= this->viewMatrix().isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
+
+        GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
+
+        // Setup GrGeometryProcessor
+        GrBatchAtlas* atlas = fAtlas;
+        SkAutoTUnref<GrGeometryProcessor> dfProcessor(
+                GrDistanceFieldPathGeoProc::Create(this->color(),
+                                                   this->viewMatrix(),
+                                                   atlas->getTexture(),
+                                                   params,
+                                                   flags));
+
+        this->initDraw(batchTarget, dfProcessor, pipeline);
+
+        FlushInfo flushInfo;
+
+        // allocate vertices
+        size_t vertexStride = dfProcessor->getVertexStride();
+        SkASSERT(vertexStride == 2 * sizeof(SkPoint));
+
+        const GrVertexBuffer* vertexBuffer;
+        void* vertices = batchTarget->makeVertSpace(vertexStride,
+                                                    kVerticesPerQuad * instanceCount,
+                                                    &vertexBuffer,
+                                                    &flushInfo.fVertexOffset);
+        flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
+        flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
+        if (!vertices || !flushInfo.fIndexBuffer) {
+            SkDebugf("Could not allocate vertices\n");
+            return;
+        }
+
+        flushInfo.fInstancesToFlush = 0;
+        for (int i = 0; i < instanceCount; i++) {
+            Geometry& args = fGeoData[i];
+
+            // get mip level
+            SkScalar maxScale = this->viewMatrix().getMaxScale();
+            const SkRect& bounds = args.fPath.getBounds();
+            SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
+            SkScalar size = maxScale * maxDim;
+            uint32_t desiredDimension;
+            if (size <= kSmallMIP) {
+                desiredDimension = kSmallMIP;
+            } else if (size <= kMediumMIP) {
+                desiredDimension = kMediumMIP;
+            } else {
+                desiredDimension = kLargeMIP;
+            }
+
+            // check to see if path is cached
+            // TODO: handle stroked vs. filled version of same path
+            PathData::Key key = { args.fPath.getGenerationID(), desiredDimension };
+            args.fPathData = fPathCache->find(key);
+            if (NULL == args.fPathData || !atlas->hasID(args.fPathData->fID)) {
+                // Remove the stale cache entry
+                if (args.fPathData) {
+                    fPathCache->remove(args.fPathData->fKey);
+                    fPathList->remove(args.fPathData);
+                    SkDELETE(args.fPathData);
+                }
+                SkScalar scale = desiredDimension/maxDim;
+                args.fPathData = SkNEW(PathData);
+                if (!this->addPathToAtlas(batchTarget,
+                                          dfProcessor,
+                                          pipeline,
+                                          &flushInfo,
+                                          atlas,
+                                          args.fPathData,
+                                          args.fPath,
+                                          args.fStroke,
+                                          args.fAntiAlias,
+                                          desiredDimension,
+                                          scale)) {
+                    SkDebugf("Can't rasterize path\n");
+                    return;
+                }
+            }
+
+            atlas->setLastUseToken(args.fPathData->fID, batchTarget->currentToken());
+
+            // Now set vertices
+            intptr_t offset = reinterpret_cast<intptr_t>(vertices);
+            offset += i * kVerticesPerQuad * vertexStride;
+            SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
+            this->writePathVertices(batchTarget,
+                                    atlas,
+                                    pipeline,
+                                    dfProcessor,
+                                    positions,
+                                    vertexStride,
+                                    this->viewMatrix(),
+                                    args.fPath,
+                                    args.fPathData);
+            flushInfo.fInstancesToFlush++;
+        }
+
+        this->flush(batchTarget, &flushInfo);
+    }
+
+    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+    AADistanceFieldPathBatch(const Geometry& geometry, GrColor color, const SkMatrix& viewMatrix,
+                             GrBatchAtlas* atlas,
+                             PathCache* pathCache, PathDataList* pathList) {
+        this->initClassID<AADistanceFieldPathBatch>();
+        fBatch.fColor = color;
+        fBatch.fViewMatrix = viewMatrix;
+        fGeoData.push_back(geometry);
+        fGeoData.back().fPathData = NULL;
+
+        fAtlas = atlas;
+        fPathCache = pathCache;
+        fPathList = pathList;
+
+        // Compute bounds
+        fBounds = geometry.fPath.getBounds();
+        viewMatrix.mapRect(&fBounds);
+    }
+
+    bool addPathToAtlas(GrBatchTarget* batchTarget,
+                        const GrGeometryProcessor* dfProcessor,
+                        const GrPipeline* pipeline,
+                        FlushInfo* flushInfo,
+                        GrBatchAtlas* atlas,
+                        PathData* pathData,
+                        const SkPath& path,
+                        const SkStrokeRec&
+                        stroke, bool antiAlias,
+                        uint32_t dimension,
+                        SkScalar scale) {
+        const SkRect& bounds = path.getBounds();
+
+        // generate bounding rect for bitmap draw
+        SkRect scaledBounds = bounds;
+        // scale to mip level size
+        scaledBounds.fLeft *= scale;
+        scaledBounds.fTop *= scale;
+        scaledBounds.fRight *= scale;
+        scaledBounds.fBottom *= scale;
+        // move the origin to an integer boundary (gives better results)
+        SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
+        SkScalar dy = SkScalarFraction(scaledBounds.fTop);
+        scaledBounds.offset(-dx, -dy);
+        // get integer boundary
+        SkIRect devPathBounds;
+        scaledBounds.roundOut(&devPathBounds);
+        // pad to allow room for antialiasing
+        devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAntiAliasPad));
+        // move origin to upper left corner
+        devPathBounds.offsetTo(0,0);
+
+        // draw path to bitmap
+        SkMatrix drawMatrix;
+        drawMatrix.setTranslate(-bounds.left(), -bounds.top());
+        drawMatrix.postScale(scale, scale);
+        drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
+
+        // setup bitmap backing
+        // Now translate so the bound's UL corner is at the origin
+        drawMatrix.postTranslate(-devPathBounds.fLeft * SK_Scalar1,
+                                 -devPathBounds.fTop * SK_Scalar1);
+        SkIRect pathBounds = SkIRect::MakeWH(devPathBounds.width(),
+                                             devPathBounds.height());
+
+        SkBitmap bmp;
+        const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(pathBounds.fRight,
+                                                            pathBounds.fBottom);
+        if (!bmp.tryAllocPixels(bmImageInfo)) {
+            return false;
+        }
+
+        sk_bzero(bmp.getPixels(), bmp.getSafeSize());
+
+        // rasterize path
+        SkPaint paint;
+        if (stroke.isHairlineStyle()) {
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(SK_Scalar1);
+        } else {
+            if (stroke.isFillStyle()) {
+                paint.setStyle(SkPaint::kFill_Style);
+            } else {
+                paint.setStyle(SkPaint::kStroke_Style);
+                paint.setStrokeJoin(stroke.getJoin());
+                paint.setStrokeCap(stroke.getCap());
+                paint.setStrokeWidth(stroke.getWidth());
+            }
+        }
+        paint.setAntiAlias(antiAlias);
+
+        SkDraw draw;
+        sk_bzero(&draw, sizeof(draw));
+
+        SkRasterClip rasterClip;
+        rasterClip.setRect(pathBounds);
+        draw.fRC = &rasterClip;
+        draw.fClip = &rasterClip.bwRgn();
+        draw.fMatrix = &drawMatrix;
+        draw.fBitmap = &bmp;
+
+        draw.drawPathCoverage(path, paint);
+
+        // generate signed distance field
+        devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
+        int width = devPathBounds.width();
+        int height = devPathBounds.height();
+        // TODO We should really generate this directly into the plot somehow
+        SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
+
+        // Generate signed distance field
+        {
+            SkAutoLockPixels alp(bmp);
+
+            SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
+                                               (const unsigned char*)bmp.getPixels(),
+                                               bmp.width(), bmp.height(), bmp.rowBytes());
+        }
+
+        // add to atlas
+        SkIPoint16 atlasLocation;
+        GrBatchAtlas::AtlasID id;
+        bool success = atlas->addToAtlas(&id, batchTarget, width, height, dfStorage.get(),
+                                         &atlasLocation);
+        if (!success) {
+            this->flush(batchTarget, flushInfo);
+            this->initDraw(batchTarget, dfProcessor, pipeline);
+
+            SkDEBUGCODE(success =) atlas->addToAtlas(&id, batchTarget, width, height,
+                                                     dfStorage.get(), &atlasLocation);
+            SkASSERT(success);
+
+        }
+
+        // add to cache
+        pathData->fKey.fGenID = path.getGenerationID();
+        pathData->fKey.fDimension = dimension;
+        pathData->fScale = scale;
+        pathData->fID = id;
+        // change the scaled rect to match the size of the inset distance field
+        scaledBounds.fRight = scaledBounds.fLeft +
+            SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
+        scaledBounds.fBottom = scaledBounds.fTop +
+            SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
+        // shift the origin to the correct place relative to the distance field
+        // need to also restore the fractional translation
+        scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
+                            -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
+        pathData->fBounds = scaledBounds;
+        // origin we render from is inset from distance field edge
+        atlasLocation.fX += SK_DistanceFieldInset;
+        atlasLocation.fY += SK_DistanceFieldInset;
+        pathData->fAtlasLocation = atlasLocation;
+
+        fPathCache->add(pathData);
+        fPathList->addToTail(pathData);
+#ifdef DF_PATH_TRACKING
+        ++g_NumCachedPaths;
+#endif
+        return true;
+    }
+
+    void writePathVertices(GrBatchTarget* target,
+                           GrBatchAtlas* atlas,
+                           const GrPipeline* pipeline,
+                           const GrGeometryProcessor* gp,
+                           SkPoint* positions,
+                           size_t vertexStride,
+                           const SkMatrix& viewMatrix,
+                           const SkPath& path,
+                           const PathData* pathData) {
+        GrTexture* texture = atlas->getTexture();
+
+        SkScalar dx = pathData->fBounds.fLeft;
+        SkScalar dy = pathData->fBounds.fTop;
+        SkScalar width = pathData->fBounds.width();
+        SkScalar height = pathData->fBounds.height();
+
+        SkScalar invScale = 1.0f / pathData->fScale;
+        dx *= invScale;
+        dy *= invScale;
+        width *= invScale;
+        height *= invScale;
+
+        SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX);
+        SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY);
+        SkFixed tw = SkScalarToFixed(pathData->fBounds.width());
+        SkFixed th = SkScalarToFixed(pathData->fBounds.height());
+
+        // vertex positions
+        // TODO make the vertex attributes a struct
+        SkRect r = SkRect::MakeXYWH(dx, dy, width, height);
+        positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertexStride);
+
+        // vertex texture coords
+        SkPoint* textureCoords = positions + 1;
+        textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
+                                  SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
+                                  SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
+                                  SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
+                                  vertexStride);
+    }
+
+    void initDraw(GrBatchTarget* batchTarget,
+                  const GrGeometryProcessor* dfProcessor,
+                  const GrPipeline* pipeline) {
+        batchTarget->initDraw(dfProcessor, pipeline);
+
+        // TODO remove this when batch is everywhere
+        GrPipelineInfo init;
+        init.fColorIgnored = fBatch.fColorIgnored;
+        init.fOverrideColor = GrColor_ILLEGAL;
+        init.fCoverageIgnored = fBatch.fCoverageIgnored;
+        init.fUsesLocalCoords = this->usesLocalCoords();
+        dfProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init);
+    }
+
+    void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
+        GrVertices vertices;
+        int maxInstancesPerDraw = flushInfo->fIndexBuffer->maxQuads();
+        vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
+            flushInfo->fIndexBuffer, flushInfo->fVertexOffset, kVerticesPerQuad,
+            kIndicesPerQuad, flushInfo->fInstancesToFlush, maxInstancesPerDraw);
+        batchTarget->draw(vertices);
+        flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
+        flushInfo->fInstancesToFlush = 0;
+    }
+
+    GrColor color() const { return fBatch.fColor; }
+    const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
+    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+
+    bool onCombineIfPossible(GrBatch* t) override {
+        AADistanceFieldPathBatch* that = t->cast<AADistanceFieldPathBatch>();
+
+        // TODO we could actually probably do a bunch of this work on the CPU, ie map viewMatrix,
+        // maybe upload color via attribute
+        if (this->color() != that->color()) {
+            return false;
+        }
+
+        if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+            return false;
+        }
+
+        fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
+        return true;
+    }
+
+    struct BatchTracker {
+        GrColor fColor;
+        SkMatrix fViewMatrix;
+        bool fUsesLocalCoords;
+        bool fColorIgnored;
+        bool fCoverageIgnored;
+    };
+
+    BatchTracker fBatch;
+    SkSTArray<1, Geometry, true> fGeoData;
+    GrBatchAtlas* fAtlas;
+    PathCache* fPathCache;
+    PathDataList* fPathList;
+};
+
+static GrBatchAtlas* create_atlas(GrContext* context, GrBatchAtlas::EvictionFunc func, void* data) {
+    GrBatchAtlas* atlas;
+    // Create a new atlas
+    GrSurfaceDesc desc;
+    desc.fFlags = kNone_GrSurfaceFlags;
+    desc.fWidth = ATLAS_TEXTURE_WIDTH;
+    desc.fHeight = ATLAS_TEXTURE_HEIGHT;
+    desc.fConfig = kAlpha_8_GrPixelConfig;
+
+    // We don't want to flush the context so we claim we're in the middle of flushing so as to
+    // guarantee we do not recieve a texture with pending IO
+    GrTexture* texture = context->textureProvider()->refScratchTexture(
+        desc, GrTextureProvider::kApprox_ScratchTexMatch, true);
+    if (texture) {
+        atlas = SkNEW_ARGS(GrBatchAtlas, (texture, NUM_PLOTS_X, NUM_PLOTS_Y));
+    } else {
+        return NULL;
+    }
+    atlas->registerEvictionCallback(func, data);
+    return atlas;
+}
+
 bool GrAADistanceFieldPathRenderer::onDrawPath(GrDrawTarget* target,
                                                GrPipelineBuilder* pipelineBuilder,
                                                GrColor color,
                                                const SkMatrix& viewMatrix,
                                                const SkPath& path,
-                                               const SkStrokeRec& stroke,
+                                               const GrStrokeInfo& stroke,
                                                bool antiAlias) {
     // we've already bailed on inverse filled paths, so this is safe
     if (path.isEmpty()) {
@@ -117,271 +589,92 @@
 
     SkASSERT(fContext);
 
-    // get mip level
-    SkScalar maxScale = viewMatrix.getMaxScale();
-    const SkRect& bounds = path.getBounds();
-    SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
-    SkScalar size = maxScale*maxDim;
-    uint32_t desiredDimension;
-    if (size <= kSmallMIP) {
-        desiredDimension = kSmallMIP;
-    } else if (size <= kMediumMIP) {
-        desiredDimension = kMediumMIP;
-    } else {
-        desiredDimension = kLargeMIP;
-    }
-
-    // check to see if path is cached
-    // TODO: handle stroked vs. filled version of same path
-    PathData::Key key = { path.getGenerationID(), desiredDimension };
-    PathData* pathData = fPathCache.find(key);
-    if (NULL == pathData) {
-        SkScalar scale = desiredDimension/maxDim;
-        pathData = this->addPathToAtlas(path, stroke, antiAlias, desiredDimension, scale);
-        if (NULL == pathData) {
+    if (!fAtlas) {
+        fAtlas = create_atlas(fContext, &GrAADistanceFieldPathRenderer::HandleEviction,
+                              (void*)this);
+        if (!fAtlas) {
             return false;
         }
     }
 
-    // use signed distance field to render
-    return this->internalDrawPath(target, pipelineBuilder, color, viewMatrix, path, pathData);
-}
+    AADistanceFieldPathBatch::Geometry geometry(stroke.getStrokeRec());
+    geometry.fPath = path;
+    geometry.fAntiAlias = antiAlias;
 
-// padding around path bounds to allow for antialiased pixels
-const SkScalar kAntiAliasPad = 1.0f;
+    SkAutoTUnref<GrBatch> batch(AADistanceFieldPathBatch::Create(geometry, color, viewMatrix,
+                                                                 fAtlas, &fPathCache, &fPathList));
+    target->drawBatch(pipelineBuilder, batch);
 
-inline bool GrAADistanceFieldPathRenderer::uploadPath(GrPlot** plot, SkIPoint16* atlasLocation,
-                                                      int width, int height, void* dfStorage) {
-    *plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage, atlasLocation);
-
-    // if atlas full
-    if (NULL == *plot) {
-        if (this->freeUnusedPlot()) {
-            *plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage, atlasLocation);
-            if (*plot) {
-                return true;
-            }
-        }
-
-        if (c_DumpPathCache) {
-#ifdef SK_DEVELOPER
-            GrTexture* texture = fAtlas->getTexture();
-            texture->surfacePriv().savePixels("pathcache.png");
-#endif
-        }
-
-        // before we purge the cache, we must flush any accumulated draws
-        fContext->flush();
-
-        if (this->freeUnusedPlot()) {
-            *plot = fAtlas->addToAtlas(&fPlotUsage, width, height, dfStorage, atlasLocation);
-            if (*plot) {
-                return true;
-            }
-        }
-        return false;
-    }
     return true;
 }
 
-GrAADistanceFieldPathRenderer::PathData* GrAADistanceFieldPathRenderer::addPathToAtlas(
-                                                                        const SkPath& path,
-                                                                        const SkStrokeRec& stroke,
-                                                                        bool antiAlias,
-                                                                        uint32_t dimension,
-                                                                        SkScalar scale) {
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
-    // generate distance field and add to atlas
-    if (NULL == fAtlas) {
-        SkISize textureSize = SkISize::Make(ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT);
-        fAtlas = SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kAlpha_8_GrPixelConfig,
-                                      kNone_GrSurfaceFlags, textureSize,
-                                      NUM_PLOTS_X, NUM_PLOTS_Y, false));
-        if (NULL == fAtlas) {
-            return NULL;
-        }
-    }
-    
-    const SkRect& bounds = path.getBounds();
-    
-    // generate bounding rect for bitmap draw
-    SkRect scaledBounds = bounds;
-    // scale to mip level size
-    scaledBounds.fLeft *= scale;
-    scaledBounds.fTop *= scale;
-    scaledBounds.fRight *= scale;
-    scaledBounds.fBottom *= scale;
-    // move the origin to an integer boundary (gives better results)
-    SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
-    SkScalar dy = SkScalarFraction(scaledBounds.fTop);
-    scaledBounds.offset(-dx, -dy);
-    // get integer boundary
-    SkIRect devPathBounds;
-    scaledBounds.roundOut(&devPathBounds);
-    // pad to allow room for antialiasing
-    devPathBounds.outset(SkScalarCeilToInt(kAntiAliasPad), SkScalarCeilToInt(kAntiAliasPad));
-    // move origin to upper left corner
-    devPathBounds.offsetTo(0,0);
-    
-    // draw path to bitmap
-    SkMatrix drawMatrix;
-    drawMatrix.setTranslate(-bounds.left(), -bounds.top());
-    drawMatrix.postScale(scale, scale);
-    drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
-    GrSWMaskHelper helper(fContext);
-    
-    if (!helper.init(devPathBounds, &drawMatrix)) {
-        return NULL;
-    }
-    helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
-    
-    // generate signed distance field
-    devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
-    int width = devPathBounds.width();
-    int height = devPathBounds.height();
-    SkAutoSMalloc<1024> dfStorage(width*height*sizeof(unsigned char));
-    helper.toSDF((unsigned char*) dfStorage.get());
-    
-    // add to atlas
-    GrPlot* plot;
-    SkIPoint16  atlasLocation;
-    if (!this->uploadPath(&plot, &atlasLocation, width, height, dfStorage.get())) {
-        return NULL;
-    }
+#ifdef GR_TEST_UTILS
 
-    // add to cache
-    PathData* pathData = SkNEW(PathData);
-    pathData->fKey.fGenID = path.getGenerationID();
-    pathData->fKey.fDimension = dimension;
-    pathData->fScale = scale;
-    pathData->fPlot = plot;
-    // change the scaled rect to match the size of the inset distance field
-    scaledBounds.fRight = scaledBounds.fLeft +
-        SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
-    scaledBounds.fBottom = scaledBounds.fTop +
-        SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
-    // shift the origin to the correct place relative to the distance field
-    // need to also restore the fractional translation
-    scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
-                        -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
-    pathData->fBounds = scaledBounds;
-    // origin we render from is inset from distance field edge
-    atlasLocation.fX += SK_DistanceFieldInset;
-    atlasLocation.fY += SK_DistanceFieldInset;
-    pathData->fAtlasLocation = atlasLocation;
-    
-    fPathCache.add(pathData);
-    fPathList.addToTail(pathData);
-#ifdef DF_PATH_TRACKING
-    ++g_NumCachedPaths;
-#endif
+struct PathTestStruct {
+    typedef GrAADistanceFieldPathRenderer::PathCache PathCache;
+    typedef GrAADistanceFieldPathRenderer::PathData PathData;
+    typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
+    PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(NULL) {}
+    ~PathTestStruct() { this->reset(); }
 
-    return pathData;
-}
-
-bool GrAADistanceFieldPathRenderer::freeUnusedPlot() {
-    // find an unused plot
-    GrPlot* plot = fAtlas->getUnusedPlot();
-    if (NULL == plot) {
-        return false;
-    }
-    plot->resetRects();
-
-    // remove any paths that use this plot
-    PathDataList::Iter iter;
-    iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
-    PathData* pathData;
-    while ((pathData = iter.get())) {
-        iter.next();
-        if (plot == pathData->fPlot) {
-            fPathCache.remove(pathData->fKey);
+    void reset() {
+        PathDataList::Iter iter;
+        iter.init(fPathList, PathDataList::Iter::kHead_IterStart);
+        PathData* pathData;
+        while ((pathData = iter.get())) {
+            iter.next();
             fPathList.remove(pathData);
             SkDELETE(pathData);
-#ifdef DF_PATH_TRACKING
-            ++g_NumFreedPaths;
-#endif
+        }
+        SkDELETE(fAtlas);
+        fPathCache.reset();
+    }
+
+    static void HandleEviction(GrBatchAtlas::AtlasID id, void* pr) {
+        PathTestStruct* dfpr = (PathTestStruct*)pr;
+        // remove any paths that use this plot
+        PathDataList::Iter iter;
+        iter.init(dfpr->fPathList, PathDataList::Iter::kHead_IterStart);
+        PathData* pathData;
+        while ((pathData = iter.get())) {
+            iter.next();
+            if (id == pathData->fID) {
+                dfpr->fPathCache.remove(pathData->fKey);
+                dfpr->fPathList.remove(pathData);
+                SkDELETE(pathData);
+            }
         }
     }
-        
-    // tell the atlas to free the plot
-    GrAtlas::RemovePlot(&fPlotUsage, plot);
-    
-    return true;
-}
 
-bool GrAADistanceFieldPathRenderer::internalDrawPath(GrDrawTarget* target,
-                                                     GrPipelineBuilder* pipelineBuilder,
-                                                     GrColor color,
-                                                     const SkMatrix& viewMatrix,
-                                                     const SkPath& path,
-                                                     const PathData* pathData) {
-    GrTexture* texture = fAtlas->getTexture();
-    GrPipelineBuilder::AutoRestoreFragmentProcessors arfp(pipelineBuilder);
-    
-    SkASSERT(pathData->fPlot);
-    GrDrawTarget::DrawToken drawToken = target->getCurrentDrawToken();
-    pathData->fPlot->setDrawToken(drawToken);
-    
-    // set up any flags
-    uint32_t flags = 0;
-    flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
+    uint32_t fContextID;
+    GrBatchAtlas* fAtlas;
+    PathCache fPathCache;
+    PathDataList fPathList;
+};
 
-    GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
-    if (flags != fEffectFlags || fCachedGeometryProcessor->color() != color ||
-        !fCachedGeometryProcessor->viewMatrix().cheapEqualTo(viewMatrix)) {
-        fCachedGeometryProcessor.reset(GrDistanceFieldNoGammaTextureEffect::Create(color,
-                                                                                   viewMatrix,
-                                                                                   texture,
-                                                                                   params,
-                                                                                   flags,
-                                                                                   false));
-        fEffectFlags = flags;
+BATCH_TEST_DEFINE(AADistanceFieldPathBatch) {
+    static PathTestStruct gTestStruct;
+
+    if (context->uniqueID() != gTestStruct.fContextID) {
+        gTestStruct.fContextID = context->uniqueID();
+        gTestStruct.reset();
+        gTestStruct.fAtlas = create_atlas(context, &PathTestStruct::HandleEviction,
+                                          (void*)&gTestStruct);
     }
 
-    void* vertices = NULL;
-    bool success = target->reserveVertexAndIndexSpace(4,
-                                                      fCachedGeometryProcessor->getVertexStride(),
-                                                      0, &vertices, NULL);
-    SkASSERT(fCachedGeometryProcessor->getVertexStride() == 2 * sizeof(SkPoint));
-    GrAlwaysAssert(success);
-    
-    SkScalar dx = pathData->fBounds.fLeft;
-    SkScalar dy = pathData->fBounds.fTop;
-    SkScalar width = pathData->fBounds.width();
-    SkScalar height = pathData->fBounds.height();
-    
-    SkScalar invScale = 1.0f/pathData->fScale;
-    dx *= invScale;
-    dy *= invScale;
-    width *= invScale;
-    height *= invScale;
-    
-    SkFixed tx = SkIntToFixed(pathData->fAtlasLocation.fX);
-    SkFixed ty = SkIntToFixed(pathData->fAtlasLocation.fY);
-    SkFixed tw = SkScalarToFixed(pathData->fBounds.width());
-    SkFixed th = SkScalarToFixed(pathData->fBounds.height());
-    
-    // vertex positions
-    SkRect r = SkRect::MakeXYWH(dx, dy, width, height);    
-    size_t vertSize = 2 * sizeof(SkPoint);
-    SkPoint* positions = reinterpret_cast<SkPoint*>(vertices);
-    positions->setRectFan(r.left(), r.top(), r.right(), r.bottom(), vertSize);
-    
-    // vertex texture coords
-    intptr_t intPtr = reinterpret_cast<intptr_t>(positions);
-    SkPoint* textureCoords = reinterpret_cast<SkPoint*>(intPtr + vertSize - sizeof(SkPoint));
-    textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
-                              SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
-                              SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
-                              SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
-                              vertSize);
-    
-    viewMatrix.mapRect(&r);
-    target->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
-    target->drawIndexedInstances(pipelineBuilder, fCachedGeometryProcessor.get(),
-                                 kTriangles_GrPrimitiveType, 1, 4, 6, &r);
-    target->resetVertexSource();
-    
-    return true;
+    SkMatrix viewMatrix = GrTest::TestMatrix(random);
+    GrColor color = GrRandomColor(random);
+
+    AADistanceFieldPathBatch::Geometry geometry(GrTest::TestStrokeRec(random));
+    geometry.fPath = GrTest::TestPath(random);
+    geometry.fAntiAlias = random->nextBool();
+
+    return AADistanceFieldPathBatch::Create(geometry, color, viewMatrix,
+                                            gTestStruct.fAtlas,
+                                            &gTestStruct.fPathCache,
+                                            &gTestStruct.fPathList);
 }
 
+#endif
diff --git a/src/gpu/GrAADistanceFieldPathRenderer.h b/src/gpu/GrAADistanceFieldPathRenderer.h
index c145e2f..bb76eff 100755
--- a/src/gpu/GrAADistanceFieldPathRenderer.h
+++ b/src/gpu/GrAADistanceFieldPathRenderer.h
@@ -9,7 +9,7 @@
 #ifndef GrAADistanceFieldPathRenderer_DEFINED
 #define GrAADistanceFieldPathRenderer_DEFINED
 
-#include "GrAtlas.h"
+#include "GrBatchAtlas.h"
 #include "GrPathRenderer.h"
 #include "GrRect.h"
 
@@ -17,7 +17,6 @@
 #include "SkTDynamicHash.h"
 
 class GrContext;
-class GrPlot;
 
 class GrAADistanceFieldPathRenderer : public GrPathRenderer {
 public:
@@ -28,22 +27,22 @@
                              const GrPipelineBuilder*,
                              const SkMatrix& viewMatrix,
                              const SkPath&,
-                             const SkStrokeRec&,
-                             bool antiAlias) const SK_OVERRIDE;
+                             const GrStrokeInfo&,
+                             bool antiAlias) const override;
 
 protected:
     virtual StencilSupport onGetStencilSupport(const GrDrawTarget*,
                                                const GrPipelineBuilder*,
                                                const SkPath&,
-                                               const SkStrokeRec&) const SK_OVERRIDE;
+                                               const GrStrokeInfo&) const override;
     
     virtual bool onDrawPath(GrDrawTarget*,
                             GrPipelineBuilder*,
                             GrColor,
                             const SkMatrix& viewMatrix,
                             const SkPath&,
-                            const SkStrokeRec&,
-                            bool antiAlias) SK_OVERRIDE;
+                            const GrStrokeInfo&,
+                            bool antiAlias) override;
 
 private:
     struct PathData {
@@ -55,11 +54,11 @@
                 return other.fGenID == fGenID && other.fDimension == fDimension;
             }
         };
-        Key        fKey;
-        SkScalar   fScale;
-        GrPlot*    fPlot;
-        SkRect     fBounds;
-        SkIPoint16 fAtlasLocation;
+        Key                   fKey;
+        SkScalar              fScale;
+        GrBatchAtlas::AtlasID fID;
+        SkRect                fBounds;
+        SkIPoint16            fAtlasLocation;
         SK_DECLARE_INTERNAL_LLIST_INTERFACE(PathData);
         
         static inline const Key& GetKey(const PathData& data) {
@@ -70,26 +69,21 @@
             return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(key));
         }
     };
+
+    static void HandleEviction(GrBatchAtlas::AtlasID, void*);
+
+    typedef SkTDynamicHash<PathData, PathData::Key> PathCache;
     typedef SkTInternalLList<PathData> PathDataList;
     
     GrContext*                         fContext;
-    GrAtlas*                           fAtlas;
-    SkAutoTUnref<GrGeometryProcessor>  fCachedGeometryProcessor;
-    // current set of flags used to create the cached geometry processor
-    uint32_t                           fEffectFlags;
-    GrAtlas::ClientPlotUsage           fPlotUsage;
-    SkTDynamicHash<PathData, PathData::Key> fPathCache;
+    GrBatchAtlas*                      fAtlas;
+    PathCache                          fPathCache;
     PathDataList                       fPathList;
     
-    bool internalDrawPath(GrDrawTarget*, GrPipelineBuilder*, GrColor, const SkMatrix& viewMatrix,
-                          const SkPath& path, const PathData* pathData);
-    inline bool uploadPath(GrPlot** plot, SkIPoint16* atlasLocation, int width, int height,
-                           void* dfStorage);
-    PathData* addPathToAtlas(const SkPath& path, const SkStrokeRec& stroke, bool antiAlias,
-                             uint32_t dimension, SkScalar scale);
-    bool freeUnusedPlot();
-    
     typedef GrPathRenderer INHERITED;
+
+    friend class AADistanceFieldPathBatch;
+    friend struct PathTestStruct;
 };
 
 #endif
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index 26cf607..dc035fc 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -9,21 +9,24 @@
 
 #include "GrBatch.h"
 #include "GrBatchTarget.h"
-#include "GrBufferAllocPool.h"
+#include "GrBatchTest.h"
 #include "GrContext.h"
 #include "GrDefaultGeoProcFactory.h"
 #include "GrDrawTargetCaps.h"
-#include "GrGpu.h"
 #include "GrIndexBuffer.h"
 #include "GrPathUtils.h"
 #include "GrPipelineBuilder.h"
 #include "GrProcessor.h"
+#include "GrResourceProvider.h"
+#include "GrVertexBuffer.h"
 #include "SkGeometry.h"
 #include "SkStroke.h"
 #include "SkTemplates.h"
 
 #include "effects/GrBezierEffect.h"
 
+#define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true>
+
 // quadratics are rendered as 5-sided polys in order to bound the
 // AA stroke around the center-curve. See comments in push_quad_index_buffer and
 // bloat_quad. Quadratics and conics share an index buffer
@@ -59,6 +62,14 @@
 static const int kIdxsPerQuad = SK_ARRAY_COUNT(kQuadIdxBufPattern);
 static const int kQuadNumVertices = 5;
 static const int kQuadsNumInIdxBuffer = 256;
+GR_DECLARE_STATIC_UNIQUE_KEY(gQuadsIndexBufferKey);
+
+static const GrIndexBuffer* ref_quads_index_buffer(GrResourceProvider* resourceProvider) {
+    GR_DEFINE_STATIC_UNIQUE_KEY(gQuadsIndexBufferKey);
+    return resourceProvider->refOrCreateInstancedIndexBuffer(
+        kQuadIdxBufPattern, kIdxsPerQuad, kQuadsNumInIdxBuffer, kQuadNumVertices,
+        gQuadsIndexBufferKey);
+}
 
 
 // Each line segment is rendered as two quads and two triangles.
@@ -85,43 +96,17 @@
 static const int kLineSegNumVertices = 6;
 static const int kLineSegsNumInIdxBuffer = 256;
 
-GrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) {
-    GrGpu* gpu = context->getGpu();
-    GrIndexBuffer* qIdxBuf = gpu->createInstancedIndexBuffer(kQuadIdxBufPattern,
-                                                             kIdxsPerQuad,
-                                                             kQuadsNumInIdxBuffer,
-                                                             kQuadNumVertices);
-    SkAutoTUnref<GrIndexBuffer> qIdxBuffer(qIdxBuf);
-    GrIndexBuffer* lIdxBuf = gpu->createInstancedIndexBuffer(kLineSegIdxBufPattern,
-                                                             kIdxsPerLineSeg,
-                                                             kLineSegsNumInIdxBuffer,
-                                                             kLineSegNumVertices);
-    SkAutoTUnref<GrIndexBuffer> lIdxBuffer(lIdxBuf);
-    return SkNEW_ARGS(GrAAHairLinePathRenderer,
-                      (context, lIdxBuf, qIdxBuf));
+GR_DECLARE_STATIC_UNIQUE_KEY(gLinesIndexBufferKey);
+
+static const GrIndexBuffer* ref_lines_index_buffer(GrResourceProvider* resourceProvider) {
+    GR_DEFINE_STATIC_UNIQUE_KEY(gLinesIndexBufferKey);
+    return resourceProvider->refOrCreateInstancedIndexBuffer(
+        kLineSegIdxBufPattern, kIdxsPerLineSeg,  kLineSegsNumInIdxBuffer, kLineSegNumVertices,
+        gLinesIndexBufferKey);
 }
 
-GrAAHairLinePathRenderer::GrAAHairLinePathRenderer(
-                                        const GrContext* context,
-                                        const GrIndexBuffer* linesIndexBuffer,
-                                        const GrIndexBuffer* quadsIndexBuffer) {
-    fLinesIndexBuffer = linesIndexBuffer;
-    linesIndexBuffer->ref();
-    fQuadsIndexBuffer = quadsIndexBuffer;
-    quadsIndexBuffer->ref();
-}
-
-GrAAHairLinePathRenderer::~GrAAHairLinePathRenderer() {
-    fLinesIndexBuffer->unref();
-    fQuadsIndexBuffer->unref();
-}
-
-namespace {
-
-#define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true>
-
 // Takes 178th time of logf on Z600 / VC2010
-int get_float_exp(float x) {
+static int get_float_exp(float x) {
     GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
 #ifdef SK_DEBUG
     static bool tested;
@@ -149,7 +134,7 @@
 // found along the curve segment it will return 1 and
 // dst[0] is the original conic. If it returns 2 the dst[0]
 // and dst[1] are the two new conics.
-int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
+static int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
     SkScalar t = SkFindQuadMaxCurvature(src);
     if (t == 0) {
         if (dst) {
@@ -169,7 +154,7 @@
 // Calls split_conic on the entire conic and then once more on each subsection.
 // Most cases will result in either 1 conic (chop point is not within t range)
 // or 3 points (split once and then one subsection is split again).
-int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
+static int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
     SkConic dstTemp[2];
     int conicCnt = split_conic(src, dstTemp, weight);
     if (2 == conicCnt) {
@@ -184,8 +169,8 @@
 // returns 0 if quad/conic is degen or close to it
 // in this case approx the path with lines
 // otherwise returns 1
-int is_degen_quad_or_conic(const SkPoint p[3], SkScalar* dsqd) {
-    static const SkScalar gDegenerateToLineTol = SK_Scalar1;
+static int is_degen_quad_or_conic(const SkPoint p[3], SkScalar* dsqd) {
+    static const SkScalar gDegenerateToLineTol = GrPathUtils::kDefaultTolerance;
     static const SkScalar gDegenerateToLineTolSqd =
         SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
 
@@ -205,14 +190,14 @@
     return 0;
 }
 
-int is_degen_quad_or_conic(const SkPoint p[3]) {
+static int is_degen_quad_or_conic(const SkPoint p[3]) {
     SkScalar dsqd;
     return is_degen_quad_or_conic(p, &dsqd);
 }
 
 // we subdivide the quads to avoid huge overfill
 // if it returns -1 then should be drawn as lines
-int num_quad_subdivs(const SkPoint p[3]) {
+static int num_quad_subdivs(const SkPoint p[3]) {
     SkScalar dsqd;
     if (is_degen_quad_or_conic(p, &dsqd)) {
         return -1;
@@ -248,14 +233,14 @@
  * subdivide large quads to reduce over-fill. This subdivision has to be
  * performed before applying the perspective matrix.
  */
-int gather_lines_and_quads(const SkPath& path,
-                           const SkMatrix& m,
-                           const SkIRect& devClipBounds,
-                           GrAAHairLinePathRenderer::PtArray* lines,
-                           GrAAHairLinePathRenderer::PtArray* quads,
-                           GrAAHairLinePathRenderer::PtArray* conics,
-                           GrAAHairLinePathRenderer::IntArray* quadSubdivCnts,
-                           GrAAHairLinePathRenderer::FloatArray* conicWeights) {
+static int gather_lines_and_quads(const SkPath& path,
+                                  const SkMatrix& m,
+                                  const SkIRect& devClipBounds,
+                                  GrAAHairLinePathRenderer::PtArray* lines,
+                                  GrAAHairLinePathRenderer::PtArray* quads,
+                                  GrAAHairLinePathRenderer::PtArray* conics,
+                                  GrAAHairLinePathRenderer::IntArray* quadSubdivCnts,
+                                  GrAAHairLinePathRenderer::FloatArray* conicWeights) {
     SkPath::Iter iter(path, false);
 
     int totalQuadCount = 0;
@@ -439,9 +424,9 @@
 
 GR_STATIC_ASSERT(sizeof(BezierVertex) == 3 * sizeof(SkPoint));
 
-void intersect_lines(const SkPoint& ptA, const SkVector& normA,
-                     const SkPoint& ptB, const SkVector& normB,
-                     SkPoint* result) {
+static void intersect_lines(const SkPoint& ptA, const SkVector& normA,
+                            const SkPoint& ptB, const SkVector& normB,
+                            SkPoint* result) {
 
     SkScalar lineAW = -normA.dot(ptA);
     SkScalar lineBW = -normB.dot(ptB);
@@ -457,14 +442,14 @@
     result->fY = SkScalarMul(result->fY, wInv);
 }
 
-void set_uv_quad(const SkPoint qpts[3], BezierVertex verts[kQuadNumVertices]) {
+static void set_uv_quad(const SkPoint qpts[3], BezierVertex verts[kQuadNumVertices]) {
     // this should be in the src space, not dev coords, when we have perspective
     GrPathUtils::QuadUVMatrix DevToUV(qpts);
     DevToUV.apply<kQuadNumVertices, sizeof(BezierVertex), sizeof(SkPoint)>(verts);
 }
 
-void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice,
-                const SkMatrix* toSrc, BezierVertex verts[kQuadNumVertices]) {
+static void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice,
+                       const SkMatrix* toSrc, BezierVertex verts[kQuadNumVertices]) {
     SkASSERT(!toDevice == !toSrc);
     // original quad is specified by tri a,b,c
     SkPoint a = qpts[0];
@@ -542,8 +527,8 @@
 // f(x, y, w) = f(P) = K^2 - LM
 // K = dot(k, P), L = dot(l, P), M = dot(m, P)
 // k, l, m are calculated in function GrPathUtils::getConicKLM
-void set_conic_coeffs(const SkPoint p[3], BezierVertex verts[kQuadNumVertices],
-                      const SkScalar weight) {
+static void set_conic_coeffs(const SkPoint p[3], BezierVertex verts[kQuadNumVertices],
+                             const SkScalar weight) {
     SkScalar klm[9];
 
     GrPathUtils::getConicKLM(p, weight, klm);
@@ -556,21 +541,21 @@
     }
 }
 
-void add_conics(const SkPoint p[3],
-                const SkScalar weight,
-                const SkMatrix* toDevice,
-                const SkMatrix* toSrc,
-                BezierVertex** vert) {
+static void add_conics(const SkPoint p[3],
+                       const SkScalar weight,
+                       const SkMatrix* toDevice,
+                       const SkMatrix* toSrc,
+                       BezierVertex** vert) {
     bloat_quad(p, toDevice, toSrc, *vert);
     set_conic_coeffs(p, *vert, weight);
     *vert += kQuadNumVertices;
 }
 
-void add_quads(const SkPoint p[3],
-               int subdiv,
-               const SkMatrix* toDevice,
-               const SkMatrix* toSrc,
-               BezierVertex** vert) {
+static void add_quads(const SkPoint p[3],
+                      int subdiv,
+                      const SkMatrix* toDevice,
+                      const SkMatrix* toSrc,
+                      BezierVertex** vert) {
     SkASSERT(subdiv >= 0);
     if (subdiv) {
         SkPoint newP[5];
@@ -584,10 +569,10 @@
     }
 }
 
-void add_line(const SkPoint p[2],
-              const SkMatrix* toSrc,
-              uint8_t coverage,
-              LineVertex** vert) {
+static void add_line(const SkPoint p[2],
+                     const SkMatrix* toSrc,
+                     uint8_t coverage,
+                     LineVertex** vert) {
     const SkPoint& a = p[0];
     const SkPoint& b = p[1];
 
@@ -629,15 +614,13 @@
     *vert += kLineSegNumVertices;
 }
 
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 bool GrAAHairLinePathRenderer::canDrawPath(const GrDrawTarget* target,
                                            const GrPipelineBuilder* pipelineBuilder,
                                            const SkMatrix& viewMatrix,
                                            const SkPath& path,
-                                           const SkStrokeRec& stroke,
+                                           const GrStrokeInfo& stroke,
                                            bool antiAlias) const {
     if (!antiAlias) {
         return false;
@@ -648,7 +631,7 @@
     }
 
     if (SkPath::kLine_SegmentMask == path.getSegmentMasks() ||
-        target->caps()->shaderDerivativeSupport()) {
+        target->caps()->shaderCaps()->shaderDerivativeSupport()) {
         return true;
     }
     return false;
@@ -698,28 +681,24 @@
         uint8_t fCoverage;
         SkMatrix fViewMatrix;
         SkPath fPath;
-        SkDEBUGCODE(SkRect fDevBounds;)
         SkIRect fDevClipBounds;
     };
 
-    // TODO Batch itself should not hold on to index buffers.  Instead, these should live in the
-    // cache.
-    static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* linesIndexBuffer,
-                           const GrIndexBuffer* quadsIndexBuffer) {
-        return SkNEW_ARGS(AAHairlineBatch, (geometry, linesIndexBuffer, quadsIndexBuffer));
+    static GrBatch* Create(const Geometry& geometry) {
+        return SkNEW_ARGS(AAHairlineBatch, (geometry));
     }
 
-    const char* name() const SK_OVERRIDE { return "AAHairlineBatch"; }
+    const char* name() const override { return "AAHairlineBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -733,10 +712,9 @@
         fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
         fBatch.fCoverageIgnored = init.fCoverageIgnored;
         fBatch.fCoverage = fGeoData[0].fCoverage;
-        SkDEBUGCODE(fBatch.fDevBounds = fGeoData[0].fDevBounds;)
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE;
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override;
 
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
 
@@ -745,16 +723,20 @@
     typedef SkTArray<int, true> IntArray;
     typedef SkTArray<float, true> FloatArray;
 
-    AAHairlineBatch(const Geometry& geometry, const GrIndexBuffer* linesIndexBuffer,
-                    const GrIndexBuffer* quadsIndexBuffer)
-        : fLinesIndexBuffer(linesIndexBuffer)
-        , fQuadsIndexBuffer(quadsIndexBuffer) {
-        SkASSERT(linesIndexBuffer && quadsIndexBuffer);
+    AAHairlineBatch(const Geometry& geometry) {
         this->initClassID<AAHairlineBatch>();
         fGeoData.push_back(geometry);
+
+        // compute bounds
+        fBounds = geometry.fPath.getBounds();
+        geometry.fViewMatrix.mapRect(&fBounds);
+
+        // This is b.c. hairlines are notionally infinitely thin so without expansion
+        // two overlapping lines could be reordered even though they hit the same pixels.
+        fBounds.outset(0.5f, 0.5f);
     }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         AAHairlineBatch* that = t->cast<AAHairlineBatch>();
 
         if (this->viewMatrix().hasPerspective() != that->viewMatrix().hasPerspective()) {
@@ -784,6 +766,7 @@
         }
 
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
         return true;
     }
 
@@ -803,8 +786,6 @@
 
     BatchTracker fBatch;
     SkSTArray<1, Geometry, true> fGeoData;
-    const GrIndexBuffer* fLinesIndexBuffer;
-    const GrIndexBuffer* fQuadsIndexBuffer;
 };
 
 void AAHairlineBatch::generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) {
@@ -836,7 +817,6 @@
                                             this->color(),
                                             *geometryProcessorViewM,
                                             *geometryProcessorLocalM,
-                                            false,
                                             this->coverage()));
 
     SkAutoTUnref<const GrGeometryProcessor> quadGP(
@@ -875,6 +855,8 @@
 
     // do lines first
     if (lineCount) {
+        SkAutoTUnref<const GrIndexBuffer> linesIndexBuffer(
+            ref_lines_index_buffer(batchTarget->resourceProvider()));
         batchTarget->initDraw(lineGP, pipeline);
 
         // TODO remove this when batch is everywhere
@@ -890,41 +872,26 @@
 
         size_t vertexStride = lineGP->getVertexStride();
         int vertexCount = kLineSegNumVertices * lineCount;
-        void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              vertexCount,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
+        LineVertex* verts = reinterpret_cast<LineVertex*>(
+            batchTarget->makeVertSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex));
 
-        if (!vertices) {
+        if (!verts|| !linesIndexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
         SkASSERT(lineGP->getVertexStride() == sizeof(LineVertex));
 
-        LineVertex* verts = reinterpret_cast<LineVertex*>(vertices);
         for (int i = 0; i < lineCount; ++i) {
             add_line(&lines[2*i], toSrc, this->coverage(), &verts);
         }
 
         {
-            GrDrawTarget::DrawInfo info;
-            info.setVertexBuffer(vertexBuffer);
-            info.setIndexBuffer(fLinesIndexBuffer);
-            info.setPrimitiveType(kTriangles_GrPrimitiveType);
-            info.setStartIndex(0);
-
-            int lines = 0;
-            while (lines < lineCount) {
-                int n = SkTMin(lineCount - lines, kLineSegsNumInIdxBuffer);
-
-                info.setStartVertex(kLineSegNumVertices*lines + firstVertex);
-                info.setVertexCount(kLineSegNumVertices*n);
-                info.setIndexCount(kIdxsPerLineSeg*n);
-                batchTarget->draw(info);
-
-                lines += n;
-            }
+            GrVertices vertices;
+            vertices.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, linesIndexBuffer,
+                                   firstVertex, kLineSegNumVertices, kIdxsPerLineSeg, lineCount,
+                                   kLineSegsNumInIdxBuffer);
+            batchTarget->draw(vertices);
         }
     }
 
@@ -932,14 +899,15 @@
         const GrVertexBuffer* vertexBuffer;
         int firstVertex;
 
+        SkAutoTUnref<const GrIndexBuffer> quadsIndexBuffer(
+            ref_quads_index_buffer(batchTarget->resourceProvider()));
+
         size_t vertexStride = sizeof(BezierVertex);
         int vertexCount = kQuadNumVertices * quadCount + kQuadNumVertices * conicCount;
-        void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              vertexCount,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
+        void *vertices = batchTarget->makeVertSpace(vertexStride, vertexCount,
+                                                    &vertexBuffer, &firstVertex);
 
-        if (!vertices) {
+        if (!vertices || !quadsIndexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
@@ -970,23 +938,12 @@
             quadGP->initBatchTracker(batchTarget->currentBatchTracker(), init);
 
             {
-               GrDrawTarget::DrawInfo info;
-               info.setVertexBuffer(vertexBuffer);
-               info.setIndexBuffer(fQuadsIndexBuffer);
-               info.setPrimitiveType(kTriangles_GrPrimitiveType);
-               info.setStartIndex(0);
-
-               int quads = 0;
-               while (quads < quadCount) {
-                   int n = SkTMin(quadCount - quads, kQuadsNumInIdxBuffer);
-
-                   info.setStartVertex(kQuadNumVertices*quads + firstVertex);
-                   info.setVertexCount(kQuadNumVertices*n);
-                   info.setIndexCount(kIdxsPerQuad*n);
-                   batchTarget->draw(info);
-
-                   quads += n;
-               }
+                GrVertices verts;
+                verts.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, quadsIndexBuffer,
+                                    firstVertex, kQuadNumVertices, kIdxsPerQuad, quadCount,
+                                    kQuadsNumInIdxBuffer);
+                batchTarget->draw(verts);
+                firstVertex += quadCount * kQuadNumVertices;
            }
         }
 
@@ -1002,67 +959,67 @@
             conicGP->initBatchTracker(batchTarget->currentBatchTracker(), init);
 
             {
-                GrDrawTarget::DrawInfo info;
-                info.setVertexBuffer(vertexBuffer);
-                info.setIndexBuffer(fQuadsIndexBuffer);
-                info.setPrimitiveType(kTriangles_GrPrimitiveType);
-                info.setStartIndex(0);
-
-                int conics = 0;
-                while (conics < conicCount) {
-                    int n = SkTMin(conicCount - conics, kQuadsNumInIdxBuffer);
-
-                    info.setStartVertex(kQuadNumVertices*(quadCount + conics) + firstVertex);
-                    info.setVertexCount(kQuadNumVertices*n);
-                    info.setIndexCount(kIdxsPerQuad*n);
-                    batchTarget->draw(info);
-
-                    conics += n;
-                }
+                GrVertices verts;
+                verts.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, quadsIndexBuffer,
+                                    firstVertex, kQuadNumVertices, kIdxsPerQuad, conicCount,
+                                    kQuadsNumInIdxBuffer);
+                batchTarget->draw(verts);
             }
         }
     }
 }
 
+static GrBatch* create_hairline_batch(GrColor color,
+                                      const SkMatrix& viewMatrix,
+                                      const SkPath& path,
+                                      const GrStrokeInfo& stroke,
+                                      const SkIRect& devClipBounds) {
+    SkScalar hairlineCoverage;
+    uint8_t newCoverage = 0xff;
+    if (GrPathRenderer::IsStrokeHairlineOrEquivalent(stroke, viewMatrix, &hairlineCoverage)) {
+        newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
+    }
+
+    AAHairlineBatch::Geometry geometry;
+    geometry.fColor = color;
+    geometry.fCoverage = newCoverage;
+    geometry.fViewMatrix = viewMatrix;
+    geometry.fPath = path;
+    geometry.fDevClipBounds = devClipBounds;
+
+    return AAHairlineBatch::Create(geometry);
+}
+
 bool GrAAHairLinePathRenderer::onDrawPath(GrDrawTarget* target,
                                           GrPipelineBuilder* pipelineBuilder,
                                           GrColor color,
                                           const SkMatrix& viewMatrix,
                                           const SkPath& path,
-                                          const SkStrokeRec& stroke,
+                                          const GrStrokeInfo& stroke,
                                           bool) {
-    if (!fLinesIndexBuffer || !fQuadsIndexBuffer) {
-        SkDebugf("unable to allocate indices\n");
-        return false;
-    }
-
-    SkScalar hairlineCoverage;
-    uint8_t newCoverage = 0xff;
-    if (IsStrokeHairlineOrEquivalent(stroke, viewMatrix, &hairlineCoverage)) {
-        newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
-    }
-
     SkIRect devClipBounds;
     pipelineBuilder->clip().getConservativeBounds(pipelineBuilder->getRenderTarget(),
                                                   &devClipBounds);
 
-    // This outset was determined experimentally by running skps and gms.  It probably could be a
-    // bit tighter
-    SkRect devRect = path.getBounds();
-    viewMatrix.mapRect(&devRect);
-    devRect.outset(2, 2);
-
-    AAHairlineBatch::Geometry geometry;
-    geometry.fColor = color;
-    geometry.fCoverage = newCoverage;
-    geometry.fViewMatrix = viewMatrix;
-    geometry.fPath = path;
-    SkDEBUGCODE(geometry.fDevBounds = devRect;)
-    geometry.fDevClipBounds = devClipBounds;
-
-    SkAutoTUnref<GrBatch> batch(AAHairlineBatch::Create(geometry, fLinesIndexBuffer,
-                                                        fQuadsIndexBuffer));
-    target->drawBatch(pipelineBuilder, batch, &devRect);
+    SkAutoTUnref<GrBatch> batch(create_hairline_batch(color, viewMatrix, path, stroke,
+                                                      devClipBounds));
+    target->drawBatch(pipelineBuilder, batch);
 
     return true;
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef GR_TEST_UTILS
+
+BATCH_TEST_DEFINE(AAHairlineBatch) {
+    GrColor color = GrRandomColor(random);
+    SkMatrix viewMatrix = GrTest::TestMatrix(random);
+    GrStrokeInfo stroke(SkStrokeRec::kHairline_InitStyle);
+    SkPath path = GrTest::TestPath(random);
+    SkIRect devClipBounds;
+    devClipBounds.setEmpty();
+    return create_hairline_batch(color, viewMatrix, path, stroke, devClipBounds);
+}
+
+#endif
diff --git a/src/gpu/GrAAHairLinePathRenderer.h b/src/gpu/GrAAHairLinePathRenderer.h
index ea1d8ed..b523493 100644
--- a/src/gpu/GrAAHairLinePathRenderer.h
+++ b/src/gpu/GrAAHairLinePathRenderer.h
@@ -13,37 +13,30 @@
 
 class GrAAHairLinePathRenderer : public GrPathRenderer {
 public:
-    virtual ~GrAAHairLinePathRenderer();
+    static GrPathRenderer* Create()  { return SkNEW(GrAAHairLinePathRenderer); }
 
-    static GrPathRenderer* Create(GrContext* context);
-
-    virtual bool canDrawPath(const GrDrawTarget*,
-                             const GrPipelineBuilder*,
-                             const SkMatrix& viewMatrix,
-                             const SkPath&,
-                             const SkStrokeRec&,
-                             bool antiAlias) const SK_OVERRIDE;
+    bool canDrawPath(const GrDrawTarget*,
+                     const GrPipelineBuilder*,
+                     const SkMatrix& viewMatrix,
+                     const SkPath&,
+                     const GrStrokeInfo&,
+                     bool antiAlias) const override;
 
     typedef SkTArray<SkPoint, true> PtArray;
     typedef SkTArray<int, true> IntArray;
     typedef SkTArray<float, true> FloatArray;
 
 protected:
-    virtual bool onDrawPath(GrDrawTarget*,
-                            GrPipelineBuilder*,
-                            GrColor,
-                            const SkMatrix& viewMatrix,
-                            const SkPath&,
-                            const SkStrokeRec&,
-                            bool antiAlias) SK_OVERRIDE;
+    bool onDrawPath(GrDrawTarget*,
+                    GrPipelineBuilder*,
+                    GrColor,
+                    const SkMatrix& viewMatrix,
+                    const SkPath&,
+                    const GrStrokeInfo&,
+                    bool antiAlias) override;
 
 private:
-    GrAAHairLinePathRenderer(const GrContext* context,
-                             const GrIndexBuffer* fLinesIndexBuffer,
-                             const GrIndexBuffer* fQuadsIndexBuffer);
-
-    const GrIndexBuffer*        fLinesIndexBuffer;
-    const GrIndexBuffer*        fQuadsIndexBuffer;
+    GrAAHairLinePathRenderer() {}
 
     typedef GrPathRenderer INHERITED;
 };
diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
index 6affc92..9243e21 100644
--- a/src/gpu/GrAARectRenderer.cpp
+++ b/src/gpu/GrAARectRenderer.cpp
@@ -8,11 +8,15 @@
 #include "GrAARectRenderer.h"
 #include "GrBatch.h"
 #include "GrBatchTarget.h"
-#include "GrBufferAllocPool.h"
+#include "GrBatchTest.h"
+#include "GrContext.h"
 #include "GrDefaultGeoProcFactory.h"
 #include "GrGeometryProcessor.h"
-#include "GrGpu.h"
 #include "GrInvariantOutput.h"
+#include "GrResourceKey.h"
+#include "GrResourceProvider.h"
+#include "GrTestUtils.h"
+#include "GrVertexBuffer.h"
 #include "SkColorPriv.h"
 #include "gl/GrGLProcessor.h"
 #include "gl/GrGLGeometryProcessor.h"
@@ -26,33 +30,21 @@
                     r.fRight - dx, r.fBottom - dy, stride);
 }
 
-static const uint16_t gFillAARectIdx[] = {
-    0, 1, 5, 5, 4, 0,
-    1, 2, 6, 6, 5, 1,
-    2, 3, 7, 7, 6, 2,
-    3, 0, 4, 4, 7, 3,
-    4, 5, 6, 6, 7, 4,
-};
-
-static const int kIndicesPerAAFillRect = SK_ARRAY_COUNT(gFillAARectIdx);
-static const int kVertsPerAAFillRect = 8;
-static const int kNumAAFillRectsInIndexBuffer = 256;
-
 static const GrGeometryProcessor* create_fill_rect_gp(bool tweakAlphaForCoverage,
                                                       const SkMatrix& localMatrix) {
     uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
     const GrGeometryProcessor* gp;
     if (tweakAlphaForCoverage) {
-        gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix,
-                                             false, 0xff);
+        gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix);
     } else {
         flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
-        gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix,
-                                             false, 0xff);
+        gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix);
     }
     return gp;
 }
 
+GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
+
 class AAFillRectBatch : public GrBatch {
 public:
     struct Geometry {
@@ -62,22 +54,22 @@
         SkRect fDevRect;
     };
 
-    static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
-        return SkNEW_ARGS(AAFillRectBatch, (geometry, indexBuffer));
+    static GrBatch* Create(const Geometry& geometry) {
+        return SkNEW_ARGS(AAFillRectBatch, (geometry));
     }
 
-    const char* name() const SK_OVERRIDE { return "AAFillRectBatch"; }
+    const char* name() const override { return "AAFillRectBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
 
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -93,7 +85,7 @@
         fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
 
         SkMatrix localMatrix;
@@ -118,23 +110,18 @@
         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
 
         size_t vertexStride = gp->getVertexStride();
-
         SkASSERT(canTweakAlphaForCoverage ?
                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
-
         int instanceCount = fGeoData.count();
-        int vertexCount = kVertsPerAAFillRect * instanceCount;
 
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
-
-        void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              vertexCount,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices) {
+        SkAutoTUnref<const GrIndexBuffer> indexBuffer(this->getIndexBuffer(
+            batchTarget->resourceProvider()));
+        InstancedHelper helper;
+        void* vertices = helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride,
+                                     indexBuffer, kVertsPerAAFillRect, kIndicesPerAAFillRect,
+                                     instanceCount);
+        if (!vertices || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
@@ -151,37 +138,37 @@
                                              canTweakAlphaForCoverage);
         }
 
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
-        drawInfo.setStartVertex(0);
-        drawInfo.setStartIndex(0);
-        drawInfo.setVerticesPerInstance(kVertsPerAAFillRect);
-        drawInfo.setIndicesPerInstance(kIndicesPerAAFillRect);
-        drawInfo.adjustStartVertex(firstVertex);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setIndexBuffer(fIndexBuffer);
-
-        int maxInstancesPerDraw = kNumAAFillRectsInIndexBuffer;
-
-        while (instanceCount) {
-            drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
-            drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
-            drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
-
-            batchTarget->draw(drawInfo);
-
-            drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
-            instanceCount -= drawInfo.instanceCount();
-        }
+        helper.issueDraw(batchTarget);
     }
 
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
 
 private:
-    AAFillRectBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
-        : fIndexBuffer(indexBuffer) {
+    AAFillRectBatch(const Geometry& geometry) {
         this->initClassID<AAFillRectBatch>();
         fGeoData.push_back(geometry);
+
+        this->setBounds(geometry.fDevRect);
+    }
+
+    static const int kNumAAFillRectsInIndexBuffer = 256;
+    static const int kVertsPerAAFillRect = 8;
+    static const int kIndicesPerAAFillRect = 30;
+
+    const GrIndexBuffer* getIndexBuffer(GrResourceProvider* resourceProvider) {
+        GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
+
+        static const uint16_t gFillAARectIdx[] = {
+            0, 1, 5, 5, 4, 0,
+            1, 2, 6, 6, 5, 1,
+            2, 3, 7, 7, 6, 2,
+            3, 0, 4, 4, 7, 3,
+            4, 5, 6, 6, 7, 4,
+        };
+        GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
+        return resourceProvider->refOrCreateInstancedIndexBuffer(gFillAARectIdx,
+            kIndicesPerAAFillRect, kNumAAFillRectsInIndexBuffer, kVertsPerAAFillRect,
+            gAAFillRectIndexBufferKey);
     }
 
     GrColor color() const { return fBatch.fColor; }
@@ -190,7 +177,7 @@
     bool colorIgnored() const { return fBatch.fColorIgnored; }
     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         AAFillRectBatch* that = t->cast<AAFillRectBatch>();
 
         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
@@ -212,6 +199,7 @@
         }
 
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
         return true;
     }
 
@@ -317,7 +305,6 @@
     };
 
     BatchTracker fBatch;
-    const GrIndexBuffer* fIndexBuffer;
     SkSTArray<1, Geometry, true> fGeoData;
 };
 
@@ -329,147 +316,21 @@
 };
 }
 
-void GrAARectRenderer::reset() {
-    SkSafeSetNull(fAAFillRectIndexBuffer);
-    SkSafeSetNull(fAAMiterStrokeRectIndexBuffer);
-    SkSafeSetNull(fAABevelStrokeRectIndexBuffer);
-}
-
-static const uint16_t gMiterStrokeAARectIdx[] = {
-    0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
-    1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
-    2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
-    3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
-
-    0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
-    1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
-    2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
-    3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
-
-    0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
-    1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
-    2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
-    3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
-};
-
-static const int kIndicesPerMiterStrokeRect = SK_ARRAY_COUNT(gMiterStrokeAARectIdx);
-static const int kVertsPerMiterStrokeRect = 16;
-static const int kNumMiterStrokeRectsInIndexBuffer = 256;
-
-/**
- * As in miter-stroke, index = a + b, and a is the current index, b is the shift
- * from the first index. The index layout:
- * outer AA line: 0~3, 4~7
- * outer edge:    8~11, 12~15
- * inner edge:    16~19
- * inner AA line: 20~23
- * Following comes a bevel-stroke rect and its indices:
- *
- *           4                                 7
- *            *********************************
- *          *   ______________________________  *
- *         *  / 12                          15 \  *
- *        *  /                                  \  *
- *     0 *  |8     16_____________________19  11 |  * 3
- *       *  |       |                    |       |  *
- *       *  |       |  ****************  |       |  *
- *       *  |       |  * 20        23 *  |       |  *
- *       *  |       |  *              *  |       |  *
- *       *  |       |  * 21        22 *  |       |  *
- *       *  |       |  ****************  |       |  *
- *       *  |       |____________________|       |  *
- *     1 *  |9    17                      18   10|  * 2
- *        *  \                                  /  *
- *         *  \13 __________________________14/  *
- *          *                                   *
- *           **********************************
- *          5                                  6
- */
-static const uint16_t gBevelStrokeAARectIdx[] = {
-    // Draw outer AA, from outer AA line to outer edge, shift is 0.
-    0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0,
-    1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0,
-    5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
-    6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
-    2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
-    3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
-    7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
-    4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0,
-
-    // Draw the stroke, from outer edge to inner edge, shift is 8.
-    0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
-    1 + 8, 5 + 8, 9 + 8,
-    5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
-    6 + 8, 2 + 8, 10 + 8,
-    2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
-    3 + 8, 7 + 8, 11 + 8,
-    7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
-    4 + 8, 0 + 8, 8 + 8,
-
-    // Draw the inner AA, from inner edge to inner AA line, shift is 16.
-    0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
-    1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
-    2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
-    3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
-};
-
-static const int kIndicesPerBevelStrokeRect = SK_ARRAY_COUNT(gBevelStrokeAARectIdx);
-static const int kVertsPerBevelStrokeRect = 24;
-static const int kNumBevelStrokeRectsInIndexBuffer = 256;
-
-static int aa_stroke_rect_index_count(bool miterStroke) {
-    return miterStroke ? SK_ARRAY_COUNT(gMiterStrokeAARectIdx) :
-                         SK_ARRAY_COUNT(gBevelStrokeAARectIdx);
-}
-
-GrIndexBuffer* GrAARectRenderer::aaStrokeRectIndexBuffer(bool miterStroke) {
-    if (miterStroke) {
-        if (NULL == fAAMiterStrokeRectIndexBuffer) {
-            fAAMiterStrokeRectIndexBuffer =
-                    fGpu->createInstancedIndexBuffer(gMiterStrokeAARectIdx,
-                                                     kIndicesPerMiterStrokeRect,
-                                                     kNumMiterStrokeRectsInIndexBuffer,
-                                                     kVertsPerMiterStrokeRect);
-        }
-        return fAAMiterStrokeRectIndexBuffer;
-    } else {
-        if (NULL == fAABevelStrokeRectIndexBuffer) {
-            fAABevelStrokeRectIndexBuffer =
-                    fGpu->createInstancedIndexBuffer(gBevelStrokeAARectIdx,
-                                                     kIndicesPerBevelStrokeRect,
-                                                     kNumBevelStrokeRectsInIndexBuffer,
-                                                     kVertsPerBevelStrokeRect);
-        }
-        return fAABevelStrokeRectIndexBuffer;
-    }
-}
-
 void GrAARectRenderer::geometryFillAARect(GrDrawTarget* target,
                                           GrPipelineBuilder* pipelineBuilder,
                                           GrColor color,
                                           const SkMatrix& viewMatrix,
                                           const SkRect& rect,
                                           const SkRect& devRect) {
-    if (!fAAFillRectIndexBuffer) {
-        fAAFillRectIndexBuffer = fGpu->createInstancedIndexBuffer(gFillAARectIdx,
-                                                                  kIndicesPerAAFillRect,
-                                                                  kNumAAFillRectsInIndexBuffer,
-                                                                  kVertsPerAAFillRect);
-    }
-
-    if (!fAAFillRectIndexBuffer) {
-        SkDebugf("Unable to create index buffer\n");
-        return;
-    }
-
     AAFillRectBatch::Geometry geometry;
     geometry.fRect = rect;
     geometry.fViewMatrix = viewMatrix;
     geometry.fDevRect = devRect;
     geometry.fColor = color;
 
-    SkAutoTUnref<GrBatch> batch(AAFillRectBatch::Create(geometry, fAAFillRectIndexBuffer));
-    target->drawBatch(pipelineBuilder, batch, &devRect);
+
+    SkAutoTUnref<GrBatch> batch(AAFillRectBatch::Create(geometry));
+    target->drawBatch(pipelineBuilder, batch);
 }
 
 void GrAARectRenderer::strokeAARect(GrDrawTarget* target,
@@ -494,14 +355,6 @@
     const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
     const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
 
-    // Temporarily #if'ed out. We don't want to pass in the devRect but
-    // right now it is computed in GrContext::apply_aa_to_rect and we don't
-    // want to throw away the work
-#if 0
-    SkRect devRect;
-    combinedMatrix.mapRect(&devRect, rect);
-#endif
-
     SkScalar spare;
     {
         SkScalar w = devRect.width() - dx;
@@ -542,6 +395,9 @@
                                devOutsideAssist, devInside, miterStroke);
 }
 
+GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
+GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
+
 class AAStrokeRectBatch : public GrBatch {
 public:
     // TODO support AA rotated stroke rects by copying around view matrices
@@ -553,23 +409,22 @@
         bool fMiterStroke;
     };
 
-    static GrBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix,
-                           const GrIndexBuffer* indexBuffer) {
-        return SkNEW_ARGS(AAStrokeRectBatch, (geometry, viewMatrix, indexBuffer));
+    static GrBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix) {
+        return SkNEW_ARGS(AAStrokeRectBatch, (geometry, viewMatrix));
     }
 
-    const char* name() const SK_OVERRIDE { return "AAStrokeRect"; }
+    const char* name() const override { return "AAStrokeRect"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
 
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -586,7 +441,7 @@
         fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
 
         // Local matrix is ignored if we don't have local coords.  If we have localcoords we only
@@ -617,31 +472,27 @@
         SkASSERT(canTweakAlphaForCoverage ?
                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
-
         int innerVertexNum = 4;
         int outerVertexNum = this->miterStroke() ? 4 : 8;
-        int totalVertexNum = (outerVertexNum + innerVertexNum) * 2;
-
+        int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
+        int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
         int instanceCount = fGeoData.count();
-        int vertexCount = totalVertexNum * instanceCount;
 
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
-
-        void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              vertexCount,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices) {
-            SkDebugf("Could not allocate vertices\n");
-            return;
-        }
+        const SkAutoTUnref<const GrIndexBuffer> indexBuffer(
+            GetIndexBuffer(batchTarget->resourceProvider(), this->miterStroke()));
+        InstancedHelper helper;
+        void* vertices = helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride,
+                                     indexBuffer, verticesPerInstance,  indicesPerInstance,
+                                     instanceCount);
+        if (!vertices || !indexBuffer) {
+             SkDebugf("Could not allocate vertices\n");
+             return;
+         }
 
         for (int i = 0; i < instanceCount; i++) {
             const Geometry& args = fGeoData[i];
             this->generateAAStrokeRectGeometry(vertices,
-                                               i * totalVertexNum * vertexStride,
+                                               i * verticesPerInstance * vertexStride,
                                                vertexStride,
                                                outerVertexNum,
                                                innerVertexNum,
@@ -652,40 +503,121 @@
                                                args.fMiterStroke,
                                                canTweakAlphaForCoverage);
         }
-
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
-        drawInfo.setStartVertex(0);
-        drawInfo.setStartIndex(0);
-        drawInfo.setVerticesPerInstance(totalVertexNum);
-        drawInfo.setIndicesPerInstance(aa_stroke_rect_index_count(this->miterStroke()));
-        drawInfo.adjustStartVertex(firstVertex);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setIndexBuffer(fIndexBuffer);
-
-        int maxInstancesPerDraw = kNumBevelStrokeRectsInIndexBuffer;
-
-        while (instanceCount) {
-            drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
-            drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
-            drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
-
-            batchTarget->draw(drawInfo);
-
-            drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
-            instanceCount -= drawInfo.instanceCount();
-        }
+        helper.issueDraw(batchTarget);
     }
 
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
 
 private:
-    AAStrokeRectBatch(const Geometry& geometry, const SkMatrix& viewMatrix,
-                      const GrIndexBuffer* indexBuffer)
-        : fIndexBuffer(indexBuffer) {
+    AAStrokeRectBatch(const Geometry& geometry, const SkMatrix& viewMatrix)  {
         this->initClassID<AAStrokeRectBatch>();
         fBatch.fViewMatrix = viewMatrix;
         fGeoData.push_back(geometry);
+
+        // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need
+        // the join for proper bounds
+        fBounds = geometry.fDevOutside;
+        fBounds.join(geometry.fDevOutsideAssist);
+    }
+
+
+    static const int kMiterIndexCnt = 3 * 24;
+    static const int kMiterVertexCnt = 16;
+    static const int kNumMiterRectsInIndexBuffer = 256;
+
+    static const int kBevelIndexCnt = 48 + 36 + 24;
+    static const int kBevelVertexCnt = 24;
+    static const int kNumBevelRectsInIndexBuffer = 256;
+
+    static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider,
+                                               bool miterStroke) {
+
+        if (miterStroke) {
+            static const uint16_t gMiterIndices[] = {
+                0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
+                1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
+                2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
+                3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
+
+                0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
+                1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
+                2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
+                3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
+
+                0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
+                1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
+                2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
+                3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
+            };
+            GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
+            GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
+            return resourceProvider->refOrCreateInstancedIndexBuffer(gMiterIndices,
+                kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
+                gMiterIndexBufferKey);
+        } else {
+            /**
+             * As in miter-stroke, index = a + b, and a is the current index, b is the shift
+             * from the first index. The index layout:
+             * outer AA line: 0~3, 4~7
+             * outer edge:    8~11, 12~15
+             * inner edge:    16~19
+             * inner AA line: 20~23
+             * Following comes a bevel-stroke rect and its indices:
+             *
+             *           4                                 7
+             *            *********************************
+             *          *   ______________________________  *
+             *         *  / 12                          15 \  *
+             *        *  /                                  \  *
+             *     0 *  |8     16_____________________19  11 |  * 3
+             *       *  |       |                    |       |  *
+             *       *  |       |  ****************  |       |  *
+             *       *  |       |  * 20        23 *  |       |  *
+             *       *  |       |  *              *  |       |  *
+             *       *  |       |  * 21        22 *  |       |  *
+             *       *  |       |  ****************  |       |  *
+             *       *  |       |____________________|       |  *
+             *     1 *  |9    17                      18   10|  * 2
+             *        *  \                                  /  *
+             *         *  \13 __________________________14/  *
+             *          *                                   *
+             *           **********************************
+             *          5                                  6
+             */
+            static const uint16_t gBevelIndices[] = {
+                // Draw outer AA, from outer AA line to outer edge, shift is 0.
+                0 + 0, 1 + 0,  9 + 0,  9 + 0,  8 + 0, 0 + 0,
+                1 + 0, 5 + 0, 13 + 0, 13 + 0,  9 + 0, 1 + 0,
+                5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
+                6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
+                2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
+                3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
+                7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
+                4 + 0, 0 + 0,  8 + 0,  8 + 0, 12 + 0, 4 + 0,
+
+                // Draw the stroke, from outer edge to inner edge, shift is 8.
+                0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
+                1 + 8, 5 + 8, 9 + 8,
+                5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
+                6 + 8, 2 + 8, 10 + 8,
+                2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
+                3 + 8, 7 + 8, 11 + 8,
+                7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
+                4 + 8, 0 + 8, 8 + 8,
+
+                // Draw the inner AA, from inner edge to inner AA line, shift is 16.
+                0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
+                1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
+                2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
+                3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
+            };
+            GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
+
+            GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
+            return resourceProvider->refOrCreateInstancedIndexBuffer(gBevelIndices,
+                kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
+                gBevelIndexBufferKey);
+        }
     }
 
     GrColor color() const { return fBatch.fColor; }
@@ -695,7 +627,7 @@
     const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
     bool miterStroke() const { return fBatch.fMiterStroke; }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
 
         // TODO batch across miterstroke changes
@@ -720,6 +652,7 @@
             fBatch.fColor = GrColor_ILLEGAL;
         }
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
         return true;
     }
 
@@ -847,11 +780,9 @@
     };
 
     BatchTracker fBatch;
-    const GrIndexBuffer* fIndexBuffer;
     SkSTArray<1, Geometry, true> fGeoData;
 };
 
-
 void GrAARectRenderer::geometryStrokeAARect(GrDrawTarget* target,
                                             GrPipelineBuilder* pipelineBuilder,
                                             GrColor color,
@@ -860,12 +791,6 @@
                                             const SkRect& devOutsideAssist,
                                             const SkRect& devInside,
                                             bool miterStroke) {
-    GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer(miterStroke);
-    if (!indexBuffer) {
-        SkDebugf("Failed to create index buffer!\n");
-        return;
-    }
-
     AAStrokeRectBatch::Geometry geometry;
     geometry.fColor = color;
     geometry.fDevOutside = devOutside;
@@ -873,7 +798,7 @@
     geometry.fDevInside = devInside;
     geometry.fMiterStroke = miterStroke;
 
-    SkAutoTUnref<GrBatch> batch(AAStrokeRectBatch::Create(geometry, viewMatrix, indexBuffer));
+    SkAutoTUnref<GrBatch> batch(AAStrokeRectBatch::Create(geometry, viewMatrix));
     target->drawBatch(pipelineBuilder, batch);
 }
 
@@ -885,7 +810,7 @@
     SkASSERT(viewMatrix.rectStaysRect());
     SkASSERT(!rects[1].isEmpty());
 
-    SkRect devOutside, devOutsideAssist, devInside;
+    SkRect devOutside, devInside;
     viewMatrix.mapRect(&devOutside, rects[0]);
     // can't call mapRect for devInside since it calls sort
     viewMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2);
@@ -896,5 +821,42 @@
     }
 
     this->geometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
-                               devOutsideAssist, devInside, true);
+                               devOutside, devInside, true);
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef GR_TEST_UTILS
+
+BATCH_TEST_DEFINE(AAFillRectBatch) {
+    AAFillRectBatch::Geometry geo;
+    geo.fColor = GrRandomColor(random);
+    geo.fViewMatrix = GrTest::TestMatrix(random);
+    geo.fRect = GrTest::TestRect(random);
+    geo.fDevRect = GrTest::TestRect(random);
+    return AAFillRectBatch::Create(geo);
+}
+
+BATCH_TEST_DEFINE(AAStrokeRectBatch) {
+    bool miterStroke = random->nextBool();
+
+    // Create mock stroke rect
+    SkRect outside = GrTest::TestRect(random);
+    SkScalar minDim = SkMinScalar(outside.width(), outside.height());
+    SkScalar strokeWidth = minDim * 0.1f;
+    SkRect outsideAssist = outside;
+    outsideAssist.outset(strokeWidth, strokeWidth);
+    SkRect inside = outside;
+    inside.inset(strokeWidth, strokeWidth);
+
+    AAStrokeRectBatch::Geometry geo;
+    geo.fColor = GrRandomColor(random);
+    geo.fDevOutside = outside;
+    geo.fDevOutsideAssist = outsideAssist;
+    geo.fDevInside = inside;
+    geo.fMiterStroke = miterStroke;
+
+    return AAStrokeRectBatch::Create(geo, GrTest::TestMatrix(random));
+}
+
+#endif
diff --git a/src/gpu/GrAARectRenderer.h b/src/gpu/GrAARectRenderer.h
index 25fa00c..023eadc 100644
--- a/src/gpu/GrAARectRenderer.h
+++ b/src/gpu/GrAARectRenderer.h
@@ -16,7 +16,6 @@
 
 class GrClip;
 class GrDrawTarget;
-class GrGpu;
 class GrIndexBuffer;
 class GrPipelineBuilder;
 
@@ -27,19 +26,6 @@
 public:
     SK_DECLARE_INST_COUNT(GrAARectRenderer)
 
-    GrAARectRenderer(GrGpu* gpu)
-    : fGpu(gpu)
-    , fAAFillRectIndexBuffer(NULL)
-    , fAAMiterStrokeRectIndexBuffer(NULL)
-    , fAABevelStrokeRectIndexBuffer(NULL) {
-    }
-
-    void reset();
-
-    ~GrAARectRenderer() {
-        this->reset();
-    }
-
     // TODO: potentialy fuse the fill & stroke methods and differentiate
     // between them by passing in stroke (==NULL means fill).
 
@@ -68,8 +54,6 @@
                            const SkRect rects[2]);
 
 private:
-    GrIndexBuffer* aaStrokeRectIndexBuffer(bool miterStroke);
-
     void geometryFillAARect(GrDrawTarget*,
                             GrPipelineBuilder*,
                             GrColor,
@@ -86,11 +70,6 @@
                               const SkRect& devInside,
                               bool miterStroke);
 
-    GrGpu*                      fGpu;
-    GrIndexBuffer*              fAAFillRectIndexBuffer;
-    GrIndexBuffer*              fAAMiterStrokeRectIndexBuffer;
-    GrIndexBuffer*              fAABevelStrokeRectIndexBuffer;
-
     typedef SkRefCnt INHERITED;
 };
 
diff --git a/src/gpu/GrAddPathRenderers_default.cpp b/src/gpu/GrAddPathRenderers_default.cpp
index f5d6934..1797f57 100644
--- a/src/gpu/GrAddPathRenderers_default.cpp
+++ b/src/gpu/GrAddPathRenderers_default.cpp
@@ -11,6 +11,7 @@
 #include "GrAAHairLinePathRenderer.h"
 #include "GrAAConvexPathRenderer.h"
 #include "GrAADistanceFieldPathRenderer.h"
+#include "GrDashLinePathRenderer.h"
 #include "GrTessellatingPathRenderer.h"
 #if GR_STROKE_PATH_RENDERING
 #include "../../experimental/StrokePathRenderer/GrStrokePathRenderer.h"
@@ -20,23 +21,25 @@
 #endif
 
 #ifndef GR_TESSELLATING_PATH_RENDERING
-#define GR_TESSELLATING_PATH_RENDERING 0
+#define GR_TESSELLATING_PATH_RENDERING 1
 #endif
 
 void GrPathRenderer::AddPathRenderers(GrContext* ctx, GrPathRendererChain* chain) {
+    chain->addPathRenderer(SkNEW_ARGS(GrDashLinePathRenderer, (ctx)))->unref();
+
 #if GR_STROKE_PATH_RENDERING
     chain->addPathRenderer(SkNEW(GrStrokePathRenderer))->unref();
 #endif
 #if GR_ANDROID_PATH_RENDERING
     chain->addPathRenderer(SkNEW(GrAndroidPathRenderer))->unref();
 #endif
-#if GR_TESSELLATING_PATH_RENDERING
-    chain->addPathRenderer(new GrTessellatingPathRenderer)->unref();
-#endif
     if (GrPathRenderer* pr = GrStencilAndCoverPathRenderer::Create(ctx)) {
         chain->addPathRenderer(pr)->unref();
     }
-    if (GrPathRenderer* pr = GrAAHairLinePathRenderer::Create(ctx)) {
+#if GR_TESSELLATING_PATH_RENDERING
+    chain->addPathRenderer(new GrTessellatingPathRenderer)->unref();
+#endif
+    if (GrPathRenderer* pr = GrAAHairLinePathRenderer::Create()) {
         chain->addPathRenderer(pr)->unref();
     }
     chain->addPathRenderer(SkNEW(GrAAConvexPathRenderer))->unref();
diff --git a/src/gpu/GrAtlasTextContext.cpp b/src/gpu/GrAtlasTextContext.cpp
new file mode 100644
index 0000000..dfb07ed
--- /dev/null
+++ b/src/gpu/GrAtlasTextContext.cpp
@@ -0,0 +1,2281 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "GrAtlasTextContext.h"
+
+#include "GrBatch.h"
+#include "GrBatchFontCache.h"
+#include "GrBatchTarget.h"
+#include "GrBatchTest.h"
+#include "GrDefaultGeoProcFactory.h"
+#include "GrDrawTarget.h"
+#include "GrFontScaler.h"
+#include "GrIndexBuffer.h"
+#include "GrResourceProvider.h"
+#include "GrStrokeInfo.h"
+#include "GrTextBlobCache.h"
+#include "GrTexturePriv.h"
+#include "GrVertexBuffer.h"
+
+#include "SkAutoKern.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkDistanceFieldGen.h"
+#include "SkDraw.h"
+#include "SkDrawFilter.h"
+#include "SkDrawProcs.h"
+#include "SkGlyphCache.h"
+#include "SkGpuDevice.h"
+#include "SkGr.h"
+#include "SkPath.h"
+#include "SkRTConf.h"
+#include "SkStrokeRec.h"
+#include "SkTextBlob.h"
+#include "SkTextMapStateProc.h"
+
+#include "effects/GrBitmapTextGeoProc.h"
+#include "effects/GrDistanceFieldGeoProc.h"
+
+namespace {
+static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
+
+// position + local coord
+static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
+
+static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
+
+static const int kMinDFFontSize = 18;
+static const int kSmallDFFontSize = 32;
+static const int kSmallDFFontLimit = 32;
+static const int kMediumDFFontSize = 72;
+static const int kMediumDFFontLimit = 72;
+static const int kLargeDFFontSize = 162;
+static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
+
+SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;)
+static const int kDistanceAdjustLumShift = 5;
+
+static const int kVerticesPerGlyph = 4;
+static const int kIndicesPerGlyph = 6;
+
+static size_t get_vertex_stride(GrMaskFormat maskFormat) {
+    switch (maskFormat) {
+        case kA8_GrMaskFormat:
+            return kGrayTextVASize;
+        case kARGB_GrMaskFormat:
+            return kColorTextVASize;
+        default:
+            return kLCDTextVASize;
+    }
+}
+
+static size_t get_vertex_stride_df(GrMaskFormat maskFormat, bool useLCDText) {
+    SkASSERT(maskFormat == kA8_GrMaskFormat);
+    if (useLCDText) {
+        return kLCDTextVASize;
+    } else {
+        return kGrayTextVASize;
+    }
+}
+
+static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
+    unsigned r = SkColorGetR(c);
+    unsigned g = SkColorGetG(c);
+    unsigned b = SkColorGetB(c);
+    return GrColorPackRGBA(r, g, b, 0xff);
+}
+
+};
+
+// TODO
+// Distance field text in textblobs
+
+GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
+                                       SkGpuDevice* gpuDevice,
+                                       const SkDeviceProperties& properties,
+                                       bool enableDistanceFields)
+    : INHERITED(context, gpuDevice, properties)
+    , fDistanceAdjustTable(SkNEW_ARGS(DistanceAdjustTable, (properties.gamma()))) {
+    // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
+    // vertexStride
+    SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
+                      vertex_attribute_changed);
+    fCurrStrike = NULL;
+    fCache = context->getTextBlobCache();
+
+#if SK_FORCE_DISTANCE_FIELD_TEXT
+    fEnableDFRendering = true;
+#else
+    fEnableDFRendering = enableDistanceFields;
+#endif
+}
+
+void GrAtlasTextContext::DistanceAdjustTable::buildDistanceAdjustTable(float gamma) {
+
+    // This is used for an approximation of the mask gamma hack, used by raster and bitmap
+    // text. The mask gamma hack is based off of guessing what the blend color is going to
+    // be, and adjusting the mask so that when run through the linear blend will
+    // produce the value closest to the desired result. However, in practice this means
+    // that the 'adjusted' mask is just increasing or decreasing the coverage of
+    // the mask depending on what it is thought it will blit against. For black (on
+    // assumed white) this means that coverages are decreased (on a curve). For white (on
+    // assumed black) this means that coverages are increased (on a a curve). At
+    // middle (perceptual) gray (which could be blit against anything) the coverages
+    // remain the same.
+    //
+    // The idea here is that instead of determining the initial (real) coverage and
+    // then adjusting that coverage, we determine an adjusted coverage directly by
+    // essentially manipulating the geometry (in this case, the distance to the glyph
+    // edge). So for black (on assumed white) this thins a bit; for white (on
+    // assumed black) this fake bolds the geometry a bit.
+    //
+    // The distance adjustment is calculated by determining the actual coverage value which
+    // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This
+    // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the
+    // actual edge. So by subtracting this distance adjustment and computing without the
+    // the coverage adjustment we should get 0.5 coverage at the same point.
+    //
+    // This has several implications:
+    //     For non-gray lcd smoothed text, each subpixel essentially is using a
+    //     slightly different geometry.
+    //
+    //     For black (on assumed white) this may not cover some pixels which were
+    //     previously covered; however those pixels would have been only slightly
+    //     covered and that slight coverage would have been decreased anyway. Also, some pixels
+    //     which were previously fully covered may no longer be fully covered.
+    //
+    //     For white (on assumed black) this may cover some pixels which weren't
+    //     previously covered at all.
+
+    int width, height;
+    size_t size;
+
+#ifdef SK_GAMMA_CONTRAST
+    SkScalar contrast = SK_GAMMA_CONTRAST;
+#else
+    SkScalar contrast = 0.5f;
+#endif
+    SkScalar paintGamma = gamma;
+    SkScalar deviceGamma = gamma;
+
+    size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
+        &width, &height);
+
+    SkASSERT(kExpectedDistanceAdjustTableSize == height);
+    fTable = SkNEW_ARRAY(SkScalar, height);
+
+    SkAutoTArray<uint8_t> data((int)size);
+    SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
+
+    // find the inverse points where we cross 0.5
+    // binsearch might be better, but we only need to do this once on creation
+    for (int row = 0; row < height; ++row) {
+        uint8_t* rowPtr = data.get() + row*width;
+        for (int col = 0; col < width - 1; ++col) {
+            if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) {
+                // compute point where a mask value will give us a result of 0.5
+                float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]);
+                float borderAlpha = (col + interp) / 255.f;
+
+                // compute t value for that alpha
+                // this is an approximate inverse for smoothstep()
+                float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f;
+
+                // compute distance which gives us that t value
+                const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor
+                float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor;
+
+                fTable[row] = d;
+                break;
+            }
+        }
+    }
+}
+
+GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
+                                               SkGpuDevice* gpuDevice,
+                                               const SkDeviceProperties& props,
+                                               bool enableDistanceFields) {
+    return SkNEW_ARGS(GrAtlasTextContext, (context, gpuDevice, props, enableDistanceFields));
+}
+
+bool GrAtlasTextContext::canDraw(const GrRenderTarget*,
+                                 const GrClip&,
+                                 const GrPaint&,
+                                 const SkPaint& skPaint,
+                                 const SkMatrix& viewMatrix) {
+    return this->canDrawAsDistanceFields(skPaint, viewMatrix) ||
+           !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
+}
+
+GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
+    GrColor canonicalColor = paint.computeLuminanceColor();
+    if (lcd) {
+        // This is the correct computation, but there are tons of cases where LCD can be overridden.
+        // For now we just regenerate if any run in a textblob has LCD.
+        // TODO figure out where all of these overrides are and see if we can incorporate that logic
+        // at a higher level *OR* use sRGB
+        SkASSERT(false);
+        //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
+    } else {
+        // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
+        // gamma corrected masks anyways, nor color
+        U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
+                                       SkColorGetG(canonicalColor),
+                                       SkColorGetB(canonicalColor));
+        // reduce to our finite number of bits
+        canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
+    }
+    return canonicalColor;
+}
+
+// TODO if this function ever shows up in profiling, then we can compute this value when the
+// textblob is being built and cache it.  However, for the time being textblobs mostly only have 1
+// run so this is not a big deal to compute here.
+bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) {
+    SkTextBlob::RunIterator it(blob);
+    for (; !it.done(); it.next()) {
+        if (it.isLCD()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool GrAtlasTextContext::MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
+                                            const BitmapTextBlob& blob, const SkPaint& paint,
+                                            const SkMaskFilter::BlurRec& blurRec,
+                                            const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
+    // If we have LCD text then our canonical color will be set to transparent, in this case we have
+    // to regenerate the blob on any color change
+    if (blob.fKey.fCanonicalColor == SK_ColorTRANSPARENT && blob.fPaintColor != paint.getColor()) {
+        return true;
+    }
+
+    if (blob.fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
+        return true;
+    }
+
+    if (blob.fViewMatrix.hasPerspective() && !blob.fViewMatrix.cheapEqualTo(viewMatrix)) {
+        return true;
+    }
+
+    // We only cache one masked version
+    if (blob.fKey.fHasBlur &&
+        (blob.fBlurRec.fSigma != blurRec.fSigma ||
+         blob.fBlurRec.fStyle != blurRec.fStyle ||
+         blob.fBlurRec.fQuality != blurRec.fQuality)) {
+        return true;
+    }
+
+    // Similarly, we only cache one version for each style
+    if (blob.fKey.fStyle != SkPaint::kFill_Style &&
+        (blob.fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
+         blob.fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
+         blob.fStrokeInfo.fJoin != paint.getStrokeJoin())) {
+        return true;
+    }
+
+    // Mixed blobs must be regenerated.  We could probably figure out a way to do integer scrolls
+    // for mixed blobs if this becomes an issue.
+    if (blob.hasBitmap() && blob.hasDistanceField()) {
+        // Identical viewmatrices and we can reuse in all cases
+        if (blob.fViewMatrix.cheapEqualTo(viewMatrix) && x == blob.fX && y == blob.fY) {
+            return false;
+        }
+        return true;
+    }
+
+    if (blob.hasBitmap()) {
+        if (blob.fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
+            blob.fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
+            blob.fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
+            blob.fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
+            return true;
+        }
+
+        // We can update the positions in the cachedtextblobs without regenerating the whole blob,
+        // but only for integer translations.
+        // This cool bit of math will determine the necessary translation to apply to the already
+        // generated vertex coordinates to move them to the correct position
+        SkScalar transX = viewMatrix.getTranslateX() +
+                          viewMatrix.getScaleX() * (x - blob.fX) +
+                          viewMatrix.getSkewX() * (y - blob.fY) -
+                          blob.fViewMatrix.getTranslateX();
+        SkScalar transY = viewMatrix.getTranslateY() +
+                          viewMatrix.getSkewY() * (x - blob.fX) +
+                          viewMatrix.getScaleY() * (y - blob.fY) -
+                          blob.fViewMatrix.getTranslateY();
+        if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
+            return true;
+        }
+
+        (*outTransX) = transX;
+        (*outTransY) = transY;
+    } else if (blob.hasDistanceField()) {
+        // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
+        // distance field being generated, so we have to regenerate in those cases
+        SkScalar newMaxScale = viewMatrix.getMaxScale();
+        SkScalar oldMaxScale = blob.fViewMatrix.getMaxScale();
+        SkScalar scaleAdjust = newMaxScale / oldMaxScale;
+        if (scaleAdjust < blob.fMaxMinScale || scaleAdjust > blob.fMinMaxScale) {
+            return true;
+        }
+
+        (*outTransX) = x - blob.fX;
+        (*outTransY) = y - blob.fY;
+    }
+    // It is possible that a blob has neither distanceField nor bitmaptext.  This is in the case
+    // when all of the runs inside the blob are drawn as paths.  In this case, we always regenerate
+    // the blob anyways at flush time, so no need to regenerate explicitly
+
+    return false;
+}
+
+
+inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run,
+                                                    const SkPaint& skPaint,
+                                                    const SkMatrix* viewMatrix,
+                                                    bool noGamma) {
+    skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, viewMatrix, noGamma);
+    run->fTypeface.reset(SkSafeRef(skPaint.getTypeface()));
+    return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
+}
+
+void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
+                                      const SkPaint& skPaint, const SkMatrix& viewMatrix,
+                                      const SkTextBlob* blob, SkScalar x, SkScalar y,
+                                      SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
+    // If we have been abandoned, then don't draw
+    if (!fContext->getTextTarget()) {
+        return;
+    }
+
+    SkAutoTUnref<BitmapTextBlob> cacheBlob;
+    SkMaskFilter::BlurRec blurRec;
+    BitmapTextBlob::Key key;
+    // It might be worth caching these things, but its not clear at this time
+    // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
+    const SkMaskFilter* mf = skPaint.getMaskFilter();
+    bool canCache = !(skPaint.getPathEffect() ||
+                      (mf && !mf->asABlur(&blurRec)) ||
+                      drawFilter);
+
+    if (canCache) {
+        bool hasLCD = HasLCD(blob);
+
+        // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
+        SkPixelGeometry pixelGeometry = hasLCD ? fDeviceProperties.pixelGeometry() :
+                                                 kUnknown_SkPixelGeometry;
+
+        // TODO we want to figure out a way to be able to use the canonical color on LCD text,
+        // see the note on ComputeCanonicalColor above.  We pick a dummy value for LCD text to
+        // ensure we always match the same key
+        GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
+                                          ComputeCanonicalColor(skPaint, hasLCD);
+
+        key.fPixelGeometry = pixelGeometry;
+        key.fUniqueID = blob->uniqueID();
+        key.fStyle = skPaint.getStyle();
+        key.fHasBlur = SkToBool(mf);
+        key.fCanonicalColor = canonicalColor;
+        cacheBlob.reset(SkSafeRef(fCache->find(key)));
+    }
+
+    SkIRect clipRect;
+    clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
+
+    SkScalar transX = 0.f;
+    SkScalar transY = 0.f;
+
+    // Though for the time being runs in the textblob can override the paint, they only touch font
+    // info.
+    GrPaint grPaint;
+    if (!SkPaint2GrPaint(fContext, rt, skPaint, viewMatrix, true, &grPaint)) {
+        return;
+    }
+
+    if (cacheBlob) {
+        if (MustRegenerateBlob(&transX, &transY, *cacheBlob, skPaint, blurRec, viewMatrix, x, y)) {
+            // We have to remake the blob because changes may invalidate our masks.
+            // TODO we could probably get away reuse most of the time if the pointer is unique,
+            // but we'd have to clear the subrun information
+            fCache->remove(cacheBlob);
+            cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
+                                                           kGrayTextVASize)));
+            this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y,
+                                     drawFilter, clipRect, rt, clip, grPaint);
+        } else {
+            // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
+            // offsets
+            cacheBlob->fViewMatrix = viewMatrix;
+            cacheBlob->fX = x;
+            cacheBlob->fY = y;
+            fCache->makeMRU(cacheBlob);
+        }
+    } else {
+        if (canCache) {
+            cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, skPaint,
+                                                           kGrayTextVASize)));
+        } else {
+            cacheBlob.reset(fCache->createBlob(blob, kGrayTextVASize));
+        }
+        this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMatrix, blob, x, y,
+                                 drawFilter, clipRect, rt, clip, grPaint);
+    }
+
+    cacheBlob->fPaintColor = skPaint.getColor();
+    this->flush(fContext->getTextTarget(), blob, cacheBlob, rt, skPaint, grPaint, drawFilter,
+                clip, viewMatrix, clipBounds, x, y, transX, transY);
+}
+
+inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint,
+                                                        const SkMatrix& viewMatrix) {
+    // TODO: support perspective (need getMaxScale replacement)
+    if (viewMatrix.hasPerspective()) {
+        return false;
+    }
+
+    SkScalar maxScale = viewMatrix.getMaxScale();
+    SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
+    // Hinted text looks far better at small resolutions
+    // Scaling up beyond 2x yields undesireable artifacts
+    if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
+        return false;
+    }
+
+    if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() &&
+        scaledTextSize < kLargeDFFontSize) {
+        return false;
+    }
+
+    // rasterizers and mask filters modify alpha, which doesn't
+    // translate well to distance
+    if (skPaint.getRasterizer() || skPaint.getMaskFilter() ||
+        !fContext->getTextTarget()->caps()->shaderCaps()->shaderDerivativeSupport()) {
+        return false;
+    }
+
+    // TODO: add some stroking support
+    if (skPaint.getStyle() != SkPaint::kFill_Style) {
+        return false;
+    }
+
+    return true;
+}
+
+void GrAtlasTextContext::regenerateTextBlob(BitmapTextBlob* cacheBlob,
+                                            const SkPaint& skPaint, GrColor color,
+                                            const SkMatrix& viewMatrix,
+                                            const SkTextBlob* blob, SkScalar x, SkScalar y,
+                                            SkDrawFilter* drawFilter, const SkIRect& clipRect,
+                                            GrRenderTarget* rt, const GrClip& clip,
+                                            const GrPaint& paint) {
+    cacheBlob->fViewMatrix = viewMatrix;
+    cacheBlob->fX = x;
+    cacheBlob->fY = y;
+
+    // Regenerate textblob
+    SkPaint runPaint = skPaint;
+    SkTextBlob::RunIterator it(blob);
+    for (int run = 0; !it.done(); it.next(), run++) {
+        int glyphCount = it.glyphCount();
+        size_t textLen = glyphCount * sizeof(uint16_t);
+        const SkPoint& offset = it.offset();
+        // applyFontToPaint() always overwrites the exact same attributes,
+        // so it is safe to not re-seed the paint for this reason.
+        it.applyFontToPaint(&runPaint);
+
+        if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
+            // A false return from filter() means we should abort the current draw.
+            runPaint = skPaint;
+            continue;
+        }
+
+        runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
+
+        // setup vertex / glyphIndex for the new run
+        if (run > 0) {
+            PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
+            PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
+
+            newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
+            newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
+
+            newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
+            newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
+        }
+
+        if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
+            cacheBlob->setHasDistanceField();
+            SkPaint dfPaint = runPaint;
+            SkScalar textRatio;
+            this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
+            Run& runIdx = cacheBlob->fRuns[run];
+            PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
+            subRun.fUseLCDText = runPaint.isLCDRenderText();
+            subRun.fDrawAsDistanceFields = true;
+
+            SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], dfPaint, NULL, true);
+
+            SkTDArray<char> fallbackTxt;
+            SkTDArray<SkScalar> fallbackPos;
+            SkPoint dfOffset;
+            int scalarsPerPosition = 2;
+            switch (it.positioning()) {
+                case SkTextBlob::kDefault_Positioning: {
+                    this->internalDrawDFText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
+                                             (const char *)it.glyphs(), textLen,
+                                             x + offset.x(), y + offset.y(), clipRect, textRatio,
+                                             &fallbackTxt, &fallbackPos, &dfOffset, runPaint);
+                    break;
+                }
+                case SkTextBlob::kHorizontal_Positioning: {
+                    scalarsPerPosition = 1;
+                    dfOffset = SkPoint::Make(x, y + offset.y());
+                    this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
+                                                (const char*)it.glyphs(), textLen, it.pos(),
+                                                scalarsPerPosition, dfOffset, clipRect, textRatio,
+                                                &fallbackTxt, &fallbackPos);
+                    break;
+                }
+                case SkTextBlob::kFull_Positioning: {
+                    dfOffset = SkPoint::Make(x, y);
+                    this->internalDrawDFPosText(cacheBlob, run, cache, dfPaint, color, viewMatrix,
+                                                (const char*)it.glyphs(), textLen, it.pos(),
+                                                scalarsPerPosition, dfOffset, clipRect, textRatio,
+                                                &fallbackTxt, &fallbackPos);
+                    break;
+                }
+            }
+            if (fallbackTxt.count()) {
+                this->fallbackDrawPosText(cacheBlob, run, rt, clip, paint, runPaint, viewMatrix,
+                                          fallbackTxt, fallbackPos, scalarsPerPosition, dfOffset,
+                                          clipRect);
+            }
+
+            SkGlyphCache::AttachCache(cache);
+        } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) {
+            cacheBlob->fRuns[run].fDrawAsPaths = true;
+        } else {
+            cacheBlob->setHasBitmap();
+            SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint, &viewMatrix,
+                                                   false);
+            switch (it.positioning()) {
+                case SkTextBlob::kDefault_Positioning:
+                    this->internalDrawBMPText(cacheBlob, run, cache, runPaint, color, viewMatrix,
+                                              (const char *)it.glyphs(), textLen,
+                                              x + offset.x(), y + offset.y(), clipRect);
+                    break;
+                case SkTextBlob::kHorizontal_Positioning:
+                    this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
+                                                 (const char*)it.glyphs(), textLen, it.pos(), 1,
+                                                 SkPoint::Make(x, y + offset.y()), clipRect);
+                    break;
+                case SkTextBlob::kFull_Positioning:
+                    this->internalDrawBMPPosText(cacheBlob, run, cache, runPaint, color, viewMatrix,
+                                                 (const char*)it.glyphs(), textLen, it.pos(), 2,
+                                                 SkPoint::Make(x, y), clipRect);
+                    break;
+            }
+            SkGlyphCache::AttachCache(cache);
+        }
+
+        if (drawFilter) {
+            // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
+            runPaint = skPaint;
+        }
+    }
+}
+
+inline void GrAtlasTextContext::initDistanceFieldPaint(BitmapTextBlob* blob,
+                                                       SkPaint* skPaint,
+                                                       SkScalar* textRatio,
+                                                       const SkMatrix& viewMatrix) {
+    // getMaxScale doesn't support perspective, so neither do we at the moment
+    SkASSERT(!viewMatrix.hasPerspective());
+    SkScalar maxScale = viewMatrix.getMaxScale();
+    SkScalar textSize = skPaint->getTextSize();
+    SkScalar scaledTextSize = textSize;
+    // if we have non-unity scale, we need to choose our base text size
+    // based on the SkPaint's text size multiplied by the max scale factor
+    // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
+    if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
+        scaledTextSize *= maxScale;
+    }
+
+    // We have three sizes of distance field text, and within each size 'bucket' there is a floor
+    // and ceiling.  A scale outside of this range would require regenerating the distance fields
+    SkScalar dfMaskScaleFloor;
+    SkScalar dfMaskScaleCeil;
+    if (scaledTextSize <= kSmallDFFontLimit) {
+        dfMaskScaleFloor = kMinDFFontSize;
+        dfMaskScaleCeil = kSmallDFFontLimit;
+        *textRatio = textSize / kSmallDFFontSize;
+        skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
+    } else if (scaledTextSize <= kMediumDFFontLimit) {
+        dfMaskScaleFloor = kSmallDFFontLimit;
+        dfMaskScaleCeil = kMediumDFFontLimit;
+        *textRatio = textSize / kMediumDFFontSize;
+        skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
+    } else {
+        dfMaskScaleFloor = kMediumDFFontLimit;
+        dfMaskScaleCeil = kLargeDFFontLimit;
+        *textRatio = textSize / kLargeDFFontSize;
+        skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
+    }
+
+    // Because there can be multiple runs in the blob, we want the overall maxMinScale, and
+    // minMaxScale to make regeneration decisions.  Specifically, we want the maximum minimum scale
+    // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
+    // tolerate before we'd have to move to a large mip size.  When we actually test these values
+    // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
+    // against these values to decide if we can reuse or not(ie, will a given scale change our mip
+    // level)
+    SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
+    blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
+    blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
+
+    skPaint->setLCDRenderText(false);
+    skPaint->setAutohinted(false);
+    skPaint->setHinting(SkPaint::kNormal_Hinting);
+    skPaint->setSubpixelText(true);
+}
+
+inline void GrAtlasTextContext::fallbackDrawPosText(BitmapTextBlob* blob,
+                                                    int runIndex,
+                                                    GrRenderTarget* rt, const GrClip& clip,
+                                                    const GrPaint& paint,
+                                                    const SkPaint& skPaint,
+                                                    const SkMatrix& viewMatrix,
+                                                    const SkTDArray<char>& fallbackTxt,
+                                                    const SkTDArray<SkScalar>& fallbackPos,
+                                                    int scalarsPerPosition,
+                                                    const SkPoint& offset,
+                                                    const SkIRect& clipRect) {
+    SkASSERT(fallbackTxt.count());
+    blob->setHasBitmap();
+    Run& run = blob->fRuns[runIndex];
+    // Push back a new subrun to fill and set the override descriptor
+    run.push_back();
+    run.fOverrideDescriptor.reset(SkNEW(SkAutoDescriptor));
+    skPaint.getScalerContextDescriptor(run.fOverrideDescriptor,
+                                       &fDeviceProperties, &viewMatrix, false);
+    SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface,
+                                                    run.fOverrideDescriptor->getDesc());
+    this->internalDrawBMPPosText(blob, runIndex, cache, skPaint, paint.getColor(), viewMatrix,
+                                 fallbackTxt.begin(), fallbackTxt.count(),
+                                 fallbackPos.begin(), scalarsPerPosition, offset, clipRect);
+    SkGlyphCache::AttachCache(cache);
+}
+
+inline GrAtlasTextContext::BitmapTextBlob*
+GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
+                                const SkMatrix& viewMatrix, SkGlyphCache** cache,
+                                SkPaint* dfPaint, SkScalar* textRatio) {
+    BitmapTextBlob* blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
+
+    *dfPaint = origPaint;
+    this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix);
+    blob->fViewMatrix = viewMatrix;
+    Run& run = blob->fRuns[0];
+    PerSubRunInfo& subRun = run.fSubRunInfo.back();
+    subRun.fUseLCDText = origPaint.isLCDRenderText();
+    subRun.fDrawAsDistanceFields = true;
+
+    *cache = this->setupCache(&blob->fRuns[0], *dfPaint, NULL, true);
+    return blob;
+}
+
+inline GrAtlasTextContext::BitmapTextBlob*
+GrAtlasTextContext::createDrawTextBlob(GrRenderTarget* rt, const GrClip& clip,
+                                       const GrPaint& paint, const SkPaint& skPaint,
+                                       const SkMatrix& viewMatrix,
+                                       const char text[], size_t byteLength,
+                                       SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
+    int glyphCount = skPaint.countText(text, byteLength);
+    SkIRect clipRect;
+    clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
+
+    BitmapTextBlob* blob;
+    if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
+        SkPaint dfPaint;
+        SkScalar textRatio;
+        SkGlyphCache* cache;
+        blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
+
+        SkTDArray<char> fallbackTxt;
+        SkTDArray<SkScalar> fallbackPos;
+        SkPoint offset;
+        this->internalDrawDFText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
+                                 byteLength, x, y, clipRect, textRatio, &fallbackTxt, &fallbackPos,
+                                 &offset, skPaint);
+        SkGlyphCache::AttachCache(cache);
+        if (fallbackTxt.count()) {
+            this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
+                                      fallbackPos, 2, offset, clipRect);
+        }
+    } else {
+        blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
+        blob->fViewMatrix = viewMatrix;
+
+        SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
+        this->internalDrawBMPText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
+                                  byteLength, x, y, clipRect);
+        SkGlyphCache::AttachCache(cache);
+    }
+    return blob;
+}
+
+inline GrAtlasTextContext::BitmapTextBlob*
+GrAtlasTextContext::createDrawPosTextBlob(GrRenderTarget* rt, const GrClip& clip,
+                                          const GrPaint& paint, const SkPaint& skPaint,
+                                          const SkMatrix& viewMatrix,
+                                          const char text[], size_t byteLength,
+                                          const SkScalar pos[], int scalarsPerPosition,
+                                          const SkPoint& offset, const SkIRect& regionClipBounds) {
+    int glyphCount = skPaint.countText(text, byteLength);
+
+    SkIRect clipRect;
+    clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
+
+    BitmapTextBlob* blob;
+    if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) {
+        SkPaint dfPaint;
+        SkScalar textRatio;
+        SkGlyphCache* cache;
+        blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &cache, &dfPaint, &textRatio);
+
+        SkTDArray<char> fallbackTxt;
+        SkTDArray<SkScalar> fallbackPos;
+        this->internalDrawDFPosText(blob, 0, cache, dfPaint, paint.getColor(), viewMatrix, text,
+                                    byteLength, pos, scalarsPerPosition, offset, clipRect,
+                                    textRatio, &fallbackTxt, &fallbackPos);
+        SkGlyphCache::AttachCache(cache);
+        if (fallbackTxt.count()) {
+            this->fallbackDrawPosText(blob, 0, rt, clip, paint, skPaint, viewMatrix, fallbackTxt,
+                                      fallbackPos, scalarsPerPosition, offset, clipRect);
+        }
+    } else {
+        blob = fCache->createBlob(glyphCount, 1, kGrayTextVASize);
+        blob->fViewMatrix = viewMatrix;
+        SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMatrix, false);
+        this->internalDrawBMPPosText(blob, 0, cache, skPaint, paint.getColor(), viewMatrix, text,
+                                     byteLength, pos, scalarsPerPosition, offset, clipRect);
+        SkGlyphCache::AttachCache(cache);
+    }
+    return blob;
+}
+
+void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
+                                    const GrPaint& paint, const SkPaint& skPaint,
+                                    const SkMatrix& viewMatrix,
+                                    const char text[], size_t byteLength,
+                                    SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
+    SkAutoTUnref<BitmapTextBlob> blob(
+            this->createDrawTextBlob(rt, clip, paint, skPaint, viewMatrix,
+                                     text, byteLength, x, y, regionClipBounds));
+    this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, regionClipBounds);
+}
+
+void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
+                                       const GrPaint& paint, const SkPaint& skPaint,
+                                       const SkMatrix& viewMatrix,
+                                       const char text[], size_t byteLength,
+                                       const SkScalar pos[], int scalarsPerPosition,
+                                       const SkPoint& offset, const SkIRect& regionClipBounds) {
+    SkAutoTUnref<BitmapTextBlob> blob(
+            this->createDrawPosTextBlob(rt, clip, paint, skPaint, viewMatrix,
+                                        text, byteLength,
+                                        pos, scalarsPerPosition,
+                                        offset, regionClipBounds));
+
+    this->flush(fContext->getTextTarget(), blob, rt, skPaint, paint, clip, regionClipBounds);
+}
+
+void GrAtlasTextContext::internalDrawBMPText(BitmapTextBlob* blob, int runIndex,
+                                             SkGlyphCache* cache, const SkPaint& skPaint,
+                                             GrColor color,
+                                             const SkMatrix& viewMatrix,
+                                             const char text[], size_t byteLength,
+                                             SkScalar x, SkScalar y, const SkIRect& clipRect) {
+    SkASSERT(byteLength == 0 || text != NULL);
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0) {
+        return;
+    }
+
+    fCurrStrike = NULL;
+    SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
+
+    // Get GrFontScaler from cache
+    GrFontScaler* fontScaler = GetGrFontScaler(cache);
+
+    // transform our starting point
+    {
+        SkPoint loc;
+        viewMatrix.mapXY(x, y, &loc);
+        x = loc.fX;
+        y = loc.fY;
+    }
+
+    // need to measure first
+    if (skPaint.getTextAlign() != SkPaint::kLeft_Align) {
+        SkVector    stopVector;
+        MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
+
+        SkScalar    stopX = stopVector.fX;
+        SkScalar    stopY = stopVector.fY;
+
+        if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
+            stopX = SkScalarHalf(stopX);
+            stopY = SkScalarHalf(stopY);
+        }
+        x -= stopX;
+        y -= stopY;
+    }
+
+    const char* stop = text + byteLength;
+
+    SkAutoKern autokern;
+
+    SkFixed fxMask = ~0;
+    SkFixed fyMask = ~0;
+    SkScalar halfSampleX, halfSampleY;
+    if (cache->isSubpixel()) {
+        halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
+        SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
+        if (kX_SkAxisAlignment == baseline) {
+            fyMask = 0;
+            halfSampleY = SK_ScalarHalf;
+        } else if (kY_SkAxisAlignment == baseline) {
+            fxMask = 0;
+            halfSampleX = SK_ScalarHalf;
+        }
+    } else {
+        halfSampleX = halfSampleY = SK_ScalarHalf;
+    }
+
+    Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
+    Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
+
+    while (text < stop) {
+        const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
+
+        fx += autokern.adjust(glyph);
+
+        if (glyph.fWidth) {
+            this->bmpAppendGlyph(blob,
+                                 runIndex,
+                                 GrGlyph::Pack(glyph.getGlyphID(),
+                                               glyph.getSubXFixed(),
+                                               glyph.getSubYFixed(),
+                                               GrGlyph::kCoverage_MaskStyle),
+                                 Sk48Dot16FloorToInt(fx),
+                                 Sk48Dot16FloorToInt(fy),
+                                 color,
+                                 fontScaler,
+                                 clipRect);
+        }
+
+        fx += glyph.fAdvanceX;
+        fy += glyph.fAdvanceY;
+    }
+}
+
+void GrAtlasTextContext::internalDrawBMPPosText(BitmapTextBlob* blob, int runIndex,
+                                                SkGlyphCache* cache, const SkPaint& skPaint,
+                                                GrColor color,
+                                                const SkMatrix& viewMatrix,
+                                                const char text[], size_t byteLength,
+                                                const SkScalar pos[], int scalarsPerPosition,
+                                                const SkPoint& offset, const SkIRect& clipRect) {
+    SkASSERT(byteLength == 0 || text != NULL);
+    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0) {
+        return;
+    }
+
+    fCurrStrike = NULL;
+    SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
+
+    // Get GrFontScaler from cache
+    GrFontScaler* fontScaler = GetGrFontScaler(cache);
+
+    const char*        stop = text + byteLength;
+    SkTextAlignProc    alignProc(skPaint.getTextAlign());
+    SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
+
+    if (cache->isSubpixel()) {
+        // maybe we should skip the rounding if linearText is set
+        SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
+
+        SkFixed fxMask = ~0;
+        SkFixed fyMask = ~0;
+        SkScalar halfSampleX = SkFixedToScalar(SkGlyph::kSubpixelRound);
+        SkScalar halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
+        if (kX_SkAxisAlignment == baseline) {
+            fyMask = 0;
+            halfSampleY = SK_ScalarHalf;
+        } else if (kY_SkAxisAlignment == baseline) {
+            fxMask = 0;
+            halfSampleX = SK_ScalarHalf;
+        }
+
+        if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
+            while (text < stop) {
+                SkPoint tmsLoc;
+                tmsProc(pos, &tmsLoc);
+                Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
+                Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
+
+                const SkGlyph& glyph = glyphCacheProc(cache, &text,
+                                                      fx & fxMask, fy & fyMask);
+
+                if (glyph.fWidth) {
+                    this->bmpAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kCoverage_MaskStyle),
+                                         Sk48Dot16FloorToInt(fx),
+                                         Sk48Dot16FloorToInt(fy),
+                                         color,
+                                         fontScaler,
+                                         clipRect);
+                }
+                pos += scalarsPerPosition;
+            }
+        } else {
+            while (text < stop) {
+                const char* currentText = text;
+                const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
+
+                if (metricGlyph.fWidth) {
+                    SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
+                    SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
+                    SkPoint tmsLoc;
+                    tmsProc(pos, &tmsLoc);
+                    SkPoint alignLoc;
+                    alignProc(tmsLoc, metricGlyph, &alignLoc);
+
+                    Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
+                    Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
+
+                    // have to call again, now that we've been "aligned"
+                    const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
+                                                          fx & fxMask, fy & fyMask);
+                    // the assumption is that the metrics haven't changed
+                    SkASSERT(prevAdvX == glyph.fAdvanceX);
+                    SkASSERT(prevAdvY == glyph.fAdvanceY);
+                    SkASSERT(glyph.fWidth);
+
+                    this->bmpAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kCoverage_MaskStyle),
+                                         Sk48Dot16FloorToInt(fx),
+                                         Sk48Dot16FloorToInt(fy),
+                                         color,
+                                         fontScaler,
+                                         clipRect);
+                }
+                pos += scalarsPerPosition;
+            }
+        }
+    } else {    // not subpixel
+
+        if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
+            while (text < stop) {
+                // the last 2 parameters are ignored
+                const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+                if (glyph.fWidth) {
+                    SkPoint tmsLoc;
+                    tmsProc(pos, &tmsLoc);
+
+                    Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
+                    Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
+                    this->bmpAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kCoverage_MaskStyle),
+                                         Sk48Dot16FloorToInt(fx),
+                                         Sk48Dot16FloorToInt(fy),
+                                         color,
+                                         fontScaler,
+                                         clipRect);
+                }
+                pos += scalarsPerPosition;
+            }
+        } else {
+            while (text < stop) {
+                // the last 2 parameters are ignored
+                const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+                if (glyph.fWidth) {
+                    SkPoint tmsLoc;
+                    tmsProc(pos, &tmsLoc);
+
+                    SkPoint alignLoc;
+                    alignProc(tmsLoc, glyph, &alignLoc);
+
+                    Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
+                    Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
+                    this->bmpAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kCoverage_MaskStyle),
+                                         Sk48Dot16FloorToInt(fx),
+                                         Sk48Dot16FloorToInt(fy),
+                                         color,
+                                         fontScaler,
+                                         clipRect);
+                }
+                pos += scalarsPerPosition;
+            }
+        }
+    }
+}
+
+
+void GrAtlasTextContext::internalDrawDFText(BitmapTextBlob* blob, int runIndex,
+                                            SkGlyphCache* cache, const SkPaint& skPaint,
+                                            GrColor color,
+                                            const SkMatrix& viewMatrix,
+                                            const char text[], size_t byteLength,
+                                            SkScalar x, SkScalar y, const SkIRect& clipRect,
+                                            SkScalar textRatio,
+                                            SkTDArray<char>* fallbackTxt,
+                                            SkTDArray<SkScalar>* fallbackPos,
+                                            SkPoint* offset,
+                                            const SkPaint& origPaint) {
+    SkASSERT(byteLength == 0 || text != NULL);
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0) {
+        return;
+    }
+
+    SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc();
+    SkAutoDescriptor desc;
+    origPaint.getScalerContextDescriptor(&desc, &fDeviceProperties, NULL, true);
+    SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypeface(),
+                                                             desc.getDesc());
+
+    SkTArray<SkScalar> positions;
+
+    const char* textPtr = text;
+    SkFixed stopX = 0;
+    SkFixed stopY = 0;
+    SkFixed origin = 0;
+    switch (origPaint.getTextAlign()) {
+        case SkPaint::kRight_Align: origin = SK_Fixed1; break;
+        case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
+        case SkPaint::kLeft_Align: origin = 0; break;
+    }
+
+    SkAutoKern autokern;
+    const char* stop = text + byteLength;
+    while (textPtr < stop) {
+        // don't need x, y here, since all subpixel variants will have the
+        // same advance
+        const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0);
+
+        SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
+        positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
+
+        SkFixed height = glyph.fAdvanceY;
+        positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
+
+        stopX += width;
+        stopY += height;
+    }
+    SkASSERT(textPtr == stop);
+
+    // now adjust starting point depending on alignment
+    SkScalar alignX = SkFixedToScalar(stopX);
+    SkScalar alignY = SkFixedToScalar(stopY);
+    if (origPaint.getTextAlign() == SkPaint::kCenter_Align) {
+        alignX = SkScalarHalf(alignX);
+        alignY = SkScalarHalf(alignY);
+    } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) {
+        alignX = 0;
+        alignY = 0;
+    }
+    x -= alignX;
+    y -= alignY;
+    *offset = SkPoint::Make(x, y);
+
+    this->internalDrawDFPosText(blob, runIndex, cache, skPaint, color, viewMatrix, text, byteLength,
+                                positions.begin(), 2, *offset, clipRect, textRatio, fallbackTxt,
+                                fallbackPos);
+    SkGlyphCache::AttachCache(origPaintCache);
+}
+
+void GrAtlasTextContext::internalDrawDFPosText(BitmapTextBlob* blob, int runIndex,
+                                               SkGlyphCache* cache, const SkPaint& skPaint,
+                                               GrColor color,
+                                               const SkMatrix& viewMatrix,
+                                               const char text[], size_t byteLength,
+                                               const SkScalar pos[], int scalarsPerPosition,
+                                               const SkPoint& offset, const SkIRect& clipRect,
+                                               SkScalar textRatio,
+                                               SkTDArray<char>* fallbackTxt,
+                                               SkTDArray<SkScalar>* fallbackPos) {
+
+    SkASSERT(byteLength == 0 || text != NULL);
+    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
+
+    // nothing to draw
+    if (text == NULL || byteLength == 0) {
+        return;
+    }
+
+    fCurrStrike = NULL;
+
+    SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
+    GrFontScaler* fontScaler = GetGrFontScaler(cache);
+
+    const char* stop = text + byteLength;
+
+    if (SkPaint::kLeft_Align == skPaint.getTextAlign()) {
+        while (text < stop) {
+            const char* lastText = text;
+            // the last 2 parameters are ignored
+            const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+            if (glyph.fWidth) {
+                SkScalar x = offset.x() + pos[0];
+                SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
+
+                if (!this->dfAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kDistance_MaskStyle),
+                                         x, y, color, fontScaler, clipRect,
+                                         textRatio, viewMatrix)) {
+                    // couldn't append, send to fallback
+                    fallbackTxt->append(SkToInt(text-lastText), lastText);
+                    *fallbackPos->append() = pos[0];
+                    if (2 == scalarsPerPosition) {
+                        *fallbackPos->append() = pos[1];
+                    }
+                }
+            }
+            pos += scalarsPerPosition;
+        }
+    } else {
+        SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? SK_ScalarHalf
+                                                                             : SK_Scalar1;
+        while (text < stop) {
+            const char* lastText = text;
+            // the last 2 parameters are ignored
+            const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+            if (glyph.fWidth) {
+                SkScalar x = offset.x() + pos[0];
+                SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
+
+                SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
+                SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
+
+                if (!this->dfAppendGlyph(blob,
+                                         runIndex,
+                                         GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed(),
+                                                       GrGlyph::kDistance_MaskStyle),
+                                         x - advanceX, y - advanceY, color,
+                                         fontScaler,
+                                         clipRect,
+                                         textRatio,
+                                         viewMatrix)) {
+                    // couldn't append, send to fallback
+                    fallbackTxt->append(SkToInt(text-lastText), lastText);
+                    *fallbackPos->append() = pos[0];
+                    if (2 == scalarsPerPosition) {
+                        *fallbackPos->append() = pos[1];
+                    }
+                }
+            }
+            pos += scalarsPerPosition;
+        }
+    }
+}
+
+void GrAtlasTextContext::bmpAppendGlyph(BitmapTextBlob* blob, int runIndex,
+                                        GrGlyph::PackedID packed,
+                                        int vx, int vy, GrColor color, GrFontScaler* scaler,
+                                        const SkIRect& clipRect) {
+    Run& run = blob->fRuns[runIndex];
+    if (!fCurrStrike) {
+        fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
+        run.fStrike.reset(SkRef(fCurrStrike));
+    }
+
+    GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
+    if (!glyph) {
+        return;
+    }
+
+    int x = vx + glyph->fBounds.fLeft;
+    int y = vy + glyph->fBounds.fTop;
+
+    // keep them as ints until we've done the clip-test
+    int width = glyph->fBounds.width();
+    int height = glyph->fBounds.height();
+
+#if 0
+    // Not checking the clip bounds might introduce a performance regression.  However, its not
+    // clear if this is still true today with the larger tiles we use in Chrome.  For repositionable
+    // blobs, we want to make sure we have all of the glyphs, so clipping them out is not ideal.
+    // We could store the cliprect in the key, but then we'd lose the ability to do integer scrolls
+    // TODO verify this
+    // check if we clipped out
+    if (clipRect.quickReject(x, y, x + width, y + height)) {
+        return;
+    }
+#endif
+
+    // If the glyph is too large we fall back to paths
+    if (glyph->fTooLargeForAtlas) {
+        this->appendGlyphPath(blob, glyph, scaler, SkIntToScalar(vx), SkIntToScalar(vy));
+        return;
+    }
+
+    GrMaskFormat format = glyph->fMaskFormat;
+
+    PerSubRunInfo* subRun = &run.fSubRunInfo.back();
+    if (run.fInitialized && subRun->fMaskFormat != format) {
+        subRun = &run.fSubRunInfo.push_back();
+    }
+
+    run.fInitialized = true;
+
+    size_t vertexStride = get_vertex_stride(format);
+
+    SkRect r;
+    r.fLeft = SkIntToScalar(x);
+    r.fTop = SkIntToScalar(y);
+    r.fRight = r.fLeft + SkIntToScalar(width);
+    r.fBottom = r.fTop + SkIntToScalar(height);
+    subRun->fMaskFormat = format;
+    this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
+                            glyph);
+}
+
+bool GrAtlasTextContext::dfAppendGlyph(BitmapTextBlob* blob, int runIndex,
+                                       GrGlyph::PackedID packed,
+                                       SkScalar sx, SkScalar sy, GrColor color,
+                                       GrFontScaler* scaler,
+                                       const SkIRect& clipRect,
+                                       SkScalar textRatio, const SkMatrix& viewMatrix) {
+    Run& run = blob->fRuns[runIndex];
+    if (!fCurrStrike) {
+        fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler);
+        run.fStrike.reset(SkRef(fCurrStrike));
+    }
+
+    GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler);
+    if (!glyph) {
+        return true;
+    }
+
+    // fallback to color glyph support
+    if (kA8_GrMaskFormat != glyph->fMaskFormat) {
+        return false;
+    }
+
+    SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
+    SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
+    SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
+    SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
+
+    SkScalar scale = textRatio;
+    dx *= scale;
+    dy *= scale;
+    width *= scale;
+    height *= scale;
+    sx += dx;
+    sy += dy;
+    SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
+
+#if 0
+    // check if we clipped out
+    SkRect dstRect;
+    viewMatrix.mapRect(&dstRect, glyphRect);
+    if (clipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
+                             SkScalarTruncToInt(dstRect.top()),
+                             SkScalarTruncToInt(dstRect.right()),
+                             SkScalarTruncToInt(dstRect.bottom()))) {
+        return true;
+    }
+#endif
+
+    // TODO combine with the above
+    // If the glyph is too large we fall back to paths
+    if (glyph->fTooLargeForAtlas) {
+        this->appendGlyphPath(blob, glyph, scaler, sx - dx, sy - dy);
+        return true;
+    }
+
+    PerSubRunInfo* subRun = &run.fSubRunInfo.back();
+    SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
+    subRun->fMaskFormat = kA8_GrMaskFormat;
+
+    size_t vertexStride = get_vertex_stride_df(kA8_GrMaskFormat, subRun->fUseLCDText);
+
+    bool useColorVerts = !subRun->fUseLCDText;
+    this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
+                            glyph);
+    return true;
+}
+
+inline void GrAtlasTextContext::appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
+                                                GrFontScaler* scaler, SkScalar x, SkScalar y) {
+    if (NULL == glyph->fPath) {
+        SkPath* path = SkNEW(SkPath);
+        if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
+            // flag the glyph as being dead?
+            SkDELETE(path);
+            return;
+        }
+        glyph->fPath = path;
+    }
+    SkASSERT(glyph->fPath);
+    blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, x, y));
+}
+
+inline void GrAtlasTextContext::appendGlyphCommon(BitmapTextBlob* blob, Run* run,
+                                                  Run::SubRunInfo* subRun,
+                                                  const SkRect& positions, GrColor color,
+                                                  size_t vertexStride, bool useVertexColor,
+                                                  GrGlyph* glyph) {
+    blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
+    run->fVertexBounds.joinNonEmptyArg(positions);
+    run->fColor = color;
+
+    intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
+
+    if (useVertexColor) {
+        // V0
+        SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
+        position->set(positions.fLeft, positions.fTop);
+        SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
+        *colorPtr = color;
+        vertex += vertexStride;
+
+        // V1
+        position = reinterpret_cast<SkPoint*>(vertex);
+        position->set(positions.fLeft, positions.fBottom);
+        colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
+        *colorPtr = color;
+        vertex += vertexStride;
+
+        // V2
+        position = reinterpret_cast<SkPoint*>(vertex);
+        position->set(positions.fRight, positions.fBottom);
+        colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
+        *colorPtr = color;
+        vertex += vertexStride;
+
+        // V3
+        position = reinterpret_cast<SkPoint*>(vertex);
+        position->set(positions.fRight, positions.fTop);
+        colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
+        *colorPtr = color;
+    } else {
+        // V0
+        SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
+        position->set(positions.fLeft, positions.fTop);
+        vertex += vertexStride;
+
+        // V1
+        position = reinterpret_cast<SkPoint*>(vertex);
+        position->set(positions.fLeft, positions.fBottom);
+        vertex += vertexStride;
+
+        // V2
+        position = reinterpret_cast<SkPoint*>(vertex);
+        position->set(positions.fRight, positions.fBottom);
+        vertex += vertexStride;
+
+        // V3
+        position = reinterpret_cast<SkPoint*>(vertex);
+        position->set(positions.fRight, positions.fTop);
+    }
+
+    subRun->fGlyphEndIndex++;
+    subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph;
+}
+
+class BitmapTextBatch : public GrBatch {
+public:
+    typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
+    typedef GrAtlasTextContext::BitmapTextBlob Blob;
+    typedef Blob::Run Run;
+    typedef Run::SubRunInfo TextInfo;
+    struct Geometry {
+        Blob* fBlob;
+        int fRun;
+        int fSubRun;
+        GrColor fColor;
+        SkScalar fTransX;
+        SkScalar fTransY;
+    };
+
+    static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
+                                   GrBatchFontCache* fontCache) {
+        return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache));
+    }
+
+    static BitmapTextBatch* Create(GrMaskFormat maskFormat, int glyphCount,
+                                   GrBatchFontCache* fontCache,
+                                   DistanceAdjustTable* distanceAdjustTable,
+                                   SkColor filteredColor, bool useLCDText,
+                                   bool useBGR, float gamma) {
+        return SkNEW_ARGS(BitmapTextBatch, (maskFormat, glyphCount, fontCache, distanceAdjustTable,
+                                            filteredColor, useLCDText, useBGR, gamma));
+    }
+
+    const char* name() const override { return "BitmapTextBatch"; }
+
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
+        if (kARGB_GrMaskFormat == fMaskFormat) {
+            out->setUnknownFourComponents();
+        } else {
+            out->setKnownFourComponents(fBatch.fColor);
+        }
+    }
+
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
+        if (!fUseDistanceFields) {
+            // Bitmap Text
+            if (kARGB_GrMaskFormat != fMaskFormat) {
+                if (GrPixelConfigIsAlphaOnly(fPixelConfig)) {
+                    out->setUnknownSingleComponent();
+                } else if (GrPixelConfigIsOpaque(fPixelConfig)) {
+                    out->setUnknownOpaqueFourComponents();
+                    out->setUsingLCDCoverage();
+                } else {
+                    out->setUnknownFourComponents();
+                    out->setUsingLCDCoverage();
+                }
+            } else {
+                out->setKnownSingleComponent(0xff);
+            }
+        } else {
+            // Distance fields
+            if (!fUseLCDText) {
+                out->setUnknownSingleComponent();
+            } else {
+                out->setUnknownFourComponents();
+                out->setUsingLCDCoverage();
+            }
+        }
+    }
+
+    void initBatchTracker(const GrPipelineInfo& init) override {
+        // Handle any color overrides
+        if (init.fColorIgnored) {
+            fBatch.fColor = GrColor_ILLEGAL;
+        } else if (GrColor_ILLEGAL != init.fOverrideColor) {
+            fBatch.fColor = init.fOverrideColor;
+        }
+
+        // setup batch properties
+        fBatch.fColorIgnored = init.fColorIgnored;
+        fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
+        fBatch.fCoverageIgnored = init.fCoverageIgnored;
+    }
+
+    struct FlushInfo {
+        SkAutoTUnref<const GrVertexBuffer> fVertexBuffer;
+        SkAutoTUnref<const GrIndexBuffer> fIndexBuffer;
+        int fGlyphsToFlush;
+        int fVertexOffset;
+    };
+
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
+        // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
+        // TODO actually only invert if we don't have RGBA
+        SkMatrix localMatrix;
+        if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
+            SkDebugf("Cannot invert viewmatrix\n");
+            return;
+        }
+
+        GrTexture* texture = fFontCache->getTexture(fMaskFormat);
+        if (!texture) {
+            SkDebugf("Could not allocate backing texture for atlas\n");
+            return;
+        }
+
+        SkAutoTUnref<const GrGeometryProcessor> gp;
+        if (fUseDistanceFields) {
+            gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(),
+                                            texture));
+        } else {
+            GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
+            gp.reset(GrBitmapTextGeoProc::Create(this->color(),
+                                                 texture,
+                                                 params,
+                                                 fMaskFormat,
+                                                 localMatrix));
+        }
+
+        FlushInfo flushInfo;
+        flushInfo.fGlyphsToFlush = 0;
+        size_t vertexStride = gp->getVertexStride();
+        SkASSERT(vertexStride == (fUseDistanceFields ?
+                                  get_vertex_stride_df(fMaskFormat, fUseLCDText) :
+                                  get_vertex_stride(fMaskFormat)));
+
+        this->initDraw(batchTarget, gp, pipeline);
+
+        int glyphCount = this->numGlyphs();
+        int instanceCount = fInstanceCount;
+        const GrVertexBuffer* vertexBuffer;
+
+        void* vertices = batchTarget->makeVertSpace(vertexStride,
+                                                    glyphCount * kVerticesPerGlyph,
+                                                    &vertexBuffer,
+                                                    &flushInfo.fVertexOffset);
+        flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
+        flushInfo.fIndexBuffer.reset(batchTarget->resourceProvider()->refQuadIndexBuffer());
+        if (!vertices || !flushInfo.fVertexBuffer) {
+            SkDebugf("Could not allocate vertices\n");
+            return;
+        }
+
+        unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
+
+        // We cache some values to avoid going to the glyphcache for the same fontScaler twice
+        // in a row
+        const SkDescriptor* desc = NULL;
+        SkGlyphCache* cache = NULL;
+        GrFontScaler* scaler = NULL;
+        SkTypeface* typeface = NULL;
+
+        for (int i = 0; i < instanceCount; i++) {
+            Geometry& args = fGeoData[i];
+            Blob* blob = args.fBlob;
+            Run& run = blob->fRuns[args.fRun];
+            TextInfo& info = run.fSubRunInfo[args.fSubRun];
+
+            uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat);
+            bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen;
+            bool regenerateColors;
+            if (fUseDistanceFields) {
+                regenerateColors = !fUseLCDText && run.fColor != args.fColor;
+            } else {
+                regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColor != args.fColor;
+            }
+            bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
+            int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
+
+            // We regenerate both texture coords and colors in the blob itself, and update the
+            // atlas generation.  If we don't end up purging any unused plots, we can avoid
+            // regenerating the coords.  We could take a finer grained approach to updating texture
+            // coords but its not clear if the extra bookkeeping would offset any gains.
+            // To avoid looping over the glyphs twice, we do one loop and conditionally update color
+            // or coords as needed.  One final note, if we have to break a run for an atlas eviction
+            // then we can't really trust the atlas has all of the correct data.  Atlas evictions
+            // should be pretty rare, so we just always regenerate in those cases
+            if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
+                // first regenerate texture coordinates / colors if need be
+                bool brokenRun = false;
+
+                // Because the GrBatchFontCache may evict the strike a blob depends on using for
+                // generating its texture coords, we have to track whether or not the strike has
+                // been abandoned.  If it hasn't been abandoned, then we can use the GrGlyph*s as is
+                // otherwise we have to get the new strike, and use that to get the correct glyphs.
+                // Because we do not have the packed ids, and thus can't look up our glyphs in the
+                // new strike, we instead keep our ref to the old strike and use the packed ids from
+                // it.  These ids will still be valid as long as we hold the ref.  When we are done
+                // updating our cache of the GrGlyph*s, we drop our ref on the old strike
+                bool regenerateGlyphs = false;
+                GrBatchTextStrike* strike = NULL;
+                if (regenerateTextureCoords) {
+                    info.fBulkUseToken.reset();
+
+                    // We can reuse if we have a valid strike and our descriptors / typeface are the
+                    // same
+                    const SkDescriptor* newDesc = run.fOverrideDescriptor ?
+                                                  run.fOverrideDescriptor->getDesc() :
+                                                  run.fDescriptor.getDesc();
+                    if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
+                                  !(desc->equals(*newDesc))) {
+                        if (cache) {
+                            SkGlyphCache::AttachCache(cache);
+                        }
+                        desc = newDesc;
+                        cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
+                        scaler = GrTextContext::GetGrFontScaler(cache);
+                        strike = run.fStrike;
+                        typeface = run.fTypeface;
+                    }
+
+                    if (run.fStrike->isAbandoned()) {
+                        regenerateGlyphs = true;
+                        strike = fFontCache->getStrike(scaler);
+                    } else {
+                        strike = run.fStrike;
+                    }
+                }
+
+                for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
+                    if (regenerateTextureCoords) {
+                        size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
+                        GrGlyph* glyph;
+                        if (regenerateGlyphs) {
+                            // Get the id from the old glyph, and use the new strike to lookup
+                            // the glyph.
+                            glyph = blob->fGlyphs[glyphOffset];
+                            blob->fGlyphs[glyphOffset] = strike->getGlyph(glyph->fPackedID,
+                                                                          scaler);
+                        }
+                        glyph = blob->fGlyphs[glyphOffset];
+                        SkASSERT(glyph);
+
+                        if (!fFontCache->hasGlyph(glyph) &&
+                            !strike->addGlyphToAtlas(batchTarget, glyph, scaler)) {
+                            this->flush(batchTarget, &flushInfo);
+                            this->initDraw(batchTarget, gp, pipeline);
+                            brokenRun = glyphIdx > 0;
+
+                            SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(batchTarget,
+                                                                                glyph,
+                                                                                scaler);
+                            SkASSERT(success);
+                        }
+                        fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken, glyph,
+                                                                 batchTarget->currentToken());
+
+                        // Texture coords are the last vertex attribute so we get a pointer to the
+                        // first one and then map with stride in regenerateTextureCoords
+                        intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
+                        vertex += info.fVertexStartIndex;
+                        vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
+                        vertex += vertexStride - sizeof(SkIPoint16);
+
+                        this->regenerateTextureCoords(glyph, vertex, vertexStride);
+                    }
+
+                    if (regenerateColors) {
+                        intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
+                        vertex += info.fVertexStartIndex;
+                        vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint);
+                        this->regenerateColors(vertex, vertexStride, args.fColor);
+                    }
+
+                    if (regeneratePositions) {
+                        intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
+                        vertex += info.fVertexStartIndex;
+                        vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
+                        SkScalar transX = args.fTransX;
+                        SkScalar transY = args.fTransY;
+                        this->regeneratePositions(vertex, vertexStride, transX, transY);
+                    }
+                    flushInfo.fGlyphsToFlush++;
+                }
+
+                // We my have changed the color so update it here
+                run.fColor = args.fColor;
+                if (regenerateTextureCoords) {
+                    if (regenerateGlyphs) {
+                        run.fStrike.reset(SkRef(strike));
+                    }
+                    info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
+                                                        fFontCache->atlasGeneration(fMaskFormat);
+                }
+            } else {
+                flushInfo.fGlyphsToFlush += glyphCount;
+
+                // set use tokens for all of the glyphs in our subrun.  This is only valid if we
+                // have a valid atlas generation
+                fFontCache->setUseTokenBulk(info.fBulkUseToken,
+                                            batchTarget->currentToken(),
+                                            fMaskFormat);
+            }
+
+            // now copy all vertices
+            size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
+            memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
+
+            currVertex += byteCount;
+        }
+        // Make sure to attach the last cache if applicable
+        if (cache) {
+            SkGlyphCache::AttachCache(cache);
+        }
+        this->flush(batchTarget, &flushInfo);
+    }
+
+    // The minimum number of Geometry we will try to allocate.
+    static const int kMinAllocated = 32;
+
+    // Total number of Geometry this Batch owns
+    int instanceCount() const { return fInstanceCount; }
+    SkAutoSTMalloc<kMinAllocated, Geometry>* geoData() { return &fGeoData; }
+
+    // to avoid even the initial copy of the struct, we have a getter for the first item which
+    // is used to seed the batch with its initial geometry.  After seeding, the client should call
+    // init() so the Batch can initialize itself
+    Geometry& geometry() { return fGeoData[0]; }
+    void init() {
+        const Geometry& geo = fGeoData[0];
+        fBatch.fColor = geo.fColor;
+        fBatch.fViewMatrix = geo.fBlob->fViewMatrix;
+
+        // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
+        // into device space
+        const Run& run = geo.fBlob->fRuns[geo.fRun];
+        if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
+            SkRect bounds = run.fVertexBounds;
+            fBatch.fViewMatrix.mapRect(&bounds);
+            this->setBounds(bounds);
+        } else {
+            this->setBounds(run.fVertexBounds);
+        }
+    }
+
+private:
+    BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache)
+            : fMaskFormat(maskFormat)
+            , fPixelConfig(fontCache->getPixelConfig(maskFormat))
+            , fFontCache(fontCache)
+            , fUseDistanceFields(false) {
+        this->initClassID<BitmapTextBatch>();
+        fBatch.fNumGlyphs = glyphCount;
+        fInstanceCount = 1;
+        fAllocatedCount = kMinAllocated;
+    }
+
+    BitmapTextBatch(GrMaskFormat maskFormat, int glyphCount, GrBatchFontCache* fontCache,
+                    DistanceAdjustTable* distanceAdjustTable, SkColor filteredColor,
+                    bool useLCDText, bool useBGR, float gamma)
+            : fMaskFormat(maskFormat)
+            , fPixelConfig(fontCache->getPixelConfig(maskFormat))
+            , fFontCache(fontCache)
+            , fDistanceAdjustTable(SkRef(distanceAdjustTable))
+            , fFilteredColor(filteredColor)
+            , fUseDistanceFields(true)
+            , fUseLCDText(useLCDText)
+            , fUseBGR(useBGR)
+            , fGamma(gamma) {
+        this->initClassID<BitmapTextBatch>();
+        fBatch.fNumGlyphs = glyphCount;
+        fInstanceCount = 1;
+        fAllocatedCount = kMinAllocated;
+        SkASSERT(fMaskFormat == kA8_GrMaskFormat);
+    }
+
+    ~BitmapTextBatch() {
+        for (int i = 0; i < fInstanceCount; i++) {
+            fGeoData[i].fBlob->unref();
+        }
+    }
+
+    void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexStride) {
+        int width = glyph->fBounds.width();
+        int height = glyph->fBounds.height();
+
+        int u0, v0, u1, v1;
+        if (fUseDistanceFields) {
+            u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
+            v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
+            u1 = u0 + width - 2 * SK_DistanceFieldInset;
+            v1 = v0 + height - 2 * SK_DistanceFieldInset;
+        } else {
+            u0 = glyph->fAtlasLocation.fX;
+            v0 = glyph->fAtlasLocation.fY;
+            u1 = u0 + width;
+            v1 = v0 + height;
+        }
+
+        SkIPoint16* textureCoords;
+        // V0
+        textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
+        textureCoords->set(u0, v0);
+        vertex += vertexStride;
+
+        // V1
+        textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
+        textureCoords->set(u0, v1);
+        vertex += vertexStride;
+
+        // V2
+        textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
+        textureCoords->set(u1, v1);
+        vertex += vertexStride;
+
+        // V3
+        textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
+        textureCoords->set(u1, v0);
+    }
+
+    void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) {
+        for (int i = 0; i < kVerticesPerGlyph; i++) {
+            SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
+            *vcolor = color;
+            vertex += vertexStride;
+        }
+    }
+
+    void regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
+                             SkScalar transY) {
+        for (int i = 0; i < kVerticesPerGlyph; i++) {
+            SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
+            point->fX += transX;
+            point->fY += transY;
+            vertex += vertexStride;
+        }
+    }
+
+    void initDraw(GrBatchTarget* batchTarget,
+                  const GrGeometryProcessor* gp,
+                  const GrPipeline* pipeline) {
+        batchTarget->initDraw(gp, pipeline);
+
+        // TODO remove this when batch is everywhere
+        GrPipelineInfo init;
+        init.fColorIgnored = fBatch.fColorIgnored;
+        init.fOverrideColor = GrColor_ILLEGAL;
+        init.fCoverageIgnored = fBatch.fCoverageIgnored;
+        init.fUsesLocalCoords = this->usesLocalCoords();
+        gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
+    }
+
+    void flush(GrBatchTarget* batchTarget, FlushInfo* flushInfo) {
+        GrVertices vertices;
+        int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
+        vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
+                               flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
+                               kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyphsToFlush,
+                               maxGlyphsPerDraw);
+        batchTarget->draw(vertices);
+        flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
+        flushInfo->fGlyphsToFlush = 0;
+    }
+
+    GrColor color() const { return fBatch.fColor; }
+    const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
+    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+    int numGlyphs() const { return fBatch.fNumGlyphs; }
+
+    bool onCombineIfPossible(GrBatch* t) override {
+        BitmapTextBatch* that = t->cast<BitmapTextBatch>();
+
+        if (fUseDistanceFields != that->fUseDistanceFields) {
+            return false;
+        }
+
+        if (!fUseDistanceFields) {
+            // Bitmap Text
+            if (fMaskFormat != that->fMaskFormat) {
+                return false;
+            }
+
+            // TODO we can often batch across LCD text if we have dual source blending and don't
+            // have to use the blend constant
+            if (fMaskFormat != kA8_GrMaskFormat && this->color() != that->color()) {
+                return false;
+            }
+
+            if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+                return false;
+            }
+        } else {
+            // Distance Fields
+            SkASSERT(this->fMaskFormat == that->fMaskFormat &&
+                     this->fMaskFormat == kA8_GrMaskFormat);
+
+            if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+                return false;
+            }
+
+            if (fFilteredColor != that->fFilteredColor) {
+                return false;
+            }
+
+            if (fUseLCDText != that->fUseLCDText) {
+                return false;
+            }
+
+            if (fUseBGR != that->fUseBGR) {
+                return false;
+            }
+
+            if (fGamma != that->fGamma) {
+                return false;
+            }
+
+            // TODO see note above
+            if (fUseLCDText && this->color() != that->color()) {
+                return false;
+            }
+        }
+
+        fBatch.fNumGlyphs += that->numGlyphs();
+
+        // copy that->geoData().  We do this manually for performance reasons
+        SkAutoSTMalloc<kMinAllocated, Geometry>* otherGeoData = that->geoData();
+        int otherInstanceCount = that->instanceCount();
+        int allocSize = otherInstanceCount + fInstanceCount;
+        if (allocSize > fAllocatedCount) {
+            while (allocSize > fAllocatedCount) {
+                fAllocatedCount = fAllocatedCount << 1;
+            }
+            fGeoData.realloc(fAllocatedCount);
+        }
+
+        memcpy(&fGeoData[fInstanceCount], otherGeoData->get(),
+               otherInstanceCount * sizeof(Geometry));
+        int total = fInstanceCount + otherInstanceCount;
+        for (int i = fInstanceCount; i < total; i++) {
+            fGeoData[i].fBlob->ref();
+        }
+        fInstanceCount = total;
+
+        this->joinBounds(that->bounds());
+        return true;
+    }
+
+    // TODO just use class params
+    // TODO trying to figure out why lcd is so whack
+    GrGeometryProcessor* setupDfProcessor(const SkMatrix& viewMatrix, SkColor filteredColor,
+                                          GrColor color, GrTexture* texture) {
+        GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
+
+        // set up any flags
+        uint32_t flags = 0;
+        flags |= viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
+        flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
+        flags |= fUseLCDText && viewMatrix.rectStaysRect() ?
+                                kRectToRect_DistanceFieldEffectFlag : 0;
+        flags |= fUseLCDText && fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
+
+        // see if we need to create a new effect
+        if (fUseLCDText) {
+            GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
+
+            float redCorrection =
+                (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift];
+            float greenCorrection =
+                (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift];
+            float blueCorrection =
+                (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift];
+            GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
+                GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
+                                                                    greenCorrection,
+                                                                    blueCorrection);
+
+            return GrDistanceFieldLCDTextGeoProc::Create(color,
+                                                         viewMatrix,
+                                                         texture,
+                                                         params,
+                                                         widthAdjust,
+                                                         flags);
+        } else {
+            flags |= kColorAttr_DistanceFieldEffectFlag;
+#ifdef SK_GAMMA_APPLY_TO_A8
+            U8CPU lum = SkColorSpaceLuminance::computeLuminance(fGamma, filteredColor);
+            float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShift];
+            return GrDistanceFieldA8TextGeoProc::Create(color,
+                                                        viewMatrix,
+                                                        texture,
+                                                        params,
+                                                        correction,
+                                                        flags);
+#else
+            return GrDistanceFieldA8TextGeoProc::Create(color,
+                                                        viewMatrix,
+                                                        texture,
+                                                        params,
+                                                        flags);
+#endif
+        }
+
+    }
+
+    struct BatchTracker {
+        GrColor fColor;
+        SkMatrix fViewMatrix;
+        bool fUsesLocalCoords;
+        bool fColorIgnored;
+        bool fCoverageIgnored;
+        int fNumGlyphs;
+    };
+
+    BatchTracker fBatch;
+    SkAutoSTMalloc<kMinAllocated, Geometry> fGeoData;
+    int fInstanceCount;
+    int fAllocatedCount;
+    GrMaskFormat fMaskFormat;
+    GrPixelConfig fPixelConfig;
+    GrBatchFontCache* fFontCache;
+
+    // Distance field properties
+    SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable;
+    SkColor fFilteredColor;
+    bool fUseDistanceFields;
+    bool fUseLCDText;
+    bool fUseBGR;
+    float fGamma;
+};
+
+void GrAtlasTextContext::flushRunAsPaths(const SkTextBlob::RunIterator& it, const SkPaint& skPaint,
+                                         SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
+                                         const SkIRect& clipBounds, SkScalar x, SkScalar y) {
+    SkPaint runPaint = skPaint;
+
+    size_t textLen = it.glyphCount() * sizeof(uint16_t);
+    const SkPoint& offset = it.offset();
+
+    it.applyFontToPaint(&runPaint);
+
+    if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
+        return;
+    }
+
+    runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
+
+    switch (it.positioning()) {
+        case SkTextBlob::kDefault_Positioning:
+            this->drawTextAsPath(runPaint, viewMatrix, (const char *)it.glyphs(),
+                                 textLen, x + offset.x(), y + offset.y(), clipBounds);
+            break;
+        case SkTextBlob::kHorizontal_Positioning:
+            this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
+                                    textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
+                                    clipBounds);
+            break;
+        case SkTextBlob::kFull_Positioning:
+            this->drawPosTextAsPath(runPaint, viewMatrix, (const char*)it.glyphs(),
+                                    textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
+            break;
+    }
+}
+
+
+inline BitmapTextBatch*
+GrAtlasTextContext::createBatch(BitmapTextBlob* cacheBlob, const PerSubRunInfo& info,
+                                int glyphCount, int run, int subRun,
+                                GrColor color, SkScalar transX, SkScalar transY,
+                                const SkPaint& skPaint) {
+    GrMaskFormat format = info.fMaskFormat;
+    GrColor subRunColor;
+    if (kARGB_GrMaskFormat == format) {
+        uint8_t paintAlpha = skPaint.getAlpha();
+        subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
+    } else {
+        subRunColor = color;
+    }
+
+    BitmapTextBatch* batch;
+    if (info.fDrawAsDistanceFields) {
+        SkColor filteredColor;
+        SkColorFilter* colorFilter = skPaint.getColorFilter();
+        if (colorFilter) {
+            filteredColor = colorFilter->filterColor(skPaint.getColor());
+        } else {
+            filteredColor = skPaint.getColor();
+        }
+        bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
+        float gamma = fDeviceProperties.gamma();
+        batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache(),
+                                        fDistanceAdjustTable, filteredColor,
+                                        info.fUseLCDText, useBGR,
+                                        gamma);
+    } else {
+        batch = BitmapTextBatch::Create(format, glyphCount, fContext->getBatchFontCache());
+    }
+    BitmapTextBatch::Geometry& geometry = batch->geometry();
+    geometry.fBlob = SkRef(cacheBlob);
+    geometry.fRun = run;
+    geometry.fSubRun = subRun;
+    geometry.fColor = subRunColor;
+    geometry.fTransX = transX;
+    geometry.fTransY = transY;
+    batch->init();
+
+    return batch;
+}
+
+inline void GrAtlasTextContext::flushRun(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder,
+                                         BitmapTextBlob* cacheBlob, int run, GrColor color,
+                                         SkScalar transX, SkScalar transY, const SkPaint& skPaint) {
+    for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
+        const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
+        int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
+        if (0 == glyphCount) {
+            continue;
+        }
+
+        SkAutoTUnref<BitmapTextBatch> batch(this->createBatch(cacheBlob, info, glyphCount, run,
+                                                              subRun, color, transX, transY,
+                                                              skPaint));
+        target->drawBatch(pipelineBuilder, batch);
+    }
+}
+
+inline void GrAtlasTextContext::flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
+                                               const SkPaint& skPaint,
+                                               SkScalar transX, SkScalar transY,
+                                               const SkIRect& clipBounds) {
+    if (!cacheBlob->fBigGlyphs.count()) {
+        return;
+    }
+
+    SkMatrix pathMatrix;
+    if (!cacheBlob->fViewMatrix.invert(&pathMatrix)) {
+        SkDebugf("could not invert viewmatrix\n");
+        return;
+    }
+
+    for (int i = 0; i < cacheBlob->fBigGlyphs.count(); i++) {
+        BitmapTextBlob::BigGlyph& bigGlyph = cacheBlob->fBigGlyphs[i];
+        bigGlyph.fVx += transX;
+        bigGlyph.fVy += transY;
+        SkMatrix translate = cacheBlob->fViewMatrix;
+        translate.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
+
+        fGpuDevice->internalDrawPath(bigGlyph.fPath, skPaint, translate, &pathMatrix, clipBounds,
+                                     false);
+    }
+}
+
+void GrAtlasTextContext::flush(GrDrawTarget* target,
+                               const SkTextBlob* blob,
+                               BitmapTextBlob* cacheBlob,
+                               GrRenderTarget* rt,
+                               const SkPaint& skPaint,
+                               const GrPaint& grPaint,
+                               SkDrawFilter* drawFilter,
+                               const GrClip& clip,
+                               const SkMatrix& viewMatrix,
+                               const SkIRect& clipBounds,
+                               SkScalar x, SkScalar y,
+                               SkScalar transX, SkScalar transY) {
+    // We loop through the runs of the blob, flushing each.  If any run is too large, then we flush
+    // it as paths
+    GrPipelineBuilder pipelineBuilder;
+    pipelineBuilder.setFromPaint(grPaint, rt, clip);
+
+    GrColor color = grPaint.getColor();
+
+    SkTextBlob::RunIterator it(blob);
+    for (int run = 0; !it.done(); it.next(), run++) {
+        if (cacheBlob->fRuns[run].fDrawAsPaths) {
+            this->flushRunAsPaths(it, skPaint, drawFilter, viewMatrix, clipBounds, x, y);
+            continue;
+        }
+        cacheBlob->fRuns[run].fVertexBounds.offset(transX, transY);
+        this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, transX, transY, skPaint);
+    }
+
+    // Now flush big glyphs
+    this->flushBigGlyphs(cacheBlob, rt, skPaint, transX, transY, clipBounds);
+}
+
+void GrAtlasTextContext::flush(GrDrawTarget* target,
+                               BitmapTextBlob* cacheBlob,
+                               GrRenderTarget* rt,
+                               const SkPaint& skPaint,
+                               const GrPaint& grPaint,
+                               const GrClip& clip,
+                               const SkIRect& clipBounds) {
+    GrPipelineBuilder pipelineBuilder;
+    pipelineBuilder.setFromPaint(grPaint, rt, clip);
+
+    GrColor color = grPaint.getColor();
+    for (int run = 0; run < cacheBlob->fRunCount; run++) {
+        this->flushRun(target, &pipelineBuilder, cacheBlob, run, color, 0, 0, skPaint);
+    }
+
+    // Now flush big glyphs
+    this->flushBigGlyphs(cacheBlob, rt, skPaint, 0, 0, clipBounds);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef GR_TEST_UTILS
+
+BATCH_TEST_DEFINE(TextBlobBatch) {
+    static uint32_t gContextID = SK_InvalidGenID;
+    static GrAtlasTextContext* gTextContext = NULL;
+    static SkDeviceProperties gDeviceProperties(SkDeviceProperties::kLegacyLCD_InitType);
+
+    if (context->uniqueID() != gContextID) {
+        gContextID = context->uniqueID();
+        SkDELETE(gTextContext);
+        // We don't yet test the fall back to paths in the GrTextContext base class.  This is mostly
+        // because we don't really want to have a gpu device here.
+        // We enable distance fields by twiddling a knob on the paint
+        gTextContext = GrAtlasTextContext::Create(context, NULL, gDeviceProperties, false);
+    }
+
+    // create dummy render target
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;
+    desc.fWidth = 1024;
+    desc.fHeight = 1024;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fSampleCnt = 0;
+    SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, true, NULL, 0));
+    SkASSERT(texture);
+    SkASSERT(NULL != texture->asRenderTarget());
+    GrRenderTarget* rt = texture->asRenderTarget();
+
+    // Setup dummy SkPaint / GrPaint
+    GrColor color = GrRandomColor(random);
+    SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
+    SkPaint skPaint;
+    skPaint.setDistanceFieldTextTEMP(random->nextBool());
+    skPaint.setColor(color);
+    skPaint.setLCDRenderText(random->nextBool());
+    skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
+    skPaint.setSubpixelText(random->nextBool());
+
+    GrPaint grPaint;
+    if (!SkPaint2GrPaint(context, rt, skPaint, viewMatrix, true, &grPaint)) {
+        SkFAIL("couldn't convert paint\n");
+    }
+
+    const char* text = "The quick brown fox jumps over the lazy dog.";
+    int textLen = (int)strlen(text);
+
+    // Setup clip
+    GrClip clip;
+    SkIRect noClip = SkIRect::MakeLargest();
+
+    // right now we don't handle textblobs, nor do we handle drawPosText.  Since we only
+    // intend to test the batch with this unit test, that is okay.
+    SkAutoTUnref<GrAtlasTextContext::BitmapTextBlob> blob(
+            gTextContext->createDrawTextBlob(rt, clip, grPaint, skPaint, viewMatrix, text,
+                                             static_cast<size_t>(textLen), 0, 0, noClip));
+
+    SkScalar transX = static_cast<SkScalar>(random->nextU());
+    SkScalar transY = static_cast<SkScalar>(random->nextU());
+    const GrAtlasTextContext::BitmapTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0];
+    return gTextContext->createBatch(blob, info, textLen, 0, 0, color, transX, transY, skPaint);
+}
+
+#endif
diff --git a/src/gpu/GrAtlasTextContext.h b/src/gpu/GrAtlasTextContext.h
new file mode 100644
index 0000000..eb7ba47
--- /dev/null
+++ b/src/gpu/GrAtlasTextContext.h
@@ -0,0 +1,397 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrAtlasTextContext_DEFINED
+#define GrAtlasTextContext_DEFINED
+
+#include "GrTextContext.h"
+
+#include "GrBatchAtlas.h"
+#include "GrBatchFontCache.h"
+#include "GrGeometryProcessor.h"
+#include "SkDescriptor.h"
+#include "GrMemoryPool.h"
+#include "SkMaskFilter.h"
+#include "SkTextBlob.h"
+#include "SkTInternalLList.h"
+
+#ifdef GR_TEST_UTILS
+#include "GrBatchTest.h"
+#endif
+
+class BitmapTextBatch;
+class GrPipelineBuilder;
+class GrTextBlobCache;
+
+/*
+ * This class implements GrTextContext using standard bitmap fonts, and can also process textblobs.
+ * TODO replace GrBitmapTextContext
+ */
+class GrAtlasTextContext : public GrTextContext {
+public:
+    static GrAtlasTextContext* Create(GrContext*, SkGpuDevice*, const SkDeviceProperties&,
+                                      bool enableDistanceFields);
+
+private:
+    GrAtlasTextContext(GrContext*, SkGpuDevice*, const SkDeviceProperties&,
+                       bool enableDistanceFields);
+    ~GrAtlasTextContext() override {}
+
+    bool canDraw(const GrRenderTarget*, const GrClip&, const GrPaint&,
+                 const SkPaint&, const SkMatrix& viewMatrix) override;
+
+    void onDrawText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
+                    const SkMatrix& viewMatrix, const char text[], size_t byteLength,
+                    SkScalar x, SkScalar y, const SkIRect& regionClipBounds) override;
+    void onDrawPosText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
+                       const SkMatrix& viewMatrix,
+                       const char text[], size_t byteLength,
+                       const SkScalar pos[], int scalarsPerPosition,
+                       const SkPoint& offset, const SkIRect& regionClipBounds) override;
+    void drawTextBlob(GrRenderTarget*, const GrClip&, const SkPaint&,
+                      const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y,
+                      SkDrawFilter*, const SkIRect& clipBounds) override;
+
+    /*
+     * A BitmapTextBlob contains a fully processed SkTextBlob, suitable for nearly immediate drawing
+     * on the GPU.  These are initially created with valid positions and colors, but invalid
+     * texture coordinates.  The BitmapTextBlob itself has a few Blob-wide properties, and also
+     * consists of a number of runs.  Runs inside a blob are flushed individually so they can be
+     * reordered.
+     *
+     * The only thing(aside from a memcopy) required to flush a BitmapTextBlob is to ensure that
+     * the GrAtlas will not evict anything the Blob needs.
+     */
+    struct BitmapTextBlob : public SkRefCnt {
+        SK_DECLARE_INTERNAL_LLIST_INTERFACE(BitmapTextBlob);
+
+        /*
+         * Each Run inside of the blob can have its texture coordinates regenerated if required.
+         * To determine if regeneration is necessary, fAtlasGeneration is used.  If there have been
+         * any evictions inside of the atlas, then we will simply regenerate Runs.  We could track
+         * this at a more fine grained level, but its not clear if this is worth it, as evictions
+         * should be fairly rare.
+         *
+         * One additional point, each run can contain glyphs with any of the three mask formats.
+         * We call these SubRuns.  Because a subrun must be a contiguous range, we have to create
+         * a new subrun each time the mask format changes in a run.  In theory, a run can have as
+         * many SubRuns as it has glyphs, ie if a run alternates between color emoji and A8.  In
+         * practice, the vast majority of runs have only a single subrun.
+         *
+         * Finally, for runs where the entire thing is too large for the GrAtlasTextContext to
+         * handle, we have a bit to mark the run as flusahable via rendering as paths.  It is worth
+         * pointing. It would be a bit expensive to figure out ahead of time whether or not a run
+         * can flush in this manner, so we always allocate vertices for the run, regardless of
+         * whether or not it is too large.  The benefit of this strategy is that we can always reuse
+         * a blob allocation regardless of viewmatrix changes.  We could store positions for these
+         * glyphs.  However, its not clear if this is a win because we'd still have to either go the
+         * glyph cache to get the path at flush time, or hold onto the path in the cache, which
+         * would greatly increase the memory of these cached items.
+         */
+        struct Run {
+            Run()
+                : fColor(GrColor_ILLEGAL)
+                , fInitialized(false)
+                , fDrawAsPaths(false) {
+                fVertexBounds.setLargestInverted();
+                // To ensure we always have one subrun, we push back a fresh run here
+                fSubRunInfo.push_back();
+            }
+            struct SubRunInfo {
+                SubRunInfo()
+                    : fAtlasGeneration(GrBatchAtlas::kInvalidAtlasGeneration)
+                    , fVertexStartIndex(0)
+                    , fVertexEndIndex(0)
+                    , fGlyphStartIndex(0)
+                    , fGlyphEndIndex(0)
+                    , fDrawAsDistanceFields(false) {}
+                // Distance field text cannot draw coloremoji, and so has to fall back.  However,
+                // though the distance field text and the coloremoji may share the same run, they
+                // will have different descriptors.  If fOverrideDescriptor is non-NULL, then it
+                // will be used in place of the run's descriptor to regen texture coords
+                // TODO we could have a descriptor cache, it would reduce the size of these blobs
+                // significantly, and then the subrun could just have a refed pointer to the
+                // correct descriptor.
+                GrBatchAtlas::BulkUseTokenUpdater fBulkUseToken;
+                uint64_t fAtlasGeneration;
+                size_t fVertexStartIndex;
+                size_t fVertexEndIndex;
+                uint32_t fGlyphStartIndex;
+                uint32_t fGlyphEndIndex;
+                SkScalar fTextRatio; // df property
+                GrMaskFormat fMaskFormat;
+                bool fDrawAsDistanceFields; // df property
+                bool fUseLCDText; // df property
+            };
+
+            SubRunInfo& push_back() {
+                // Forward glyph / vertex information to seed the new sub run
+                SubRunInfo& prevSubRun = fSubRunInfo.back();
+                SubRunInfo& newSubRun = fSubRunInfo.push_back();
+                newSubRun.fGlyphStartIndex = prevSubRun.fGlyphEndIndex;
+                newSubRun.fGlyphEndIndex = prevSubRun.fGlyphEndIndex;
+
+                newSubRun.fVertexStartIndex = prevSubRun.fVertexEndIndex;
+                newSubRun.fVertexEndIndex = prevSubRun.fVertexEndIndex;
+                return newSubRun;
+            }
+            static const int kMinSubRuns = 1;
+            SkAutoTUnref<GrBatchTextStrike> fStrike;
+            SkAutoTUnref<SkTypeface> fTypeface;
+            SkRect fVertexBounds;
+            SkSTArray<kMinSubRuns, SubRunInfo> fSubRunInfo;
+            SkAutoDescriptor fDescriptor;
+            SkAutoTDelete<SkAutoDescriptor> fOverrideDescriptor; // df properties
+            GrColor fColor;
+            bool fInitialized;
+            bool fDrawAsPaths;
+        };
+
+        struct BigGlyph {
+            BigGlyph(const SkPath& path, SkScalar vx, SkScalar vy)
+                : fPath(path)
+                , fVx(vx)
+                , fVy(vy) {}
+            SkPath fPath;
+            SkScalar fVx;
+            SkScalar fVy;
+        };
+
+        struct Key {
+            Key() {
+                sk_bzero(this, sizeof(Key));
+            }
+            uint32_t fUniqueID;
+            // Color may affect the gamma of the mask we generate, but in a fairly limited way.
+            // Each color is assigned to on of a fixed number of buckets based on its
+            // luminance. For each luminance bucket there is a "canonical color" that
+            // represents the bucket.  This functionality is currently only supported for A8
+            SkColor fCanonicalColor;
+            SkPaint::Style fStyle;
+            SkPixelGeometry fPixelGeometry;
+            bool fHasBlur;
+
+            bool operator==(const Key& other) const {
+                return 0 == memcmp(this, &other, sizeof(Key));
+            }
+        };
+
+        struct StrokeInfo {
+            SkScalar fFrameWidth;
+            SkScalar fMiterLimit;
+            SkPaint::Join fJoin;
+        };
+
+        enum TextType {
+            kHasDistanceField_TextType = 0x1,
+            kHasBitmap_TextType = 0x2,
+        };
+
+        // all glyph / vertex offsets are into these pools.
+        unsigned char* fVertices;
+        GrGlyph** fGlyphs;
+        Run* fRuns;
+        GrMemoryPool* fPool;
+        SkMaskFilter::BlurRec fBlurRec;
+        StrokeInfo fStrokeInfo;
+        SkTArray<BigGlyph> fBigGlyphs;
+        Key fKey;
+        SkMatrix fViewMatrix;
+        SkColor fPaintColor;
+        SkScalar fX;
+        SkScalar fY;
+
+        // We can reuse distance field text, but only if the new viewmatrix would not result in
+        // a mip change.  Because there can be multiple runs in a blob, we track the overall
+        // maximum minimum scale, and minimum maximum scale, we can support before we need to regen
+        SkScalar fMaxMinScale;
+        SkScalar fMinMaxScale;
+        int fRunCount;
+        uint8_t fTextType;
+
+        BitmapTextBlob()
+            : fMaxMinScale(-SK_ScalarMax)
+            , fMinMaxScale(SK_ScalarMax)
+            , fTextType(0) {}
+
+        ~BitmapTextBlob() override {
+            for (int i = 0; i < fRunCount; i++) {
+                fRuns[i].~Run();
+            }
+        }
+
+        static const Key& GetKey(const BitmapTextBlob& blob) {
+            return blob.fKey;
+        }
+
+        static uint32_t Hash(const Key& key) {
+            return SkChecksum::Murmur3(&key, sizeof(Key));
+        }
+
+        void operator delete(void* p) {
+            BitmapTextBlob* blob = reinterpret_cast<BitmapTextBlob*>(p);
+            blob->fPool->release(p);
+        }
+        void* operator new(size_t) {
+            SkFAIL("All blobs are created by placement new.");
+            return sk_malloc_throw(0);
+        }
+
+        void* operator new(size_t, void* p) { return p; }
+        void operator delete(void* target, void* placement) {
+            ::operator delete(target, placement);
+        }
+
+        bool hasDistanceField() const { return SkToBool(fTextType & kHasDistanceField_TextType); }
+        bool hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
+        void setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
+        void setHasBitmap() { fTextType |= kHasBitmap_TextType; }
+    };
+
+    typedef BitmapTextBlob::Run Run;
+    typedef Run::SubRunInfo PerSubRunInfo;
+
+    inline bool canDrawAsDistanceFields(const SkPaint&, const SkMatrix& viewMatrix);
+    BitmapTextBlob* setupDFBlob(int glyphCount, const SkPaint& origPaint,
+                                const SkMatrix& viewMatrix, SkGlyphCache** cache,
+                                SkPaint* dfPaint, SkScalar* textRatio);
+    void bmpAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, int left, int top,
+                        GrColor color, GrFontScaler*, const SkIRect& clipRect);
+    bool dfAppendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, SkScalar sx, SkScalar sy,
+                       GrColor color, GrFontScaler*, const SkIRect& clipRect, SkScalar textRatio,
+                       const SkMatrix& viewMatrix);
+    inline void appendGlyphPath(BitmapTextBlob* blob, GrGlyph* glyph,
+                                GrFontScaler* scaler, SkScalar x, SkScalar y);
+    inline void appendGlyphCommon(BitmapTextBlob*, Run*, Run::SubRunInfo*,
+                                  const SkRect& positions, GrColor color,
+                                  size_t vertexStride, bool useVertexColor,
+                                  GrGlyph*);
+
+    inline void flushRunAsPaths(const SkTextBlob::RunIterator&, const SkPaint&, SkDrawFilter*,
+                                const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x,
+                                SkScalar y);
+    inline BitmapTextBatch* createBatch(BitmapTextBlob*, const PerSubRunInfo&,
+                                        int glyphCount, int run, int subRun,
+                                        GrColor, SkScalar transX, SkScalar transY,
+                                        const SkPaint&);
+    inline void flushRun(GrDrawTarget*, GrPipelineBuilder*, BitmapTextBlob*, int run, GrColor,
+                         SkScalar transX, SkScalar transY, const SkPaint&);
+    inline void flushBigGlyphs(BitmapTextBlob* cacheBlob, GrRenderTarget* rt,
+                               const SkPaint& skPaint,
+                               SkScalar transX, SkScalar transY, const SkIRect& clipBounds);
+
+    // We have to flush SkTextBlobs differently from drawText / drawPosText
+    void flush(GrDrawTarget*, const SkTextBlob*, BitmapTextBlob*, GrRenderTarget*, const SkPaint&,
+               const GrPaint&, SkDrawFilter*, const GrClip&, const SkMatrix& viewMatrix,
+               const SkIRect& clipBounds, SkScalar x, SkScalar y, SkScalar transX, SkScalar transY);
+    void flush(GrDrawTarget*, BitmapTextBlob*, GrRenderTarget*, const SkPaint&,
+               const GrPaint&, const GrClip&, const SkIRect& clipBounds);
+
+    // A helper for drawing BitmapText in a run of distance fields
+    inline void fallbackDrawPosText(BitmapTextBlob*, int runIndex,
+                                    GrRenderTarget*, const GrClip&,
+                                    const GrPaint&,
+                                    const SkPaint&, const SkMatrix& viewMatrix,
+                                    const SkTDArray<char>& fallbackTxt,
+                                    const SkTDArray<SkScalar>& fallbackPos,
+                                    int scalarsPerPosition,
+                                    const SkPoint& offset,
+                                    const SkIRect& clipRect);
+
+    void internalDrawBMPText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
+                             GrColor color, const SkMatrix& viewMatrix,
+                             const char text[], size_t byteLength,
+                             SkScalar x, SkScalar y, const SkIRect& clipRect);
+    void internalDrawBMPPosText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
+                                GrColor color, const SkMatrix& viewMatrix,
+                                const char text[], size_t byteLength,
+                                const SkScalar pos[], int scalarsPerPosition,
+                                const SkPoint& offset, const SkIRect& clipRect);
+
+    void internalDrawDFText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
+                            GrColor color, const SkMatrix& viewMatrix,
+                            const char text[], size_t byteLength,
+                            SkScalar x, SkScalar y, const SkIRect& clipRect,
+                            SkScalar textRatio,
+                            SkTDArray<char>* fallbackTxt,
+                            SkTDArray<SkScalar>* fallbackPos,
+                            SkPoint* offset, const SkPaint& origPaint);
+    void internalDrawDFPosText(BitmapTextBlob*, int runIndex, SkGlyphCache*, const SkPaint&,
+                               GrColor color, const SkMatrix& viewMatrix,
+                               const char text[], size_t byteLength,
+                               const SkScalar pos[], int scalarsPerPosition,
+                               const SkPoint& offset, const SkIRect& clipRect,
+                               SkScalar textRatio,
+                               SkTDArray<char>* fallbackTxt,
+                               SkTDArray<SkScalar>* fallbackPos);
+
+    // sets up the descriptor on the blob and returns a detached cache.  Client must attach
+    inline static GrColor ComputeCanonicalColor(const SkPaint&, bool lcd);
+    inline SkGlyphCache* setupCache(Run*, const SkPaint&, const SkMatrix* viewMatrix, bool noGamma);
+    static inline bool MustRegenerateBlob(SkScalar* outTransX, SkScalar* outTransY,
+                                          const BitmapTextBlob&, const SkPaint&,
+                                          const SkMaskFilter::BlurRec&,
+                                          const SkMatrix& viewMatrix, SkScalar x, SkScalar y);
+    void regenerateTextBlob(BitmapTextBlob* bmp, const SkPaint& skPaint, GrColor,
+                            const SkMatrix& viewMatrix,
+                            const SkTextBlob* blob, SkScalar x, SkScalar y,
+                            SkDrawFilter* drawFilter, const SkIRect& clipRect, GrRenderTarget*,
+                            const GrClip&, const GrPaint&);
+    inline static bool HasLCD(const SkTextBlob*);
+    inline void initDistanceFieldPaint(BitmapTextBlob*, SkPaint*, SkScalar* textRatio,
+                                       const SkMatrix&);
+
+    // Test methods
+    // TODO this is really ugly.  It'd be much nicer if positioning could be moved to batch
+    inline BitmapTextBlob* createDrawTextBlob(GrRenderTarget*, const GrClip&, const GrPaint&,
+                                              const SkPaint&, const SkMatrix& viewMatrix,
+                                              const char text[], size_t byteLength,
+                                              SkScalar x, SkScalar y,
+                                              const SkIRect& regionClipBounds);
+    inline BitmapTextBlob* createDrawPosTextBlob(GrRenderTarget*, const GrClip&, const GrPaint&,
+                                                 const SkPaint&, const SkMatrix& viewMatrix,
+                                                 const char text[], size_t byteLength,
+                                                 const SkScalar pos[], int scalarsPerPosition,
+                                                 const SkPoint& offset,
+                                                 const SkIRect& regionClipBounds);
+
+    // Distance field text needs this table to compute a value for use in the fragment shader.
+    // Because the GrAtlasTextContext can go out of scope before the final flush, this needs to be
+    // refcnted and malloced
+    struct DistanceAdjustTable : public SkNVRefCnt<DistanceAdjustTable> {
+        DistanceAdjustTable(float gamma) { this->buildDistanceAdjustTable(gamma); }
+        ~DistanceAdjustTable() { SkDELETE_ARRAY(fTable); }
+
+        void buildDistanceAdjustTable(float gamma);
+
+        SkScalar& operator[] (int i) {
+            return fTable[i];
+        }
+
+        const SkScalar& operator[] (int i) const {
+            return fTable[i];
+        }
+
+        SkScalar* fTable;
+    };
+
+    GrBatchTextStrike* fCurrStrike;
+    GrTextBlobCache* fCache;
+    bool fEnableDFRendering;
+    SkAutoTUnref<DistanceAdjustTable> fDistanceAdjustTable;
+
+    friend class GrTextBlobCache;
+    friend class BitmapTextBatch;
+
+#ifdef GR_TEST_UTILS
+    BATCH_TEST_FRIEND(TextBlobBatch);
+#endif
+
+    typedef GrTextContext INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrAutoLocaleSetter.h b/src/gpu/GrAutoLocaleSetter.h
new file mode 100644
index 0000000..dd17bbe
--- /dev/null
+++ b/src/gpu/GrAutoLocaleSetter.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrAutoLocaleSetter_DEFINED
+#define GrAutoLocaleSetter_DEFINED
+
+#include "GrTypes.h"
+
+#if !defined(SK_BUILD_FOR_ANDROID)
+#include <locale.h>
+#endif
+
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+#include <xlocale.h>
+#endif
+
+/**
+ * Helper class for ensuring that we don't use the wrong locale when building shaders. Android
+ * doesn't support locale in the NDK, so this is a no-op there.
+ */
+class GrAutoLocaleSetter {
+public:
+    GrAutoLocaleSetter (const char* name) {
+#if defined(SK_BUILD_FOR_WIN)
+        fOldPerThreadLocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+        fOldLocale = setlocale(LC_ALL, name);
+#elif !defined(SK_BUILD_FOR_ANDROID)
+        fLocale = newlocale(LC_ALL, name, 0);
+        if (fLocale) {
+            fOldLocale = uselocale(fLocale);
+        }
+#else
+        (void) name; // suppress unused param warning.
+#endif
+    }
+
+    ~GrAutoLocaleSetter () {
+#if defined(SK_BUILD_FOR_WIN)
+        setlocale(LC_ALL, fOldLocale);
+        _configthreadlocale(fOldPerThreadLocale);
+#elif !defined(SK_BUILD_FOR_ANDROID)
+        if (fLocale) {
+             uselocale(fOldLocale);
+             freelocale(fLocale);
+        }
+#endif
+    }
+
+private:
+#if defined(SK_BUILD_FOR_WIN)
+    int fOldPerThreadLocale;
+    const char* fOldLocale;
+#elif !defined(SK_BUILD_FOR_ANDROID)
+    locale_t fOldLocale;
+    locale_t fLocale;
+#endif
+};
+
+#endif
+
diff --git a/src/gpu/GrBatch.cpp b/src/gpu/GrBatch.cpp
index 4df765e..e69819e 100644
--- a/src/gpu/GrBatch.cpp
+++ b/src/gpu/GrBatch.cpp
@@ -1,24 +1,33 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "GrBatch.h"
+#include "GrBatchTarget.h"
+#include "GrResourceProvider.h"
 
 #include "GrMemoryPool.h"
-#include "SkMutex.h"
+#include "SkSpinlock.h"
 
 // TODO I noticed a small benefit to using a larger exclusive pool for batches.  Its very small,
 // but seems to be mostly consistent.  There is a lot in flux right now, but we should really
 // revisit this when batch is everywhere
 
 
-// We use a global pool protected by a mutex. Chrome may use the same GrContext on different
-// threads. The GrContext is not used concurrently on different threads and there is a memory
-// barrier between accesses of a context on different threads. Also, there may be multiple
+// We use a global pool protected by a mutex(spinlock). Chrome may use the same GrContext on
+// different threads. The GrContext is not used concurrently on different threads and there is a
+// memory barrier between accesses of a context on different threads. Also, there may be multiple
 // GrContexts and those contexts may be in use concurrently on different threads.
 namespace {
-SK_DECLARE_STATIC_MUTEX(gBatchPoolMutex);
+SK_DECLARE_STATIC_SPINLOCK(gBatchSpinlock);
 class MemoryPoolAccessor {
 public:
-    MemoryPoolAccessor() { gBatchPoolMutex.acquire(); }
+    MemoryPoolAccessor() { gBatchSpinlock.acquire(); }
 
-    ~MemoryPoolAccessor() { gBatchPoolMutex.release(); }
+    ~MemoryPoolAccessor() { gBatchSpinlock.release(); }
 
     GrMemoryPool* pool() const {
         static GrMemoryPool gPool(16384, 16384);
@@ -36,3 +45,41 @@
 void GrBatch::operator delete(void* target) {
     return MemoryPoolAccessor().pool()->release(target);
 }
+
+void* GrBatch::InstancedHelper::init(GrBatchTarget* batchTarget, GrPrimitiveType primType,
+                                     size_t vertexStride, const GrIndexBuffer* indexBuffer,
+                                     int verticesPerInstance, int indicesPerInstance,
+                                     int instancesToDraw) {
+    SkASSERT(batchTarget);
+    if (!indexBuffer) {
+        return NULL;
+    }
+    const GrVertexBuffer* vertexBuffer;
+    int firstVertex;
+    int vertexCount = verticesPerInstance * instancesToDraw;
+    void* vertices = batchTarget->makeVertSpace(vertexStride, vertexCount,
+                                                &vertexBuffer, &firstVertex);
+    if (!vertices) {
+        SkDebugf("Vertices could not be allocated for instanced rendering.");
+        return NULL;
+    }
+    SkASSERT(vertexBuffer);
+    size_t ibSize = indexBuffer->gpuMemorySize();
+    int maxInstancesPerDraw = static_cast<int>(ibSize / (sizeof(uint16_t) * indicesPerInstance));
+
+    fVertices.initInstanced(primType, vertexBuffer, indexBuffer,
+        firstVertex, verticesPerInstance, indicesPerInstance, instancesToDraw,
+        maxInstancesPerDraw);
+    return vertices;
+}
+
+void* GrBatch::QuadHelper::init(GrBatchTarget* batchTarget, size_t vertexStride, int quadsToDraw) {
+    SkAutoTUnref<const GrIndexBuffer> quadIndexBuffer(
+        batchTarget->resourceProvider()->refQuadIndexBuffer());
+    if (!quadIndexBuffer) {
+        SkDebugf("Could not get quad index buffer.");
+        return NULL;
+    }
+    return this->INHERITED::init(batchTarget, kTriangles_GrPrimitiveType, vertexStride,
+                                 quadIndexBuffer, kVerticesPerQuad, kIndicesPerQuad, quadsToDraw);
+}
diff --git a/src/gpu/GrBatch.h b/src/gpu/GrBatch.h
index 48327c6..9c1647d 100644
--- a/src/gpu/GrBatch.h
+++ b/src/gpu/GrBatch.h
@@ -9,17 +9,15 @@
 #define GrBatch_DEFINED
 
 #include <new>
-// TODO remove this header when we move entirely to batch
+#include "GrBatchTarget.h"
 #include "GrGeometryProcessor.h"
+#include "GrVertices.h"
 #include "SkRefCnt.h"
 #include "SkThread.h"
 #include "SkTypes.h"
 
-class GrBatchTarget;
 class GrGpu;
-class GrIndexBufferAllocPool;
 class GrPipeline;
-class GrVertexBufferAllocPool;
 
 struct GrInitInvariantOutput;
 
@@ -42,7 +40,7 @@
 class GrBatch : public SkRefCnt {
 public:
     SK_DECLARE_INST_COUNT(GrBatch)
-    GrBatch() : fNumberOfDraws(0) { SkDEBUGCODE(fUsed = false;) }
+    GrBatch() : fClassID(kIllegalBatchClassID), fNumberOfDraws(0) { SkDEBUGCODE(fUsed = false;) }
     virtual ~GrBatch() {}
 
     virtual const char* name() const = 0;
@@ -60,13 +58,15 @@
             return false;
         }
 
-        return onCombineIfPossible(that);
+        return this->onCombineIfPossible(that);
     }
 
     virtual bool onCombineIfPossible(GrBatch*) = 0;
 
     virtual void generateGeometry(GrBatchTarget*, const GrPipeline*) = 0;
 
+    const SkRect& bounds() const { return fBounds; }
+
     // TODO this goes away when batches are everywhere
     void setNumberOfDraws(int numberOfDraws) { fNumberOfDraws = numberOfDraws; }
     int numberOfDraws() const { return fNumberOfDraws; }
@@ -102,6 +102,54 @@
 
     uint32_t fClassID;
 
+    // NOTE, compute some bounds, even if extremely conservative.  Do *NOT* setLargest on the bounds
+    // rect because we outset it for dst copy textures
+    void setBounds(const SkRect& newBounds) { fBounds = newBounds; }
+
+    void joinBounds(const SkRect& otherBounds) {
+        return fBounds.joinPossiblyEmptyRect(otherBounds);
+    }
+
+    /** Helper for rendering instances using an instanced index index buffer. This class creates the
+        space for the vertices and flushes the draws to the batch target.*/
+   class InstancedHelper {
+   public:
+        InstancedHelper() {}
+        /** Returns the allocated storage for the vertices. The caller should populate the before
+            vertices before calling issueDraws(). */
+        void* init(GrBatchTarget* batchTarget, GrPrimitiveType, size_t vertexStride,
+                   const GrIndexBuffer*, int verticesPerInstance, int indicesPerInstance,
+                   int instancesToDraw);
+
+        /** Call after init() to issue draws to the batch target.*/
+        void issueDraw(GrBatchTarget* batchTarget) {
+            SkASSERT(fVertices.instanceCount());
+            batchTarget->draw(fVertices);
+        }
+    private:
+        GrVertices  fVertices;
+    };
+
+    static const int kVerticesPerQuad = 4;
+    static const int kIndicesPerQuad = 6;
+
+    /** A specialization of InstanceHelper for quad rendering. */
+    class QuadHelper : private InstancedHelper {
+    public:
+        QuadHelper() : INHERITED() {}
+        /** Finds the cached quad index buffer and reserves vertex space. Returns NULL on failure
+            and on sucess a pointer to the vertex data that the caller should populate before
+            calling issueDraws(). */
+        void* init(GrBatchTarget* batchTarget, size_t vertexStride, int quadsToDraw);
+
+        using InstancedHelper::issueDraw;
+
+    private:
+        typedef InstancedHelper INHERITED;
+    };
+
+    SkRect fBounds;
+
 private:
     static uint32_t GenClassID() {
         // fCurrProcessorClassID has been initialized to kIllegalProcessorClassID. The
diff --git a/src/gpu/GrBatchAtlas.cpp b/src/gpu/GrBatchAtlas.cpp
new file mode 100644
index 0000000..20e73e4
--- /dev/null
+++ b/src/gpu/GrBatchAtlas.cpp
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrBatchAtlas.h"
+#include "GrBatchTarget.h"
+#include "GrGpu.h"
+#include "GrRectanizer.h"
+#include "GrTracing.h"
+#include "GrVertexBuffer.h"
+
+static inline void adjust_for_offset(SkIPoint16* loc, const SkIPoint16& offset) {
+    loc->fX += offset.fX;
+    loc->fY += offset.fY;
+}
+
+static GrBatchAtlas::AtlasID create_id(int index, int generation) {
+    // Generation ID can roll over because we only check for equality
+    SkASSERT(index < (1 << 16));
+    return generation << 16 | index;
+}
+
+// The backing GrTexture for a GrBatchAtlas is broken into a spatial grid of GrBatchPlots.
+// The GrBatchPlots keep track of subimage placement via their GrRectanizer. In turn, a GrBatchPlot
+// manages the lifetime of its data using two tokens, a last ref toke and a last upload token.
+// Once a GrBatchPlot is "full" (i.e. there is no room for the new subimage according to the
+// GrRectanizer), it can no longer be used unless the last ref on the GrPlot has already been
+// flushed through to the gpu.
+
+class BatchPlot : public SkRefCnt {
+public:
+    typedef GrBatchAtlas::BatchToken BatchToken;
+    SK_DECLARE_INST_COUNT(BatchPlot);
+    SK_DECLARE_INTERNAL_LLIST_INTERFACE(BatchPlot);
+
+    // index() refers to the index of the plot in the owning GrAtlas's plot array.  genID() is a
+    // monotonically incrementing number which is bumped every time the cpu backing store is
+    // wiped, or when the plot itself is evicted from the atlas(ie, there is continuity in genID()
+    // across atlas spills)
+    int index() const { return fIndex; }
+    int genID() const { return fGenID; }
+    GrBatchAtlas::AtlasID id() { return fID; }
+
+    GrTexture* texture() const { return fTexture; }
+
+    bool addSubImage(int width, int height, const void* image, SkIPoint16* loc, size_t rowBytes)  {
+        if (!fRects->addRect(width, height, loc)) {
+            return false;
+        }
+
+        if (!fData) {
+            fData = reinterpret_cast<unsigned char*>(sk_calloc_throw(fBytesPerPixel * fWidth *
+                                                                     fHeight));
+        }
+        const unsigned char* imagePtr = (const unsigned char*)image;
+        // point ourselves at the right starting spot
+        unsigned char* dataPtr = fData;
+        dataPtr += fBytesPerPixel * fWidth * loc->fY;
+        dataPtr += fBytesPerPixel * loc->fX;
+        // copy into the data buffer
+        for (int i = 0; i < height; ++i) {
+            memcpy(dataPtr, imagePtr, rowBytes);
+            dataPtr += fBytesPerPixel * fWidth;
+            imagePtr += rowBytes;
+        }
+
+        fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
+        adjust_for_offset(loc, fOffset);
+        SkDEBUGCODE(fDirty = true;)
+
+        return true;
+    }
+
+    // to manage the lifetime of a plot, we use two tokens.  We use last upload token to know when
+    // we can 'piggy back' uploads, ie if the last upload hasn't been flushed to gpu, we don't need
+    // to issue a new upload even if we update the cpu backing store.  We use lastref to determine
+    // when we can evict a plot from the cache, ie if the last ref has already flushed through
+    // the gpu then we can reuse the plot
+    BatchToken lastUploadToken() const { return fLastUpload; }
+    BatchToken lastUseToken() const { return fLastUse; }
+    void setLastUploadToken(BatchToken batchToken) {
+        SkASSERT(batchToken >= fLastUpload);
+        fLastUpload = batchToken;
+    }
+    void setLastUseToken(BatchToken batchToken) {
+        SkASSERT(batchToken >= fLastUse);
+        fLastUse = batchToken;
+    }
+
+    void uploadToTexture(GrBatchTarget::TextureUploader uploader)  {
+        // We should only be issuing uploads if we are in fact dirty
+        SkASSERT(fDirty && fData && fTexture);
+        TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrBatchPlot::uploadToTexture");
+        size_t rowBytes = fBytesPerPixel * fRects->width();
+        const unsigned char* dataPtr = fData;
+        dataPtr += rowBytes * fDirtyRect.fTop;
+        dataPtr += fBytesPerPixel * fDirtyRect.fLeft;
+        uploader.writeTexturePixels(fTexture,
+                                    fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
+                                    fDirtyRect.width(), fDirtyRect.height(),
+                                    fTexture->config(), dataPtr, rowBytes);
+        fDirtyRect.setEmpty();
+        SkDEBUGCODE(fDirty = false;)
+    }
+
+    void resetRects() {
+        SkASSERT(fRects);
+        fRects->reset();
+        fGenID++;
+        fID = create_id(fIndex, fGenID);
+
+        // zero out the plot
+        if (fData) {
+            sk_bzero(fData, fBytesPerPixel * fWidth * fHeight);
+        }
+
+        fDirtyRect.setEmpty();
+        SkDEBUGCODE(fDirty = false;)
+    }
+
+    int x() const { return fX; }
+    int y() const { return fY; }
+
+private:
+    BatchPlot()
+        : fLastUpload(0)
+        , fLastUse(0)
+        , fIndex(-1)
+        , fGenID(-1)
+        , fID(0)
+        , fData(NULL)
+        , fWidth(0)
+        , fHeight(0)
+        , fX(0)
+        , fY(0)
+        , fTexture(NULL)
+        , fRects(NULL)
+        , fAtlas(NULL)
+        , fBytesPerPixel(1)
+    #ifdef SK_DEBUG
+        , fDirty(false)
+    #endif
+    {
+        fOffset.set(0, 0);
+    }
+
+    ~BatchPlot() {
+        sk_free(fData);
+        fData = NULL;
+        delete fRects;
+    }
+
+    void init(GrBatchAtlas* atlas, GrTexture* texture, int index, uint32_t generation,
+              int offX, int offY, int width, int height, size_t bpp) {
+        fIndex = index;
+        fGenID = generation;
+        fID = create_id(index, generation);
+        fWidth = width;
+        fHeight = height;
+        fX = offX;
+        fY = offY;
+        fRects = GrRectanizer::Factory(width, height);
+        fAtlas = atlas;
+        fOffset.set(offX * width, offY * height);
+        fBytesPerPixel = bpp;
+        fData = NULL;
+        fDirtyRect.setEmpty();
+        SkDEBUGCODE(fDirty = false;)
+        fTexture = texture;
+    }
+
+    BatchToken fLastUpload;
+    BatchToken fLastUse;
+
+    uint32_t fIndex;
+    uint32_t fGenID;
+    GrBatchAtlas::AtlasID fID;
+    unsigned char* fData;
+    int fWidth;
+    int fHeight;
+    int fX;
+    int fY;
+    GrTexture* fTexture;
+    GrRectanizer* fRects;
+    GrBatchAtlas* fAtlas;
+    SkIPoint16 fOffset;        // the offset of the plot in the backing texture
+    size_t fBytesPerPixel;
+    SkIRect fDirtyRect;
+    SkDEBUGCODE(bool fDirty;)
+
+    friend class GrBatchAtlas;
+
+    typedef SkRefCnt INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class GrPlotUploader : public GrBatchTarget::Uploader {
+public:
+    GrPlotUploader(BatchPlot* plot)
+        : INHERITED(plot->lastUploadToken())
+        , fPlot(SkRef(plot)) {
+        SkASSERT(plot);
+    }
+
+    void upload(GrBatchTarget::TextureUploader uploader) override {
+        fPlot->uploadToTexture(uploader);
+    }
+
+private:
+    SkAutoTUnref<BatchPlot> fPlot;
+
+    typedef GrBatchTarget::Uploader INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrBatchAtlas::GrBatchAtlas(GrTexture* texture, int numPlotsX, int numPlotsY)
+    : fTexture(texture)
+    , fNumPlotsX(numPlotsX)
+    , fNumPlotsY(numPlotsY)
+    , fPlotWidth(texture->width() / numPlotsX)
+    , fPlotHeight(texture->height() / numPlotsY)
+    , fAtlasGeneration(kInvalidAtlasGeneration + 1) {
+    SkASSERT(fNumPlotsX * fNumPlotsY <= BulkUseTokenUpdater::kMaxPlots);
+    SkASSERT(fPlotWidth * fNumPlotsX == texture->width());
+    SkASSERT(fPlotHeight * fNumPlotsY == texture->height());
+
+    // We currently do not support compressed atlases...
+    SkASSERT(!GrPixelConfigIsCompressed(texture->desc().fConfig));
+
+    // set up allocated plots
+    fBPP = GrBytesPerPixel(texture->desc().fConfig);
+    fPlotArray = SkNEW_ARRAY(SkAutoTUnref<BatchPlot>, (fNumPlotsX * fNumPlotsY));
+
+    SkAutoTUnref<BatchPlot>* currPlot = fPlotArray;
+    for (int y = fNumPlotsY - 1, r = 0; y >= 0; --y, ++r) {
+        for (int x = fNumPlotsX - 1, c = 0; x >= 0; --x, ++c) {
+            int id = r * fNumPlotsX + c;
+            currPlot->reset(SkNEW(BatchPlot));
+            (*currPlot)->init(this, texture, id, 1, x, y, fPlotWidth, fPlotHeight, fBPP);
+
+            // build LRU list
+            fPlotList.addToHead(currPlot->get());
+            ++currPlot;
+        }
+    }
+}
+
+GrBatchAtlas::~GrBatchAtlas() {
+    SkSafeUnref(fTexture);
+    SkDELETE_ARRAY(fPlotArray);
+}
+
+void GrBatchAtlas::processEviction(AtlasID id) {
+    for (int i = 0; i < fEvictionCallbacks.count(); i++) {
+        (*fEvictionCallbacks[i].fFunc)(id, fEvictionCallbacks[i].fData);
+    }
+}
+
+void GrBatchAtlas::makeMRU(BatchPlot* plot) {
+    if (fPlotList.head() == plot) {
+        return;
+    }
+
+    fPlotList.remove(plot);
+    fPlotList.addToHead(plot);
+}
+
+inline void GrBatchAtlas::updatePlot(GrBatchTarget* batchTarget, AtlasID* id, BatchPlot* plot) {
+    this->makeMRU(plot);
+
+    // If our most recent upload has already occurred then we have to insert a new
+    // upload. Otherwise, we already have a scheduled upload that hasn't yet ocurred.
+    // This new update will piggy back on that previously scheduled update.
+    if (batchTarget->isIssued(plot->lastUploadToken())) {
+        plot->setLastUploadToken(batchTarget->asapToken());
+        SkAutoTUnref<GrPlotUploader> uploader(SkNEW_ARGS(GrPlotUploader, (plot)));
+        batchTarget->upload(uploader);
+    }
+    *id = plot->id();
+}
+
+bool GrBatchAtlas::addToAtlas(AtlasID* id, GrBatchTarget* batchTarget,
+                              int width, int height, const void* image, SkIPoint16* loc) {
+    // We should already have a texture, TODO clean this up
+    SkASSERT(fTexture && width <= fPlotWidth && height <= fPlotHeight);
+
+    // now look through all allocated plots for one we can share, in Most Recently Refed order
+    GrBatchPlotList::Iter plotIter;
+    plotIter.init(fPlotList, GrBatchPlotList::Iter::kHead_IterStart);
+    BatchPlot* plot;
+    while ((plot = plotIter.get())) {
+        if (plot->addSubImage(width, height, image, loc, fBPP * width)) {
+            this->updatePlot(batchTarget, id, plot);
+            return true;
+        }
+        plotIter.next();
+    }
+
+    // If the above fails, then see if the least recently refed plot has already been flushed to the
+    // gpu
+    plotIter.init(fPlotList, GrBatchPlotList::Iter::kTail_IterStart);
+    plot = plotIter.get();
+    SkASSERT(plot);
+    if (batchTarget->isIssued(plot->lastUseToken())) {
+        this->processEviction(plot->id());
+        plot->resetRects();
+        SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc, fBPP * width);
+        SkASSERT(verify);
+        this->updatePlot(batchTarget, id, plot);
+        fAtlasGeneration++;
+        return true;
+    }
+
+    // The least recently refed plot hasn't been flushed to the gpu yet, however, if we have flushed
+    // it to the batch target than we can reuse it.  Our last ref token is guaranteed to be less
+    // than or equal to the current token.  If its 'less than' the current token, than we can spin
+    // off the plot(ie let the batch target manage it) and create a new plot in its place in our
+    // array.  If it is equal to the currentToken, then the caller has to flush draws to the batch
+    // target so we can spin off the plot
+    if (plot->lastUseToken() == batchTarget->currentToken()) {
+        return false;
+    }
+
+    // We take an extra ref here so our plot isn't deleted when we reset its index in the array.
+    plot->ref();
+    int index = plot->index();
+    int x = plot->x();
+    int y = plot->y();
+    int generation = plot->genID();
+
+    this->processEviction(plot->id());
+    fPlotList.remove(plot);
+    SkAutoTUnref<BatchPlot>& newPlot = fPlotArray[plot->index()];
+    newPlot.reset(SkNEW(BatchPlot));
+    newPlot->init(this, fTexture, index, ++generation, x, y, fPlotWidth, fPlotHeight, fBPP);
+
+    fPlotList.addToHead(newPlot.get());
+    SkDEBUGCODE(bool verify = )newPlot->addSubImage(width, height, image, loc, fBPP * width);
+    SkASSERT(verify);
+    newPlot->setLastUploadToken(batchTarget->currentToken());
+    SkAutoTUnref<GrPlotUploader> uploader(SkNEW_ARGS(GrPlotUploader, (newPlot)));
+    batchTarget->upload(uploader);
+    *id = newPlot->id();
+    plot->unref();
+    fAtlasGeneration++;
+    return true;
+}
+
+bool GrBatchAtlas::hasID(AtlasID id) {
+    int index = GetIndexFromID(id);
+    SkASSERT(index < fNumPlotsX * fNumPlotsY);
+    return fPlotArray[index]->genID() == GetGenerationFromID(id);
+}
+
+void GrBatchAtlas::setLastUseToken(AtlasID id, BatchToken batchToken) {
+    SkASSERT(this->hasID(id));
+    int index = GetIndexFromID(id);
+    SkASSERT(index < fNumPlotsX * fNumPlotsY);
+    this->makeMRU(fPlotArray[index]);
+    fPlotArray[index]->setLastUseToken(batchToken);
+}
+
+void GrBatchAtlas::setLastUseTokenBulk(const BulkUseTokenUpdater& updater, BatchToken batchToken) {
+    int count = updater.fPlotsToUpdate.count();
+    for (int i = 0; i < count; i++) {
+        BatchPlot* plot = fPlotArray[updater.fPlotsToUpdate[i]];
+        this->makeMRU(plot);
+        plot->setLastUseToken(batchToken);
+    }
+}
diff --git a/src/gpu/GrBatchAtlas.h b/src/gpu/GrBatchAtlas.h
new file mode 100644
index 0000000..83ef5ec
--- /dev/null
+++ b/src/gpu/GrBatchAtlas.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrBatchAtlas_DEFINED
+#define GrBatchAtlas_DEFINED
+
+#include "GrTexture.h"
+#include "SkPoint.h"
+#include "SkTDArray.h"
+#include "SkTInternalLList.h"
+
+class BatchPlot;
+class GrBatchTarget;
+class GrRectanizer;
+
+typedef SkTInternalLList<BatchPlot> GrBatchPlotList;
+
+class GrBatchAtlas {
+public:
+    typedef uint64_t BatchToken;
+    // An AtlasID is an opaque handle which callers can use to determine if the atlas contains
+    // a specific piece of data
+    typedef uint32_t AtlasID;
+    static const uint32_t kInvalidAtlasID = 0;
+    static const uint64_t kInvalidAtlasGeneration = 0;
+
+    // A function pointer for use as a callback during eviction.  Whenever GrBatchAtlas evicts a
+    // specific AtlasID, it will call all of the registered listeners so they can optionally process
+    // the eviction
+    typedef void (*EvictionFunc)(GrBatchAtlas::AtlasID, void*);
+
+    GrBatchAtlas(GrTexture*, int numPlotsX, int numPlotsY);
+    ~GrBatchAtlas();
+
+    // Adds a width x height subimage to the atlas. Upon success it returns
+    // the containing GrPlot and absolute location in the backing texture.
+    // NULL is returned if the subimage cannot fit in the atlas.
+    // If provided, the image data will be written to the CPU-side backing bitmap.
+    // NOTE: If the client intends to refer to the atlas, they should immediately call 'setUseToken'
+    // with the currentToken from the batch target, otherwise the next call to addToAtlas might
+    // cause an eviction
+    bool addToAtlas(AtlasID*, GrBatchTarget*, int width, int height, const void* image,
+                    SkIPoint16* loc);
+
+    GrTexture* getTexture() const { return fTexture; }
+
+    uint64_t atlasGeneration() const { return fAtlasGeneration; }
+    bool hasID(AtlasID id);
+
+    // To ensure the atlas does not evict a given entry, the client must set the last use token
+    void setLastUseToken(AtlasID id, BatchToken batchToken);
+    void registerEvictionCallback(EvictionFunc func, void* userData) {
+        EvictionData* data = fEvictionCallbacks.append();
+        data->fFunc = func;
+        data->fData = userData;
+    }
+
+    /*
+     * A class which can be handed back to GrBatchAtlas for updating in bulk last use tokens.  The
+     * current max number of plots the GrBatchAtlas can handle is 32, if in the future this is
+     * insufficient then we can move to a 64 bit int
+     */
+    class BulkUseTokenUpdater {
+    public:
+        BulkUseTokenUpdater() : fPlotAlreadyUpdated(0) {}
+        void add(AtlasID id) {
+            int index = GrBatchAtlas::GetIndexFromID(id);
+            if (!this->find(index)) {
+                this->set(index);
+            }
+        }
+
+        void reset() {
+            fPlotsToUpdate.reset();
+            fPlotAlreadyUpdated = 0;
+        }
+
+    private:
+        bool find(int index) const {
+            SkASSERT(index < kMaxPlots);
+            return (fPlotAlreadyUpdated >> index) & 1;
+        }
+
+        void set(int index) {
+            SkASSERT(!this->find(index));
+            fPlotAlreadyUpdated = fPlotAlreadyUpdated | (1 << index);
+            fPlotsToUpdate.push_back(index);
+        }
+
+        static const int kMinItems = 4;
+        static const int kMaxPlots = 32;
+        SkSTArray<kMinItems, int, true> fPlotsToUpdate;
+        uint32_t fPlotAlreadyUpdated;
+
+        friend class GrBatchAtlas;
+    };
+
+    void setLastUseTokenBulk(const BulkUseTokenUpdater& reffer, BatchToken);
+
+    static const int kGlyphMaxDim = 256;
+    static bool GlyphTooLargeForAtlas(int width, int height) {
+        return width > kGlyphMaxDim || height > kGlyphMaxDim;
+    }
+
+private:
+    static int GetIndexFromID(AtlasID id) {
+        return id & 0xffff;
+    }
+
+    static int GetGenerationFromID(AtlasID id) {
+        return (id >> 16) & 0xffff;
+    }
+
+    inline void updatePlot(GrBatchTarget*, AtlasID*, BatchPlot*);
+
+    inline void makeMRU(BatchPlot* plot);
+
+    inline void processEviction(AtlasID);
+
+    GrTexture* fTexture;
+    int fNumPlotsX;
+    int fNumPlotsY;
+    int fPlotWidth;
+    int fPlotHeight;
+    size_t fBPP;
+    uint64_t fAtlasGeneration;
+
+    struct EvictionData {
+        EvictionFunc fFunc;
+        void* fData;
+    };
+
+    SkTDArray<EvictionData> fEvictionCallbacks;
+    // allocated array of GrBatchPlots
+    SkAutoTUnref<BatchPlot>* fPlotArray;
+    // LRU list of GrPlots (MRU at head - LRU at tail)
+    GrBatchPlotList fPlotList;
+};
+
+#endif
diff --git a/src/gpu/GrBatchFontCache.cpp b/src/gpu/GrBatchFontCache.cpp
new file mode 100644
index 0000000..a1f87cb
--- /dev/null
+++ b/src/gpu/GrBatchFontCache.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrBatchFontCache.h"
+#include "GrFontAtlasSizes.h"
+#include "GrGpu.h"
+#include "GrRectanizer.h"
+#include "GrSurfacePriv.h"
+#include "SkString.h"
+
+#include "SkDistanceFieldGen.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GrBatchAtlas* make_atlas(GrContext* context, GrPixelConfig config,
+                                int textureWidth, int textureHeight,
+                                int numPlotsX, int numPlotsY) {
+    GrSurfaceDesc desc;
+    desc.fFlags = kNone_GrSurfaceFlags;
+    desc.fWidth = textureWidth;
+    desc.fHeight = textureHeight;
+    desc.fConfig = config;
+
+    // We don't want to flush the context so we claim we're in the middle of flushing so as to
+    // guarantee we do not recieve a texture with pending IO
+    GrTexture* texture = context->textureProvider()->refScratchTexture(
+        desc, GrTextureProvider::kApprox_ScratchTexMatch, true);
+    if (!texture) {
+        return NULL;
+    }
+    return SkNEW_ARGS(GrBatchAtlas, (texture, numPlotsX, numPlotsY));
+}
+
+bool GrBatchFontCache::initAtlas(GrMaskFormat format) {
+    int index = MaskFormatToAtlasIndex(format);
+    if (!fAtlases[index]) {
+        GrPixelConfig config = this->getPixelConfig(format);
+        if (kA8_GrMaskFormat == format) {
+            fAtlases[index] = make_atlas(fContext, config,
+                                         GR_FONT_ATLAS_A8_TEXTURE_WIDTH,
+                                         GR_FONT_ATLAS_TEXTURE_HEIGHT,
+                                         GR_FONT_ATLAS_A8_NUM_PLOTS_X,
+                                         GR_FONT_ATLAS_NUM_PLOTS_Y);
+        } else {
+            fAtlases[index] = make_atlas(fContext, config,
+                                         GR_FONT_ATLAS_TEXTURE_WIDTH,
+                                         GR_FONT_ATLAS_TEXTURE_HEIGHT,
+                                         GR_FONT_ATLAS_NUM_PLOTS_X,
+                                         GR_FONT_ATLAS_NUM_PLOTS_Y);
+        }
+
+        // Atlas creation can fail
+        if (fAtlases[index]) {
+            fAtlases[index]->registerEvictionCallback(&GrBatchFontCache::HandleEviction,
+                                                      (void*)this);
+        } else {
+            return false;
+        }
+    }
+    return true;
+}
+
+GrBatchFontCache::GrBatchFontCache(GrContext* context)
+    : fContext(context)
+    , fPreserveStrike(NULL) {
+    for (int i = 0; i < kMaskFormatCount; ++i) {
+        fAtlases[i] = NULL;
+    }
+}
+
+GrBatchFontCache::~GrBatchFontCache() {
+    SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
+    while (!iter.done()) {
+        (*iter).unref();
+        ++iter;
+    }
+    for (int i = 0; i < kMaskFormatCount; ++i) {
+        SkDELETE(fAtlases[i]);
+    }
+}
+
+void GrBatchFontCache::freeAll() {
+    SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
+    while (!iter.done()) {
+        (*iter).unref();
+        ++iter;
+    }
+    fCache.rewind();
+    for (int i = 0; i < kMaskFormatCount; ++i) {
+        SkDELETE(fAtlases[i]);
+        fAtlases[i] = NULL;
+    }
+}
+
+GrPixelConfig GrBatchFontCache::getPixelConfig(GrMaskFormat format) const {
+    static const GrPixelConfig kPixelConfigs[] = {
+        kAlpha_8_GrPixelConfig,
+        kRGB_565_GrPixelConfig,
+        kSkia8888_GrPixelConfig
+    };
+    SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kPixelConfigs) == kMaskFormatCount, array_size_mismatch);
+
+    return kPixelConfigs[format];
+}
+
+void GrBatchFontCache::HandleEviction(GrBatchAtlas::AtlasID id, void* ptr) {
+    GrBatchFontCache* fontCache = reinterpret_cast<GrBatchFontCache*>(ptr);
+
+    SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fontCache->fCache);
+    for (; !iter.done(); ++iter) {
+        GrBatchTextStrike* strike = &*iter;
+        strike->removeID(id);
+
+        // clear out any empty strikes.  We will preserve the strike whose call to addToAtlas
+        // triggered the eviction
+        if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
+            fontCache->fCache.remove(*(strike->fFontScalerKey));
+            strike->fIsAbandoned = true;
+            strike->unref();
+        }
+    }
+}
+
+void GrBatchFontCache::dump() const {
+    static int gDumpCount = 0;
+    for (int i = 0; i < kMaskFormatCount; ++i) {
+        if (fAtlases[i]) {
+            GrTexture* texture = fAtlases[i]->getTexture();
+            if (texture) {
+                SkString filename;
+#ifdef SK_BUILD_FOR_ANDROID
+                filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
+#else
+                filename.printf("fontcache_%d%d.png", gDumpCount, i);
+#endif
+                texture->surfacePriv().savePixels(filename.c_str());
+            }
+        }
+    }
+    ++gDumpCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+    The text strike is specific to a given font/style/matrix setup, which is
+    represented by the GrHostFontScaler object we are given in getGlyph().
+
+    We map a 32bit glyphID to a GrGlyph record, which in turn points to a
+    atlas and a position within that texture.
+ */
+
+GrBatchTextStrike::GrBatchTextStrike(GrBatchFontCache* cache, const GrFontDescKey* key)
+    : fFontScalerKey(SkRef(key))
+    , fPool(9/*start allocations at 512 bytes*/)
+    , fAtlasedGlyphs(0)
+    , fIsAbandoned(false) {
+
+    fBatchFontCache = cache;     // no need to ref, it won't go away before we do
+}
+
+GrBatchTextStrike::~GrBatchTextStrike() {
+    SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
+    while (!iter.done()) {
+        (*iter).free();
+        ++iter;
+    }
+}
+
+GrGlyph* GrBatchTextStrike::generateGlyph(GrGlyph::PackedID packed,
+                                          GrFontScaler* scaler) {
+    SkIRect bounds;
+    if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
+        if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
+            return NULL;
+        }
+    } else {
+        if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
+            return NULL;
+        }
+    }
+    GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
+    
+    GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW);
+    glyph->init(packed, bounds, format);
+    fCache.add(glyph);
+    return glyph;
+}
+
+void GrBatchTextStrike::removeID(GrBatchAtlas::AtlasID id) {
+    SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
+    while (!iter.done()) {
+        if (id == (*iter).fID) {
+            (*iter).fID = GrBatchAtlas::kInvalidAtlasID;
+            fAtlasedGlyphs--;
+            SkASSERT(fAtlasedGlyphs >= 0);
+        }
+        ++iter;
+    }
+}
+
+bool GrBatchTextStrike::addGlyphToAtlas(GrBatchTarget* batchTarget, GrGlyph* glyph,
+                                        GrFontScaler* scaler) {
+    SkASSERT(glyph);
+    SkASSERT(scaler);
+    SkASSERT(fCache.find(glyph->fPackedID));
+    SkASSERT(NULL == glyph->fPlot);
+
+    SkAutoUnref ar(SkSafeRef(scaler));
+
+    int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
+
+    size_t size = glyph->fBounds.area() * bytesPerPixel;
+    GrAutoMalloc<1024> storage(size);
+
+    if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
+        if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
+                                           glyph->height(),
+                                           storage.get())) {
+            return false;
+        }
+    } else {
+        if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
+                                         glyph->height(),
+                                         glyph->width() * bytesPerPixel,
+                                         storage.get())) {
+            return false;
+        }
+    }
+
+    bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, batchTarget, glyph->fMaskFormat,
+                                               glyph->width(), glyph->height(),
+                                               storage.get(), &glyph->fAtlasLocation);
+    if (success) {
+        fAtlasedGlyphs++;
+    }
+    return success;
+}
diff --git a/src/gpu/GrBatchFontCache.h b/src/gpu/GrBatchFontCache.h
new file mode 100644
index 0000000..f1c56ac
--- /dev/null
+++ b/src/gpu/GrBatchFontCache.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrBatchFontCache_DEFINED
+#define GrBatchFontCache_DEFINED
+
+#include "GrBatchAtlas.h"
+#include "GrDrawTarget.h"
+#include "GrFontScaler.h"
+#include "GrGlyph.h"
+#include "SkTDynamicHash.h"
+#include "SkVarAlloc.h"
+
+class GrBatchFontCache;
+class GrBatchTarget;
+class GrGpu;
+
+/**
+ *  The GrBatchTextStrike manages a pool of CPU backing memory for Glyph Masks.  This backing memory
+ *  is abstracted by GrGlyph, and indexed by a PackedID and GrFontScaler.  The GrFontScaler is what
+ *  actually creates the mask.
+ */
+class GrBatchTextStrike : public SkNVRefCnt<GrBatchTextStrike> {
+public:
+    GrBatchTextStrike(GrBatchFontCache*, const GrFontDescKey* fontScalerKey);
+    ~GrBatchTextStrike();
+
+    const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; }
+    GrBatchFontCache* getBatchFontCache() const { return fBatchFontCache; }
+
+    inline GrGlyph* getGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler) {
+        GrGlyph* glyph = fCache.find(packed);
+        if (NULL == glyph) {
+            glyph = this->generateGlyph(packed, scaler);
+        }
+        return glyph;
+    }
+
+    // returns true if glyph successfully added to texture atlas, false otherwise
+    bool addGlyphToAtlas(GrBatchTarget*, GrGlyph*, GrFontScaler*);
+
+    // testing
+    int countGlyphs() const { return fCache.count(); }
+
+    // remove any references to this plot
+    void removeID(GrBatchAtlas::AtlasID);
+
+    // If a TextStrike is abandoned by the cache, then the caller must get a new strike
+    bool isAbandoned() const { return fIsAbandoned; }
+
+    static const GrFontDescKey& GetKey(const GrBatchTextStrike& ts) {
+        return *(ts.fFontScalerKey);
+    }
+    static uint32_t Hash(const GrFontDescKey& key) {
+        return key.getHash();
+    }
+
+private:
+    SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
+    SkAutoTUnref<const GrFontDescKey> fFontScalerKey;
+    SkVarAlloc fPool;
+
+    GrBatchFontCache* fBatchFontCache;
+    int fAtlasedGlyphs;
+    bool fIsAbandoned;
+
+    GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
+
+    friend class GrBatchFontCache;
+};
+
+/*
+ * GrBatchFontCache manages strikes which are indexed by a GrFontScaler.  These strikes can then be
+ * used to individual Glyph Masks.  The GrBatchFontCache also manages GrBatchAtlases, though this is
+ * more or less transparent to the client(aside from atlasGeneration, described below).
+ * Note - we used to initialize the backing atlas for the GrBatchFontCache at initialization time.
+ * However, this caused a regression, even when the GrBatchFontCache was unused.  We now initialize
+ * the backing atlases lazily.  Its not immediately clear why this improves the situation.
+ */
+class GrBatchFontCache {
+public:
+    GrBatchFontCache(GrContext*);
+    ~GrBatchFontCache();
+    // The user of the cache may hold a long-lived ref to the returned strike. However, actions by
+    // another client of the cache may cause the strike to be purged while it is still reffed.
+    // Therefore, the caller must check GrBatchTextStrike::isAbandoned() if there are other
+    // interactions with the cache since the strike was received.
+    inline GrBatchTextStrike* getStrike(GrFontScaler* scaler) {
+        GrBatchTextStrike* strike = fCache.find(*(scaler->getKey()));
+        if (NULL == strike) {
+            strike = this->generateStrike(scaler);
+        }
+        return strike;
+    }
+
+    void freeAll();
+
+    // if getTexture returns NULL, the client must not try to use other functions on the
+    // GrBatchFontCache which use the atlas.  This function *must* be called first, before other
+    // functions which use the atlas.
+    GrTexture* getTexture(GrMaskFormat format) {
+        if (this->initAtlas(format)) {
+            return this->getAtlas(format)->getTexture();
+        }
+        return NULL;
+    }
+
+    bool hasGlyph(GrGlyph* glyph) {
+        SkASSERT(glyph);
+        return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID);
+    }
+
+    // To ensure the GrBatchAtlas does not evict the Glyph Mask from its texture backing store,
+    // the client must pass in the currentToken from the GrBatchTarget along with the GrGlyph.
+    // A BulkUseTokenUpdater is used to manage bulk last use token updating in the Atlas.
+    // For convenience, this function will also set the use token for the current glyph if required
+    // NOTE: the bulk uploader is only valid if the subrun has a valid atlasGeneration
+    void addGlyphToBulkAndSetUseToken(GrBatchAtlas::BulkUseTokenUpdater* updater,
+                                      GrGlyph* glyph, GrBatchAtlas::BatchToken token) {
+        SkASSERT(glyph);
+        updater->add(glyph->fID);
+        this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token);
+    }
+
+    void setUseTokenBulk(const GrBatchAtlas::BulkUseTokenUpdater& updater,
+                         GrBatchAtlas::BatchToken token,
+                         GrMaskFormat format) {
+        this->getAtlas(format)->setLastUseTokenBulk(updater, token);
+    }
+
+    // add to texture atlas that matches this format
+    bool addToAtlas(GrBatchTextStrike* strike, GrBatchAtlas::AtlasID* id,
+                    GrBatchTarget* batchTarget,
+                    GrMaskFormat format, int width, int height, const void* image,
+                    SkIPoint16* loc) {
+        fPreserveStrike = strike;
+        return this->getAtlas(format)->addToAtlas(id, batchTarget, width, height, image, loc);
+    }
+
+    // Some clients may wish to verify the integrity of the texture backing store of the
+    // GrBatchAtlas.  The atlasGeneration returned below is a monitonically increasing number which
+    // changes everytime something is removed from the texture backing store.
+    uint64_t atlasGeneration(GrMaskFormat format) const {
+        return this->getAtlas(format)->atlasGeneration();
+    }
+
+    GrPixelConfig getPixelConfig(GrMaskFormat) const;
+
+    void dump() const;
+
+private:
+    // There is a 1:1 mapping between GrMaskFormats and atlas indices
+    static int MaskFormatToAtlasIndex(GrMaskFormat format) {
+        static const int sAtlasIndices[] = {
+            kA8_GrMaskFormat,
+            kA565_GrMaskFormat,
+            kARGB_GrMaskFormat,
+        };
+        SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
+
+        SkASSERT(sAtlasIndices[format] < kMaskFormatCount);
+        return sAtlasIndices[format];
+    }
+
+    bool initAtlas(GrMaskFormat);
+
+    GrBatchTextStrike* generateStrike(GrFontScaler* scaler) {
+        GrBatchTextStrike* strike = SkNEW_ARGS(GrBatchTextStrike, (this, scaler->getKey()));
+        fCache.add(strike);
+        return strike;
+    }
+
+    GrBatchAtlas* getAtlas(GrMaskFormat format) const {
+        int atlasIndex = MaskFormatToAtlasIndex(format);
+        SkASSERT(fAtlases[atlasIndex]);
+        return fAtlases[atlasIndex];
+    }
+
+    static void HandleEviction(GrBatchAtlas::AtlasID, void*);
+
+    GrContext* fContext;
+    SkTDynamicHash<GrBatchTextStrike, GrFontDescKey> fCache;
+    GrBatchAtlas* fAtlases[kMaskFormatCount];
+    GrBatchTextStrike* fPreserveStrike;
+};
+
+#endif
diff --git a/src/gpu/GrBatchTarget.cpp b/src/gpu/GrBatchTarget.cpp
index 21658d2..31b4cc9 100644
--- a/src/gpu/GrBatchTarget.cpp
+++ b/src/gpu/GrBatchTarget.cpp
@@ -7,42 +7,69 @@
 
 #include "GrBatchTarget.h"
 
+#include "GrBatchAtlas.h"
 #include "GrPipeline.h"
 
-/*
-void GrBatchTarget::flush() {
-    FlushBuffer::Iter iter(fFlushBuffer);
-    fVertexPool->unmap();
-    fIndexPool->unmap();
+static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 15;
+static const int DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS = 4;
 
-    while (iter.next()) {
-        GrProgramDesc desc;
-        BufferedFlush* bf = iter.get();
-        const GrPipeline* pipeline = bf->fPipeline;
-        const GrPrimitiveProcessor* primProc = bf->fPrimitiveProcessor.get();
-        fGpu->buildProgramDesc(&desc, *primProc, *pipeline, bf->fBatchTracker);
+static const size_t DRAW_BUFFER_IBPOOL_BUFFER_SIZE = 1 << 11;
+static const int DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS = 4;
 
-        GrGpu::DrawArgs args(primProc, pipeline, &desc, &bf->fBatchTracker);
-        for (int i = 0; i < bf->fDraws.count(); i++) {
-            fGpu->draw(args, bf->fDraws[i]);
-        }
-    }
-    fFlushBuffer.reset();
-}*/
-/*
-void GrBatchTarget::flushNext(int n) {
+GrBatchTarget::GrBatchTarget(GrGpu* gpu)
+    : fGpu(gpu)
+    , fFlushBuffer(kFlushBufferInitialSizeInBytes)
+    , fIter(fFlushBuffer)
+    , fNumberOfDraws(0)
+    , fCurrentToken(0)
+    , fLastFlushedToken(0)
+    , fInlineUpdatesIndex(0) {
+
+    fVertexPool.reset(SkNEW_ARGS(GrVertexBufferAllocPool, (fGpu,
+                                             DRAW_BUFFER_VBPOOL_BUFFER_SIZE,
+                                             DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS)));
+    fIndexPool.reset(SkNEW_ARGS(GrIndexBufferAllocPool, (fGpu,
+                                            DRAW_BUFFER_IBPOOL_BUFFER_SIZE,
+                                            DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS)));
+}
+
+void GrBatchTarget::flushNext(int n)  {
     for (; n > 0; n--) {
+        fLastFlushedToken++;
         SkDEBUGCODE(bool verify =) fIter.next();
         SkASSERT(verify);
-        GrProgramDesc desc;
+
         BufferedFlush* bf = fIter.get();
+
+        // Flush all texture uploads
+        int uploadCount = fInlineUploads.count();
+        while (fInlineUpdatesIndex < uploadCount &&
+               fInlineUploads[fInlineUpdatesIndex]->lastUploadToken() <= fLastFlushedToken) {
+            fInlineUploads[fInlineUpdatesIndex++]->upload(TextureUploader(fGpu));
+        }
+
+        GrProgramDesc desc;
         const GrPipeline* pipeline = bf->fPipeline;
         const GrPrimitiveProcessor* primProc = bf->fPrimitiveProcessor.get();
         fGpu->buildProgramDesc(&desc, *primProc, *pipeline, bf->fBatchTracker);
 
         GrGpu::DrawArgs args(primProc, pipeline, &desc, &bf->fBatchTracker);
-        for (int i = 0; i < bf->fDraws.count(); i++) {
-            fGpu->draw(args, bf->fDraws[i]);
+
+        int drawCount = bf->fVertexDraws.count();
+        const SkSTArray<1, GrVertices, true>& vertexDraws = bf->fVertexDraws;
+        for (int i = 0; i < drawCount; i++) {
+            fGpu->draw(args, vertexDraws[i]);
         }
     }
-}*/
+}
+
+void* GrBatchTarget::makeVertSpace(size_t vertexSize, int vertexCount,
+                    const GrVertexBuffer** buffer, int* startVertex) {
+    return fVertexPool->makeSpace(vertexSize, vertexCount, buffer, startVertex);
+}
+
+uint16_t* GrBatchTarget::makeIndexSpace(int indexCount,
+                                        const GrIndexBuffer** buffer, int* startIndex) {
+    return reinterpret_cast<uint16_t*>(fIndexPool->makeSpace(indexCount, buffer, startIndex));
+}
+
diff --git a/src/gpu/GrBatchTarget.h b/src/gpu/GrBatchTarget.h
index ae046c1..f317f30 100644
--- a/src/gpu/GrBatchTarget.h
+++ b/src/gpu/GrBatchTarget.h
@@ -8,67 +8,101 @@
 #ifndef GrBatchBuffer_DEFINED
 #define GrBatchBuffer_DEFINED
 
+#include "GrBatchAtlas.h"
 #include "GrBufferAllocPool.h"
 #include "GrPendingProgramElement.h"
 #include "GrPipeline.h"
-#include "GrGpu.h"
 #include "GrTRecorder.h"
+#include "GrVertices.h"
 
 /*
  * GrBatch instances use this object to allocate space for their geometry and to issue the draws
  * that render their batch.
  */
-
-class GrIndexBufferAllocPool;
-class GrVertexBufferAllocPool;
-
 class GrBatchTarget : public SkNoncopyable {
 public:
-    GrBatchTarget(GrGpu* gpu,
-                  GrVertexBufferAllocPool* vpool,
-                  GrIndexBufferAllocPool* ipool)
-        : fGpu(gpu)
-        , fVertexPool(vpool)
-        , fIndexPool(ipool)
-        , fFlushBuffer(kFlushBufferInitialSizeInBytes)
-        , fIter(fFlushBuffer)
-        , fNumberOfDraws(0) {}
+    typedef GrBatchAtlas::BatchToken BatchToken;
+    GrBatchTarget(GrGpu* gpu);
 
-    typedef GrDrawTarget::DrawInfo DrawInfo;
     void initDraw(const GrPrimitiveProcessor* primProc, const GrPipeline* pipeline) {
         GrNEW_APPEND_TO_RECORDER(fFlushBuffer, BufferedFlush, (primProc, pipeline));
         fNumberOfDraws++;
+        fCurrentToken++;
     }
 
-    void draw(const GrDrawTarget::DrawInfo& draw) {
-        fFlushBuffer.back().fDraws.push_back(draw);
-    }
+    class TextureUploader {
+    public:
+        TextureUploader(GrGpu* gpu) : fGpu(gpu) { SkASSERT(gpu); }
 
-    // TODO this is temporary until batch is everywhere
-    //void flush();
-    void resetNumberOfDraws() { fNumberOfDraws = 0; }
-    int numberOfDraws() const { return fNumberOfDraws; }
-    void preFlush() { fIter = FlushBuffer::Iter(fFlushBuffer); }
-    void flushNext(int n) {
-        for (; n > 0; n--) {
-            SkDEBUGCODE(bool verify =) fIter.next();
-            SkASSERT(verify);
-            GrProgramDesc desc;
-            BufferedFlush* bf = fIter.get();
-            const GrPipeline* pipeline = bf->fPipeline;
-            const GrPrimitiveProcessor* primProc = bf->fPrimitiveProcessor.get();
-            fGpu->buildProgramDesc(&desc, *primProc, *pipeline, bf->fBatchTracker);
+        /**
+         * Updates the pixels in a rectangle of a texture.
+         *
+         * @param left          left edge of the rectangle to write (inclusive)
+         * @param top           top edge of the rectangle to write (inclusive)
+         * @param width         width of rectangle to write in pixels.
+         * @param height        height of rectangle to write in pixels.
+         * @param config        the pixel config of the source buffer
+         * @param buffer        memory to read pixels from
+         * @param rowBytes      number of bytes between consecutive rows. Zero
+         *                      means rows are tightly packed.
+         */
+        bool writeTexturePixels(GrTexture* texture,
+                                int left, int top, int width, int height,
+                                GrPixelConfig config, const void* buffer,
+                                size_t rowBytes) {
+            return fGpu->writeTexturePixels(texture, left, top, width, height, config, buffer,
+                                            rowBytes);
+        }
 
-            GrGpu::DrawArgs args(primProc, pipeline, &desc, &bf->fBatchTracker);
+    private:
+        GrGpu* fGpu;
+    };
 
-            int drawCount = bf->fDraws.count();
-            const SkSTArray<1, DrawInfo, true>& draws = bf->fDraws;
-            for (int i = 0; i < drawCount; i++) {
-                fGpu->draw(args, draws[i]);
-            }
+    class Uploader : public SkRefCnt {
+    public:
+        Uploader(BatchToken lastUploadToken) : fLastUploadToken(lastUploadToken) {}
+        BatchToken lastUploadToken() const { return fLastUploadToken; }
+        virtual void upload(TextureUploader)=0;
+
+    private:
+        BatchToken fLastUploadToken;
+    };
+
+    void upload(Uploader* upload) {
+        if (this->asapToken() == upload->lastUploadToken()) {
+            fAsapUploads.push_back().reset(SkRef(upload));
+        } else {
+            fInlineUploads.push_back().reset(SkRef(upload));
         }
     }
-    void postFlush() { SkASSERT(!fIter.next()); fFlushBuffer.reset(); }
+
+    void draw(const GrVertices& vertices) {
+        fFlushBuffer.back().fVertexDraws.push_back(vertices);
+    }
+
+    bool isIssued(BatchToken token) const { return fLastFlushedToken >= token; }
+    BatchToken currentToken() const { return fCurrentToken; }
+    BatchToken asapToken() const { return fLastFlushedToken + 1; }
+
+    // TODO much of this complexity goes away when batch is everywhere
+    void resetNumberOfDraws() { fNumberOfDraws = 0; }
+    int numberOfDraws() const { return fNumberOfDraws; }
+    void preFlush() {
+        this->unmapVertexAndIndexBuffers();
+        int updateCount = fAsapUploads.count();
+        for (int i = 0; i < updateCount; i++) {
+            fAsapUploads[i]->upload(TextureUploader(fGpu));
+        }
+        fInlineUpdatesIndex = 0;
+        fIter = FlushBuffer::Iter(fFlushBuffer);
+    }
+    void flushNext(int n);
+    void postFlush() {
+        SkASSERT(!fIter.next());
+        fFlushBuffer.reset();
+        fAsapUploads.reset();
+        fInlineUploads.reset();
+    }
 
     // TODO This goes away when everything uses batch
     GrBatchTracker* currentBatchTracker() {
@@ -78,10 +112,12 @@
 
     const GrDrawTargetCaps& caps() const { return *fGpu->caps(); }
 
-    GrVertexBufferAllocPool* vertexPool() { return fVertexPool; }
-    GrIndexBufferAllocPool* indexPool() { return fIndexPool; }
+    GrResourceProvider* resourceProvider() const { return fGpu->getContext()->resourceProvider(); }
 
-    const GrIndexBuffer* quadIndexBuffer() const { return fGpu->getQuadIndexBuffer(); }
+    void* makeVertSpace(size_t vertexSize, int vertexCount,
+                        const GrVertexBuffer** buffer, int* startVertex);
+    uint16_t* makeIndexSpace(int indexCount,
+                             const GrIndexBuffer** buffer, int* startIndex);
 
     // A helper for draws which overallocate and then return data to the pool
     void putBackIndices(size_t indices) { fIndexPool->putBack(indices * sizeof(uint16_t)); }
@@ -90,10 +126,20 @@
         fVertexPool->putBack(vertices * vertexStride);
     }
 
+    void reset() {
+        fVertexPool->reset();
+        fIndexPool->reset();    
+    }
+
 private:
+    void unmapVertexAndIndexBuffers() {
+        fVertexPool->unmap();
+        fIndexPool->unmap();
+    }
+
     GrGpu* fGpu;
-    GrVertexBufferAllocPool* fVertexPool;
-    GrIndexBufferAllocPool* fIndexPool;
+    SkAutoTDelete<GrVertexBufferAllocPool> fVertexPool;
+    SkAutoTDelete<GrIndexBufferAllocPool> fIndexPool;
 
     typedef void* TBufferAlign; // This wouldn't be enough align if a command used long double.
 
@@ -105,7 +151,7 @@
         ProgramPrimitiveProcessor fPrimitiveProcessor;
         const GrPipeline* fPipeline;
         GrBatchTracker fBatchTracker;
-        SkSTArray<1, DrawInfo, true> fDraws;
+        SkSTArray<1, GrVertices, true> fVertexDraws;
     };
 
     enum {
@@ -118,6 +164,11 @@
     // TODO this is temporary
     FlushBuffer::Iter fIter;
     int fNumberOfDraws;
+    BatchToken fCurrentToken;
+    BatchToken fLastFlushedToken; // The next token to be flushed
+    SkTArray<SkAutoTUnref<Uploader>, true> fAsapUploads;
+    SkTArray<SkAutoTUnref<Uploader>, true> fInlineUploads;
+    int fInlineUpdatesIndex;
 };
 
 #endif
diff --git a/src/gpu/GrBatchTest.cpp b/src/gpu/GrBatchTest.cpp
new file mode 100644
index 0000000..d19fcaf
--- /dev/null
+++ b/src/gpu/GrBatchTest.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrBatchTest.h"
+#include "SkRandom.h"
+#include "SkTypes.h"
+
+#ifdef GR_TEST_UTILS
+
+BATCH_TEST_EXTERN(AAConvexPathBatch);
+BATCH_TEST_EXTERN(AADistanceFieldPathBatch);
+BATCH_TEST_EXTERN(AAFillRectBatch);
+BATCH_TEST_EXTERN(AAHairlineBatch);
+BATCH_TEST_EXTERN(AAStrokeRectBatch);
+BATCH_TEST_EXTERN(DashBatch);
+BATCH_TEST_EXTERN(DefaultPathBatch);
+BATCH_TEST_EXTERN(CircleBatch);
+BATCH_TEST_EXTERN(DIEllipseBatch);
+BATCH_TEST_EXTERN(EllipseBatch);
+BATCH_TEST_EXTERN(RRectBatch);
+BATCH_TEST_EXTERN(StrokeRectBatch);
+BATCH_TEST_EXTERN(TesselatingPathBatch);
+BATCH_TEST_EXTERN(TextBlobBatch);
+BATCH_TEST_EXTERN(VerticesBatch);
+
+static BatchTestFunc gTestBatches[] = {
+    BATCH_TEST_ENTRY(AAConvexPathBatch),
+    BATCH_TEST_ENTRY(AADistanceFieldPathBatch),
+    BATCH_TEST_ENTRY(AAFillRectBatch),
+    BATCH_TEST_ENTRY(AAHairlineBatch),
+    BATCH_TEST_ENTRY(AAStrokeRectBatch),
+    BATCH_TEST_ENTRY(DashBatch),
+    BATCH_TEST_ENTRY(DefaultPathBatch),
+    BATCH_TEST_ENTRY(CircleBatch),
+    BATCH_TEST_ENTRY(DIEllipseBatch),
+    BATCH_TEST_ENTRY(EllipseBatch),
+    BATCH_TEST_ENTRY(RRectBatch),
+    BATCH_TEST_ENTRY(StrokeRectBatch),
+    BATCH_TEST_ENTRY(TesselatingPathBatch),
+    BATCH_TEST_ENTRY(TextBlobBatch),
+    BATCH_TEST_ENTRY(VerticesBatch)
+};
+
+GrBatch* GrRandomBatch(SkRandom* random, GrContext* context) {
+    uint32_t index = random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gTestBatches)));
+    BatchTestFunc func = gTestBatches[index];
+    return (*func)(random, context);
+}
+#endif
diff --git a/src/gpu/GrBatchTest.h b/src/gpu/GrBatchTest.h
new file mode 100644
index 0000000..9daa960
--- /dev/null
+++ b/src/gpu/GrBatchTest.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrBatchTest_DEFINED
+#define GrBatchTest_DEFINED
+
+#include "GrTestUtils.h"
+
+#ifdef GR_TEST_UTILS
+
+class GrBatch;
+class GrContext;
+class SkRandom;
+
+/*
+ * This file defines some macros for testing batches, and also declares functions / objects which
+ * are generally useful for GrBatch testing
+ */
+
+// Batches should define test functions using BATCH_TEST_DEFINE.  The other macros defined below
+// are used exclusively by the test harness.
+typedef GrBatch* (*BatchTestFunc)(SkRandom* random, GrContext* context);
+#define BATCH_TEST_DEFINE(Batch) \
+    GrBatch* Batch##__Test(SkRandom* random, GrContext* context)
+#define BATCH_TEST_EXTERN(Batch) \
+    extern GrBatch* Batch##__Test(SkRandom*, GrContext* context);
+#define BATCH_TEST_ENTRY(Batch) \
+    Batch##__Test
+#define BATCH_TEST_FRIEND(Batch) \
+    friend GrBatch* Batch##__Test(SkRandom* random, GrContext* context);
+
+GrBatch* GrRandomBatch(SkRandom*, GrContext*);
+
+#endif
+#endif
diff --git a/src/gpu/GrBitmapTextContext.cpp b/src/gpu/GrBitmapTextContext.cpp
deleted file mode 100755
index 3031f98..0000000
--- a/src/gpu/GrBitmapTextContext.cpp
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- * 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 "GrBitmapTextContext.h"
-#include "GrAtlas.h"
-#include "GrDefaultGeoProcFactory.h"
-#include "GrDrawTarget.h"
-#include "GrFontCache.h"
-#include "GrFontScaler.h"
-#include "GrIndexBuffer.h"
-#include "GrStrokeInfo.h"
-#include "GrTexturePriv.h"
-
-#include "SkAutoKern.h"
-#include "SkColorPriv.h"
-#include "SkDraw.h"
-#include "SkDrawProcs.h"
-#include "SkGlyphCache.h"
-#include "SkGpuDevice.h"
-#include "SkGr.h"
-#include "SkPath.h"
-#include "SkRTConf.h"
-#include "SkStrokeRec.h"
-#include "SkTextMapStateProc.h"
-
-#include "effects/GrBitmapTextGeoProc.h"
-#include "effects/GrSimpleTextureEffect.h"
-
-SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
-                "Dump the contents of the font cache before every purge.");
-
-namespace {
-static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
-
-// position + local coord
-static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
-
-static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
-
-static const int kVerticesPerGlyph = 4;
-static const int kIndicesPerGlyph = 6;
-};
-
-GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
-                                         const SkDeviceProperties& properties)
-                                       : GrTextContext(context, properties) {
-    fStrike = NULL;
-
-    fCurrTexture = NULL;
-    fEffectTextureUniqueID = SK_InvalidUniqueID;
-
-    fVertices = NULL;
-    fCurrVertex = 0;
-    fAllocVertexCount = 0;
-    fTotalVertexCount = 0;
-
-    fVertexBounds.setLargestInverted();
-}
-
-GrBitmapTextContext* GrBitmapTextContext::Create(GrContext* context,
-                                                 const SkDeviceProperties& props) {
-    return SkNEW_ARGS(GrBitmapTextContext, (context, props));
-}
-
-bool GrBitmapTextContext::canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) {
-    return !SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix);
-}
-
-inline void GrBitmapTextContext::init(GrRenderTarget* rt, const GrClip& clip,
-                                      const GrPaint& paint, const SkPaint& skPaint) {
-    GrTextContext::init(rt, clip, paint, skPaint);
-
-    fStrike = NULL;
-
-    fCurrTexture = NULL;
-    fCurrVertex = 0;
-
-    fVertices = NULL;
-    fAllocVertexCount = 0;
-    fTotalVertexCount = 0;
-}
-
-void GrBitmapTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
-                                     const GrPaint& paint, const SkPaint& skPaint,
-                                     const SkMatrix& viewMatrix,
-                                     const char text[], size_t byteLength,
-                                     SkScalar x, SkScalar y) {
-    SkASSERT(byteLength == 0 || text != NULL);
-
-    // nothing to draw
-    if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
-        return;
-    }
-
-    this->init(rt, clip, paint, skPaint);
-
-    SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
-
-    SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, &viewMatrix);
-    SkGlyphCache*       cache = autoCache.getCache();
-    GrFontScaler*       fontScaler = GetGrFontScaler(cache);
-
-    // transform our starting point
-    {
-        SkPoint loc;
-        viewMatrix.mapXY(x, y, &loc);
-        x = loc.fX;
-        y = loc.fY;
-    }
-
-    // need to measure first
-    int numGlyphs;
-    if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
-        SkVector    stopVector;
-        numGlyphs = MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
-
-        SkScalar    stopX = stopVector.fX;
-        SkScalar    stopY = stopVector.fY;
-
-        if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
-            stopX = SkScalarHalf(stopX);
-            stopY = SkScalarHalf(stopY);
-        }
-        x -= stopX;
-        y -= stopY;
-    } else {
-        numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
-    }
-    fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
-
-    const char* stop = text + byteLength;
-
-    SkAutoKern autokern;
-
-    SkFixed fxMask = ~0;
-    SkFixed fyMask = ~0;
-    SkScalar halfSampleX, halfSampleY;
-    if (cache->isSubpixel()) {
-        halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
-        SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
-        if (kX_SkAxisAlignment == baseline) {
-            fyMask = 0;
-            halfSampleY = SK_ScalarHalf;
-        } else if (kY_SkAxisAlignment == baseline) {
-            fxMask = 0;
-            halfSampleX = SK_ScalarHalf;
-        }
-    } else {
-        halfSampleX = halfSampleY = SK_ScalarHalf;
-    }
-
-    Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
-    Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
-
-    // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix, but for
-    // performance reasons we just invert here instead
-    if (!viewMatrix.invert(&fLocalMatrix)) {
-        SkDebugf("Cannot invert viewmatrix\n");
-        return;
-    }
-
-    while (text < stop) {
-        const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
-
-        fx += autokern.adjust(glyph);
-
-        if (glyph.fWidth) {
-            this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
-                                            glyph.getSubXFixed(),
-                                            glyph.getSubYFixed()),
-                              Sk48Dot16FloorToInt(fx),
-                              Sk48Dot16FloorToInt(fy),
-                              fontScaler);
-        }
-
-        fx += glyph.fAdvanceX;
-        fy += glyph.fAdvanceY;
-    }
-
-    this->finish();
-}
-
-void GrBitmapTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
-                                        const GrPaint& paint, const SkPaint& skPaint,
-                                        const SkMatrix& viewMatrix,
-                                        const char text[], size_t byteLength,
-                                        const SkScalar pos[], int scalarsPerPosition,
-                                        const SkPoint& offset) {
-    SkASSERT(byteLength == 0 || text != NULL);
-    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
-
-    // nothing to draw
-    if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
-        return;
-    }
-
-    this->init(rt, clip, paint, skPaint);
-
-    SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
-
-    SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, &viewMatrix);
-    SkGlyphCache*       cache = autoCache.getCache();
-    GrFontScaler*       fontScaler = GetGrFontScaler(cache);
-
-    // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix, but for
-    // performance reasons we just invert here instead
-    if (!viewMatrix.invert(&fLocalMatrix)) {
-        SkDebugf("Cannot invert viewmatrix\n");
-        return;
-    }
-
-    int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
-    fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
-
-    const char*        stop = text + byteLength;
-    SkTextAlignProc    alignProc(fSkPaint.getTextAlign());
-    SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
-    SkScalar halfSampleX = 0, halfSampleY = 0;
-
-    if (cache->isSubpixel()) {
-        // maybe we should skip the rounding if linearText is set
-        SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
-
-        SkFixed fxMask = ~0;
-        SkFixed fyMask = ~0;
-        if (kX_SkAxisAlignment == baseline) {
-            fyMask = 0;
-            halfSampleY = SK_ScalarHalf;
-        } else if (kY_SkAxisAlignment == baseline) {
-            fxMask = 0;
-            halfSampleX = SK_ScalarHalf;
-        }
-
-        if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
-            while (text < stop) {
-                SkPoint tmsLoc;
-                tmsProc(pos, &tmsLoc);
-                Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
-                Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
-
-                const SkGlyph& glyph = glyphCacheProc(cache, &text,
-                                                      fx & fxMask, fy & fyMask);
-
-                if (glyph.fWidth) {
-                    this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
-                                                    glyph.getSubXFixed(),
-                                                    glyph.getSubYFixed()),
-                                      Sk48Dot16FloorToInt(fx),
-                                      Sk48Dot16FloorToInt(fy),
-                                      fontScaler);
-                }
-                pos += scalarsPerPosition;
-            }
-        } else {
-            while (text < stop) {
-                const char* currentText = text;
-                const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
-
-                if (metricGlyph.fWidth) {
-                    SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
-                    SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
-                    SkPoint tmsLoc;
-                    tmsProc(pos, &tmsLoc);
-                    SkPoint alignLoc;
-                    alignProc(tmsLoc, metricGlyph, &alignLoc);
-
-                    Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
-                    Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
-
-                    // have to call again, now that we've been "aligned"
-                    const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
-                                                          fx & fxMask, fy & fyMask);
-                    // the assumption is that the metrics haven't changed
-                    SkASSERT(prevAdvX == glyph.fAdvanceX);
-                    SkASSERT(prevAdvY == glyph.fAdvanceY);
-                    SkASSERT(glyph.fWidth);
-
-                    this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
-                                                    glyph.getSubXFixed(),
-                                                    glyph.getSubYFixed()),
-                                      Sk48Dot16FloorToInt(fx),
-                                      Sk48Dot16FloorToInt(fy),
-                                      fontScaler);
-                }
-                pos += scalarsPerPosition;
-            }
-        }
-    } else {    // not subpixel
-
-        if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
-            while (text < stop) {
-                // the last 2 parameters are ignored
-                const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
-
-                if (glyph.fWidth) {
-                    SkPoint tmsLoc;
-                    tmsProc(pos, &tmsLoc);
-
-                    Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
-                    Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
-                    this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
-                                                    glyph.getSubXFixed(),
-                                                    glyph.getSubYFixed()),
-                                      Sk48Dot16FloorToInt(fx),
-                                      Sk48Dot16FloorToInt(fy),
-                                      fontScaler);
-                }
-                pos += scalarsPerPosition;
-            }
-        } else {
-            while (text < stop) {
-                // the last 2 parameters are ignored
-                const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
-
-                if (glyph.fWidth) {
-                    SkPoint tmsLoc;
-                    tmsProc(pos, &tmsLoc);
-
-                    SkPoint alignLoc;
-                    alignProc(tmsLoc, glyph, &alignLoc);
-
-                    Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
-                    Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
-                    this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
-                                                    glyph.getSubXFixed(),
-                                                    glyph.getSubYFixed()),
-                                      Sk48Dot16FloorToInt(fx),
-                                      Sk48Dot16FloorToInt(fy),
-                                      fontScaler);
-                }
-                pos += scalarsPerPosition;
-            }
-        }
-    }
-
-    this->finish();
-}
-
-static size_t get_vertex_stride(GrMaskFormat maskFormat) {
-    switch (maskFormat) {
-        case kA8_GrMaskFormat:
-            return kGrayTextVASize;
-        case kARGB_GrMaskFormat:
-            return kColorTextVASize;
-        default:
-            return kLCDTextVASize;
-    }
-}
-
-static void* alloc_vertices(GrDrawTarget* drawTarget,
-                            int numVertices,
-                            GrMaskFormat maskFormat) {
-    if (numVertices <= 0) {
-        return NULL;
-    }
-
-    // set up attributes
-    void* vertices = NULL;
-    bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
-                                                          get_vertex_stride(maskFormat),
-                                                          0,
-                                                          &vertices,
-                                                          NULL);
-    GrAlwaysAssert(success);
-    return vertices;
-}
-
-inline bool GrBitmapTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scaler) {
-    if (!fStrike->glyphTooLargeForAtlas(glyph)) {
-        if (fStrike->addGlyphToAtlas(glyph, scaler)) {
-            return true;
-        }
-        
-        // try to clear out an unused plot before we flush
-        if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
-            fStrike->addGlyphToAtlas(glyph, scaler)) {
-            return true;
-        }
-        
-        if (c_DumpFontCache) {
-#ifdef SK_DEVELOPER
-            fContext->getFontCache()->dump();
-#endif
-        }
-        
-        // before we purge the cache, we must flush any accumulated draws
-        this->flush();
-        fContext->flush();
-        
-        // we should have an unused plot now
-        if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
-            fStrike->addGlyphToAtlas(glyph, scaler)) {
-            return true;
-        }
-        
-        // we should never get here
-        SkASSERT(false);
-    }
-    
-    return false;
-}
-
-void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed,
-                                      int vx, int vy,
-                                      GrFontScaler* scaler) {
-    if (NULL == fDrawTarget) {
-        return;
-    }
-
-    if (NULL == fStrike) {
-        fStrike = fContext->getFontCache()->getStrike(scaler, false);
-    }
-
-    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
-    if (NULL == glyph || glyph->fBounds.isEmpty()) {
-        return;
-    }
-
-    int x = vx + glyph->fBounds.fLeft;
-    int y = vy + glyph->fBounds.fTop;
-
-    // keep them as ints until we've done the clip-test
-    int width = glyph->fBounds.width();
-    int height = glyph->fBounds.height();
-
-    // check if we clipped out
-    if (fClipRect.quickReject(x, y, x + width, y + height)) {
-        return;
-    }
-
-    // If the glyph is too large we fall back to paths
-    if (NULL == glyph->fPlot && !uploadGlyph(glyph, scaler)) {
-        if (NULL == glyph->fPath) {
-            SkPath* path = SkNEW(SkPath);
-            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
-                // flag the glyph as being dead?
-                delete path;
-                return;
-            }
-            glyph->fPath = path;
-        }
-
-        // flush any accumulated draws before drawing this glyph as a path.
-        this->flush();
-
-        SkMatrix translate;
-        translate.setTranslate(SkIntToScalar(vx), SkIntToScalar(vy));
-        SkPath tmpPath(*glyph->fPath);
-        tmpPath.transform(translate);
-        GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
-        fContext->drawPath(fRenderTarget, fClip, fPaint, SkMatrix::I(), tmpPath, strokeInfo);
-
-        // remove this glyph from the vertices we need to allocate
-        fTotalVertexCount -= kVerticesPerGlyph;
-        return;
-    }
-
-    SkASSERT(glyph->fPlot);
-    GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
-    glyph->fPlot->setDrawToken(drawToken);
-
-    // the current texture/maskformat must match what the glyph needs
-    GrTexture* texture = glyph->fPlot->texture();
-    SkASSERT(texture);
-
-    if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fAllocVertexCount) {
-        this->flush();
-        fCurrTexture = texture;
-        fCurrTexture->ref();
-        fCurrMaskFormat = glyph->fMaskFormat;
-    }
-
-    if (NULL == fVertices) {
-        int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
-        fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
-        fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, fCurrMaskFormat);
-    }
-
-    SkRect r;
-    r.fLeft = SkIntToScalar(x);
-    r.fTop = SkIntToScalar(y);
-    r.fRight = r.fLeft + SkIntToScalar(width);
-    r.fBottom = r.fTop + SkIntToScalar(height);
-
-    fVertexBounds.joinNonEmptyArg(r);
-
-    int u0 = glyph->fAtlasLocation.fX;
-    int v0 = glyph->fAtlasLocation.fY;
-    int u1 = u0 + width;
-    int v1 = v0 + height;
-
-    size_t vertSize = get_vertex_stride(fCurrMaskFormat);
-    intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex;
-
-    // V0
-    SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(r.fLeft, r.fTop);
-    if (kA8_GrMaskFormat == fCurrMaskFormat) {
-        SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
-        *color = fPaint.getColor();
-    }
-    SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize -
-                                                              sizeof(SkIPoint16));
-    textureCoords->set(u0, v0);
-    vertex += vertSize;
-
-    // V1
-    position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(r.fLeft, r.fBottom);
-    if (kA8_GrMaskFormat == fCurrMaskFormat) {
-        SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
-        *color = fPaint.getColor();
-    }
-    textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize  - sizeof(SkIPoint16));
-    textureCoords->set(u0, v1);
-    vertex += vertSize;
-
-    // V2
-    position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(r.fRight, r.fBottom);
-    if (kA8_GrMaskFormat == fCurrMaskFormat) {
-        SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
-        *color = fPaint.getColor();
-    }
-    textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize  - sizeof(SkIPoint16));
-    textureCoords->set(u1, v1);
-    vertex += vertSize;
-
-    // V3
-    position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(r.fRight, r.fTop);
-    if (kA8_GrMaskFormat == fCurrMaskFormat) {
-        SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
-        *color = fPaint.getColor();
-    }
-    textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize  - sizeof(SkIPoint16));
-    textureCoords->set(u1, v0);
-
-    fCurrVertex += 4;
-}
-
-void GrBitmapTextContext::flush() {
-    if (NULL == fDrawTarget) {
-        return;
-    }
-
-    if (fCurrVertex > 0) {
-        GrPipelineBuilder pipelineBuilder;
-        pipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
-
-        // setup our sampler state for our text texture/atlas
-        SkASSERT(SkIsAlign4(fCurrVertex));
-        SkASSERT(fCurrTexture);
-
-        SkASSERT(fStrike);
-        GrColor color = fPaint.getColor();
-        switch (fCurrMaskFormat) {
-                // Color bitmap text
-            case kARGB_GrMaskFormat: {
-                int a = fSkPaint.getAlpha();
-                color = SkColorSetARGB(a, a, a, a);
-                break;
-            }
-                // LCD text
-            case kA565_GrMaskFormat: {
-                // TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD
-                // processor if the xp can support it. For now we will simply assume that if
-                // fUseLCDText is true, then we have a known color output.
-                const GrXPFactory* xpFactory = pipelineBuilder.getXPFactory();
-                if (!xpFactory->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) {
-                    SkDebugf("LCD Text will not draw correctly.\n");
-                }
-                break;
-            }
-                // Grayscale/BW text
-            case kA8_GrMaskFormat:
-                break;
-            default:
-                SkFAIL("Unexpected mask format.");
-        }
-
-        GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
-        uint32_t textureUniqueID = fCurrTexture->getUniqueID();
-        if (textureUniqueID != fEffectTextureUniqueID ||
-            fCachedGeometryProcessor->color() != color ||
-            !fCachedGeometryProcessor->localMatrix().cheapEqualTo(fLocalMatrix)) {
-            // This will be ignored in the non A8 case
-            bool opaqueVertexColors = GrColorIsOpaque(fPaint.getColor());
-            fCachedGeometryProcessor.reset(GrBitmapTextGeoProc::Create(color,
-                                                                       fCurrTexture,
-                                                                       params,
-                                                                       fCurrMaskFormat,
-                                                                       opaqueVertexColors,
-                                                                       fLocalMatrix));
-            fEffectTextureUniqueID = textureUniqueID;
-        }
-
-        int nGlyphs = fCurrVertex / kVerticesPerGlyph;
-        fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
-        fDrawTarget->drawIndexedInstances(&pipelineBuilder,
-                                          fCachedGeometryProcessor.get(),
-                                          kTriangles_GrPrimitiveType,
-                                          nGlyphs,
-                                          kVerticesPerGlyph,
-                                          kIndicesPerGlyph,
-                                          &fVertexBounds);
-
-        fDrawTarget->resetVertexSource();
-        fVertices = NULL;
-        fAllocVertexCount = 0;
-        // reset to be those that are left
-        fTotalVertexCount -= fCurrVertex;
-        fCurrVertex = 0;
-        fVertexBounds.setLargestInverted();
-        SkSafeSetNull(fCurrTexture);
-    }
-}
-
-inline void GrBitmapTextContext::finish() {
-    this->flush();
-    fTotalVertexCount = 0;
-
-    GrTextContext::finish();
-}
-
diff --git a/src/gpu/GrBitmapTextContext.h b/src/gpu/GrBitmapTextContext.h
deleted file mode 100644
index 2c50873..0000000
--- a/src/gpu/GrBitmapTextContext.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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 GrBitmapTextContext_DEFINED
-#define GrBitmapTextContext_DEFINED
-
-#include "GrTextContext.h"
-
-class GrGeometryProcessor;
-class GrTextStrike;
-
-/*
- * This class implements GrTextContext using standard bitmap fonts
- */
-class GrBitmapTextContext : public GrTextContext {
-public:
-    static GrBitmapTextContext* Create(GrContext*, const SkDeviceProperties&);
-
-    virtual ~GrBitmapTextContext() {}
-
-private:
-    GrTextStrike*                     fStrike;
-    void*                             fVertices;
-    int                               fCurrVertex;
-    int                               fAllocVertexCount;
-    int                               fTotalVertexCount;
-    SkRect                            fVertexBounds;
-    GrTexture*                        fCurrTexture;
-    GrMaskFormat                      fCurrMaskFormat;
-    SkAutoTUnref<const GrGeometryProcessor> fCachedGeometryProcessor;
-    // Used to check whether fCachedEffect is still valid.
-    uint32_t                          fEffectTextureUniqueID;
-    SkMatrix                          fLocalMatrix;
-
-    GrBitmapTextContext(GrContext*, const SkDeviceProperties&);
-
-    bool canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) SK_OVERRIDE;
-
-    virtual void onDrawText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
-                            const SkMatrix& viewMatrix, const char text[], size_t byteLength,
-                            SkScalar x, SkScalar y) SK_OVERRIDE;
-    virtual void onDrawPosText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
-                               const SkMatrix& viewMatrix,
-                               const char text[], size_t byteLength,
-                               const SkScalar pos[], int scalarsPerPosition,
-                               const SkPoint& offset) SK_OVERRIDE;
-
-    void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&);
-    void appendGlyph(GrGlyph::PackedID, SkFixed left, SkFixed top, GrFontScaler*);
-    bool uploadGlyph(GrGlyph*, GrFontScaler*);
-    void flush();                 // automatically called by destructor
-    void finish();
-};
-
-#endif
diff --git a/src/gpu/GrBufferAllocPool.cpp b/src/gpu/GrBufferAllocPool.cpp
index f233118..878d537 100644
--- a/src/gpu/GrBufferAllocPool.cpp
+++ b/src/gpu/GrBufferAllocPool.cpp
@@ -37,18 +37,13 @@
 
 GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu,
                                      BufferType bufferType,
-                                     bool frequentResetHint,
                                      size_t blockSize,
-                                     int preallocBufferCnt) :
-        fBlocks(SkTMax(8, 2*preallocBufferCnt)) {
+                                     int preallocBufferCnt)
+    : fBlocks(SkTMax(8, 2*preallocBufferCnt)) {
 
-    SkASSERT(gpu);
-    fGpu = gpu;
-    fGpu->ref();
-    fGpuIsReffed = true;
+    fGpu = SkRef(gpu);
 
     fBufferType = bufferType;
-    fFrequentResetHint = frequentResetHint;
     fBufferPtr = NULL;
     fMinBlockSize = SkTMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
 
@@ -73,17 +68,10 @@
         }
     }
     while (!fBlocks.empty()) {
-        destroyBlock();
+        this->destroyBlock();
     }
     fPreallocBuffers.unrefAll();
-    releaseGpuRef();
-}
-
-void GrBufferAllocPool::releaseGpuRef() {
-    if (fGpuIsReffed) {
-        fGpu->unref();
-        fGpuIsReffed = false;
-    }
+    fGpu->unref();
 }
 
 void GrBufferAllocPool::reset() {
@@ -203,7 +191,7 @@
     // updateData() if the amount of data passed is less than the full buffer
     // size.
 
-    if (!createBlock(size)) {
+    if (!this->createBlock(size)) {
         return NULL;
     }
     SkASSERT(fBufferPtr);
@@ -217,27 +205,6 @@
     return fBufferPtr;
 }
 
-int GrBufferAllocPool::currentBufferItems(size_t itemSize) const {
-    VALIDATE();
-    if (fBufferPtr) {
-        const BufferBlock& back = fBlocks.back();
-        size_t usedBytes = back.fBuffer->gpuMemorySize() - back.fBytesFree;
-        size_t pad = GrSizeAlignUpPad(usedBytes, itemSize);
-        return static_cast<int>((back.fBytesFree - pad) / itemSize);
-    } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
-        return static_cast<int>(fMinBlockSize / itemSize);
-    }
-    return 0;
-}
-
-int GrBufferAllocPool::preallocatedBuffersRemaining() const {
-    return fPreallocBuffers.count() - fPreallocBuffersInUse;
-}
-
-int GrBufferAllocPool::preallocatedBufferCount() const {
-    return fPreallocBuffers.count();
-}
-
 void GrBufferAllocPool::putBack(size_t bytes) {
     VALIDATE();
 
@@ -248,7 +215,7 @@
     int preallocBuffersInUse = fPreallocBuffersInUse;
 
     while (bytes) {
-        // caller shouldnt try to put back more than they've taken
+        // caller shouldn't try to put back more than they've taken
         SkASSERT(!fBlocks.empty());
         BufferBlock& block = fBlocks.back();
         size_t bytesUsed = block.fBuffer->gpuMemorySize() - block.fBytesFree;
@@ -317,17 +284,11 @@
     SkASSERT(NULL == fBufferPtr);
 
     // If the buffer is CPU-backed we map it because it is free to do so and saves a copy.
-    // Otherwise when buffer mapping is supported:
-    //      a) If the frequently reset hint is set we only map when the requested size meets a
-    //      threshold (since we don't expect it is likely that we will see more vertex data)
-    //      b) If the hint is not set we map if the buffer size is greater than the threshold.
+    // Otherwise when buffer mapping is supported we map if the buffer size is greater than the
+    // threshold.
     bool attemptMap = block.fBuffer->isCPUBacked();
     if (!attemptMap && GrDrawTargetCaps::kNone_MapFlags != fGpu->caps()->mapBufferFlags()) {
-        if (fFrequentResetHint) {
-            attemptMap = requestSize > GR_GEOM_BUFFER_MAP_THRESHOLD;
-        } else {
-            attemptMap = size > GR_GEOM_BUFFER_MAP_THRESHOLD;
-        }
+        attemptMap = size > GR_GEOM_BUFFER_MAP_THRESHOLD;
     }
 
     if (attemptMap) {
@@ -395,14 +356,12 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu,
-                                                 bool frequentResetHint,
                                                  size_t bufferSize,
                                                  int preallocBufferCnt)
-: GrBufferAllocPool(gpu,
-                    kVertex_BufferType,
-                    frequentResetHint,
-                    bufferSize,
-                    preallocBufferCnt) {
+    : GrBufferAllocPool(gpu,
+                        kVertex_BufferType,
+                        bufferSize,
+                        preallocBufferCnt) {
 }
 
 void* GrVertexBufferAllocPool::makeSpace(size_t vertexSize,
@@ -427,25 +386,15 @@
     return ptr;
 }
 
-int GrVertexBufferAllocPool::preallocatedBufferVertices(size_t vertexSize) const {
-    return static_cast<int>(INHERITED::preallocatedBufferSize() / vertexSize);
-}
-
-int GrVertexBufferAllocPool::currentBufferVertices(size_t vertexSize) const {
-    return currentBufferItems(vertexSize);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
 GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu,
-                                               bool frequentResetHint,
                                                size_t bufferSize,
                                                int preallocBufferCnt)
-: GrBufferAllocPool(gpu,
-                    kIndex_BufferType,
-                    frequentResetHint,
-                    bufferSize,
-                    preallocBufferCnt) {
+    : GrBufferAllocPool(gpu,
+                        kIndex_BufferType,
+                        bufferSize,
+                        preallocBufferCnt) {
 }
 
 void* GrIndexBufferAllocPool::makeSpace(int indexCount,
@@ -469,10 +418,4 @@
     return ptr;
 }
 
-int GrIndexBufferAllocPool::preallocatedBufferIndices() const {
-    return static_cast<int>(INHERITED::preallocatedBufferSize() / sizeof(uint16_t));
-}
 
-int GrIndexBufferAllocPool::currentBufferIndices() const {
-    return currentBufferItems(sizeof(uint16_t));
-}
diff --git a/src/gpu/GrBufferAllocPool.h b/src/gpu/GrBufferAllocPool.h
index 40904cf..df0a0cc 100644
--- a/src/gpu/GrBufferAllocPool.h
+++ b/src/gpu/GrBufferAllocPool.h
@@ -41,25 +41,10 @@
     void reset();
 
     /**
-     * Gets the number of preallocated buffers that are yet to be used.
-     */
-    int preallocatedBuffersRemaining() const;
-
-    /**
-     * gets the number of preallocated buffers
-     */
-    int preallocatedBufferCount() const;
-
-    /**
      * Frees data from makeSpaces in LIFO order.
      */
     void putBack(size_t bytes);
 
-    /**
-     * Gets the GrGpu that this pool is associated with.
-     */
-    GrGpu* getGpu() { return fGpu; }
-
 protected:
     /**
      * Used to determine what type of buffers to create. We could make the
@@ -76,10 +61,6 @@
      *
      * @param gpu                   The GrGpu used to create the buffers.
      * @param bufferType            The type of buffers to create.
-     * @param frequentResetHint     A hint that indicates that the pool
-     *                              should expect frequent unmap() calls
-     *                              (as opposed to many makeSpace / acquires
-     *                              between resets).
      * @param bufferSize            The minimum size of created buffers.
      *                              This value will be clamped to some
      *                              reasonable minimum.
@@ -89,22 +70,12 @@
      */
      GrBufferAllocPool(GrGpu* gpu,
                        BufferType bufferType,
-                       bool frequentResetHint,
                        size_t   bufferSize = 0,
                        int preallocBufferCnt = 0);
 
     virtual ~GrBufferAllocPool();
 
     /**
-     * Gets the size of the preallocated buffers.
-     *
-     * @return the size of preallocated buffers.
-     */
-    size_t preallocatedBufferSize() const {
-        return fPreallocBuffers.count() ? fMinBlockSize : 0;
-    }
-
-    /**
      * Returns a block of memory to hold data. A buffer designated to hold the
      * data is given to the caller. The buffer may or may not be locked. The
      * returned ptr remains valid until any of the following:
@@ -128,26 +99,9 @@
                     const GrGeometryBuffer** buffer,
                     size_t* offset);
 
-    /**
-     * Gets the number of items of a size that can be added to the current
-     * buffer without spilling to another buffer. If the pool has been reset, or
-     * the previous makeSpace completely exhausted a buffer then the returned
-     * size will be the size of the next available preallocated buffer, or zero
-     * if no preallocated buffer remains available. It is assumed that items
-     * should be itemSize-aligned from the start of a buffer.
-     *
-     * @return the number of items that would fit in the current buffer.
-     */
-    int currentBufferItems(size_t itemSize) const;
-
     GrGeometryBuffer* createBuffer(size_t size);
 
 private:
-
-    // The GrGpu must be able to clear the ref of pools it creates as members
-    friend class GrGpu;
-    void releaseGpuRef();
-
     struct BufferBlock {
         size_t              fBytesFree;
         GrGeometryBuffer*   fBuffer;
@@ -163,8 +117,6 @@
     size_t                          fBytesInUse;
 
     GrGpu*                          fGpu;
-    bool                            fGpuIsReffed;
-    bool                            fFrequentResetHint;
     SkTDArray<GrGeometryBuffer*>    fPreallocBuffers;
     size_t                          fMinBlockSize;
     BufferType                      fBufferType;
@@ -189,20 +141,13 @@
      * Constructor
      *
      * @param gpu                   The GrGpu used to create the vertex buffers.
-     * @param frequentResetHint     A hint that indicates that the pool
-     *                              should expect frequent unmap() calls
-     *                              (as opposed to many makeSpace / acquires
-     *                              between resets).
-     * @param bufferSize            The minimum size of created VBs This value
+     * @param bufferSize            The minimum size of created VBs. This value
      *                              will be clamped to some reasonable minimum.
      * @param preallocBufferCnt     The pool will allocate this number of VBs at
      *                              bufferSize and keep them until it is
      *                              destroyed.
      */
-    GrVertexBufferAllocPool(GrGpu* gpu,
-                            bool frequentResetHint,
-                            size_t bufferSize = 0,
-                            int preallocBufferCnt = 0);
+    GrVertexBufferAllocPool(GrGpu* gpu, size_t bufferSize = 0, int preallocBufferCnt = 0);
 
     /**
      * Returns a block of memory to hold vertices. A buffer designated to hold
@@ -230,29 +175,6 @@
                     const GrVertexBuffer** buffer,
                     int* startVertex);
 
-    /**
-     * Gets the number of vertices that can be added to the current VB without
-     * spilling to another VB. If the pool has been reset, or the previous
-     * makeSpace completely exhausted a VB then the returned number of vertices
-     * 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 size of a vertex to compute space for.
-     * @return the number of vertices that would fit in the current buffer.
-     */
-    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 size of a vertex to compute space for.
-     *
-     * @return number of vertices that fit in one of the preallocated vertex
-     *         buffers.
-     */
-    int preallocatedBufferVertices(size_t vertexSize) const;
-
 private:
     typedef GrBufferAllocPool INHERITED;
 };
@@ -268,18 +190,13 @@
      * Constructor
      *
      * @param gpu                   The GrGpu used to create the index buffers.
-     * @param frequentResetHint     A hint that indicates that the pool
-     *                              should expect frequent unmap() calls
-     *                              (as opposed to many makeSpace / acquires
-     *                              between resets).
-     * @param bufferSize            The minimum size of created IBs This value
+     * @param bufferSize            The minimum size of created IBs. This value
      *                              will be clamped to some reasonable minimum.
      * @param preallocBufferCnt     The pool will allocate this number of VBs at
      *                              bufferSize and keep them until it is
      *                              destroyed.
      */
     GrIndexBufferAllocPool(GrGpu* gpu,
-                           bool frequentResetHint,
                            size_t bufferSize = 0,
                            int preallocBufferCnt = 0);
 
@@ -305,24 +222,6 @@
                     const GrIndexBuffer** buffer,
                     int* startIndex);
 
-    /**
-     * Gets the number of indices that can be added to the current IB without
-     * spilling to another IB. If the pool has been reset, or the previous
-     * makeSpace completely exhausted a IB then the returned number of indices
-     * would fit in the next available preallocated buffer. If any makeSpace
-     * would force a new IB to be created the return value will be zero.
-     */
-    int currentBufferIndices() const;
-
-    /**
-     * Gets the number of indices that can fit in a preallocated index buffer.
-     * Zero if no preallocated buffers.
-     *
-     * @return number of indices that fit in one of the preallocated index
-     *         buffers.
-     */
-    int preallocatedBufferIndices() const;
-
 private:
     typedef GrBufferAllocPool INHERITED;
 };
diff --git a/src/gpu/GrClipMaskCache.h b/src/gpu/GrClipMaskCache.h
index 0329c84..3aa80df 100644
--- a/src/gpu/GrClipMaskCache.h
+++ b/src/gpu/GrClipMaskCache.h
@@ -206,8 +206,8 @@
 
             // HACK: set the last param to true to indicate that this request is at
             // flush time and therefore we require a scratch texture with no pending IO operations.
-            fLastMask.reset(context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch,
-                                                       /*flushing=*/true));
+            fLastMask.reset(context->textureProvider()->refScratchTexture(
+                desc, GrTextureProvider::kApprox_ScratchTexMatch, /*flushing=*/true));
 
             fLastBound = bound;
         }
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 66d817e..15677fc 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -14,10 +14,9 @@
 #include "GrPathRenderer.h"
 #include "GrRenderTarget.h"
 #include "GrRenderTargetPriv.h"
-#include "GrStencilBuffer.h"
+#include "GrStencilAttachment.h"
 #include "GrSWMaskHelper.h"
 #include "SkRasterClip.h"
-#include "SkStrokeRec.h"
 #include "SkTLazy.h"
 #include "effects/GrConvexPolyEffect.h"
 #include "effects/GrPorterDuffXferProcessor.h"
@@ -60,7 +59,7 @@
                             const GrPipelineBuilder* pipelineBuilder,
                             const SkMatrix& viewMatrix,
                             const SkPath& origPath,
-                            const SkStrokeRec& stroke,
+                            const GrStrokeInfo& stroke,
                             bool doAA) {
     // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
     SkTCopyOnFirstWrite<SkPath> path(origPath);
@@ -88,7 +87,7 @@
     // TODO: generalize this function so that when
     // a clip gets complex enough it can just be done in SW regardless
     // of whether it would invoke the GrSoftwarePathRenderer.
-    SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+    GrStrokeInfo stroke(SkStrokeRec::kFill_InitStyle);
 
     // Set the matrix so that rendered clip elements are transformed to mask space from clip
     // space.
@@ -377,7 +376,6 @@
                                     GrTexture* target,
                                     const SkClipStack::Element* element,
                                     GrPathRenderer* pr) {
-    GrDrawTarget::AutoGeometryPush agp(fClipTarget);
 
     pipelineBuilder->setRenderTarget(target->asRenderTarget());
 
@@ -413,7 +411,7 @@
             if (path.isInverseFillType()) {
                 path.toggleInverseFillType();
             }
-            SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+            GrStrokeInfo stroke(SkStrokeRec::kFill_InitStyle);
             if (NULL == pr) {
                 GrPathRendererChain::DrawType type;
                 type = element->isAA() ? GrPathRendererChain::kColorAntiAlias_DrawType :
@@ -449,7 +447,7 @@
         if (path.isInverseFillType()) {
             path.toggleInverseFillType();
         }
-        SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+        GrStrokeInfo stroke(SkStrokeRec::kFill_InitStyle);
         GrPathRendererChain::DrawType type = element->isAA() ?
             GrPathRendererChain::kStencilAndColorAntiAlias_DrawType :
             GrPathRendererChain::kStencilAndColor_DrawType;
@@ -479,6 +477,7 @@
                                       GrTextureDomain::MakeTexelDomain(srcMask, srcBound),
                                       GrTextureDomain::kDecal_Mode,
                                       GrTextureParams::kNone_FilterMode))->unref();
+
     // The color passed in here does not matter since the coverageSetOpXP won't read it.
     fClipTarget->drawSimpleRect(pipelineBuilder,
                                 GrColor_WHITE,
@@ -497,7 +496,8 @@
         desc.fConfig = kRGBA_8888_GrPixelConfig;
     }
 
-    return this->getContext()->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch);
+    return this->getContext()->textureProvider()->refScratchTexture(
+        desc, GrTextureProvider::kApprox_ScratchTexMatch);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -670,6 +670,7 @@
                                              0x0000,
                                              0xffff);
                 backgroundPipelineBuilder.setStencil(kDrawOutsideElement);
+
                 // The color passed in here does not matter since the coverageSetOpXP won't read it.
                 fClipTarget->drawSimpleRect(&backgroundPipelineBuilder, GrColor_WHITE, translate,
                                             clipSpaceIBounds);
@@ -700,13 +701,13 @@
     SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);
     SkASSERT(rt);
 
-    GrStencilBuffer* stencilBuffer = rt->renderTargetPriv().attachStencilBuffer();
-    if (NULL == stencilBuffer) {
+    GrStencilAttachment* stencilAttachment = rt->renderTargetPriv().attachStencilAttachment();
+    if (NULL == stencilAttachment) {
         return false;
     }
 
-    if (stencilBuffer->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) {
-        stencilBuffer->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset);
+    if (stencilAttachment->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) {
+        stencilAttachment->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset);
         // Set the matrix so that rendered clip elements are transformed from clip to stencil space.
         SkVector translate = {
             SkIntToScalar(clipSpaceToStencilOffset.fX),
@@ -720,7 +721,7 @@
         stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
         GrClip clip(stencilSpaceIBounds);
 
-        int clipBit = stencilBuffer->bits();
+        int clipBit = stencilAttachment->bits();
         SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers");
         clipBit = (1 << (clipBit-1));
 
@@ -741,7 +742,7 @@
 
             // if the target is MSAA then we want MSAA enabled when the clip is soft
             if (rt->isMultisampled()) {
-                pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_StateBit, element->isAA());
+                pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, element->isAA());
             }
 
             bool fillInverted = false;
@@ -752,7 +753,7 @@
             // stencil with arbitrary stencil settings.
             GrPathRenderer::StencilSupport stencilSupport;
 
-            SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+            GrStrokeInfo stroke(SkStrokeRec::kFill_InitStyle);
             SkRegion::Op op = element->getOp();
 
             GrPathRenderer* pr = NULL;
@@ -806,13 +807,14 @@
                                              0xffff);
                 if (Element::kRect_Type == element->getType()) {
                     *pipelineBuilder.stencil() = gDrawToStencil;
+
+                    // We need this AGP until everything is in GrBatch
                     fClipTarget->drawSimpleRect(&pipelineBuilder,
                                                 GrColor_WHITE,
                                                 viewMatrix,
                                                 element->getRect());
                 } else {
                     if (!clipPath.isEmpty()) {
-                        GrDrawTarget::AutoGeometryPush agp(fClipTarget);
                         if (canRenderDirectToStencil) {
                             *pipelineBuilder.stencil() = gDrawToStencil;
                             pr->drawPath(fClipTarget, &pipelineBuilder, GrColor_WHITE,
@@ -834,12 +836,12 @@
 
                 if (canDrawDirectToClip) {
                     if (Element::kRect_Type == element->getType()) {
+                        // We need this AGP until everything is in GrBatch
                         fClipTarget->drawSimpleRect(&pipelineBuilderCopy,
                                                     GrColor_WHITE,
                                                     viewMatrix,
                                                     element->getRect());
                     } else {
-                        GrDrawTarget::AutoGeometryPush agp(fClipTarget);
                         pr->drawPath(fClipTarget, &pipelineBuilderCopy, GrColor_WHITE,
                                      viewMatrix, clipPath, stroke, false);
                     }
@@ -938,9 +940,9 @@
 
     int stencilBits = 0;
     GrRenderTarget* rt = pipelineBuilder->getRenderTarget();
-    GrStencilBuffer* stencilBuffer = rt->renderTargetPriv().attachStencilBuffer();
-    if (stencilBuffer) {
-        stencilBits = stencilBuffer->bits();
+    GrStencilAttachment* stencilAttachment = rt->renderTargetPriv().attachStencilAttachment();
+    if (stencilAttachment) {
+        stencilBits = stencilAttachment->bits();
     }
 
     SkASSERT(fClipTarget->caps()->stencilWrapOpsSupport() || !settings.usesWrapOp());
@@ -1113,10 +1115,10 @@
     fAACache.setContext(clipTarget->getContext());
 }
 
-void GrClipMaskManager::adjustPathStencilParams(const GrStencilBuffer* stencilBuffer,
+void GrClipMaskManager::adjustPathStencilParams(const GrStencilAttachment* stencilAttachment,
                                                 GrStencilSettings* settings) {
-    if (stencilBuffer) {
-        int stencilBits = stencilBuffer->bits();
+    if (stencilAttachment) {
+        int stencilBits = stencilAttachment->bits();
         this->adjustStencilParams(settings, fClipMode, stencilBits);
     }
 }
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index 935ffa1..33a77c1 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -74,7 +74,7 @@
 
     void setClipTarget(GrClipTarget*);
 
-    void adjustPathStencilParams(const GrStencilBuffer*, GrStencilSettings*);
+    void adjustPathStencilParams(const GrStencilAttachment*, GrStencilSettings*);
 
 private:
     /**
diff --git a/src/gpu/GrCommandBuilder.cpp b/src/gpu/GrCommandBuilder.cpp
new file mode 100644
index 0000000..106e781
--- /dev/null
+++ b/src/gpu/GrCommandBuilder.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrCommandBuilder.h"
+
+#include "GrInOrderCommandBuilder.h"
+#include "GrReorderCommandBuilder.h"
+
+GrCommandBuilder* GrCommandBuilder::Create(GrGpu* gpu, bool reorder) {
+    if (reorder) {
+        return SkNEW_ARGS(GrReorderCommandBuilder, (gpu));
+    } else {
+        return SkNEW_ARGS(GrInOrderCommandBuilder, (gpu));
+    }
+}
+
+GrTargetCommands::Cmd* GrCommandBuilder::recordClear(const SkIRect* rect,
+                                                     GrColor color,
+                                                     bool canIgnoreRect,
+                                                     GrRenderTarget* renderTarget) {
+    SkASSERT(renderTarget);
+
+    SkIRect r;
+    if (NULL == rect) {
+        // We could do something smart and remove previous draws and clears to
+        // the current render target. If we get that smart we have to make sure
+        // those draws aren't read before this clear (render-to-texture).
+        r.setLTRB(0, 0, renderTarget->width(), renderTarget->height());
+        rect = &r;
+    }
+    Clear* clr = GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(), Clear, (renderTarget));
+    GrColorIsPMAssert(color);
+    clr->fColor = color;
+    clr->fRect = *rect;
+    clr->fCanIgnoreRect = canIgnoreRect;
+    return clr;
+}
+
+GrTargetCommands::Cmd* GrCommandBuilder::recordClearStencilClip(const SkIRect& rect,
+                                                                bool insideClip,
+                                                                GrRenderTarget* renderTarget) {
+    SkASSERT(renderTarget);
+
+    ClearStencilClip* clr = GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(),
+                                                     ClearStencilClip,
+                                                     (renderTarget));
+    clr->fRect = rect;
+    clr->fInsideClip = insideClip;
+    return clr;
+}
+
+GrTargetCommands::Cmd* GrCommandBuilder::recordDiscard(GrRenderTarget* renderTarget) {
+    SkASSERT(renderTarget);
+
+    Clear* clr = GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(), Clear, (renderTarget));
+    clr->fColor = GrColor_ILLEGAL;
+    return clr;
+}
+
+GrTargetCommands::Cmd* GrCommandBuilder::recordCopySurface(GrSurface* dst,
+                                                           GrSurface* src,
+                                                           const SkIRect& srcRect,
+                                                           const SkIPoint& dstPoint) {
+    CopySurface* cs = GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(), CopySurface, (dst, src));
+    cs->fSrcRect = srcRect;
+    cs->fDstPoint = dstPoint;
+    return cs;
+}
+
+GrTargetCommands::Cmd*
+GrCommandBuilder::recordXferBarrierIfNecessary(const GrPipeline& pipeline,
+                                               const GrDrawTargetCaps& caps) {
+    const GrXferProcessor& xp = *pipeline.getXferProcessor();
+    GrRenderTarget* rt = pipeline.getRenderTarget();
+
+    GrXferBarrierType barrierType;
+    if (!xp.willNeedXferBarrier(rt, caps, &barrierType)) {
+        return NULL;
+    }
+
+    XferBarrier* xb = GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(), XferBarrier, (rt));
+    xb->fBarrierType = barrierType;
+    return xb;
+}
diff --git a/src/gpu/GrCommandBuilder.h b/src/gpu/GrCommandBuilder.h
new file mode 100644
index 0000000..90099ee
--- /dev/null
+++ b/src/gpu/GrCommandBuilder.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrCommandBuilder_DEFINED
+#define GrCommandBuilder_DEFINED
+
+#include "GrTargetCommands.h"
+
+class GrInOrderDrawBuffer;
+
+class GrCommandBuilder : ::SkNoncopyable {
+public:
+    typedef GrTargetCommands::Cmd Cmd;
+    typedef GrTargetCommands::State State;
+
+    static GrCommandBuilder* Create(GrGpu* gpu, bool reorder);
+
+    virtual ~GrCommandBuilder() {}
+
+    void reset() { fCommands.reset(); }
+    void flush(GrInOrderDrawBuffer* iodb) { fCommands.flush(iodb); }
+
+    virtual Cmd* recordClearStencilClip(const SkIRect& rect,
+                                        bool insideClip,
+                                        GrRenderTarget* renderTarget);
+    virtual Cmd* recordDiscard(GrRenderTarget*);
+    virtual Cmd* recordDrawBatch(State*, GrBatch*) = 0;
+    virtual Cmd* recordStencilPath(const GrPipelineBuilder&,
+                                   const GrPathProcessor*,
+                                   const GrPath*,
+                                   const GrScissorState&,
+                                   const GrStencilSettings&) = 0;
+    virtual Cmd* recordDrawPath(State*,
+                                const GrPathProcessor*,
+                                const GrPath*,
+                                const GrStencilSettings&) = 0;
+    virtual Cmd* recordDrawPaths(State*,
+                                 GrInOrderDrawBuffer*,
+                                 const GrPathProcessor*,
+                                 const GrPathRange*,
+                                 const void*,
+                                 GrDrawTarget::PathIndexType,
+                                 const float transformValues[],
+                                 GrDrawTarget::PathTransformType ,
+                                 int,
+                                 const GrStencilSettings&,
+                                 const GrDrawTarget::PipelineInfo&) = 0;
+    virtual Cmd* recordClear(const SkIRect* rect,
+                             GrColor,
+                             bool canIgnoreRect,
+                             GrRenderTarget*);
+    virtual Cmd* recordCopySurface(GrSurface* dst,
+                                   GrSurface* src,
+                                   const SkIRect& srcRect,
+                                   const SkIPoint& dstPoint);
+    virtual Cmd* recordXferBarrierIfNecessary(const GrPipeline&, const GrDrawTargetCaps&);
+
+protected:
+    typedef GrTargetCommands::DrawBatch DrawBatch;
+    typedef GrTargetCommands::StencilPath StencilPath;
+    typedef GrTargetCommands::DrawPath DrawPath;
+    typedef GrTargetCommands::DrawPaths DrawPaths;
+    typedef GrTargetCommands::Clear Clear;
+    typedef GrTargetCommands::ClearStencilClip ClearStencilClip;
+    typedef GrTargetCommands::CopySurface CopySurface;
+    typedef GrTargetCommands::XferBarrier XferBarrier;
+
+    GrCommandBuilder(GrGpu* gpu) : fCommands(gpu) {}
+
+    GrTargetCommands::CmdBuffer* cmdBuffer() { return fCommands.cmdBuffer(); }
+    GrBatchTarget* batchTarget() { return fCommands.batchTarget(); }
+
+private:
+    GrTargetCommands fCommands;
+
+};
+
+#endif
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 78f29e7..266ed57 100755
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -9,14 +9,14 @@
 #include "GrContext.h"
 
 #include "GrAARectRenderer.h"
+#include "GrAtlasTextContext.h"
 #include "GrBatch.h"
+#include "GrBatchFontCache.h"
 #include "GrBatchTarget.h"
-#include "GrBufferAllocPool.h"
+#include "GrBatchTest.h"
 #include "GrDefaultGeoProcFactory.h"
-#include "GrFontCache.h"
 #include "GrGpuResource.h"
 #include "GrGpuResourcePriv.h"
-#include "GrDistanceFieldTextContext.h"
 #include "GrDrawTargetCaps.h"
 #include "GrGpu.h"
 #include "GrIndexBuffer.h"
@@ -27,13 +27,16 @@
 #include "GrPathUtils.h"
 #include "GrRenderTargetPriv.h"
 #include "GrResourceCache.h"
+#include "GrResourceProvider.h"
 #include "GrSoftwarePathRenderer.h"
 #include "GrStencilAndCoverTextContext.h"
 #include "GrStrokeInfo.h"
 #include "GrSurfacePriv.h"
+#include "GrTextBlobCache.h"
 #include "GrTexturePriv.h"
 #include "GrTraceMarker.h"
 #include "GrTracing.h"
+#include "GrVertices.h"
 #include "SkDashPathPriv.h"
 #include "SkConfig8888.h"
 #include "SkGr.h"
@@ -47,12 +50,6 @@
 #include "effects/GrDashingEffect.h"
 #include "effects/GrSingleTextureEffect.h"
 
-static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 15;
-static const int DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS = 4;
-
-static const size_t DRAW_BUFFER_IBPOOL_BUFFER_SIZE = 1 << 11;
-static const int DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS = 4;
-
 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this)
 #define RETURN_IF_ABANDONED if (!fDrawBuffer) { return; }
 #define RETURN_FALSE_IF_ABANDONED if (!fDrawBuffer) { return false; }
@@ -89,15 +86,23 @@
     }
 }
 
-GrContext::GrContext(const Options& opts) : fOptions(opts) {
+static int32_t gNextID = 1;
+static int32_t next_id() {
+    int32_t id;
+    do {
+        id = sk_atomic_inc(&gNextID);
+    } while (id == SK_InvalidGenID);
+    return id;
+}
+
+GrContext::GrContext(const Options& opts) : fOptions(opts), fUniqueID(next_id()) {
     fGpu = NULL;
+    fResourceCache = NULL;
+    fResourceProvider = NULL;
     fPathRendererChain = NULL;
     fSoftwarePathRenderer = NULL;
-    fResourceCache = NULL;
-    fFontCache = NULL;
+    fBatchFontCache = NULL;
     fDrawBuffer = NULL;
-    fDrawBufferVBAllocPool = NULL;
-    fDrawBufferIBAllocPool = NULL;
     fFlushToReduceCacheSize = false;
     fAARectRenderer = NULL;
     fOvalRenderer = NULL;
@@ -118,17 +123,21 @@
 void GrContext::initCommon() {
     fResourceCache = SkNEW(GrResourceCache);
     fResourceCache->setOverBudgetCallback(OverBudgetCB, this);
-
-    fFontCache = SkNEW_ARGS(GrFontCache, (fGpu));
+    fResourceProvider = SkNEW_ARGS(GrResourceProvider, (fGpu, fResourceCache));
 
     fLayerCache.reset(SkNEW_ARGS(GrLayerCache, (this)));
 
-    fAARectRenderer = SkNEW_ARGS(GrAARectRenderer, (fGpu));
-    fOvalRenderer = SkNEW_ARGS(GrOvalRenderer, (fGpu));
+    fAARectRenderer = SkNEW(GrAARectRenderer);
+    fOvalRenderer = SkNEW(GrOvalRenderer);
 
     fDidTestPMConversions = false;
 
-    this->setupDrawBuffer();
+    fDrawBuffer = SkNEW_ARGS(GrInOrderDrawBuffer, (this));
+
+    // GrBatchFontCache will eventually replace GrFontCache
+    fBatchFontCache = SkNEW_ARGS(GrBatchFontCache, (this));
+
+    fTextBlobCache.reset(SkNEW_ARGS(GrTextBlobCache, (TextBlobCacheOverBudgetCB, this)));
 }
 
 GrContext::~GrContext() {
@@ -142,11 +151,10 @@
         (*fCleanUpData[i].fFunc)(this, fCleanUpData[i].fInfo);
     }
 
+    SkDELETE(fResourceProvider);
     SkDELETE(fResourceCache);
-    SkDELETE(fFontCache);
+    SkDELETE(fBatchFontCache);
     SkDELETE(fDrawBuffer);
-    SkDELETE(fDrawBufferVBAllocPool);
-    SkDELETE(fDrawBufferIBAllocPool);
 
     fAARectRenderer->unref();
     fOvalRenderer->unref();
@@ -157,6 +165,7 @@
 }
 
 void GrContext::abandonContext() {
+    fResourceProvider->abandon();
     // abandon first to so destructors
     // don't try to free the resources in the API.
     fResourceCache->abandonAll();
@@ -168,20 +177,12 @@
     SkSafeSetNull(fPathRendererChain);
     SkSafeSetNull(fSoftwarePathRenderer);
 
-    delete fDrawBuffer;
+    SkDELETE(fDrawBuffer);
     fDrawBuffer = NULL;
 
-    delete fDrawBufferVBAllocPool;
-    fDrawBufferVBAllocPool = NULL;
-
-    delete fDrawBufferIBAllocPool;
-    fDrawBufferIBAllocPool = NULL;
-
-    fAARectRenderer->reset();
-    fOvalRenderer->reset();
-
-    fFontCache->freeAll();
+    fBatchFontCache->freeAll();
     fLayerCache->freeAll();
+    fTextBlobCache->freeAll();
 }
 
 void GrContext::resetContext(uint32_t state) {
@@ -195,14 +196,13 @@
         fDrawBuffer->purgeResources();
     }
 
-    fAARectRenderer->reset();
-    fOvalRenderer->reset();
-
-    fFontCache->freeAll();
+    fBatchFontCache->freeAll();
     fLayerCache->freeAll();
     // a path renderer may be holding onto resources
     SkSafeSetNull(fPathRendererChain);
     SkSafeSetNull(fSoftwarePathRenderer);
+
+    fResourceCache->purgeAllUnlocked();
 }
 
 void GrContext::getResourceCacheUsage(int* resourceCount, size_t* resourceBytes) const {
@@ -215,25 +215,21 @@
 }
 
 GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget,
+                                            SkGpuDevice* gpuDevice,
                                             const SkDeviceProperties&
                                             leakyProperties,
                                             bool enableDistanceFieldFonts) {
-    if (fGpu->caps()->pathRenderingSupport() && renderTarget->isMultisampled()) {
-        GrStencilBuffer* sb = renderTarget->renderTargetPriv().attachStencilBuffer();
+    if (fGpu->caps()->shaderCaps()->pathRenderingSupport() && renderTarget->isMultisampled()) {
+        GrStencilAttachment* sb = renderTarget->renderTargetPriv().attachStencilAttachment();
         if (sb) {
-            return GrStencilAndCoverTextContext::Create(this, leakyProperties);
+            return GrStencilAndCoverTextContext::Create(this, gpuDevice, leakyProperties);
         }
     } 
 
-    return GrDistanceFieldTextContext::Create(this, leakyProperties, enableDistanceFieldFonts);
+    return GrAtlasTextContext::Create(this, gpuDevice, leakyProperties, enableDistanceFieldFonts);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-enum ScratchTextureFlags {
-    kExact_ScratchTextureFlag           = 0x1,
-    kNoPendingIO_ScratchTextureFlag     = 0x2,
-    kNoCreate_ScratchTextureFlag        = 0x4,
-};
 
 bool GrContext::isConfigTexturable(GrPixelConfig config) const {
     return fGpu->caps()->isConfigTexturable(config);
@@ -243,90 +239,6 @@
     return fGpu->caps()->npotTextureTileSupport();
 }
 
-GrTexture* GrContext::createTexture(const GrSurfaceDesc& desc, bool budgeted, const void* srcData,
-                                    size_t rowBytes) {
-    RETURN_NULL_IF_ABANDONED
-    if ((desc.fFlags & kRenderTarget_GrSurfaceFlag) &&
-        !this->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
-        return NULL;
-    }
-    if (!GrPixelConfigIsCompressed(desc.fConfig)) {
-        static const uint32_t kFlags = kExact_ScratchTextureFlag |
-                                       kNoCreate_ScratchTextureFlag;
-        if (GrTexture* texture = this->internalRefScratchTexture(desc, kFlags)) {
-            if (!srcData || texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                                                 srcData, rowBytes)) {
-                if (!budgeted) {
-                    texture->resourcePriv().makeUnbudgeted();
-                }
-                return texture;
-            }
-            texture->unref();
-        }
-    }
-    return fGpu->createTexture(desc, budgeted, srcData, rowBytes);
-}
-
-GrTexture* GrContext::refScratchTexture(const GrSurfaceDesc& desc, ScratchTexMatch match,
-                                        bool calledDuringFlush) {
-    RETURN_NULL_IF_ABANDONED
-    // Currently we don't recycle compressed textures as scratch.
-    if (GrPixelConfigIsCompressed(desc.fConfig)) {
-        return NULL;
-    } else {
-        uint32_t flags = 0;
-        if (kExact_ScratchTexMatch == match) {
-            flags |= kExact_ScratchTextureFlag;
-        }
-        if (calledDuringFlush) {
-            flags |= kNoPendingIO_ScratchTextureFlag;
-        }
-        return this->internalRefScratchTexture(desc, flags);
-    }
-}
-
-GrTexture* GrContext::internalRefScratchTexture(const GrSurfaceDesc& inDesc, uint32_t flags) {
-    SkASSERT(!GrPixelConfigIsCompressed(inDesc.fConfig));
-
-    SkTCopyOnFirstWrite<GrSurfaceDesc> desc(inDesc);
-
-    if (fGpu->caps()->reuseScratchTextures() || (desc->fFlags & kRenderTarget_GrSurfaceFlag)) {
-        if (!(kExact_ScratchTextureFlag & flags)) {
-            // bin by pow2 with a reasonable min
-            static const int MIN_SIZE = 16;
-            GrSurfaceDesc* wdesc = desc.writable();
-            wdesc->fWidth  = SkTMax(MIN_SIZE, GrNextPow2(desc->fWidth));
-            wdesc->fHeight = SkTMax(MIN_SIZE, GrNextPow2(desc->fHeight));
-        }
-
-        GrScratchKey key;
-        GrTexturePriv::ComputeScratchKey(*desc, &key);
-        uint32_t scratchFlags = 0;
-        if (kNoPendingIO_ScratchTextureFlag & flags) {
-            scratchFlags = GrResourceCache::kRequireNoPendingIO_ScratchFlag;
-        } else  if (!(desc->fFlags & kRenderTarget_GrSurfaceFlag)) {
-            // If it is not a render target then it will most likely be populated by
-            // writePixels() which will trigger a flush if the texture has pending IO.
-            scratchFlags = GrResourceCache::kPreferNoPendingIO_ScratchFlag;
-        }
-        GrGpuResource* resource = fResourceCache->findAndRefScratchResource(key, scratchFlags);
-        if (resource) {
-            GrSurface* surface = static_cast<GrSurface*>(resource);
-            GrRenderTarget* rt = surface->asRenderTarget();
-            if (rt && fGpu->caps()->discardRenderTargetSupport()) {
-                rt->discard();
-            }
-            return surface->asTexture();
-        }
-    }
-
-    if (!(kNoCreate_ScratchTextureFlag & flags)) {
-        return fGpu->createTexture(*desc, true, NULL, 0);
-    }
-
-    return NULL;
-}
-
 void GrContext::OverBudgetCB(void* data) {
     SkASSERT(data);
 
@@ -336,6 +248,17 @@
     context->fFlushToReduceCacheSize = true;
 }
 
+void GrContext::TextBlobCacheOverBudgetCB(void* data) {
+    SkASSERT(data);
+
+    // Unlike the GrResourceCache, TextBlobs are drawn at the SkGpuDevice level, therefore they
+    // cannot use fFlushTorReduceCacheSize because it uses AutoCheckFlush.  The solution is to move
+    // drawText calls to below the GrContext level, but this is not trivial because they call
+    // drawPath on SkGpuDevice
+    GrContext* context = reinterpret_cast<GrContext*>(data);
+    context->flush();
+}
+
 int GrContext::getMaxTextureSize() const {
     return SkTMin(fGpu->caps()->maxTextureSize(), fMaxTextureSizeOverride);
 }
@@ -350,18 +273,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrTexture* GrContext::wrapBackendTexture(const GrBackendTextureDesc& desc) {
-    RETURN_NULL_IF_ABANDONED
-    return fGpu->wrapBackendTexture(desc);
-}
-
-GrRenderTarget* GrContext::wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc) {
-    RETURN_NULL_IF_ABANDONED
-    return fGpu->wrapBackendRenderTarget(desc);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 void GrContext::clear(const SkIRect* rect,
                       const GrColor color,
                       bool canIgnoreRect,
@@ -434,12 +345,6 @@
     }
 }
 
-#ifdef SK_DEVELOPER
-void GrContext::dumpFontCache() const {
-    fFontCache->dump();
-}
-#endif
-
 ////////////////////////////////////////////////////////////////////////////////
 
 static inline bool is_irect(const SkRect& r) {
@@ -499,22 +404,22 @@
         SkScalar fStrokeWidth;
     };
 
-    static GrBatch* Create(const Geometry& geometry) {
-        return SkNEW_ARGS(StrokeRectBatch, (geometry));
+    static GrBatch* Create(const Geometry& geometry, bool snapToPixelCenters) {
+        return SkNEW_ARGS(StrokeRectBatch, (geometry, snapToPixelCenters));
     }
 
-    const char* name() const SK_OVERRIDE { return "StrokeRectBatch"; }
+    const char* name() const override { return "StrokeRectBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
 
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setKnownSingleComponent(0xff);
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -529,7 +434,7 @@
         fBatch.fCoverageIgnored = init.fCoverageIgnored;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         SkAutoTUnref<const GrGeometryProcessor> gp(
                 GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
                                                 this->color(),
@@ -562,17 +467,15 @@
         const GrVertexBuffer* vertexBuffer;
         int firstVertex;
 
-        void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              vertexCount,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
+        void* verts = batchTarget->makeVertSpace(vertexStride, vertexCount,
+                                                 &vertexBuffer, &firstVertex);
 
-        if (!vertices) {
+        if (!verts) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
-        SkPoint* vertex = reinterpret_cast<SkPoint*>(vertices);
+        SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
 
         GrPrimitiveType primType;
 
@@ -590,28 +493,31 @@
             vertex[4].set(args.fRect.fLeft, args.fRect.fTop);
         }
 
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(primType);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setStartVertex(firstVertex);
-        drawInfo.setVertexCount(vertexCount);
-        drawInfo.setStartIndex(0);
-        drawInfo.setIndexCount(0);
-        drawInfo.setInstanceCount(0);
-        drawInfo.setVerticesPerInstance(0);
-        drawInfo.setIndicesPerInstance(0);
-        batchTarget->draw(drawInfo);
+        GrVertices vertices;
+        vertices.init(primType, vertexBuffer, firstVertex, vertexCount);
+        batchTarget->draw(vertices);
     }
 
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
 
 private:
-    StrokeRectBatch(const Geometry& geometry) {
+    StrokeRectBatch(const Geometry& geometry, bool snapToPixelCenters) {
         this->initClassID<StrokeRectBatch>();
 
         fBatch.fHairline = geometry.fStrokeWidth == 0;
 
         fGeoData.push_back(geometry);
+
+        // setup bounds
+        fBounds = geometry.fRect;
+        SkScalar rad = SkScalarHalf(geometry.fStrokeWidth);
+        fBounds.outset(rad, rad);
+        geometry.fViewMatrix.mapRect(&fBounds);
+
+        // If our caller snaps to pixel centers then we have to round out the bounds
+        if (snapToPixelCenters) {
+            fBounds.roundOut();
+        }
     }
 
     /*  create a triangle strip that strokes the specified rect. There are 8
@@ -644,7 +550,7 @@
     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
     bool hairline() const { return fBatch.fHairline; }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         // StrokeRectBatch* that = t->cast<StrokeRectBatch>();
 
         // NonAA stroke rects cannot batch right now
@@ -760,13 +666,16 @@
         geometry.fRect = rect;
         geometry.fStrokeWidth = width;
 
-        SkAutoTUnref<GrBatch> batch(StrokeRectBatch::Create(geometry));
+        // Non-AA hairlines are snapped to pixel centers to make which pixels are hit deterministic
+        bool snapToPixelCenters = (0 == width && !rt->isMultisampled());
+        SkAutoTUnref<GrBatch> batch(StrokeRectBatch::Create(geometry, snapToPixelCenters));
 
-        SkRect bounds = rect;
-        SkScalar rad = SkScalarHalf(width);
-        bounds.outset(rad, rad);
-        viewMatrix.mapRect(&bounds);
-        target->drawBatch(&pipelineBuilder, batch, &bounds);
+        // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
+        // hairline rects. We jam all the vertices to pixel centers to avoid this, but not when MSAA
+        // is enabled because it can cause ugly artifacts.
+        pipelineBuilder.setState(GrPipelineBuilder::kSnapVerticesToPixelCenters_Flag,
+                                 snapToPixelCenters);
+        target->drawBatch(&pipelineBuilder, batch);
     } else {
         // filled BW rect
         target->drawSimpleRect(&pipelineBuilder, color, viewMatrix, rect);
@@ -836,15 +745,16 @@
                            const SkMatrix& viewMatrix,
                            const SkPoint* positions, int vertexCount,
                            const uint16_t* indices, int indexCount,
-                           const GrColor* colors, const SkPoint* localCoords) {
+                           const GrColor* colors, const SkPoint* localCoords,
+                           const SkRect& bounds) {
         return SkNEW_ARGS(DrawVerticesBatch, (geometry, primitiveType, viewMatrix, positions,
                                               vertexCount, indices, indexCount, colors,
-                                              localCoords));
+                                              localCoords, bounds));
     }
 
-    const char* name() const SK_OVERRIDE { return "DrawVerticesBatch"; }
+    const char* name() const override { return "DrawVerticesBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         if (this->hasColors()) {
             out->setUnknownFourComponents();
@@ -853,11 +763,11 @@
         }
     }
 
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setKnownSingleComponent(0xff);
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -872,7 +782,7 @@
         fBatch.fCoverageIgnored = init.fCoverageIgnored;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         int colorOffset = -1, texOffset = -1;
         SkAutoTUnref<const GrGeometryProcessor> gp(
                 set_vertex_attributes(this->hasLocalCoords(), this->hasColors(), &colorOffset,
@@ -900,24 +810,20 @@
         const GrVertexBuffer* vertexBuffer;
         int firstVertex;
 
-        void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              this->vertexCount(),
-                                                              &vertexBuffer,
-                                                              &firstVertex);
+        void* verts = batchTarget->makeVertSpace(vertexStride, this->vertexCount(),
+                                                 &vertexBuffer, &firstVertex);
 
-        if (!vertices) {
+        if (!verts) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
-        const GrIndexBuffer* indexBuffer;
-        int firstIndex;
+        const GrIndexBuffer* indexBuffer = NULL;
+        int firstIndex = 0;
 
-        void* indices = NULL;
+        uint16_t* indices = NULL;
         if (this->hasIndices()) {
-            indices = batchTarget->indexPool()->makeSpace(this->indexCount(),
-                                                          &indexBuffer,
-                                                          &firstIndex);
+            indices = batchTarget->makeIndexSpace(this->indexCount(), &indexBuffer, &firstIndex);
 
             if (!indices) {
                 SkDebugf("Could not allocate indices\n");
@@ -933,37 +839,32 @@
             // TODO we can actually cache this interleaved and then just memcopy
             if (this->hasIndices()) {
                 for (int j = 0; j < args.fIndices.count(); ++j, ++indexOffset) {
-                    *((uint16_t*)indices + indexOffset) = args.fIndices[j] + vertexOffset;
+                    *(indices + indexOffset) = args.fIndices[j] + vertexOffset;
                 }
             }
 
             for (int j = 0; j < args.fPositions.count(); ++j) {
-                *((SkPoint*)vertices) = args.fPositions[j];
+                *((SkPoint*)verts) = args.fPositions[j];
                 if (this->hasColors()) {
-                    *(GrColor*)((intptr_t)vertices + colorOffset) = args.fColors[j];
+                    *(GrColor*)((intptr_t)verts + colorOffset) = args.fColors[j];
                 }
                 if (this->hasLocalCoords()) {
-                    *(SkPoint*)((intptr_t)vertices + texOffset) = args.fLocalCoords[j];
+                    *(SkPoint*)((intptr_t)verts + texOffset) = args.fLocalCoords[j];
                 }
-                vertices = (void*)((intptr_t)vertices + vertexStride);
+                verts = (void*)((intptr_t)verts + vertexStride);
                 vertexOffset++;
             }
         }
 
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(this->primitiveType());
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setStartVertex(firstVertex);
-        drawInfo.setVertexCount(this->vertexCount());
+        GrVertices vertices;
         if (this->hasIndices()) {
-            drawInfo.setIndexBuffer(indexBuffer);
-            drawInfo.setStartIndex(firstIndex);
-            drawInfo.setIndexCount(this->indexCount());
+            vertices.initIndexed(this->primitiveType(), vertexBuffer, indexBuffer, firstVertex,
+                                 firstIndex, this->vertexCount(), this->indexCount());
+
         } else {
-            drawInfo.setStartIndex(0);
-            drawInfo.setIndexCount(0);
+            vertices.init(this->primitiveType(), vertexBuffer, firstVertex, this->vertexCount());
         }
-        batchTarget->draw(drawInfo);
+        batchTarget->draw(vertices);
     }
 
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
@@ -973,7 +874,7 @@
                       const SkMatrix& viewMatrix,
                       const SkPoint* positions, int vertexCount,
                       const uint16_t* indices, int indexCount,
-                      const GrColor* colors, const SkPoint* localCoords) {
+                      const GrColor* colors, const SkPoint* localCoords, const SkRect& bounds) {
         this->initClassID<DrawVerticesBatch>();
         SkASSERT(positions);
 
@@ -1004,6 +905,8 @@
         fBatch.fVertexCount = vertexCount;
         fBatch.fIndexCount = indexCount;
         fBatch.fPrimitiveType = primitiveType;
+
+        this->setBounds(bounds);
     }
 
     GrPrimitiveType primitiveType() const { return fBatch.fPrimitiveType; }
@@ -1022,7 +925,7 @@
     int vertexCount() const { return fBatch.fVertexCount; }
     int indexCount() const { return fBatch.fIndexCount; }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         DrawVerticesBatch* that = t->cast<DrawVerticesBatch>();
 
         if (!this->batchablePrimitiveType() || this->primitiveType() != that->primitiveType()) {
@@ -1058,6 +961,8 @@
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
         fBatch.fVertexCount += that->vertexCount();
         fBatch.fIndexCount += that->indexCount();
+
+        this->joinBounds(that->bounds());
         return true;
     }
 
@@ -1093,7 +998,6 @@
     RETURN_IF_ABANDONED
     AutoCheckFlush acf(this);
     GrPipelineBuilder pipelineBuilder;
-    GrDrawTarget::AutoReleaseGeometry geo; // must be inside AutoCheckFlush scope
 
     GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf);
     if (NULL == target) {
@@ -1102,15 +1006,29 @@
 
     GR_CREATE_TRACE_MARKER("GrContext::drawVertices", target);
 
+    // TODO clients should give us bounds
+    SkRect bounds;
+    if (!bounds.setBoundsCheck(positions, vertexCount)) {
+        SkDebugf("drawVertices call empty bounds\n");
+        return;
+    }
+
+    viewMatrix.mapRect(&bounds);
+
+    // If we don't have AA then we outset for a half pixel in each direction to account for
+    // snapping
+    if (!paint.isAntiAlias()) {
+        bounds.outset(0.5f, 0.5f);
+    }
+
     DrawVerticesBatch::Geometry geometry;
     geometry.fColor = paint.getColor();
-
     SkAutoTUnref<GrBatch> batch(DrawVerticesBatch::Create(geometry, primitiveType, viewMatrix,
                                                           positions, vertexCount, indices,
-                                                          indexCount,colors, texCoords));
+                                                          indexCount, colors, texCoords,
+                                                          bounds));
 
-    // TODO figure out bounds
-    target->drawBatch(&pipelineBuilder, batch, NULL);
+    target->drawBatch(&pipelineBuilder, batch);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1264,7 +1182,7 @@
     }
 
     SkPath::Direction dirs[2];
-    if (!path.isNestedRects(rects, dirs)) {
+    if (!path.isNestedFillRects(rects, dirs)) {
         return false;
     }
 
@@ -1311,35 +1229,6 @@
     }
 
     GrColor color = paint.getColor();
-    if (strokeInfo.isDashed()) {
-        SkPoint pts[2];
-        if (path.isLine(pts)) {
-            AutoCheckFlush acf(this);
-            GrPipelineBuilder pipelineBuilder;
-            GrDrawTarget* target = this->prepareToDraw(&pipelineBuilder, rt, clip, &paint, &acf);
-            if (NULL == target) {
-                return;
-            }
-
-            if (GrDashingEffect::DrawDashLine(fGpu, target, &pipelineBuilder, color, viewMatrix,
-                                              pts, paint, strokeInfo)) {
-                return;
-            }
-        }
-
-        // Filter dashed path into new path with the dashing applied
-        const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
-        SkTLazy<SkPath> effectPath;
-        GrStrokeInfo newStrokeInfo(strokeInfo, false);
-        SkStrokeRec* stroke = newStrokeInfo.getStrokeRecPtr();
-        if (SkDashPath::FilterDashPath(effectPath.init(), path, stroke, NULL, info)) {
-            this->drawPath(rt, clip, paint, viewMatrix, *effectPath.get(), newStrokeInfo);
-            return;
-        }
-
-        this->drawPath(rt, clip, paint, viewMatrix, path, newStrokeInfo);
-        return;
-    }
 
     // Note that internalDrawPath may sw-rasterize the path into a scratch texture.
     // Scratch textures can be recycled after they are returned to the texture
@@ -1355,35 +1244,39 @@
 
     GR_CREATE_TRACE_MARKER1("GrContext::drawPath", target, "Is Convex", path.isConvex());
 
-    const SkStrokeRec& strokeRec = strokeInfo.getStrokeRec();
+    if (!strokeInfo.isDashed()) {
+        const SkStrokeRec& strokeRec = strokeInfo.getStrokeRec();
+        bool useCoverageAA = paint.isAntiAlias() &&
+                !pipelineBuilder.getRenderTarget()->isMultisampled();
 
-    bool useCoverageAA = paint.isAntiAlias() &&
-        !pipelineBuilder.getRenderTarget()->isMultisampled();
+        if (useCoverageAA && strokeRec.getWidth() < 0 && !path.isConvex()) {
+            // Concave AA paths are expensive - try to avoid them for special cases
+            SkRect rects[2];
 
-    if (useCoverageAA && strokeRec.getWidth() < 0 && !path.isConvex()) {
-        // Concave AA paths are expensive - try to avoid them for special cases
-        SkRect rects[2];
+            if (is_nested_rects(target, &pipelineBuilder, color, viewMatrix, path, strokeRec,
+                                rects)) {
+                fAARectRenderer->fillAANestedRects(target, &pipelineBuilder, color, viewMatrix,
+                                                   rects);
+                return;
+            }
+        }
+        SkRect ovalRect;
+        bool isOval = path.isOval(&ovalRect);
 
-        if (is_nested_rects(target, &pipelineBuilder, color, viewMatrix, path, strokeRec, rects)) {
-            fAARectRenderer->fillAANestedRects(target, &pipelineBuilder, color, viewMatrix, rects);
-            return;
+        if (isOval && !path.isInverseFillType()) {
+            if (fOvalRenderer->drawOval(target,
+                                        &pipelineBuilder,
+                                        color,
+                                        viewMatrix,
+                                        paint.isAntiAlias(),
+                                        ovalRect,
+                                        strokeRec)) {
+                return;
+            }
         }
     }
-
-    SkRect ovalRect;
-    bool isOval = path.isOval(&ovalRect);
-
-    if (!isOval || path.isInverseFillType() ||
-        !fOvalRenderer->drawOval(target,
-                                 &pipelineBuilder,
-                                 color,
-                                 viewMatrix,
-                                 paint.isAntiAlias(),
-                                 ovalRect,
-                                 strokeRec)) {
-        this->internalDrawPath(target, &pipelineBuilder, viewMatrix, color, paint.isAntiAlias(),
-                               path, strokeInfo);
-    }
+    this->internalDrawPath(target, &pipelineBuilder, viewMatrix, color, paint.isAntiAlias(),
+                           path, strokeInfo);
 }
 
 void GrContext::internalDrawPath(GrDrawTarget* target,
@@ -1413,27 +1306,50 @@
 
     const SkPath* pathPtr = &path;
     SkTLazy<SkPath> tmpPath;
-    SkTCopyOnFirstWrite<SkStrokeRec> stroke(strokeInfo.getStrokeRec());
+    const GrStrokeInfo* strokeInfoPtr = &strokeInfo;
 
     // Try a 1st time without stroking the path and without allowing the SW renderer
     GrPathRenderer* pr = this->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr,
-                                               *stroke, false, type);
+                                               *strokeInfoPtr, false, type);
+
+    GrStrokeInfo dashlessStrokeInfo(strokeInfo, false);
+    if (NULL == pr && strokeInfo.isDashed()) {
+        // It didn't work above, so try again with dashed stroke converted to a dashless stroke.
+        if (!strokeInfo.applyDash(tmpPath.init(), &dashlessStrokeInfo, *pathPtr)) {
+            return;
+        }
+        pathPtr = tmpPath.get();
+        if (pathPtr->isEmpty()) {
+            return;
+        }
+        strokeInfoPtr = &dashlessStrokeInfo;
+        pr = this->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr, *strokeInfoPtr,
+                                   false, type);
+    }
 
     if (NULL == pr) {
-        if (!GrPathRenderer::IsStrokeHairlineOrEquivalent(*stroke, viewMatrix, NULL)) {
-            // It didn't work the 1st time, so try again with the stroked path
-            if (stroke->applyToPath(tmpPath.init(), *pathPtr)) {
-                pathPtr = tmpPath.get();
-                stroke.writable()->setFillStyle();
-                if (pathPtr->isEmpty()) {
-                    return;
-                }
+        if (!GrPathRenderer::IsStrokeHairlineOrEquivalent(*strokeInfoPtr, viewMatrix, NULL) &&
+            !strokeInfoPtr->isFillStyle()) {
+            // It didn't work above, so try again with stroke converted to a fill.
+            if (!tmpPath.isValid()) {
+                tmpPath.init();
             }
+            SkStrokeRec* strokeRec = dashlessStrokeInfo.getStrokeRecPtr();
+            strokeRec->setResScale(SkScalarAbs(viewMatrix.getMaxScale()));
+            if (!strokeRec->applyToPath(tmpPath.get(), *pathPtr)) {
+                return;
+            }
+            pathPtr = tmpPath.get();
+            if (pathPtr->isEmpty()) {
+                return;
+            }
+            strokeRec->setFillStyle();
+            strokeInfoPtr = &dashlessStrokeInfo;
         }
 
         // This time, allow SW renderer
-        pr = this->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr, *stroke, true,
-                                   type);
+        pr = this->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr, *strokeInfoPtr,
+                                   true, type);
     }
 
     if (NULL == pr) {
@@ -1443,7 +1359,7 @@
         return;
     }
 
-    pr->drawPath(target, pipelineBuilder, color, viewMatrix, *pathPtr, *stroke, useCoverageAA);
+    pr->drawPath(target, pipelineBuilder, color, viewMatrix, *pathPtr, *strokeInfoPtr, useCoverageAA);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1458,6 +1374,7 @@
     } else {
         fDrawBuffer->flush();
     }
+    fResourceCache->notifyFlushOccurred();
     fFlushToReduceCacheSize = false;
 }
 
@@ -1523,7 +1440,8 @@
     desc.fWidth = width;
     desc.fHeight = height;
     desc.fConfig = writeConfig;
-    SkAutoTUnref<GrTexture> texture(this->refScratchTexture(desc, kApprox_ScratchTexMatch));
+    SkAutoTUnref<GrTexture> texture(this->textureProvider()->refScratchTexture(desc,
+        GrTextureProvider::kApprox_ScratchTexMatch));
     if (!texture) {
         return false;
     }
@@ -1582,7 +1500,6 @@
         if (!drawTarget) {
             return false;
         }
-        GrDrawTarget::AutoGeometryPush agp(drawTarget);
 
         GrPipelineBuilder pipelineBuilder;
         pipelineBuilder.addColorProcessor(fp);
@@ -1669,15 +1586,15 @@
         // match the passed rect. However, if we see many different size rectangles we will trash
         // our texture cache and pay the cost of creating and destroying many textures. So, we only
         // request an exact match when the caller is reading an entire RT.
-        ScratchTexMatch match = kApprox_ScratchTexMatch;
+        GrTextureProvider::ScratchTexMatch match = GrTextureProvider::kApprox_ScratchTexMatch;
         if (0 == left &&
             0 == top &&
             target->width() == width &&
             target->height() == height &&
             fGpu->fullReadPixelsIsFasterThanPartial()) {
-            match = kExact_ScratchTexMatch;
+            match = GrTextureProvider::kExact_ScratchTexMatch;
         }
-        tempTexture.reset(this->refScratchTexture(desc, match));
+        tempTexture.reset(this->textureProvider()->refScratchTexture(desc, match));
         if (tempTexture) {
             // compute a matrix to perform the draw
             SkMatrix textureMatrix;
@@ -1705,7 +1622,6 @@
                 // clear to the caller that a draw operation (i.e., drawSimpleRect)
                 // can be invoked in this method
                 {
-                    GrDrawTarget::AutoGeometryPush agp(fDrawBuffer);
                     GrPipelineBuilder pipelineBuilder;
                     SkASSERT(fp);
                     pipelineBuilder.addColorProcessor(fp);
@@ -1839,7 +1755,7 @@
                                            const GrPipelineBuilder* pipelineBuilder,
                                            const SkMatrix& viewMatrix,
                                            const SkPath& path,
-                                           const SkStrokeRec& stroke,
+                                           const GrStrokeInfo& stroke,
                                            bool allowSW,
                                            GrPathRendererChain::DrawType drawType,
                                            GrPathRendererChain::StencilSupport* stencilSupport) {
@@ -1877,7 +1793,7 @@
         return 0;
     }
     int chosenSampleCount = 0;
-    if (fGpu->caps()->pathRenderingSupport()) {
+    if (fGpu->caps()->shaderCaps()->pathRenderingSupport()) {
         if (dpi >= 250.0f) {
             chosenSampleCount = 4;
         } else {
@@ -1888,33 +1804,10 @@
         chosenSampleCount : 0;
 }
 
-void GrContext::setupDrawBuffer() {
-    SkASSERT(NULL == fDrawBuffer);
-    SkASSERT(NULL == fDrawBufferVBAllocPool);
-    SkASSERT(NULL == fDrawBufferIBAllocPool);
-
-    fDrawBufferVBAllocPool =
-        SkNEW_ARGS(GrVertexBufferAllocPool, (fGpu, false,
-                                    DRAW_BUFFER_VBPOOL_BUFFER_SIZE,
-                                    DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS));
-    fDrawBufferIBAllocPool =
-        SkNEW_ARGS(GrIndexBufferAllocPool, (fGpu, false,
-                                   DRAW_BUFFER_IBPOOL_BUFFER_SIZE,
-                                   DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS));
-
-    fDrawBuffer = SkNEW_ARGS(GrInOrderDrawBuffer, (fGpu,
-                                                   fDrawBufferVBAllocPool,
-                                                   fDrawBufferIBAllocPool));
-}
-
 GrDrawTarget* GrContext::getTextTarget() {
     return this->prepareToDraw();
 }
 
-const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
-    return fGpu->getQuadIndexBuffer();
-}
-
 namespace {
 void test_pm_conversions(GrContext* ctx, int* pmToUPMValue, int* upmToPMValue) {
     GrConfigConversionEffect::PMConversion pmToUPM;
@@ -1972,22 +1865,6 @@
     fResourceCache->setLimits(maxTextures, maxTextureBytes);
 }
 
-void GrContext::addResourceToCache(const GrUniqueKey& key, GrGpuResource* resource) {
-    ASSERT_OWNED_RESOURCE(resource);
-    if (!resource) {
-        return;
-    }
-    resource->resourcePriv().setUniqueKey(key);
-}
-
-bool GrContext::isResourceInCache(const GrUniqueKey& key) const {
-    return fResourceCache->hasUniqueKey(key);
-}
-
-GrGpuResource* GrContext::findAndRefCachedResource(const GrUniqueKey& key) {
-    return fResourceCache->findAndRefUniqueResource(key);
-}
-
 //////////////////////////////////////////////////////////////////////////////
 
 void GrContext::addGpuTraceMarker(const GrGpuTraceMarker* marker) {
@@ -2004,3 +1881,129 @@
     }
 }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef GR_TEST_UTILS
+
+BATCH_TEST_DEFINE(StrokeRectBatch) {
+    StrokeRectBatch::Geometry geometry;
+    geometry.fViewMatrix = GrTest::TestMatrix(random);
+    geometry.fColor = GrRandomColor(random);
+    geometry.fRect = GrTest::TestRect(random);
+    geometry.fStrokeWidth = random->nextBool() ? 0.0f : 1.0f;
+
+    return StrokeRectBatch::Create(geometry, random->nextBool());
+}
+
+static uint32_t seed_vertices(GrPrimitiveType type) {
+    switch (type) {
+        case kTriangles_GrPrimitiveType:
+        case kTriangleStrip_GrPrimitiveType:
+        case kTriangleFan_GrPrimitiveType:
+            return 3;
+        case kPoints_GrPrimitiveType:
+            return 1;
+        case kLines_GrPrimitiveType:
+        case kLineStrip_GrPrimitiveType:
+            return 2;
+    }
+    SkFAIL("Incomplete switch\n");
+    return 0;
+}
+
+static uint32_t primitive_vertices(GrPrimitiveType type) {
+    switch (type) {
+        case kTriangles_GrPrimitiveType:
+            return 3;
+        case kLines_GrPrimitiveType:
+            return 2;
+        case kTriangleStrip_GrPrimitiveType:
+        case kTriangleFan_GrPrimitiveType:
+        case kPoints_GrPrimitiveType:
+        case kLineStrip_GrPrimitiveType:
+            return 1;
+    }
+    SkFAIL("Incomplete switch\n");
+    return 0;
+}
+
+static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) {
+    SkPoint p;
+    p.fX = random->nextRangeScalar(min, max);
+    p.fY = random->nextRangeScalar(min, max);
+    return p;
+}
+
+static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max,
+                             SkRandom* random,
+                             SkTArray<SkPoint>* positions,
+                             SkTArray<SkPoint>* texCoords, bool hasTexCoords,
+                             SkTArray<GrColor>* colors, bool hasColors,
+                             SkTArray<uint16_t>* indices, bool hasIndices) {
+    for (uint32_t v = 0; v < count; v++) {
+        positions->push_back(random_point(random, min, max));
+        if (hasTexCoords) {
+            texCoords->push_back(random_point(random, min, max));
+        }
+        if (hasColors) {
+            colors->push_back(GrRandomColor(random));
+        }
+        if (hasIndices) {
+            SkASSERT(maxVertex <= SK_MaxU16);
+            indices->push_back(random->nextULessThan((uint16_t)maxVertex));
+        }
+    }
+}
+
+BATCH_TEST_DEFINE(VerticesBatch) {
+    GrPrimitiveType type = GrPrimitiveType(random->nextULessThan(kLast_GrPrimitiveType + 1));
+    uint32_t primitiveCount = random->nextRangeU(1, 100);
+
+    // TODO make 'sensible' indexbuffers
+    SkTArray<SkPoint> positions;
+    SkTArray<SkPoint> texCoords;
+    SkTArray<GrColor> colors;
+    SkTArray<uint16_t> indices;
+
+    bool hasTexCoords = random->nextBool();
+    bool hasIndices = random->nextBool();
+    bool hasColors = random->nextBool();
+
+    uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type);
+
+    static const SkScalar kMinVertExtent = -100.f;
+    static const SkScalar kMaxVertExtent = 100.f;
+    randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
+                     random,
+                     &positions,
+                     &texCoords, hasTexCoords,
+                     &colors, hasColors,
+                     &indices, hasIndices);
+
+    for (uint32_t i = 1; i < primitiveCount; i++) {
+        randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
+                         random,
+                         &positions,
+                         &texCoords, hasTexCoords,
+                         &colors, hasColors,
+                         &indices, hasIndices);
+    }
+
+    SkMatrix viewMatrix = GrTest::TestMatrix(random);
+    SkRect bounds;
+    SkDEBUGCODE(bool result = ) bounds.setBoundsCheck(positions.begin(), vertexCount);
+    SkASSERT(result);
+
+    viewMatrix.mapRect(&bounds);
+
+    DrawVerticesBatch::Geometry geometry;
+    geometry.fColor = GrRandomColor(random);
+    return DrawVerticesBatch::Create(geometry, type, viewMatrix,
+                                     positions.begin(), vertexCount,
+                                     indices.begin(), hasIndices ? vertexCount : 0,
+                                     colors.begin(),
+                                     texCoords.begin(),
+                                     bounds);
+}
+
+#endif
diff --git a/src/gpu/GrCoordTransform.cpp b/src/gpu/GrCoordTransform.cpp
index a08d454..19c4ce1 100644
--- a/src/gpu/GrCoordTransform.cpp
+++ b/src/gpu/GrCoordTransform.cpp
@@ -27,10 +27,10 @@
     int subPixelThresh = filter > GrTextureParams::kNone_FilterMode ? 4 : 1;
     fPrecision = kDefault_GrSLPrecision;
     if (texture->getContext()) {
-        const GrDrawTargetCaps* caps = texture->getContext()->getGpu()->caps();
+        const GrShaderCaps* caps = texture->getContext()->getGpu()->caps()->shaderCaps();
         if (caps->floatPrecisionVaries()) {
             int maxD = SkTMax(texture->width(), texture->height());
-            const GrDrawTargetCaps::PrecisionInfo* info;
+            const GrShaderCaps::PrecisionInfo* info;
             info = &caps->getFloatShaderPrecisionInfo(kFragment_GrShaderType, fPrecision);
             do {
                 SkASSERT(info->supported());
diff --git a/src/gpu/GrDashLinePathRenderer.cpp b/src/gpu/GrDashLinePathRenderer.cpp
new file mode 100644
index 0000000..67dc6c7
--- /dev/null
+++ b/src/gpu/GrDashLinePathRenderer.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrDashLinePathRenderer.h"
+
+#include "GrGpu.h"
+#include "effects/GrDashingEffect.h"
+
+GrDashLinePathRenderer::GrDashLinePathRenderer(GrContext* context)
+        : fGpu(SkRef(context->getGpu())) {
+}
+
+GrDashLinePathRenderer::~GrDashLinePathRenderer() {
+}
+
+bool GrDashLinePathRenderer::canDrawPath(const GrDrawTarget* target,
+                                         const GrPipelineBuilder* pipelineBuilder,
+                                         const SkMatrix& viewMatrix,
+                                         const SkPath& path,
+                                         const GrStrokeInfo& stroke,
+                                         bool antiAlias) const {
+    SkPoint pts[2];
+    if (stroke.isDashed() && path.isLine(pts)) {
+        return GrDashingEffect::CanDrawDashLine(pts, stroke, viewMatrix);
+    }
+    return false;
+}
+
+bool GrDashLinePathRenderer::onDrawPath(GrDrawTarget* target,
+                                        GrPipelineBuilder* pipelineBuilder,
+                                        GrColor color,
+                                        const SkMatrix& viewMatrix,
+                                        const SkPath& path,
+                                        const GrStrokeInfo& stroke,
+                                        bool useAA) {
+    SkPoint pts[2];
+    SkAssertResult(path.isLine(pts));
+    return GrDashingEffect::DrawDashLine(target, pipelineBuilder, color,
+                                         viewMatrix, pts, useAA, stroke);
+}
diff --git a/src/gpu/GrDashLinePathRenderer.h b/src/gpu/GrDashLinePathRenderer.h
new file mode 100644
index 0000000..632f30a
--- /dev/null
+++ b/src/gpu/GrDashLinePathRenderer.h
@@ -0,0 +1,46 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrDashLinePathRenderer_DEFINED
+#define GrDashLinePathRenderer_DEFINED
+
+#include "GrPathRenderer.h"
+
+class GrDashLinePathRenderer : public GrPathRenderer {
+public:
+    GrDashLinePathRenderer(GrContext*);
+    ~GrDashLinePathRenderer();
+
+    bool canDrawPath(const GrDrawTarget*,
+                     const GrPipelineBuilder*,
+                     const SkMatrix& viewMatrix,
+                     const SkPath&,
+                     const GrStrokeInfo&,
+                     bool antiAlias) const override;
+
+protected:
+    StencilSupport onGetStencilSupport(const GrDrawTarget*,
+                                       const GrPipelineBuilder*,
+                                       const SkPath&,
+                                       const GrStrokeInfo&) const override {
+      return kNoSupport_StencilSupport;
+    }
+
+    bool onDrawPath(GrDrawTarget*,
+                    GrPipelineBuilder*,
+                    GrColor,
+                    const SkMatrix& viewMatrix,
+                    const SkPath&,
+                    const GrStrokeInfo&,
+                    bool antiAlias) override;
+    SkAutoTUnref<GrGpu> fGpu;
+    typedef GrPathRenderer INHERITED;
+};
+
+
+#endif
diff --git a/src/gpu/GrDefaultGeoProcFactory.cpp b/src/gpu/GrDefaultGeoProcFactory.cpp
index ebd685b..6dbf71e 100644
--- a/src/gpu/GrDefaultGeoProcFactory.cpp
+++ b/src/gpu/GrDefaultGeoProcFactory.cpp
@@ -24,69 +24,56 @@
                                        GrColor color,
                                        const SkMatrix& viewMatrix,
                                        const SkMatrix& localMatrix,
-                                       bool opaqueVertexColors,
                                        uint8_t coverage) {
         return SkNEW_ARGS(DefaultGeoProc, (gpTypeFlags,
                                            color,
                                            viewMatrix,
                                            localMatrix,
-                                           opaqueVertexColors,
                                            coverage));
     }
 
-    const char* name() const SK_OVERRIDE { return "DefaultGeometryProcessor"; }
+    const char* name() const override { return "DefaultGeometryProcessor"; }
 
     const Attribute* inPosition() const { return fInPosition; }
     const Attribute* inColor() const { return fInColor; }
     const Attribute* inLocalCoords() const { return fInLocalCoords; }
     const Attribute* inCoverage() const { return fInCoverage; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& viewMatrix() const { return fViewMatrix; }
+    const SkMatrix& localMatrix() const { return fLocalMatrix; }
     uint8_t coverage() const { return fCoverage; }
 
-    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
+    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
         BatchTracker* local = bt->cast<BatchTracker>();
         local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init,
                                                    SkToBool(fInColor));
 
         bool hasVertexCoverage = SkToBool(fInCoverage) && !init.fCoverageIgnored;
         bool covIsSolidWhite = !hasVertexCoverage && 0xff == this->coverage();
-        if (covIsSolidWhite) {
+        if (init.fCoverageIgnored) {
+            local->fInputCoverageType = kIgnored_GrGPInput;
+        } else if (covIsSolidWhite) {
             local->fInputCoverageType = kAllOnes_GrGPInput;
-        } else if (!hasVertexCoverage) {
-            local->fInputCoverageType = kUniform_GrGPInput;
-            local->fCoverage = this->coverage();
         } else if (hasVertexCoverage) {
             SkASSERT(fInCoverage);
             local->fInputCoverageType = kAttribute_GrGPInput;
         } else {
-            local->fInputCoverageType = kIgnored_GrGPInput;
+            local->fInputCoverageType = kUniform_GrGPInput;
+            local->fCoverage = this->coverage();
         }
-
         local->fUsesLocalCoords = init.fUsesLocalCoords;
     }
 
-    bool onCanMakeEqual(const GrBatchTracker& m,
-                        const GrGeometryProcessor& that,
-                        const GrBatchTracker& t) const SK_OVERRIDE {
-        const BatchTracker& mine = m.cast<BatchTracker>();
-        const BatchTracker& theirs = t.cast<BatchTracker>();
-        return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                       that, theirs.fUsesLocalCoords) &&
-               CanCombineOutput(mine.fInputColorType, mine.fColor,
-                                theirs.fInputColorType, theirs.fColor) &&
-               CanCombineOutput(mine.fInputCoverageType, mine.fCoverage,
-                                theirs.fInputCoverageType, theirs.fCoverage);
-    }
-
     class GLProcessor : public GrGLGeometryProcessor {
     public:
         GLProcessor(const GrGeometryProcessor& gp, const GrBatchTracker&)
             : fColor(GrColor_ILLEGAL), fCoverage(0xff) {}
 
-        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE {
+        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
             const DefaultGeoProc& gp = args.fGP.cast<DefaultGeoProc>();
             GrGLGPBuilder* pb = args.fPB;
             GrGLVertexBuilder* vsBuilder = pb->getVertexShaderBuilder();
-            GrGLGPFragmentBuilder* fs = args.fPB->getFragmentShaderBuilder();
+            GrGLFragmentBuilder* fs = args.fPB->getFragmentShaderBuilder();
             const BatchTracker& local = args.fBT.cast<BatchTracker>();
 
             // emit attributes
@@ -129,21 +116,22 @@
 
         static inline void GenKey(const GrGeometryProcessor& gp,
                                   const GrBatchTracker& bt,
-                                  const GrGLCaps&,
+                                  const GrGLSLCaps&,
                                   GrProcessorKeyBuilder* b) {
             const DefaultGeoProc& def = gp.cast<DefaultGeoProc>();
             const BatchTracker& local = bt.cast<BatchTracker>();
             uint32_t key = def.fFlags;
             key |= local.fInputColorType << 8 | local.fInputCoverageType << 16;
-            key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 24 : 0x0;
-            key |= ComputePosKey(gp.viewMatrix()) << 25;
+            key |= local.fUsesLocalCoords && def.localMatrix().hasPerspective() ? 0x1 << 24 : 0x0;
+            key |= ComputePosKey(def.viewMatrix()) << 25;
             b->add32(key);
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
                              const GrPrimitiveProcessor& gp,
-                             const GrBatchTracker& bt) SK_OVERRIDE {
-            this->setUniformViewMatrix(pdman, gp.viewMatrix());
+                             const GrBatchTracker& bt) override {
+            const DefaultGeoProc& dgp = gp.cast<DefaultGeoProc>();
+            this->setUniformViewMatrix(pdman, dgp.viewMatrix());
 
             const BatchTracker& local = bt.cast<BatchTracker>();
             if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
@@ -158,6 +146,13 @@
             }
         }
 
+        void setTransformData(const GrPrimitiveProcessor& primProc,
+                              const GrGLProgramDataManager& pdman,
+                              int index,
+                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
+            this->setTransformDataHelper<DefaultGeoProc>(primProc, pdman, index, transforms);
+        }
+
     private:
         GrColor fColor;
         uint8_t fCoverage;
@@ -168,13 +163,13 @@
     };
 
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GLProcessor::GenKey(*this, bt, caps, b);
     }
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE {
+                                                     const GrGLSLCaps&) const override {
         return SkNEW_ARGS(GLProcessor, (*this, bt));
     }
 
@@ -183,13 +178,14 @@
                    GrColor color,
                    const SkMatrix& viewMatrix,
                    const SkMatrix& localMatrix,
-                   bool opaqueVertexColors,
                    uint8_t coverage)
-        : INHERITED(color, viewMatrix, localMatrix, opaqueVertexColors)
-        , fInPosition(NULL)
+        : fInPosition(NULL)
         , fInColor(NULL)
         , fInLocalCoords(NULL)
         , fInCoverage(NULL)
+        , fColor(color)
+        , fViewMatrix(viewMatrix)
+        , fLocalMatrix(localMatrix)
         , fCoverage(coverage)
         , fFlags(gpTypeFlags) {
         this->initClassID<DefaultGeoProc>();
@@ -199,7 +195,6 @@
         fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
         if (hasColor) {
             fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
-            this->setHasVertexColor();
         }
         if (hasLocalCoord) {
             fInLocalCoords = &this->addVertexAttrib(Attribute("inLocalCoord",
@@ -212,20 +207,6 @@
         }
     }
 
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
-        const DefaultGeoProc& gp = other.cast<DefaultGeoProc>();
-        return gp.fFlags == this->fFlags;
-    }
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
-        if (fInCoverage) {
-            out->setUnknownSingleComponent();
-        } else {
-            // uniform coverage
-            out->setKnownSingleComponent(this->coverage());
-        }
-    }
-
     struct BatchTracker {
         GrGPInput fInputColorType;
         GrGPInput fInputCoverageType;
@@ -238,6 +219,9 @@
     const Attribute* fInColor;
     const Attribute* fInLocalCoords;
     const Attribute* fInCoverage;
+    GrColor fColor;
+    SkMatrix fViewMatrix;
+    SkMatrix fLocalMatrix;
     uint8_t fCoverage;
     uint32_t fFlags;
 
@@ -265,9 +249,8 @@
 
     return DefaultGeoProc::Create(flags,
                                   GrRandomColor(random),
-                                  GrProcessorUnitTest::TestMatrix(random),
-                                  GrProcessorUnitTest::TestMatrix(random),
-                                  random->nextBool(),
+                                  GrTest::TestMatrix(random),
+                                  GrTest::TestMatrix(random),
                                   GrRandomCoverage(random));
 }
 
@@ -275,12 +258,10 @@
                                                            GrColor color,
                                                            const SkMatrix& viewMatrix,
                                                            const SkMatrix& localMatrix,
-                                                           bool opaqueVertexColors,
                                                            uint8_t coverage) {
     return DefaultGeoProc::Create(gpTypeFlags,
                                   color,
                                   viewMatrix,
                                   localMatrix,
-                                  opaqueVertexColors,
                                   coverage);
 }
diff --git a/src/gpu/GrDefaultGeoProcFactory.h b/src/gpu/GrDefaultGeoProcFactory.h
index 91c6f5d..aee16fd 100644
--- a/src/gpu/GrDefaultGeoProcFactory.h
+++ b/src/gpu/GrDefaultGeoProcFactory.h
@@ -85,7 +85,6 @@
                                              GrColor,
                                              const SkMatrix& viewMatrix = SkMatrix::I(),
                                              const SkMatrix& localMatrix = SkMatrix::I(),
-                                             bool opaqueVertexColors = false,
                                              uint8_t coverage = 0xff);
 
     static size_t DefaultVertexStride() { return sizeof(PositionAttr); }
diff --git a/src/gpu/GrDefaultPathRenderer.cpp b/src/gpu/GrDefaultPathRenderer.cpp
index 9619e2a..2dcfa57 100644
--- a/src/gpu/GrDefaultPathRenderer.cpp
+++ b/src/gpu/GrDefaultPathRenderer.cpp
@@ -9,11 +9,12 @@
 
 #include "GrBatch.h"
 #include "GrBatchTarget.h"
-#include "GrBufferAllocPool.h"
+#include "GrBatchTest.h"
 #include "GrContext.h"
 #include "GrDefaultGeoProcFactory.h"
 #include "GrPathUtils.h"
 #include "GrPipelineBuilder.h"
+#include "GrVertices.h"
 #include "SkGeometry.h"
 #include "SkString.h"
 #include "SkStrokeRec.h"
@@ -170,8 +171,8 @@
 GrDefaultPathRenderer::onGetStencilSupport(const GrDrawTarget*,
                                            const GrPipelineBuilder*,
                                            const SkPath& path,
-                                           const SkStrokeRec& stroke) const {
-    if (single_pass_path(path, stroke)) {
+                                           const GrStrokeInfo& stroke) const {
+    if (single_pass_path(path, stroke.getStrokeRec())) {
         return GrPathRenderer::kNoRestriction_StencilSupport;
     } else {
         return GrPathRenderer::kStencilOnly_StencilSupport;
@@ -216,25 +217,25 @@
         GrColor fColor;
         SkPath fPath;
         SkScalar fTolerance;
-        SkDEBUGCODE(SkRect fDevBounds;)
     };
 
     static GrBatch* Create(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
-                           bool isHairline) {
-        return SkNEW_ARGS(DefaultPathBatch, (geometry, coverage, viewMatrix, isHairline));
+                           bool isHairline, const SkRect& devBounds) {
+        return SkNEW_ARGS(DefaultPathBatch, (geometry, coverage, viewMatrix, isHairline,
+                                             devBounds));
     }
 
-    const char* name() const SK_OVERRIDE { return "DefaultPathBatch"; }
+    const char* name() const override { return "DefaultPathBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setKnownSingleComponent(this->coverage());
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -249,13 +250,12 @@
         fBatch.fCoverageIgnored = init.fCoverageIgnored;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         SkAutoTUnref<const GrGeometryProcessor> gp(
                 GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
                                                 this->color(),
                                                 this->viewMatrix(),
                                                 SkMatrix::I(),
-                                                false,
                                                 this->coverage()));
 
         size_t vertexStride = gp->getVertexStride();
@@ -318,24 +318,20 @@
         const GrVertexBuffer* vertexBuffer;
         int firstVertex;
 
-        void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              maxVertices,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
+        void* verts = batchTarget->makeVertSpace(vertexStride, maxVertices,
+                                                 &vertexBuffer, &firstVertex);
 
-        if (!vertices) {
+        if (!verts) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
-        const GrIndexBuffer* indexBuffer;
-        int firstIndex;
+        const GrIndexBuffer* indexBuffer = NULL;
+        int firstIndex = 0;
 
         void* indices = NULL;
         if (isIndexed) {
-            indices = batchTarget->indexPool()->makeSpace(maxIndices,
-                                                          &indexBuffer,
-                                                          &firstIndex);
+            indices = batchTarget->makeIndexSpace(maxIndices, &indexBuffer, &firstIndex);
 
             if (!indices) {
                 SkDebugf("Could not allocate indices\n");
@@ -351,7 +347,7 @@
 
             int vertexCnt = 0;
             int indexCnt = 0;
-            if (!this->createGeom(vertices,
+            if (!this->createGeom(verts,
                                   vertexOffset,
                                   indices,
                                   indexOffset,
@@ -368,20 +364,14 @@
             SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
         }
 
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(primitiveType);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setStartVertex(firstVertex);
-        drawInfo.setVertexCount(vertexOffset);
+        GrVertices vertices;
         if (isIndexed) {
-            drawInfo.setIndexBuffer(indexBuffer);
-            drawInfo.setStartIndex(firstIndex);
-            drawInfo.setIndexCount(indexOffset);
+            vertices.initIndexed(primitiveType, vertexBuffer, indexBuffer, firstVertex, firstIndex,
+                                 vertexOffset, indexOffset);
         } else {
-            drawInfo.setStartIndex(0);
-            drawInfo.setIndexCount(0);
+            vertices.init(primitiveType, vertexBuffer, firstVertex, vertexOffset);
         }
-        batchTarget->draw(drawInfo);
+        batchTarget->draw(vertices);
 
         // put back reserves
         batchTarget->putBackIndices((size_t)(maxIndices - indexOffset));
@@ -392,15 +382,17 @@
 
 private:
     DefaultPathBatch(const Geometry& geometry, uint8_t coverage, const SkMatrix& viewMatrix,
-                     bool isHairline) {
+                     bool isHairline, const SkRect& devBounds) {
         this->initClassID<DefaultPathBatch>();
         fBatch.fCoverage = coverage;
         fBatch.fIsHairline = isHairline;
         fBatch.fViewMatrix = viewMatrix;
         fGeoData.push_back(geometry);
+
+        this->setBounds(devBounds);
     }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         DefaultPathBatch* that = t->cast<DefaultPathBatch>();
 
         if (this->color() != that->color()) {
@@ -420,6 +412,7 @@
         }
 
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
         return true;
     }
 
@@ -545,21 +538,21 @@
                                              GrColor color,
                                              const SkMatrix& viewMatrix,
                                              const SkPath& path,
-                                             const SkStrokeRec& origStroke,
+                                             const GrStrokeInfo& origStroke,
                                              bool stencilOnly) {
-    SkTCopyOnFirstWrite<SkStrokeRec> stroke(origStroke);
+    SkTCopyOnFirstWrite<GrStrokeInfo> stroke(origStroke);
 
     SkScalar hairlineCoverage;
     uint8_t newCoverage = 0xff;
     if (IsStrokeHairlineOrEquivalent(*stroke, viewMatrix, &hairlineCoverage)) {
         newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
 
-        if (!stroke->isHairlineStyle()) {
-            stroke.writable()->setHairlineStyle();
+        if (!stroke->getStrokeRec().isHairlineStyle()) {
+            stroke.writable()->getStrokeRecPtr()->setHairlineStyle();
         }
     }
 
-    const bool isHairline = stroke->isHairlineStyle();
+    const bool isHairline = stroke->getStrokeRec().isHairlineStyle();
 
     // Save the current xp on the draw state so we can reset it if needed
     SkAutoTUnref<const GrXPFactory> backupXPFactory(SkRef(pipelineBuilder->getXPFactory()));
@@ -582,7 +575,7 @@
         lastPassIsBounds = false;
         drawFace[0] = GrPipelineBuilder::kBoth_DrawFace;
     } else {
-        if (single_pass_path(path, *stroke)) {
+        if (single_pass_path(path, stroke->getStrokeRec())) {
             passCount = 1;
             if (stencilOnly) {
                 passes[0] = &gDirectToStencil;
@@ -658,7 +651,7 @@
         }
     }
 
-    SkScalar tol = SK_Scalar1;
+    SkScalar tol = GrPathUtils::kDefaultTolerance;
     SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
 
     SkRect devBounds;
@@ -691,7 +684,6 @@
             } else {
                 bounds = path.getBounds();
             }
-            GrDrawTarget::AutoGeometryPush agp(target);
             const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
                                                                                viewMatrix;
             target->drawRect(pipelineBuilder, color, viewM, bounds, NULL, &localMatrix);
@@ -704,12 +696,11 @@
             geometry.fColor = color;
             geometry.fPath = path;
             geometry.fTolerance = srcSpaceTol;
-            SkDEBUGCODE(geometry.fDevBounds = devBounds;)
 
             SkAutoTUnref<GrBatch> batch(DefaultPathBatch::Create(geometry, newCoverage, viewMatrix,
-                                                                 isHairline));
+                                                                 isHairline, devBounds));
 
-            target->drawBatch(pipelineBuilder, batch, &devBounds);
+            target->drawBatch(pipelineBuilder, batch);
         }
     }
     return true;
@@ -719,7 +710,7 @@
                                         const GrPipelineBuilder* pipelineBuilder,
                                         const SkMatrix& viewMatrix,
                                         const SkPath& path,
-                                        const SkStrokeRec& stroke,
+                                        const GrStrokeInfo& stroke,
                                         bool antiAlias) const {
     // this class can draw any path with any fill but doesn't do any anti-aliasing.
     return !antiAlias && (stroke.isFillStyle() || IsStrokeHairlineOrEquivalent(stroke,
@@ -732,7 +723,7 @@
                                        GrColor color,
                                        const SkMatrix& viewMatrix,
                                        const SkPath& path,
-                                       const SkStrokeRec& stroke,
+                                       const GrStrokeInfo& stroke,
                                        bool antiAlias) {
     return this->internalDrawPath(target,
                                   pipelineBuilder,
@@ -747,8 +738,38 @@
                                           GrPipelineBuilder* pipelineBuilder,
                                           const SkMatrix& viewMatrix,
                                           const SkPath& path,
-                                          const SkStrokeRec& stroke) {
+                                          const GrStrokeInfo& stroke) {
     SkASSERT(SkPath::kInverseEvenOdd_FillType != path.getFillType());
     SkASSERT(SkPath::kInverseWinding_FillType != path.getFillType());
     this->internalDrawPath(target, pipelineBuilder, GrColor_WHITE, viewMatrix, path, stroke, true);
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef GR_TEST_UTILS
+
+BATCH_TEST_DEFINE(DefaultPathBatch) {
+    GrColor color = GrRandomColor(random);
+    SkMatrix viewMatrix = GrTest::TestMatrix(random);
+
+    // For now just hairlines because the other types of draws require two batches.
+    // TODO we should figure out a way to combine the stencil and cover steps into one batch
+    GrStrokeInfo stroke(SkStrokeRec::kHairline_InitStyle);
+    SkPath path = GrTest::TestPath(random);
+
+    // Compute srcSpaceTol
+    SkRect bounds = path.getBounds();
+    SkScalar tol = GrPathUtils::kDefaultTolerance;
+    SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
+
+    DefaultPathBatch::Geometry geometry;
+    geometry.fColor = color;
+    geometry.fPath = path;
+    geometry.fTolerance = srcSpaceTol;
+
+    viewMatrix.mapRect(&bounds);
+    uint8_t coverage = GrRandomCoverage(random);
+    return DefaultPathBatch::Create(geometry, coverage, viewMatrix, true, bounds);
+}
+
+#endif
diff --git a/src/gpu/GrDefaultPathRenderer.h b/src/gpu/GrDefaultPathRenderer.h
index 11105cf..008eac2 100644
--- a/src/gpu/GrDefaultPathRenderer.h
+++ b/src/gpu/GrDefaultPathRenderer.h
@@ -23,36 +23,36 @@
                              const GrPipelineBuilder*,
                              const SkMatrix& viewMatrix,
                              const SkPath&,
-                             const SkStrokeRec&,
-                             bool antiAlias) const SK_OVERRIDE;
+                             const GrStrokeInfo&,
+                             bool antiAlias) const override;
 
 private:
 
     virtual StencilSupport onGetStencilSupport(const GrDrawTarget*,
                                                const GrPipelineBuilder*,
                                                const SkPath&,
-                                               const SkStrokeRec&) const SK_OVERRIDE;
+                                               const GrStrokeInfo&) const override;
 
     virtual bool onDrawPath(GrDrawTarget*,
                             GrPipelineBuilder*,
                             GrColor,
                             const SkMatrix& viewMatrix,
                             const SkPath&,
-                            const SkStrokeRec&,
-                            bool antiAlias) SK_OVERRIDE;
+                            const GrStrokeInfo&,
+                            bool antiAlias) override;
 
     virtual void onStencilPath(GrDrawTarget*,
                                GrPipelineBuilder*,
                                const SkMatrix& viewMatrix,
                                const SkPath&,
-                               const SkStrokeRec&) SK_OVERRIDE;
+                               const GrStrokeInfo&) override;
 
     bool internalDrawPath(GrDrawTarget*,
                           GrPipelineBuilder*,
                           GrColor,
                           const SkMatrix& viewMatrix,
                           const SkPath&,
-                          const SkStrokeRec&,
+                          const GrStrokeInfo&,
                           bool stencilOnly);
 
     bool    fSeparateStencil;
diff --git a/src/gpu/GrDistanceFieldTextContext.cpp b/src/gpu/GrDistanceFieldTextContext.cpp
deleted file mode 100755
index eb172af..0000000
--- a/src/gpu/GrDistanceFieldTextContext.cpp
+++ /dev/null
@@ -1,727 +0,0 @@
-/*
- * 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 "GrDistanceFieldTextContext.h"
-#include "GrAtlas.h"
-#include "GrBitmapTextContext.h"
-#include "GrDrawTarget.h"
-#include "GrDrawTargetCaps.h"
-#include "GrFontAtlasSizes.h"
-#include "GrFontCache.h"
-#include "GrFontScaler.h"
-#include "GrGpu.h"
-#include "GrIndexBuffer.h"
-#include "GrStrokeInfo.h"
-#include "GrTexturePriv.h"
-
-#include "SkAutoKern.h"
-#include "SkColorFilter.h"
-#include "SkDistanceFieldGen.h"
-#include "SkDraw.h"
-#include "SkGlyphCache.h"
-#include "SkGpuDevice.h"
-#include "SkPath.h"
-#include "SkRTConf.h"
-#include "SkStrokeRec.h"
-#include "effects/GrDistanceFieldTextureEffect.h"
-
-SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
-                "Dump the contents of the font cache before every purge.");
-
-static const int kSmallDFFontSize = 32;
-static const int kSmallDFFontLimit = 32;
-static const int kMediumDFFontSize = 72;
-static const int kMediumDFFontLimit = 72;
-static const int kLargeDFFontSize = 162;
-
-static const int kVerticesPerGlyph = 4;
-static const int kIndicesPerGlyph = 6;
-
-GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
-                                                       const SkDeviceProperties& properties,
-                                                       bool enable)
-                                                    : GrTextContext(context, properties) {
-#if SK_FORCE_DISTANCE_FIELD_TEXT
-    fEnableDFRendering = true;
-#else
-    fEnableDFRendering = enable;
-#endif
-    fStrike = NULL;
-    fGammaTexture = NULL;
-
-    fEffectTextureUniqueID = SK_InvalidUniqueID;
-    fEffectColor = GrColor_ILLEGAL;
-    fEffectFlags = kInvalid_DistanceFieldEffectFlag;
-
-    fVertices = NULL;
-    fCurrVertex = 0;
-    fAllocVertexCount = 0;
-    fTotalVertexCount = 0;
-    fCurrTexture = NULL;
-
-    fVertexBounds.setLargestInverted();
-}
-
-GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
-                                                               const SkDeviceProperties& props,
-                                                               bool enable) {
-    GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext, 
-                                                         (context, props, enable));
-    textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
-
-    return textContext;
-}
-
-GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
-    SkSafeSetNull(fGammaTexture);
-}
-
-bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) {
-    // TODO: support perspective (need getMaxScale replacement)
-    if (viewMatrix.hasPerspective()) {
-        return false;
-    }
-
-    SkScalar maxScale = viewMatrix.getMaxScale();
-    SkScalar scaledTextSize = maxScale*paint.getTextSize();
-    // Scaling up beyond 2x yields undesireable artifacts
-    if (scaledTextSize > 2*kLargeDFFontSize) {
-        return false;
-    }
-
-    if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP() &&
-        scaledTextSize < kLargeDFFontSize) {
-        return false;
-    }
-
-    // rasterizers and mask filters modify alpha, which doesn't
-    // translate well to distance
-    if (paint.getRasterizer() || paint.getMaskFilter() ||
-        !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
-        return false;
-    }
-
-    // TODO: add some stroking support
-    if (paint.getStyle() != SkPaint::kFill_Style) {
-        return false;
-    }
-
-    return true;
-}
-
-inline void GrDistanceFieldTextContext::init(GrRenderTarget* rt, const GrClip& clip,
-                                             const GrPaint& paint, const SkPaint& skPaint) {
-    GrTextContext::init(rt, clip, paint, skPaint);
-
-    fStrike = NULL;
-
-    const SkMatrix& ctm = fViewMatrix;
-
-    // getMaxScale doesn't support perspective, so neither do we at the moment
-    SkASSERT(!ctm.hasPerspective());
-    SkScalar maxScale = ctm.getMaxScale();
-    SkScalar textSize = fSkPaint.getTextSize();
-    SkScalar scaledTextSize = textSize;
-    // if we have non-unity scale, we need to choose our base text size
-    // based on the SkPaint's text size multiplied by the max scale factor
-    // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
-    if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
-        scaledTextSize *= maxScale;
-    }
-
-    fVertices = NULL;
-    fCurrVertex = 0;
-    fAllocVertexCount = 0;
-    fTotalVertexCount = 0;
-
-    if (scaledTextSize <= kSmallDFFontLimit) {
-        fTextRatio = textSize / kSmallDFFontSize;
-        fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
-#if DEBUG_TEXT_SIZE
-        fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF));
-        fPaint.setColor(GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF));
-#endif
-    } else if (scaledTextSize <= kMediumDFFontLimit) {
-        fTextRatio = textSize / kMediumDFFontSize;
-        fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
-#if DEBUG_TEXT_SIZE
-        fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00));
-        fPaint.setColor(GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF));
-#endif
-    } else {
-        fTextRatio = textSize / kLargeDFFontSize;
-        fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
-#if DEBUG_TEXT_SIZE
-        fSkPaint.setColor(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00));
-        fPaint.setColor(GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF));
-#endif
-    }
-
-    fUseLCDText = fSkPaint.isLCDRenderText();
-
-    fSkPaint.setLCDRenderText(false);
-    fSkPaint.setAutohinted(false);
-    fSkPaint.setHinting(SkPaint::kNormal_Hinting);
-    fSkPaint.setSubpixelText(true);
-}
-
-static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
-                                const SkDeviceProperties& deviceProperties,
-                                GrTexture** gammaTexture) {
-    if (NULL == *gammaTexture) {
-        int width, height;
-        size_t size;
-
-#ifdef SK_GAMMA_CONTRAST
-        SkScalar contrast = SK_GAMMA_CONTRAST;
-#else
-        SkScalar contrast = 0.5f;
-#endif
-        SkScalar paintGamma = deviceProperties.gamma();
-        SkScalar deviceGamma = deviceProperties.gamma();
-
-        size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
-                                                &width, &height);
-
-        SkAutoTArray<uint8_t> data((int)size);
-        SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
-
-        // TODO: Update this to use the cache rather than directly creating a texture.
-        GrSurfaceDesc desc;
-        desc.fFlags = kNone_GrSurfaceFlags;
-        desc.fWidth = width;
-        desc.fHeight = height;
-        desc.fConfig = kAlpha_8_GrPixelConfig;
-
-        *gammaTexture = context->getGpu()->createTexture(desc, true, NULL, 0);
-        if (NULL == *gammaTexture) {
-            return;
-        }
-
-        (*gammaTexture)->writePixels(0, 0, width, height,
-                                     (*gammaTexture)->config(), data.get(), 0,
-                                     GrContext::kDontFlush_PixelOpsFlag);
-    }
-}
-
-void GrDistanceFieldTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
-                                            const GrPaint& paint,
-                                            const SkPaint& skPaint, const SkMatrix& viewMatrix,
-                                            const char text[], size_t byteLength,
-                                            SkScalar x, SkScalar y) {
-    SkASSERT(byteLength == 0 || text != NULL);
-
-    // nothing to draw
-    if (text == NULL || byteLength == 0) {
-        return;
-    }
-
-    fViewMatrix = viewMatrix;
-    SkDrawCacheProc          glyphCacheProc = skPaint.getDrawCacheProc();
-    SkAutoGlyphCache         autoCache(skPaint, &fDeviceProperties, NULL);
-    SkGlyphCache*            cache = autoCache.getCache();
-
-    SkTArray<SkScalar> positions;
-
-    const char* textPtr = text;
-    SkFixed stopX = 0;
-    SkFixed stopY = 0;
-    SkFixed origin;
-    switch (skPaint.getTextAlign()) {
-        case SkPaint::kRight_Align: origin = SK_Fixed1; break;
-        case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
-        case SkPaint::kLeft_Align: origin = 0; break;
-        default: SkFAIL("Invalid paint origin"); return;
-    }
-
-    SkAutoKern autokern;
-    const char* stop = text + byteLength;
-    while (textPtr < stop) {
-        // don't need x, y here, since all subpixel variants will have the
-        // same advance
-        const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
-
-        SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
-        positions.push_back(SkFixedToScalar(stopX + SkFixedMul_portable(origin, width)));
-
-        SkFixed height = glyph.fAdvanceY;
-        positions.push_back(SkFixedToScalar(stopY + SkFixedMul_portable(origin, height)));
-
-        stopX += width;
-        stopY += height;
-    }
-    SkASSERT(textPtr == stop);
-
-    // now adjust starting point depending on alignment
-    SkScalar alignX = SkFixedToScalar(stopX);
-    SkScalar alignY = SkFixedToScalar(stopY);
-    if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
-        alignX = SkScalarHalf(alignX);
-        alignY = SkScalarHalf(alignY);
-    } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
-        alignX = 0;
-        alignY = 0;
-    }
-    x -= alignX;
-    y -= alignY;
-    SkPoint offset = SkPoint::Make(x, y);
-
-    this->drawPosText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, positions.begin(), 2,
-                      offset);
-}
-
-void GrDistanceFieldTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
-                                               const GrPaint& paint,
-                                               const SkPaint& skPaint, const SkMatrix& viewMatrix,
-                                               const char text[], size_t byteLength,
-                                               const SkScalar pos[], int scalarsPerPosition,
-                                               const SkPoint& offset) {
-
-    SkASSERT(byteLength == 0 || text != NULL);
-    SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
-
-    // nothing to draw
-    if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
-        return;
-    }
-
-    fViewMatrix = viewMatrix;
-    this->init(rt, clip, paint, skPaint);
-
-    SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
-
-    SkAutoGlyphCacheNoGamma    autoCache(fSkPaint, &fDeviceProperties, NULL);
-    SkGlyphCache*              cache = autoCache.getCache();
-    GrFontScaler*              fontScaler = GetGrFontScaler(cache);
-
-    setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
-
-    int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
-    fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
-
-    const char*        stop = text + byteLength;
-    SkTArray<char>     fallbackTxt;
-    SkTArray<SkScalar> fallbackPos;
-
-    if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
-        while (text < stop) {
-            const char* lastText = text;
-            // the last 2 parameters are ignored
-            const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
-
-            if (glyph.fWidth) {
-                SkScalar x = offset.x() + pos[0];
-                SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
-
-                if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
-                                                     glyph.getSubXFixed(),
-                                                     glyph.getSubYFixed()),
-                                       x, y, fontScaler)) {
-                    // couldn't append, send to fallback
-                    fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
-                    fallbackPos.push_back(pos[0]);
-                    if (2 == scalarsPerPosition) {
-                        fallbackPos.push_back(pos[1]);
-                    }
-                }
-            }
-            pos += scalarsPerPosition;
-        }
-    } else {
-        SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf
-                                                                              : SK_Scalar1;
-        while (text < stop) {
-            const char* lastText = text;
-            // the last 2 parameters are ignored
-            const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
-
-            if (glyph.fWidth) {
-                SkScalar x = offset.x() + pos[0];
-                SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
-
-                SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio;
-                SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio;
-
-                if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
-                                                     glyph.getSubXFixed(),
-                                                     glyph.getSubYFixed()),
-                                       x - advanceX, y - advanceY, fontScaler)) {
-                    // couldn't append, send to fallback
-                    fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
-                    fallbackPos.push_back(pos[0]);
-                    if (2 == scalarsPerPosition) {
-                        fallbackPos.push_back(pos[1]);
-                    }
-                }
-            }
-            pos += scalarsPerPosition;
-        }
-    }
-
-    this->finish();
-    
-    if (fallbackTxt.count() > 0) {
-        fFallbackTextContext->drawPosText(rt, clip, paint, skPaint, viewMatrix, fallbackTxt.begin(),
-                                          fallbackTxt.count(), fallbackPos.begin(),
-                                          scalarsPerPosition, offset);
-    }
-}
-
-static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
-    unsigned r = SkColorGetR(c);
-    unsigned g = SkColorGetG(c);
-    unsigned b = SkColorGetB(c);
-    return GrColorPackRGBA(r, g, b, 0xff);
-}
-
-static size_t get_vertex_stride(bool useColorVerts) {
-    return useColorVerts ? (sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16)) :
-                           (sizeof(SkPoint) + sizeof(SkIPoint16));
-}
-
-static void* alloc_vertices(GrDrawTarget* drawTarget,
-                            int numVertices,
-                            bool useColorVerts) {
-    if (numVertices <= 0) {
-        return NULL;
-    }
-
-    void* vertices = NULL;
-    bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
-                                                          get_vertex_stride(useColorVerts),
-                                                          0,
-                                                          &vertices,
-                                                          NULL);
-    GrAlwaysAssert(success);
-    return vertices;
-}
-
-void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
-    GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
-    GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
-    
-    uint32_t textureUniqueID = fCurrTexture->getUniqueID();
-    const SkMatrix& ctm = fViewMatrix;
-    
-    // set up any flags
-    uint32_t flags = 0;
-    flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
-    flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
-    flags |= fUseLCDText && ctm.rectStaysRect() ?
-    kRectToRect_DistanceFieldEffectFlag : 0;
-    bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
-    flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
-    
-    // see if we need to create a new effect
-    if (textureUniqueID != fEffectTextureUniqueID ||
-        filteredColor != fEffectColor ||
-        flags != fEffectFlags ||
-        !fCachedGeometryProcessor->viewMatrix().cheapEqualTo(fViewMatrix)) {
-        GrColor color = fPaint.getColor();
-        if (fUseLCDText) {
-            GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
-            fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(color,
-                                                                                   fViewMatrix,
-                                                                                   fCurrTexture,
-                                                                                   params,
-                                                                                   fGammaTexture,
-                                                                                   gammaParams,
-                                                                                   colorNoPreMul,
-                                                                                   flags));
-        } else {
-            flags |= kColorAttr_DistanceFieldEffectFlag;
-            bool opaque = GrColorIsOpaque(color);
-#ifdef SK_GAMMA_APPLY_TO_A8
-            U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
-                                                                filteredColor);
-            fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(color,
-                                                                                fViewMatrix,
-                                                                                fCurrTexture,
-                                                                                params,
-                                                                                fGammaTexture,
-                                                                                gammaParams,
-                                                                                lum/255.f,
-                                                                                flags,
-                                                                                opaque));
-#else
-            fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(color,
-                                                                                fViewMatrix,
-                                                                                fCurrTexture,
-                                                                                params,
-                                                                                flags,
-                                                                                opaque));
-#endif
-        }
-        fEffectTextureUniqueID = textureUniqueID;
-        fEffectColor = filteredColor;
-        fEffectFlags = flags;
-    }
-    
-}
-
-inline bool GrDistanceFieldTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scaler) {
-    if (!fStrike->glyphTooLargeForAtlas(glyph)) {
-        if (fStrike->addGlyphToAtlas(glyph, scaler)) {
-            return true;
-        }
-        
-        // try to clear out an unused plot before we flush
-        if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
-            fStrike->addGlyphToAtlas(glyph, scaler)) {
-            return true;
-        }
-        
-        if (c_DumpFontCache) {
-#ifdef SK_DEVELOPER
-            fContext->getFontCache()->dump();
-#endif
-        }
-        
-        // before we purge the cache, we must flush any accumulated draws
-        this->flush();
-        fContext->flush();
-        
-        // we should have an unused plot now
-        if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
-            fStrike->addGlyphToAtlas(glyph, scaler)) {
-            return true;
-        }
-        
-        // we should never get here
-        SkASSERT(false);
-    }
-    
-    return false;
-}
-
-
-// Returns true if this method handled the glyph, false if needs to be passed to fallback
-//
-bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
-                                             SkScalar sx, SkScalar sy,
-                                             GrFontScaler* scaler) {
-    if (NULL == fDrawTarget) {
-        return true;
-    }
-
-    if (NULL == fStrike) {
-        fStrike = fContext->getFontCache()->getStrike(scaler, true);
-    }
-
-    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
-    if (NULL == glyph || glyph->fBounds.isEmpty()) {
-        return true;
-    }
-
-    // fallback to color glyph support
-    if (kA8_GrMaskFormat != glyph->fMaskFormat) {
-        return false;
-    }
-
-    SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
-    SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
-    SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
-    SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
-
-    SkScalar scale = fTextRatio;
-    dx *= scale;
-    dy *= scale;
-    sx += dx;
-    sy += dy;
-    width *= scale;
-    height *= scale;
-    SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
-
-    // check if we clipped out
-    SkRect dstRect;
-    const SkMatrix& ctm = fViewMatrix;
-    (void) ctm.mapRect(&dstRect, glyphRect);
-    if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
-                              SkScalarTruncToInt(dstRect.top()),
-                              SkScalarTruncToInt(dstRect.right()),
-                              SkScalarTruncToInt(dstRect.bottom()))) {
-        return true;
-    }
-
-    if (NULL == glyph->fPlot) {
-        // needs to be a separate conditional to avoid over-optimization
-        // on Nexus 7 and Nexus 10
-
-        // If the glyph is too large we fall back to paths
-        if (!uploadGlyph(glyph, scaler)) {
-            if (NULL == glyph->fPath) {
-                SkPath* path = SkNEW(SkPath);
-                if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
-                    // flag the glyph as being dead?
-                    delete path;
-                    return true;
-                }
-                glyph->fPath = path;
-            }
-
-            // flush any accumulated draws before drawing this glyph as a path.
-            this->flush();
-
-            SkMatrix ctm;
-            ctm.setScale(fTextRatio, fTextRatio);
-            ctm.postTranslate(sx - dx, sy - dy);
-
-            SkPath tmpPath(*glyph->fPath);
-            tmpPath.transform(ctm);
-
-            GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
-            fContext->drawPath(fRenderTarget, fClip, fPaint, fViewMatrix, tmpPath, strokeInfo);
-
-            // remove this glyph from the vertices we need to allocate
-            fTotalVertexCount -= kVerticesPerGlyph;
-            return true;
-        }
-    }
-
-    SkASSERT(glyph->fPlot);
-    GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
-    glyph->fPlot->setDrawToken(drawToken);
-
-    GrTexture* texture = glyph->fPlot->texture();
-    SkASSERT(texture);
-
-    if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) {
-        this->flush();
-        fCurrTexture = texture;
-        fCurrTexture->ref();
-    }
-
-    bool useColorVerts = !fUseLCDText;
-
-    if (NULL == fVertices) {
-        int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
-        fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
-        fVertices = alloc_vertices(fDrawTarget,
-                                   fAllocVertexCount,
-                                   useColorVerts);
-    }
-
-    fVertexBounds.joinNonEmptyArg(glyphRect);
-
-    int u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
-    int v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
-    int u1 = u0 + glyph->fBounds.width() - 2*SK_DistanceFieldInset;
-    int v1 = v0 + glyph->fBounds.height() - 2*SK_DistanceFieldInset;
-
-    size_t vertSize = get_vertex_stride(useColorVerts);
-    intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex;
-
-    // V0
-    SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(glyphRect.fLeft, glyphRect.fTop);
-    if (useColorVerts) {
-        SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
-        *color = fPaint.getColor();
-    }
-    SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize -
-                                                              sizeof(SkIPoint16));
-    textureCoords->set(u0, v0);
-    vertex += vertSize;
-
-    // V1
-    position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(glyphRect.fLeft, glyphRect.fBottom);
-    if (useColorVerts) {
-        SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
-        *color = fPaint.getColor();
-    }
-    textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize  - sizeof(SkIPoint16));
-    textureCoords->set(u0, v1);
-    vertex += vertSize;
-
-    // V2
-    position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(glyphRect.fRight, glyphRect.fBottom);
-    if (useColorVerts) {
-        SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
-        *color = fPaint.getColor();
-    }
-    textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize  - sizeof(SkIPoint16));
-    textureCoords->set(u1, v1);
-    vertex += vertSize;
-
-    // V3
-    position = reinterpret_cast<SkPoint*>(vertex);
-    position->set(glyphRect.fRight, glyphRect.fTop);
-    if (useColorVerts) {
-        SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
-        *color = fPaint.getColor();
-    }
-    textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize  - sizeof(SkIPoint16));
-    textureCoords->set(u1, v0);
-
-    fCurrVertex += 4;
-    
-    return true;
-}
-
-void GrDistanceFieldTextContext::flush() {
-    if (NULL == fDrawTarget) {
-        return;
-    }
-
-    if (fCurrVertex > 0) {
-        GrPipelineBuilder pipelineBuilder;
-        pipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
-
-        // setup our sampler state for our text texture/atlas
-        SkASSERT(SkIsAlign4(fCurrVertex));
-
-        // get our current color
-        SkColor filteredColor;
-        SkColorFilter* colorFilter = fSkPaint.getColorFilter();
-        if (colorFilter) {
-            filteredColor = colorFilter->filterColor(fSkPaint.getColor());
-        } else {
-            filteredColor = fSkPaint.getColor();
-        }
-        this->setupCoverageEffect(filteredColor);
-
-        // Set draw state
-        if (fUseLCDText) {
-            // TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD
-            // processor if the xp can support it. For now we will simply assume that if
-            // fUseLCDText is true, then we have a known color output.
-            const GrXPFactory* xpFactory = pipelineBuilder.getXPFactory();
-            if (!xpFactory->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) {
-                SkDebugf("LCD Text will not draw correctly.\n");
-            }
-            SkASSERT(!fCachedGeometryProcessor->hasVertexColor());
-        } else {
-            // We're using per-vertex color.
-            SkASSERT(fCachedGeometryProcessor->hasVertexColor());
-        }
-        int nGlyphs = fCurrVertex / kVerticesPerGlyph;
-        fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
-        fDrawTarget->drawIndexedInstances(&pipelineBuilder,
-                                          fCachedGeometryProcessor.get(),
-                                          kTriangles_GrPrimitiveType,
-                                          nGlyphs,
-                                          kVerticesPerGlyph,
-                                          kIndicesPerGlyph,
-                                          &fVertexBounds);
-        fDrawTarget->resetVertexSource();
-        fVertices = NULL;
-        fTotalVertexCount -= fCurrVertex;
-        fCurrVertex = 0;
-        SkSafeSetNull(fCurrTexture);
-        fVertexBounds.setLargestInverted();
-    }
-}
-
-inline void GrDistanceFieldTextContext::finish() {
-    this->flush();
-    fTotalVertexCount = 0;
-
-    GrTextContext::finish();
-}
-
diff --git a/src/gpu/GrDistanceFieldTextContext.h b/src/gpu/GrDistanceFieldTextContext.h
deleted file mode 100644
index 129c6e9..0000000
--- a/src/gpu/GrDistanceFieldTextContext.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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 GrDistanceFieldTextContext_DEFINED
-#define GrDistanceFieldTextContext_DEFINED
-
-#include "GrTextContext.h"
-
-class GrGeometryProcessor;
-class GrTextStrike;
-
-/*
- * This class implements GrTextContext using distance field fonts
- */
-class GrDistanceFieldTextContext : public GrTextContext {
-public:
-    static GrDistanceFieldTextContext* Create(GrContext*, const SkDeviceProperties&, bool enable);
-
-    virtual ~GrDistanceFieldTextContext();
-
-private:
-    enum {
-        kMinRequestedGlyphs      = 1,
-        kDefaultRequestedGlyphs  = 64,
-        kMinRequestedVerts       = kMinRequestedGlyphs * 4,
-        kDefaultRequestedVerts   = kDefaultRequestedGlyphs * 4,
-    };
-
-    GrTextStrike*                      fStrike;
-    SkScalar                           fTextRatio;
-    bool                               fUseLCDText;
-    bool                               fEnableDFRendering;
-    SkAutoTUnref<GrGeometryProcessor>  fCachedGeometryProcessor;
-    // Used to check whether fCachedEffect is still valid.
-    uint32_t                           fEffectTextureUniqueID;
-    SkColor                            fEffectColor;
-    uint32_t                           fEffectFlags;
-    GrTexture*                         fGammaTexture;
-    void*                              fVertices;
-    int                                fCurrVertex;
-    int                                fAllocVertexCount;
-    int                                fTotalVertexCount;
-    GrTexture*                         fCurrTexture;
-    SkRect                             fVertexBounds;
-    SkMatrix                           fViewMatrix;
-
-    GrDistanceFieldTextContext(GrContext*, const SkDeviceProperties&, bool enable);
-
-    bool canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) SK_OVERRIDE;
-
-    virtual void onDrawText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
-                            const SkMatrix& viewMatrix,
-                            const char text[], size_t byteLength,
-                            SkScalar x, SkScalar y) SK_OVERRIDE;
-    virtual void onDrawPosText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
-                               const SkMatrix& viewMatrix,
-                               const char text[], size_t byteLength,
-                               const SkScalar pos[], int scalarsPerPosition,
-                               const SkPoint& offset) SK_OVERRIDE;
-
-    void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&);
-    bool appendGlyph(GrGlyph::PackedID, SkScalar left, SkScalar top, GrFontScaler*);
-    bool uploadGlyph(GrGlyph*, GrFontScaler*);
-    void setupCoverageEffect(const SkColor& filteredColor);
-    void flush();                 // automatically called by destructor
-    void finish();
-};
-
-#endif
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 2640e71..549ab37 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -13,6 +13,8 @@
 #include "GrDrawTargetCaps.h"
 #include "GrPath.h"
 #include "GrPipeline.h"
+#include "GrMemoryPool.h"
+#include "GrRectBatch.h"
 #include "GrRenderTarget.h"
 #include "GrRenderTargetPriv.h"
 #include "GrSurfacePriv.h"
@@ -24,359 +26,19 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrDrawTarget::DrawInfo& GrDrawTarget::DrawInfo::operator =(const DrawInfo& di) {
-    fPrimitiveType  = di.fPrimitiveType;
-    fStartVertex    = di.fStartVertex;
-    fStartIndex     = di.fStartIndex;
-    fVertexCount    = di.fVertexCount;
-    fIndexCount     = di.fIndexCount;
-
-    fInstanceCount          = di.fInstanceCount;
-    fVerticesPerInstance    = di.fVerticesPerInstance;
-    fIndicesPerInstance     = di.fIndicesPerInstance;
-
-    if (di.fDevBounds) {
-        SkASSERT(di.fDevBounds == &di.fDevBoundsStorage);
-        fDevBoundsStorage = di.fDevBoundsStorage;
-        fDevBounds = &fDevBoundsStorage;
-    } else {
-        fDevBounds = NULL;
-    }
-
-    this->setVertexBuffer(di.vertexBuffer());
-    this->setIndexBuffer(di.indexBuffer());
-
-    return *this;
-}
-
-#ifdef SK_DEBUG
-bool GrDrawTarget::DrawInfo::isInstanced() const {
-    if (fInstanceCount > 0) {
-        SkASSERT(0 == fIndexCount % fIndicesPerInstance);
-        SkASSERT(0 == fVertexCount % fVerticesPerInstance);
-        SkASSERT(fIndexCount / fIndicesPerInstance == fInstanceCount);
-        SkASSERT(fVertexCount / fVerticesPerInstance == fInstanceCount);
-        // there is no way to specify a non-zero start index to drawIndexedInstances().
-        SkASSERT(0 == fStartIndex);
-        return true;
-    } else {
-        SkASSERT(!fVerticesPerInstance);
-        SkASSERT(!fIndicesPerInstance);
-        return false;
-    }
-}
-#endif
-
-void GrDrawTarget::DrawInfo::adjustInstanceCount(int instanceOffset) {
-    SkASSERT(this->isInstanced());
-    SkASSERT(instanceOffset + fInstanceCount >= 0);
-    fInstanceCount += instanceOffset;
-    fVertexCount = fVerticesPerInstance * fInstanceCount;
-    fIndexCount = fIndicesPerInstance * fInstanceCount;
-}
-
-void GrDrawTarget::DrawInfo::adjustStartVertex(int vertexOffset) {
-    fStartVertex += vertexOffset;
-    SkASSERT(fStartVertex >= 0);
-}
-
-void GrDrawTarget::DrawInfo::adjustStartIndex(int indexOffset) {
-    SkASSERT(this->isIndexed());
-    fStartIndex += indexOffset;
-    SkASSERT(fStartIndex >= 0);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 #define DEBUG_INVAL_BUFFER 0xdeadcafe
 #define DEBUG_INVAL_START_IDX -1
 
 GrDrawTarget::GrDrawTarget(GrContext* context)
     : fContext(context)
-    , fGpuTraceMarkerCount(0) {
+    , fCaps(SkRef(context->getGpu()->caps()))
+    , fGpuTraceMarkerCount(0)
+    , fFlushing(false) {
     SkASSERT(context);
-    GeometrySrcState& geoSrc = fGeoSrcStateStack.push_back();
-#ifdef SK_DEBUG
-    geoSrc.fVertexCount = DEBUG_INVAL_START_IDX;
-    geoSrc.fVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER;
-    geoSrc.fIndexCount = DEBUG_INVAL_START_IDX;
-    geoSrc.fIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
-#endif
-    geoSrc.fVertexSrc = kNone_GeometrySrcType;
-    geoSrc.fIndexSrc  = kNone_GeometrySrcType;
-}
-
-GrDrawTarget::~GrDrawTarget() {
-    SkASSERT(1 == fGeoSrcStateStack.count());
-    SkDEBUGCODE(GeometrySrcState& geoSrc = fGeoSrcStateStack.back());
-    SkASSERT(kNone_GeometrySrcType == geoSrc.fIndexSrc);
-    SkASSERT(kNone_GeometrySrcType == geoSrc.fVertexSrc);
-}
-
-void GrDrawTarget::releaseGeometry() {
-    int popCnt = fGeoSrcStateStack.count() - 1;
-    while (popCnt) {
-        this->popGeometrySource();
-        --popCnt;
-    }
-    this->resetVertexSource();
-    this->resetIndexSource();
-}
-
-bool GrDrawTarget::reserveVertexSpace(size_t vertexSize,
-                                      int vertexCount,
-                                      void** vertices) {
-    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
-    bool acquired = false;
-    if (vertexCount > 0) {
-        SkASSERT(vertices);
-        this->releasePreviousVertexSource();
-        geoSrc.fVertexSrc = kNone_GeometrySrcType;
-
-        acquired = this->onReserveVertexSpace(vertexSize,
-                                              vertexCount,
-                                              vertices);
-    }
-    if (acquired) {
-        geoSrc.fVertexSrc = kReserved_GeometrySrcType;
-        geoSrc.fVertexCount = vertexCount;
-        geoSrc.fVertexSize = vertexSize;
-    } else if (vertices) {
-        *vertices = NULL;
-    }
-    return acquired;
-}
-
-bool GrDrawTarget::reserveIndexSpace(int indexCount,
-                                     void** indices) {
-    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
-    bool acquired = false;
-    if (indexCount > 0) {
-        SkASSERT(indices);
-        this->releasePreviousIndexSource();
-        geoSrc.fIndexSrc = kNone_GeometrySrcType;
-
-        acquired = this->onReserveIndexSpace(indexCount, indices);
-    }
-    if (acquired) {
-        geoSrc.fIndexSrc = kReserved_GeometrySrcType;
-        geoSrc.fIndexCount = indexCount;
-    } else if (indices) {
-        *indices = NULL;
-    }
-    return acquired;
-
-}
-
-bool GrDrawTarget::reserveVertexAndIndexSpace(int vertexCount,
-                                              size_t vertexStride,
-                                              int indexCount,
-                                              void** vertices,
-                                              void** indices) {
-    this->willReserveVertexAndIndexSpace(vertexCount, vertexStride, indexCount);
-    if (vertexCount) {
-        if (!this->reserveVertexSpace(vertexStride, vertexCount, vertices)) {
-            if (indexCount) {
-                this->resetIndexSource();
-            }
-            return false;
-        }
-    }
-    if (indexCount) {
-        if (!this->reserveIndexSpace(indexCount, indices)) {
-            if (vertexCount) {
-                this->resetVertexSource();
-            }
-            return false;
-        }
-    }
-    return true;
-}
-
-bool GrDrawTarget::geometryHints(size_t vertexStride,
-                                 int32_t* vertexCount,
-                                 int32_t* indexCount) const {
-    if (vertexCount) {
-        *vertexCount = -1;
-    }
-    if (indexCount) {
-        *indexCount = -1;
-    }
-    return false;
-}
-
-void GrDrawTarget::releasePreviousVertexSource() {
-    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
-    switch (geoSrc.fVertexSrc) {
-        case kNone_GeometrySrcType:
-            break;
-        case kReserved_GeometrySrcType:
-            this->releaseReservedVertexSpace();
-            break;
-        case kBuffer_GeometrySrcType:
-            geoSrc.fVertexBuffer->unref();
-#ifdef SK_DEBUG
-            geoSrc.fVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER;
-#endif
-            break;
-        default:
-            SkFAIL("Unknown Vertex Source Type.");
-            break;
-    }
-}
-
-void GrDrawTarget::releasePreviousIndexSource() {
-    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
-    switch (geoSrc.fIndexSrc) {
-        case kNone_GeometrySrcType:   // these two don't require
-            break;
-        case kReserved_GeometrySrcType:
-            this->releaseReservedIndexSpace();
-            break;
-        case kBuffer_GeometrySrcType:
-            geoSrc.fIndexBuffer->unref();
-#ifdef SK_DEBUG
-            geoSrc.fIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
-#endif
-            break;
-        default:
-            SkFAIL("Unknown Index Source Type.");
-            break;
-    }
-}
-
-void GrDrawTarget::setVertexSourceToBuffer(const GrVertexBuffer* buffer, size_t vertexStride) {
-    this->releasePreviousVertexSource();
-    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
-    geoSrc.fVertexSrc    = kBuffer_GeometrySrcType;
-    geoSrc.fVertexBuffer = buffer;
-    buffer->ref();
-    geoSrc.fVertexSize = vertexStride;
-}
-
-void GrDrawTarget::setIndexSourceToBuffer(const GrIndexBuffer* buffer) {
-    this->releasePreviousIndexSource();
-    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
-    geoSrc.fIndexSrc     = kBuffer_GeometrySrcType;
-    geoSrc.fIndexBuffer  = buffer;
-    buffer->ref();
-}
-
-void GrDrawTarget::resetVertexSource() {
-    this->releasePreviousVertexSource();
-    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
-    geoSrc.fVertexSrc = kNone_GeometrySrcType;
-}
-
-void GrDrawTarget::resetIndexSource() {
-    this->releasePreviousIndexSource();
-    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
-    geoSrc.fIndexSrc = kNone_GeometrySrcType;
-}
-
-void GrDrawTarget::pushGeometrySource() {
-    this->geometrySourceWillPush();
-    GeometrySrcState& newState = fGeoSrcStateStack.push_back();
-    newState.fIndexSrc = kNone_GeometrySrcType;
-    newState.fVertexSrc = kNone_GeometrySrcType;
-#ifdef SK_DEBUG
-    newState.fVertexCount  = ~0;
-    newState.fVertexBuffer = (GrVertexBuffer*)~0;
-    newState.fIndexCount   = ~0;
-    newState.fIndexBuffer = (GrIndexBuffer*)~0;
-#endif
-}
-
-void GrDrawTarget::popGeometrySource() {
-    // if popping last element then pops are unbalanced with pushes
-    SkASSERT(fGeoSrcStateStack.count() > 1);
-
-    this->geometrySourceWillPop(fGeoSrcStateStack.fromBack(1));
-    this->releasePreviousVertexSource();
-    this->releasePreviousIndexSource();
-    fGeoSrcStateStack.pop_back();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-bool GrDrawTarget::checkDraw(const GrPipelineBuilder& pipelineBuilder,
-                             const GrGeometryProcessor* gp,
-                             GrPrimitiveType type,
-                             int startVertex,
-                             int startIndex,
-                             int vertexCount,
-                             int indexCount) const {
-#ifdef SK_DEBUG
-    const GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
-    int maxVertex = startVertex + vertexCount;
-    int maxValidVertex;
-    switch (geoSrc.fVertexSrc) {
-        case kNone_GeometrySrcType:
-            SkFAIL("Attempting to draw without vertex src.");
-        case kReserved_GeometrySrcType: // fallthrough
-            maxValidVertex = geoSrc.fVertexCount;
-            break;
-        case kBuffer_GeometrySrcType:
-            maxValidVertex = static_cast<int>(geoSrc.fVertexBuffer->gpuMemorySize() /
-                                              geoSrc.fVertexSize);
-            break;
-    }
-    if (maxVertex > maxValidVertex) {
-        SkFAIL("Drawing outside valid vertex range.");
-    }
-    if (indexCount > 0) {
-        int maxIndex = startIndex + indexCount;
-        int maxValidIndex;
-        switch (geoSrc.fIndexSrc) {
-            case kNone_GeometrySrcType:
-                SkFAIL("Attempting to draw indexed geom without index src.");
-            case kReserved_GeometrySrcType: // fallthrough
-                maxValidIndex = geoSrc.fIndexCount;
-                break;
-            case kBuffer_GeometrySrcType:
-                maxValidIndex = static_cast<int>(geoSrc.fIndexBuffer->gpuMemorySize() /
-                                                 sizeof(uint16_t));
-                break;
-        }
-        if (maxIndex > maxValidIndex) {
-            SkFAIL("Index reads outside valid index range.");
-        }
-    }
-
-    SkASSERT(pipelineBuilder.getRenderTarget());
-
-    if (gp) {
-        int numTextures = gp->numTextures();
-        for (int t = 0; t < numTextures; ++t) {
-            GrTexture* texture = gp->texture(t);
-            SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget());
-        }
-    }
-
-    for (int s = 0; s < pipelineBuilder.numColorFragmentStages(); ++s) {
-        const GrProcessor* effect = pipelineBuilder.getColorFragmentStage(s).processor();
-        int numTextures = effect->numTextures();
-        for (int t = 0; t < numTextures; ++t) {
-            GrTexture* texture = effect->texture(t);
-            SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget());
-        }
-    }
-    for (int s = 0; s < pipelineBuilder.numCoverageFragmentStages(); ++s) {
-        const GrProcessor* effect = pipelineBuilder.getCoverageFragmentStage(s).processor();
-        int numTextures = effect->numTextures();
-        for (int t = 0; t < numTextures; ++t) {
-            GrTexture* texture = effect->texture(t);
-            SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget());
-        }
-    }
-
-#endif
-    if (NULL == pipelineBuilder.getRenderTarget()) {
-        return false;
-    }
-    return true;
-}
-
 bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuilder,
                                            const GrProcOptInfo& colorPOI,
                                            const GrProcOptInfo& coveragePOI,
@@ -385,8 +47,20 @@
     if (!pipelineBuilder.willXPNeedDstCopy(*this->caps(), colorPOI, coveragePOI)) {
         return true;
     }
-    SkIRect copyRect;
+
     GrRenderTarget* rt = pipelineBuilder.getRenderTarget();
+
+    if (this->caps()->textureBarrierSupport()) {
+        if (GrTexture* rtTex = rt->asTexture()) {
+            // The render target is a texture, se we can read from it directly in the shader. The XP
+            // will be responsible to detect this situation and request a texture barrier.
+            dstCopy->setTexture(rtTex);
+            dstCopy->setOffset(0, 0);
+            return true;
+        }
+    }
+
+    SkIRect copyRect;
     pipelineBuilder.clip().getConservativeBounds(rt, &copyRect);
 
     if (drawBounds) {
@@ -394,7 +68,8 @@
         drawBounds->roundOut(&drawIBounds);
         if (!copyRect.intersect(drawIBounds)) {
 #ifdef SK_DEBUG
-            SkDebugf("Missed an early reject. Bailing on draw from setupDstReadIfNecessary.\n");
+            GrContextDebugf(fContext, "Missed an early reject. "
+                                      "Bailing on draw from setupDstReadIfNecessary.\n");
 #endif
             return false;
         }
@@ -407,12 +82,18 @@
     // MSAA consideration: When there is support for reading MSAA samples in the shader we could
     // have per-sample dst values by making the copy multisampled.
     GrSurfaceDesc desc;
-    this->initCopySurfaceDstDesc(rt, &desc);
+    if (!this->getGpu()->initCopySurfaceDstDesc(rt, &desc)) {
+        desc.fOrigin = kDefault_GrSurfaceOrigin;
+        desc.fFlags = kRenderTarget_GrSurfaceFlag;
+        desc.fConfig = rt->config();
+    }
+
+
     desc.fWidth = copyRect.width();
     desc.fHeight = copyRect.height();
 
-    SkAutoTUnref<GrTexture> copy(
-        fContext->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch));
+    SkAutoTUnref<GrTexture> copy(fContext->textureProvider()->refScratchTexture(desc,
+        GrTextureProvider::kApprox_ScratchTexMatch));
 
     if (!copy) {
         SkDebugf("Failed to create temporary copy of destination texture.\n");
@@ -428,103 +109,24 @@
     }
 }
 
-void GrDrawTarget::drawIndexed(GrPipelineBuilder* pipelineBuilder,
-                               const GrGeometryProcessor* gp,
-                               GrPrimitiveType type,
-                               int startVertex,
-                               int startIndex,
-                               int vertexCount,
-                               int indexCount,
-                               const SkRect* devBounds) {
-    SkASSERT(pipelineBuilder);
-    if (indexCount > 0 &&
-        this->checkDraw(*pipelineBuilder, gp, type, startVertex, startIndex, vertexCount,
-                        indexCount)) {
-
-        // Setup clip
-        GrScissorState scissorState;
-        GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
-        GrPipelineBuilder::AutoRestoreStencil ars;
-        if (!this->setupClip(pipelineBuilder, &arfp, &ars, &scissorState, devBounds)) {
-            return;
-        }
-
-        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 (devBounds) {
-            info.setDevBounds(*devBounds);
-        }
-
-        GrDrawTarget::PipelineInfo pipelineInfo(pipelineBuilder, &scissorState, gp, devBounds,
-                                                this);
-        if (pipelineInfo.mustSkipDraw()) {
-            return;
-        }
-
-        this->setDrawBuffers(&info, gp->getVertexStride());
-
-        this->onDraw(gp, info, pipelineInfo);
+void GrDrawTarget::flush() {
+    if (fFlushing) {
+        return;
     }
+    fFlushing = true;
+
+    this->getGpu()->saveActiveTraceMarkers();
+
+    this->onFlush();
+
+    this->getGpu()->restoreActiveTraceMarkers();
+
+    fFlushing = false;
+    this->reset();
 }
 
-void GrDrawTarget::drawNonIndexed(GrPipelineBuilder* pipelineBuilder,
-                                  const GrGeometryProcessor* gp,
-                                  GrPrimitiveType type,
-                                  int startVertex,
-                                  int vertexCount,
-                                  const SkRect* devBounds) {
-    SkASSERT(pipelineBuilder);
-    if (vertexCount > 0 && this->checkDraw(*pipelineBuilder, gp, type, startVertex, -1, vertexCount,
-                                           -1)) {
-
-        // Setup clip
-        GrScissorState scissorState;
-        GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
-        GrPipelineBuilder::AutoRestoreStencil ars;
-        if (!this->setupClip(pipelineBuilder, &arfp, &ars, &scissorState, devBounds)) {
-            return;
-        }
-
-        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 (devBounds) {
-            info.setDevBounds(*devBounds);
-        }
-
-        GrDrawTarget::PipelineInfo pipelineInfo(pipelineBuilder, &scissorState, gp, devBounds,
-                                                this);
-        if (pipelineInfo.mustSkipDraw()) {
-            return;
-        }
-
-        this->setDrawBuffers(&info, gp->getVertexStride());
-
-        this->onDraw(gp, info, pipelineInfo);
-    }
-}
-
-
 void GrDrawTarget::drawBatch(GrPipelineBuilder* pipelineBuilder,
-                             GrBatch* batch,
-                             const SkRect* devBounds) {
+                             GrBatch* batch) {
     SkASSERT(pipelineBuilder);
     // TODO some kind of checkdraw, but not at this level
 
@@ -532,11 +134,17 @@
     GrScissorState scissorState;
     GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
     GrPipelineBuilder::AutoRestoreStencil ars;
-    if (!this->setupClip(pipelineBuilder, &arfp, &ars, &scissorState, devBounds)) {
+    if (!this->setupClip(pipelineBuilder, &arfp, &ars, &scissorState, &batch->bounds())) {
         return;
     }
 
-    GrDrawTarget::PipelineInfo pipelineInfo(pipelineBuilder, &scissorState, batch, devBounds, this);
+    // Batch bounds are tight, so for dev copies
+    // TODO move this into setupDstReadIfNecessary when paths are in batch
+    SkRect bounds = batch->bounds();
+    bounds.outset(0.5f, 0.5f);
+
+    GrDrawTarget::PipelineInfo pipelineInfo(pipelineBuilder, &scissorState, batch, &bounds,
+                                            this);
     if (pipelineInfo.mustSkipDraw()) {
         return;
     }
@@ -563,7 +171,7 @@
 }
 
 void GrDrawTarget::getPathStencilSettingsForFilltype(GrPathRendering::FillType fill,
-                                                     const GrStencilBuffer* sb,
+                                                     const GrStencilAttachment* sb,
                                                      GrStencilSettings* outStencilSettings) {
 
     switch (fill) {
@@ -585,7 +193,7 @@
                                GrPathRendering::FillType fill) {
     // TODO: extract portions of checkDraw that are relevant to path stenciling.
     SkASSERT(path);
-    SkASSERT(this->caps()->pathRenderingSupport());
+    SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport());
     SkASSERT(pipelineBuilder);
 
     // Setup clip
@@ -599,7 +207,7 @@
     // set stencil settings for path
     GrStencilSettings stencilSettings;
     GrRenderTarget* rt = pipelineBuilder->getRenderTarget();
-    GrStencilBuffer* sb = rt->renderTargetPriv().attachStencilBuffer();
+    GrStencilAttachment* sb = rt->renderTargetPriv().attachStencilAttachment();
     this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings);
 
     this->onStencilPath(*pipelineBuilder, pathProc, path, scissorState, stencilSettings);
@@ -611,7 +219,7 @@
                             GrPathRendering::FillType fill) {
     // TODO: extract portions of checkDraw that are relevant to path rendering.
     SkASSERT(path);
-    SkASSERT(this->caps()->pathRenderingSupport());
+    SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport());
     SkASSERT(pipelineBuilder);
 
     SkRect devBounds = path->getBounds();
@@ -628,7 +236,7 @@
     // set stencil settings for path
     GrStencilSettings stencilSettings;
     GrRenderTarget* rt = pipelineBuilder->getRenderTarget();
-    GrStencilBuffer* sb = rt->renderTargetPriv().attachStencilBuffer();
+    GrStencilAttachment* sb = rt->renderTargetPriv().attachStencilAttachment();
     this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings);
 
     GrDrawTarget::PipelineInfo pipelineInfo(pipelineBuilder, &scissorState, pathProc, &devBounds,
@@ -649,7 +257,7 @@
                              PathTransformType transformType,
                              int count,
                              GrPathRendering::FillType fill) {
-    SkASSERT(this->caps()->pathRenderingSupport());
+    SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport());
     SkASSERT(pathRange);
     SkASSERT(indices);
     SkASSERT(0 == reinterpret_cast<long>(indices) % GrPathRange::PathIndexSizeInBytes(indexType));
@@ -668,7 +276,7 @@
     // set stencil settings for path
     GrStencilSettings stencilSettings;
     GrRenderTarget* rt = pipelineBuilder->getRenderTarget();
-    GrStencilBuffer* sb = rt->renderTargetPriv().attachStencilBuffer();
+    GrStencilAttachment* sb = rt->renderTargetPriv().attachStencilAttachment();
     this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings);
 
     // Don't compute a bounding box for dst copy texture, we'll opt
@@ -684,6 +292,17 @@
                       transformType, count, stencilSettings, pipelineInfo);
 }
 
+void GrDrawTarget::drawRect(GrPipelineBuilder* pipelineBuilder,
+                            GrColor color,
+                            const SkMatrix& viewMatrix,
+                            const SkRect& rect,
+                            const SkRect* localRect,
+                            const SkMatrix* localMatrix) {
+   SkAutoTUnref<GrBatch> batch(GrRectBatch::Create(color, viewMatrix, rect, localRect,
+                                                   localMatrix));
+   this->drawBatch(pipelineBuilder, batch);
+}
+
 void GrDrawTarget::clear(const SkIRect* rect,
                          GrColor color,
                          bool canIgnoreRect,
@@ -749,126 +368,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void GrDrawTarget::drawIndexedInstances(GrPipelineBuilder* pipelineBuilder,
-                                        const GrGeometryProcessor* gp,
-                                        GrPrimitiveType type,
-                                        int instanceCount,
-                                        int verticesPerInstance,
-                                        int indicesPerInstance,
-                                        const SkRect* devBounds) {
-    SkASSERT(pipelineBuilder);
-
-    if (!verticesPerInstance || !indicesPerInstance) {
-        return;
-    }
-
-    int maxInstancesPerDraw = this->indexCountInCurrentSource() / indicesPerInstance;
-    if (!maxInstancesPerDraw) {
-        return;
-    }
-
-    // Setup clip
-    GrScissorState scissorState;
-    GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
-    GrPipelineBuilder::AutoRestoreStencil ars;
-    if (!this->setupClip(pipelineBuilder, &arfp, &ars, &scissorState, devBounds)) {
-        return;
-    }
-
-    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 (devBounds) {
-        info.setDevBounds(*devBounds);
-    }
-
-    while (instanceCount) {
-        info.fInstanceCount = SkTMin(instanceCount, maxInstancesPerDraw);
-        info.fVertexCount = info.fInstanceCount * verticesPerInstance;
-        info.fIndexCount = info.fInstanceCount * indicesPerInstance;
-
-        if (this->checkDraw(*pipelineBuilder,
-                            gp,
-                            type,
-                            info.fStartVertex,
-                            info.fStartIndex,
-                            info.fVertexCount,
-                            info.fIndexCount)) {
-
-            GrDrawTarget::PipelineInfo pipelineInfo(pipelineBuilder, &scissorState, gp, devBounds,
-                                                    this);
-            if (pipelineInfo.mustSkipDraw()) {
-                return;
-            }
-
-            this->setDrawBuffers(&info, gp->getVertexStride());
-            this->onDraw(gp, info, pipelineInfo);
-        }
-        info.fStartVertex += info.fVertexCount;
-        instanceCount -= info.fInstanceCount;
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-GrDrawTarget::AutoReleaseGeometry::AutoReleaseGeometry(
-                                         GrDrawTarget*  target,
-                                         int vertexCount,
-                                         size_t vertexStride,
-                                         int indexCount) {
-    fTarget = NULL;
-    this->set(target, vertexCount, vertexStride, indexCount);
-}
-
-GrDrawTarget::AutoReleaseGeometry::AutoReleaseGeometry() {
-    fTarget = NULL;
-}
-
-GrDrawTarget::AutoReleaseGeometry::~AutoReleaseGeometry() {
-    this->reset();
-}
-
-bool GrDrawTarget::AutoReleaseGeometry::set(GrDrawTarget*  target,
-                                            int vertexCount,
-                                            size_t vertexStride,
-                                            int indexCount) {
-    this->reset();
-    fTarget = target;
-    bool success = true;
-    if (fTarget) {
-        success = target->reserveVertexAndIndexSpace(vertexCount,
-                                                     vertexStride,
-                                                     indexCount,
-                                                     &fVertices,
-                                                     &fIndices);
-        if (!success) {
-            fTarget = NULL;
-            this->reset();
-        }
-    }
-    SkASSERT(success == SkToBool(fTarget));
-    return success;
-}
-
-void GrDrawTarget::AutoReleaseGeometry::reset() {
-    if (fTarget) {
-        if (fVertices) {
-            fTarget->resetVertexSource();
-        }
-        if (fIndices) {
-            fTarget->resetIndexSource();
-        }
-        fTarget = NULL;
-    }
-    fVertices = NULL;
-    fIndices = NULL;
-}
-
 namespace {
 // returns true if the read/written rect intersects the src/dst and false if not.
 bool clip_srcrect_and_dstpoint(const GrSurface* dst,
@@ -941,7 +440,8 @@
         return true;
     }
 
-    if (this->onCopySurface(dst, src, clippedSrcRect, clippedDstPoint)) {
+    if (this->getGpu()->canCopySurface(dst, src, clippedSrcRect, clippedDstPoint)) {
+        this->onCopySurface(dst, src, clippedSrcRect, clippedDstPoint);
         return true;
     }
 
@@ -985,23 +485,8 @@
                                    &clippedDstPoint)) {
         return true;
     }
-    return this->internalCanCopySurface(dst, src, clippedSrcRect, clippedDstPoint);
-}
-
-bool GrDrawTarget::internalCanCopySurface(const GrSurface* dst,
-                                          const GrSurface* src,
-                                          const SkIRect& clippedSrcRect,
-                                          const SkIPoint& clippedDstPoint) {
-    // Check that the read/write rects are contained within the src/dst bounds.
-    SkASSERT(!clippedSrcRect.isEmpty());
-    SkASSERT(SkIRect::MakeWH(src->width(), src->height()).contains(clippedSrcRect));
-    SkASSERT(clippedDstPoint.fX >= 0 && clippedDstPoint.fY >= 0);
-    SkASSERT(clippedDstPoint.fX + clippedSrcRect.width() <= dst->width() &&
-             clippedDstPoint.fY + clippedSrcRect.height() <= dst->height());
-
-    // The base class can do it as a draw or the subclass may be able to handle it.
     return ((dst != src) && dst->asRenderTarget() && src->asTexture()) ||
-           this->onCanCopySurface(dst, src, clippedSrcRect, clippedDstPoint);
+           this->getGpu()->canCopySurface(dst, src, clippedSrcRect, clippedDstPoint);
 }
 
 void GrDrawTarget::setupPipeline(const PipelineInfo& pipelineInfo,
@@ -1047,33 +532,108 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+void GrShaderCaps::reset() {
+    fShaderDerivativeSupport = false;
+    fGeometryShaderSupport = false;
+    fPathRenderingSupport = false;
+    fDstReadInShaderSupport = false;
+    fDualSourceBlendingSupport = false;
+
+    fShaderPrecisionVaries = false;
+}
+
+GrShaderCaps& GrShaderCaps::operator=(const GrShaderCaps& other) {
+    fShaderDerivativeSupport = other.fShaderDerivativeSupport;
+    fGeometryShaderSupport = other.fGeometryShaderSupport;
+    fPathRenderingSupport = other.fPathRenderingSupport;
+    fDstReadInShaderSupport = other.fDstReadInShaderSupport;
+    fDualSourceBlendingSupport = other.fDualSourceBlendingSupport;
+
+    fShaderPrecisionVaries = other.fShaderPrecisionVaries;
+    for (int s = 0; s < kGrShaderTypeCount; ++s) {
+        for (int p = 0; p < kGrSLPrecisionCount; ++p) {
+            fFloatPrecisions[s][p] = other.fFloatPrecisions[s][p];
+        }
+    }
+    return *this;
+}
+
+static const char* shader_type_to_string(GrShaderType type) {
+    switch (type) {
+    case kVertex_GrShaderType:
+        return "vertex";
+    case kGeometry_GrShaderType:
+        return "geometry";
+    case kFragment_GrShaderType:
+        return "fragment";
+    }
+    return "";
+}
+
+static const char* precision_to_string(GrSLPrecision p) {
+    switch (p) {
+    case kLow_GrSLPrecision:
+        return "low";
+    case kMedium_GrSLPrecision:
+        return "medium";
+    case kHigh_GrSLPrecision:
+        return "high";
+    }
+    return "";
+}
+
+SkString GrShaderCaps::dump() const {
+    SkString r;
+    static const char* gNY[] = { "NO", "YES" };
+    r.appendf("Shader Derivative Support          : %s\n", gNY[fShaderDerivativeSupport]);
+    r.appendf("Geometry Shader Support            : %s\n", gNY[fGeometryShaderSupport]);
+    r.appendf("Path Rendering Support             : %s\n", gNY[fPathRenderingSupport]);
+    r.appendf("Dst Read In Shader Support         : %s\n", gNY[fDstReadInShaderSupport]);
+    r.appendf("Dual Source Blending Support       : %s\n", gNY[fDualSourceBlendingSupport]);
+
+    r.appendf("Shader Float Precisions (varies: %s):\n", gNY[fShaderPrecisionVaries]);
+
+    for (int s = 0; s < kGrShaderTypeCount; ++s) {
+        GrShaderType shaderType = static_cast<GrShaderType>(s);
+        r.appendf("\t%s:\n", shader_type_to_string(shaderType));
+        for (int p = 0; p < kGrSLPrecisionCount; ++p) {
+            if (fFloatPrecisions[s][p].supported()) {
+                GrSLPrecision precision = static_cast<GrSLPrecision>(p);
+                r.appendf("\t\t%s: log_low: %d log_high: %d bits: %d\n",
+                    precision_to_string(precision),
+                    fFloatPrecisions[s][p].fLogRangeLow,
+                    fFloatPrecisions[s][p].fLogRangeHigh,
+                    fFloatPrecisions[s][p].fBits);
+            }
+        }
+    }
+
+    return r;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 void GrDrawTargetCaps::reset() {
     fMipMapSupport = false;
     fNPOTTextureTileSupport = false;
     fTwoSidedStencilSupport = false;
     fStencilWrapOpsSupport = false;
-    fHWAALineSupport = false;
-    fShaderDerivativeSupport = false;
-    fGeometryShaderSupport = false;
-    fDualSourceBlendingSupport = false;
-    fPathRenderingSupport = false;
-    fDstReadInShaderSupport = false;
     fDiscardRenderTargetSupport = false;
     fReuseScratchTextures = true;
     fGpuTracingSupport = false;
     fCompressedTexSubImageSupport = false;
     fOversizedStencilSupport = false;
+    fTextureBarrierSupport = false;
 
     fUseDrawInsteadOfClear = false;
 
+    fBlendEquationSupport = kBasic_BlendEquationSupport;
     fMapBufferFlags = kNone_MapFlags;
 
     fMaxRenderTargetSize = 0;
     fMaxTextureSize = 0;
     fMaxSampleCount = 0;
 
-    fShaderPrecisionVaries = false;
-
     memset(fConfigRenderSupport, 0, sizeof(fConfigRenderSupport));
     memset(fConfigTextureSupport, 0, sizeof(fConfigTextureSupport));
 }
@@ -1083,20 +643,16 @@
     fNPOTTextureTileSupport = other.fNPOTTextureTileSupport;
     fTwoSidedStencilSupport = other.fTwoSidedStencilSupport;
     fStencilWrapOpsSupport = other.fStencilWrapOpsSupport;
-    fHWAALineSupport = other.fHWAALineSupport;
-    fShaderDerivativeSupport = other.fShaderDerivativeSupport;
-    fGeometryShaderSupport = other.fGeometryShaderSupport;
-    fDualSourceBlendingSupport = other.fDualSourceBlendingSupport;
-    fPathRenderingSupport = other.fPathRenderingSupport;
-    fDstReadInShaderSupport = other.fDstReadInShaderSupport;
     fDiscardRenderTargetSupport = other.fDiscardRenderTargetSupport;
     fReuseScratchTextures = other.fReuseScratchTextures;
     fGpuTracingSupport = other.fGpuTracingSupport;
     fCompressedTexSubImageSupport = other.fCompressedTexSubImageSupport;
     fOversizedStencilSupport = other.fOversizedStencilSupport;
+    fTextureBarrierSupport = other.fTextureBarrierSupport;
 
     fUseDrawInsteadOfClear = other.fUseDrawInsteadOfClear;
 
+    fBlendEquationSupport = other.fBlendEquationSupport;
     fMapBufferFlags = other.fMapBufferFlags;
 
     fMaxRenderTargetSize = other.fMaxRenderTargetSize;
@@ -1106,12 +662,6 @@
     memcpy(fConfigRenderSupport, other.fConfigRenderSupport, sizeof(fConfigRenderSupport));
     memcpy(fConfigTextureSupport, other.fConfigTextureSupport, sizeof(fConfigTextureSupport));
 
-    fShaderPrecisionVaries = other.fShaderPrecisionVaries;
-    for (int s = 0; s < kGrShaderTypeCount; ++s) {
-        for (int p = 0; p < kGrSLPrecisionCount; ++p) {
-            fFloatPrecisions[s][p] = other.fFloatPrecisions[s][p];
-        }
-    }
     return *this;
 }
 
@@ -1135,30 +685,6 @@
     return str;
 }
 
-static const char* shader_type_to_string(GrShaderType type) {
-    switch (type) {
-        case kVertex_GrShaderType:
-            return "vertex";
-        case kGeometry_GrShaderType:
-            return "geometry";
-        case kFragment_GrShaderType:
-            return "fragment";
-    }
-    return "";
-}
-
-static const char* precision_to_string(GrSLPrecision p) {
-    switch (p) {
-        case kLow_GrSLPrecision:
-            return "low";
-        case kMedium_GrSLPrecision:
-            return "medium";
-        case kHigh_GrSLPrecision:
-            return "high";
-    }
-    return "";
-}
-
 SkString GrDrawTargetCaps::dump() const {
     SkString r;
     static const char* gNY[] = {"NO", "YES"};
@@ -1166,23 +692,30 @@
     r.appendf("NPOT Texture Tile Support          : %s\n", gNY[fNPOTTextureTileSupport]);
     r.appendf("Two Sided Stencil Support          : %s\n", gNY[fTwoSidedStencilSupport]);
     r.appendf("Stencil Wrap Ops  Support          : %s\n", gNY[fStencilWrapOpsSupport]);
-    r.appendf("HW AA Lines Support                : %s\n", gNY[fHWAALineSupport]);
-    r.appendf("Shader Derivative Support          : %s\n", gNY[fShaderDerivativeSupport]);
-    r.appendf("Geometry Shader Support            : %s\n", gNY[fGeometryShaderSupport]);
-    r.appendf("Dual Source Blending Support       : %s\n", gNY[fDualSourceBlendingSupport]);
-    r.appendf("Path Rendering Support             : %s\n", gNY[fPathRenderingSupport]);
-    r.appendf("Dst Read In Shader Support         : %s\n", gNY[fDstReadInShaderSupport]);
     r.appendf("Discard Render Target Support      : %s\n", gNY[fDiscardRenderTargetSupport]);
     r.appendf("Reuse Scratch Textures             : %s\n", gNY[fReuseScratchTextures]);
     r.appendf("Gpu Tracing Support                : %s\n", gNY[fGpuTracingSupport]);
     r.appendf("Compressed Update Support          : %s\n", gNY[fCompressedTexSubImageSupport]);
     r.appendf("Oversized Stencil Support          : %s\n", gNY[fOversizedStencilSupport]);
+    r.appendf("Texture Barrier Support            : %s\n", gNY[fTextureBarrierSupport]);
     r.appendf("Draw Instead of Clear [workaround] : %s\n", gNY[fUseDrawInsteadOfClear]);
 
     r.appendf("Max Texture Size                   : %d\n", fMaxTextureSize);
     r.appendf("Max Render Target Size             : %d\n", fMaxRenderTargetSize);
     r.appendf("Max Sample Count                   : %d\n", fMaxSampleCount);
 
+    static const char* kBlendEquationSupportNames[] = {
+        "Basic",
+        "Advanced",
+        "Advanced Coherent",
+    };
+    GR_STATIC_ASSERT(0 == kBasic_BlendEquationSupport);
+    GR_STATIC_ASSERT(1 == kAdvanced_BlendEquationSupport);
+    GR_STATIC_ASSERT(2 == kAdvancedCoherent_BlendEquationSupport);
+    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kBlendEquationSupportNames) == kLast_BlendEquationSupport + 1);
+
+    r.appendf("Blend Equation Support             : %s\n",
+              kBlendEquationSupportNames[fBlendEquationSupport]);
     r.appendf("Map Buffer Support                 : %s\n",
               map_flags_to_string(fMapBufferFlags).c_str());
 
@@ -1236,35 +769,9 @@
                   gNY[fConfigTextureSupport[i]]);
     }
 
-    r.appendf("Shader Float Precisions (varies: %s):\n", gNY[fShaderPrecisionVaries]);
-
-    for (int s = 0; s < kGrShaderTypeCount; ++s) {
-        GrShaderType shaderType = static_cast<GrShaderType>(s);
-        r.appendf("\t%s:\n", shader_type_to_string(shaderType));
-        for (int p = 0; p < kGrSLPrecisionCount; ++p) {
-            if (fFloatPrecisions[s][p].supported()) {
-                GrSLPrecision precision = static_cast<GrSLPrecision>(p);
-                r.appendf("\t\t%s: log_low: %d log_high: %d bits: %d\n",
-                          precision_to_string(precision),
-                          fFloatPrecisions[s][p].fLogRangeLow,
-                          fFloatPrecisions[s][p].fLogRangeHigh,
-                          fFloatPrecisions[s][p].fBits);
-            }
-        }
-    }
-
     return r;
 }
 
-uint32_t GrDrawTargetCaps::CreateUniqueID() {
-    static int32_t gUniqueID = SK_InvalidUniqueID;
-    uint32_t id;
-    do {
-        id = static_cast<uint32_t>(sk_atomic_inc(&gUniqueID) + 1);
-    } while (id == SK_InvalidUniqueID);
-    return id;
-}
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 bool GrClipTarget::setupClip(GrPipelineBuilder* pipelineBuilder,
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index 88207da..84f7eb2 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -28,7 +28,6 @@
 #include "SkTypes.h"
 #include "SkXfermode.h"
 
-class GrBatch;
 class GrClip;
 class GrDrawTargetCaps;
 class GrPath;
@@ -47,207 +46,26 @@
     // The context may not be fully constructed and should not be used during GrDrawTarget
     // construction.
     GrDrawTarget(GrContext* context);
-    virtual ~GrDrawTarget();
+
+    virtual ~GrDrawTarget() {}
+
+    /**
+     * Empties the draw buffer of any queued up draws.
+     */
+    void reset() { this->onReset(); }
+
+    /**
+     * This plays any queued up draws to its GrGpu target. It also resets this object (i.e. flushing
+     * is destructive).
+     */
+    void flush();
 
     /**
      * Gets the capabilities of the draw target.
      */
     const GrDrawTargetCaps* caps() const { return fCaps.get(); }
 
-    /**
-     * There are two 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
-     * specified it can be used for multiple draws. However, the time at which
-     * the geometry data is no longer editable depends on the source type.
-     *
-     * Sometimes it is necessary to perform a draw while upstack code has
-     * already specified geometry that it isn't finished with. So there are push
-     * and pop methods. This allows the client to push the sources, draw
-     * something using alternate sources, and then pop to restore the original
-     * sources.
-     *
-     * Aside from pushes and pops, a source remains valid until another source
-     * is set or resetVertexSource / resetIndexSource is called. Drawing from
-     * a reset source is an error.
-     *
-     * The two types of sources are:
-     *
-     * 1. Reserve. This is most useful when the caller has data it must
-     *    transform before drawing and is not long-lived. The caller requests
-     *    that the draw target make room for some amount of vertex and/or index
-     *    data. The target provides ptrs to hold the vertex and/or index data.
-     *
-     *    The data is writable up until the next drawIndexed, drawNonIndexed,
-     *    drawIndexedInstances, drawRect, copySurface, or pushGeometrySource. At
-     *    this point the data is frozen and the ptrs are no longer valid.
-     *
-     *    Where the space is allocated and how it is uploaded to the GPU is
-     *    subclass-dependent.
-     *
-     * 2. Vertex and Index Buffers. This is most useful for geometry that will
-     *    is long-lived. When the data in the buffer is consumed depends on the
-     *    GrDrawTarget subclass. For deferred subclasses the caller has to
-     *    guarantee that the data is still available in the buffers at playback.
-     *    (TODO: Make this more automatic as we have done for read/write pixels)
-     */
-
-    /**
-     * Reserves space for vertices and/or indices. Zero can be specifed as
-     * either the vertex or index count if the caller desires to only reserve
-     * space for only indices or only vertices. If zero is specifed for
-     * vertexCount then the vertex source will be unmodified and likewise for
-     * indexCount.
-     *
-     * If the function returns true then the reserve suceeded and the vertices
-     * and indices pointers will point to the space created.
-     *
-     * If the target cannot make space for the request then this function will
-     * return false. If vertexCount was non-zero then upon failure the vertex
-     * source is reset and likewise for indexCount.
-     *
-     * The pointers to the space allocated for vertices and indices remain valid
-     * until a drawIndexed, drawNonIndexed, drawIndexedInstances, drawRect,
-     * copySurface, or push/popGeomtrySource is called. At that point logically a
-     * snapshot of the data is made and the pointers are invalid.
-     *
-     * @param vertexCount  the number of vertices to reserve space for. Can be
-     *                     0. Vertex size is queried from the current GrPipelineBuilder.
-     * @param indexCount   the number of indices to reserve space for. Can be 0.
-     * @param vertices     will point to reserved vertex space if vertexCount is
-     *                     non-zero. Illegal to pass NULL if vertexCount > 0.
-     * @param indices      will point to reserved index space if indexCount is
-     *                     non-zero. Illegal to pass NULL if indexCount > 0.
-     */
-     bool reserveVertexAndIndexSpace(int vertexCount,
-                                     size_t vertexStride,
-                                     int indexCount,
-                                     void** vertices,
-                                     void** indices);
-
-    /**
-     * Provides hints to caller about the number of vertices and indices
-     * that can be allocated cheaply. This can be useful if caller is reserving
-     * space but doesn't know exactly how much geometry is needed.
-     *
-     * Also may hint whether the draw target should be flushed first. This is
-     * useful for deferred targets.
-     *
-     * @param vertexCount  in: hint about how many vertices the caller would
-     *                     like to allocate. Vertex size is queried from the
-     *                     current GrPipelineBuilder.
-     *                     out: a hint about the number of vertices that can be
-     *                     allocated cheaply. Negative means no hint.
-     *                     Ignored if NULL.
-     * @param indexCount   in: hint about how many indices the caller would
-     *                     like to allocate.
-     *                     out: a hint about the number of indices that can be
-     *                     allocated cheaply. Negative means no hint.
-     *                     Ignored if NULL.
-     *
-     * @return  true if target should be flushed based on the input values.
-     */
-    virtual bool geometryHints(size_t vertexStride, int* vertexCount, int* indexCount) const;
-
-    /**
-     * Sets source of vertex data for the next draw. Data does not have to be
-     * in the buffer until drawIndexed, drawNonIndexed, or drawIndexedInstances.
-     *
-     * @param buffer        vertex buffer containing vertex data. Must be
-     *                      unlocked before draw call. Vertex size is queried
-     *                      from current GrPipelineBuilder.
-     */
-    void setVertexSourceToBuffer(const GrVertexBuffer* buffer, size_t vertexStride);
-
-    /**
-     * Sets source of index data for the next indexed draw. Data does not have
-     * to be in the buffer until drawIndexed.
-     *
-     * @param buffer index buffer containing indices. Must be unlocked
-     *               before indexed draw call.
-     */
-    void setIndexSourceToBuffer(const GrIndexBuffer* buffer);
-
-    /**
-     * Resets vertex source. Drawing from reset vertices is illegal. Set vertex
-     * source to reserved, array, or buffer before next draw. May be able to free
-     * up temporary storage allocated by setVertexSourceToArray or
-     * reserveVertexSpace.
-     */
-    void resetVertexSource();
-
-    /**
-     * Resets index source. Indexed Drawing from reset indices is illegal. Set
-     * index source to reserved, array, or buffer before next indexed draw. May
-     * be able to free up temporary storage allocated by setIndexSourceToArray
-     * or reserveIndexSpace.
-     */
-    void resetIndexSource();
-
-    /**
-     * Query to find out if the vertex or index source is reserved.
-     */
-    bool hasReservedVerticesOrIndices() const {
-        return kReserved_GeometrySrcType == this->getGeomSrc().fVertexSrc ||
-        kReserved_GeometrySrcType == this->getGeomSrc().fIndexSrc;
-    }
-
-    /**
-     * Pushes and resets the vertex/index sources. Any reserved vertex / index
-     * data is finalized (i.e. cannot be updated after the matching pop but can
-     * be drawn from). Must be balanced by a pop.
-     */
-    void pushGeometrySource();
-
-    /**
-     * Pops the vertex / index sources from the matching push.
-     */
-    void popGeometrySource();
-
-    /**
-     * Draws indexed geometry using the current state and current vertex / index
-     * sources.
-     *
-     * @param type         The type of primitives to draw.
-     * @param startVertex  the vertex in the vertex array/buffer corresponding
-     *                     to index 0
-     * @param startIndex   first index to read from index src.
-     * @param vertexCount  one greater than the max index.
-     * @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(GrPipelineBuilder*,
-                     const GrGeometryProcessor*,
-                     GrPrimitiveType type,
-                     int startVertex,
-                     int startIndex,
-                     int vertexCount,
-                     int indexCount,
-                     const SkRect* devBounds = NULL);
-
-    /**
-     * Draws non-indexed geometry using the current state and current vertex
-     * sources.
-     *
-     * @param type         The type of primitives to draw.
-     * @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(GrPipelineBuilder*,
-                        const GrGeometryProcessor*,
-                        GrPrimitiveType type,
-                        int startVertex,
-                        int vertexCount,
-                        const SkRect* devBounds = NULL);
-
-    // TODO devbounds should live on the batch
-    void drawBatch(GrPipelineBuilder*, GrBatch*, const SkRect* devBounds = NULL);
+    void drawBatch(GrPipelineBuilder*, GrBatch*);
 
     /**
      * Draws path into the stencil buffer. The fill must be either even/odd or
@@ -289,8 +107,7 @@
                    GrPathRendering::FillType fill);
 
     /**
-     * Helper function for drawing rects. It performs a geometry src push and pop
-     * and thus will finalize any reserved geometry.
+     * Helper function for drawing rects.
      *
      * @param rect        the rect to draw
      * @param localRect   optional rect that specifies local coords to map onto
@@ -305,9 +122,7 @@
                   const SkMatrix& viewMatrix,
                   const SkRect& rect,
                   const SkRect* localRect,
-                  const SkMatrix* localMatrix) {
-        this->onDrawRect(pipelineBuilder, color, viewMatrix, rect, localRect, localMatrix);
-    }
+                  const SkMatrix* localMatrix);
 
     /**
      * Helper for drawRect when the caller doesn't need separate local rects or matrices.
@@ -322,43 +137,6 @@
         this->drawRect(ds, color, viewM, rect, 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
-     * the index source must have the form i[k+I] == i[k] + V. Also, all indices
-     * i[kI] ... i[(k+1)I-1] must be elements of the range kV ... (k+1)V-1. As a
-     * concrete example, the following index buffer for drawing a series of
-     * quads each as two triangles each satisfies these conditions with V=4 and
-     * I=6:
-     *      (0,1,2,0,2,3, 4,5,6,4,6,7, 8,9,10,8,10,11, ...)
-     *
-     * The call assumes that the pattern of indices fills the entire index
-     * 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. Moreover, when drawIndexedInstances is called
-     * multiple times it may be possible for GrDrawTarget to group them into a
-     * single GPU draw.
-     *
-     * @param type          the type of primitives to draw
-     * @param instanceCount the number of instances to draw. Each instance
-     *                      consists of verticesPerInstance vertices indexed by
-     *                      indicesPerInstance indices drawn as the primitive
-     *                      type specified by type.
-     * @param verticesPerInstance   The number of vertices in each instance (V
-     *                              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.
-     */
-    void drawIndexedInstances(GrPipelineBuilder*,
-                              const GrGeometryProcessor*,
-                              GrPrimitiveType type,
-                              int instanceCount,
-                              int verticesPerInstance,
-                              int indicesPerInstance,
-                              const SkRect* devBounds = NULL);
 
     /**
      * Clear the passed in render target. Ignores the GrPipelineBuilder and clip. Clears the whole
@@ -424,55 +202,6 @@
      */
     virtual void purgeResources() {};
 
-    ////////////////////////////////////////////////////////////////////////////
-
-    class AutoReleaseGeometry : public ::SkNoncopyable {
-    public:
-        AutoReleaseGeometry(GrDrawTarget*  target,
-                            int            vertexCount,
-                            size_t         vertexStride,
-                            int            indexCount);
-        AutoReleaseGeometry();
-        ~AutoReleaseGeometry();
-        bool set(GrDrawTarget*  target,
-                 int            vertexCount,
-                 size_t         vertexStride,
-                 int            indexCount);
-        bool succeeded() const { return SkToBool(fTarget); }
-        void* vertices() const { SkASSERT(this->succeeded()); return fVertices; }
-        void* indices() const { SkASSERT(this->succeeded()); return fIndices; }
-        SkPoint* positions() const {
-            return static_cast<SkPoint*>(this->vertices());
-        }
-
-    private:
-        void reset();
-
-        GrDrawTarget* fTarget;
-        void*         fVertices;
-        void*         fIndices;
-    };
-
-    ////////////////////////////////////////////////////////////////////////////
-
-    /**
-     * Saves the geometry src state at construction and restores in the destructor. It also saves
-     * and then restores the vertex attrib state.
-     */
-    class AutoGeometryPush : public ::SkNoncopyable {
-    public:
-        AutoGeometryPush(GrDrawTarget* target) {
-            SkASSERT(target);
-            fTarget = target;
-            target->pushGeometrySource();
-        }
-
-        ~AutoGeometryPush() { fTarget->popGeometrySource(); }
-
-    private:
-        GrDrawTarget*                           fTarget;
-    };
-
     ///////////////////////////////////////////////////////////////////////////
     // Draw execution tracking (for font atlases and other resources)
     class DrawToken {
@@ -490,151 +219,25 @@
 
     virtual DrawToken getCurrentDrawToken() { return DrawToken(this, 0); }
 
-    /**
-     * Used to communicate draws to GPUs / subclasses
-     */
-    class DrawInfo {
-    public:
-        DrawInfo() { fDevBounds = NULL; }
-        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; }
-
-        void setPrimitiveType(GrPrimitiveType type) { fPrimitiveType = type; }
-        void setStartVertex(int startVertex) { fStartVertex = startVertex; }
-        void setStartIndex(int startIndex) { fStartIndex = startIndex; }
-        void setVertexCount(int vertexCount) { fVertexCount = vertexCount; }
-        void setIndexCount(int indexCount) { fIndexCount = indexCount; }
-        void setVerticesPerInstance(int verticesPerI) { fVerticesPerInstance = verticesPerI; }
-        void setIndicesPerInstance(int indicesPerI) { fIndicesPerInstance = indicesPerI; }
-        void setInstanceCount(int instanceCount) { fInstanceCount = instanceCount; }
-
-        bool isIndexed() const { return fIndexCount > 0; }
-#ifdef SK_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 GrVertexBuffer* vertexBuffer() const { return fVertexBuffer.get(); }
-        const GrIndexBuffer* indexBuffer() const { return fIndexBuffer.get(); }
-        void setVertexBuffer(const GrVertexBuffer* vb) {
-            fVertexBuffer.reset(vb);
-        }
-        void setIndexBuffer(const GrIndexBuffer* ib) {
-            fIndexBuffer.reset(ib);
-        }
-        const SkRect* getDevBounds() const { return fDevBounds; }
-
-    private:
-        friend class GrDrawTarget;
-
-        GrPrimitiveType         fPrimitiveType;
-
-        int                     fStartVertex;
-        int                     fStartIndex;
-        int                     fVertexCount;
-        int                     fIndexCount;
-
-        int                     fInstanceCount;
-        int                     fVerticesPerInstance;
-        int                     fIndicesPerInstance;
-
-        SkRect                  fDevBoundsStorage;
-        SkRect*                 fDevBounds;
-
-        GrPendingIOResource<const GrVertexBuffer, kRead_GrIOType> fVertexBuffer;
-        GrPendingIOResource<const GrIndexBuffer, kRead_GrIOType>  fIndexBuffer;
-    };
-
-    /**
-     * Used to populate the vertex and index buffer on the draw info before onDraw is called.
-     */
-    virtual void setDrawBuffers(DrawInfo*, size_t vertexStride) = 0;;
     bool programUnitTest(int maxStages);
 
 protected:
+    friend class GrCommandBuilder; // for PipelineInfo
+    friend class GrInOrderCommandBuilder; // for PipelineInfo
+    friend class GrReorderCommandBuilder; // for PipelineInfo
     friend class GrTargetCommands; // for PipelineInfo
 
-    enum GeometrySrcType {
-        kNone_GeometrySrcType,     //<! src has not been specified
-        kReserved_GeometrySrcType, //<! src was set using reserve*Space
-        kBuffer_GeometrySrcType    //<! src was set using set*SourceToBuffer
-    };
-
-    struct GeometrySrcState {
-        GeometrySrcType         fVertexSrc;
-        union {
-            // valid if src type is buffer
-            const GrVertexBuffer*   fVertexBuffer;
-            // valid if src type is reserved or array
-            int                     fVertexCount;
-        };
-
-        GeometrySrcType         fIndexSrc;
-        union {
-            // valid if src type is buffer
-            const GrIndexBuffer*    fIndexBuffer;
-            // valid if src type is reserved or array
-            int                     fIndexCount;
-        };
-
-        size_t                  fVertexSize;
-    };
-
-    int indexCountInCurrentSource() const {
-        const GeometrySrcState& src = this->getGeomSrc();
-        switch (src.fIndexSrc) {
-            case kNone_GeometrySrcType:
-                return 0;
-            case kReserved_GeometrySrcType:
-                return src.fIndexCount;
-            case kBuffer_GeometrySrcType:
-                return static_cast<int>(src.fIndexBuffer->gpuMemorySize() / sizeof(uint16_t));
-            default:
-                SkFAIL("Unexpected Index Source.");
-                return 0;
-        }
-    }
-
     GrContext* getContext() { return fContext; }
     const GrContext* getContext() const { return fContext; }
 
-    // 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()->fVertexSize because of the assert.
-    size_t getVertexSize() const {
-        // the vertex layout is only valid if a vertex source has been specified.
-        SkASSERT(this->getGeomSrc().fVertexSrc != kNone_GeometrySrcType);
-        return this->getGeomSrc().fVertexSize;
+    GrGpu* getGpu() {
+        SkASSERT(fContext && fContext->getGpu());
+        return fContext->getGpu();
     }
-
-    // Subclass must initialize this in its constructor.
-    SkAutoTUnref<const GrDrawTargetCaps> fCaps;
+    const GrGpu* getGpu() const {
+        SkASSERT(fContext && fContext->getGpu());
+        return fContext->getGpu();
+    }
 
     const GrTraceMarkerSet& getActiveTraceMarkers() { return fActiveTraceMarkers; }
 
@@ -673,52 +276,12 @@
 
     void setupPipeline(const PipelineInfo& pipelineInfo, GrPipeline* pipeline);
 
-    // A subclass can optionally overload this function to be notified before
-    // vertex and index space is reserved.
-    virtual void willReserveVertexAndIndexSpace(int vertexCount,
-                                                size_t vertexStride,
-                                                int indexCount) {}
-
 private:
-    /**
-     * This will be called before allocating a texture as a dst for copySurface. This function
-     * populates the dstDesc's config, flags, and origin so as to maximize efficiency and guarantee
-     * success of the copySurface call.
-     */
-    void initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* dstDesc) {
-        if (!this->onInitCopySurfaceDstDesc(src, dstDesc)) {
-            dstDesc->fOrigin = kDefault_GrSurfaceOrigin;
-            dstDesc->fFlags = kRenderTarget_GrSurfaceFlag;
-            dstDesc->fConfig = src->config();
-        }
-    }
+    virtual void onReset() = 0;
 
-    /** Internal implementation of canCopySurface. */
-    bool internalCanCopySurface(const GrSurface* dst,
-                                const GrSurface* src,
-                                const SkIRect& clippedSrcRect,
-                                const SkIPoint& clippedDstRect);
+    virtual void onFlush() = 0;
 
-    // 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 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 GrGeometryProcessor*, const DrawInfo&, const PipelineInfo&) = 0;
     virtual void onDrawBatch(GrBatch*, const PipelineInfo&) = 0;
-    // TODO copy in order drawbuffer onDrawRect to here
-    virtual void onDrawRect(GrPipelineBuilder*,
-                            GrColor color,
-                            const SkMatrix& viewMatrix,
-                            const SkRect& rect,
-                            const SkRect* localRect,
-                            const SkMatrix* localMatrix) = 0;
-
     virtual void onStencilPath(const GrPipelineBuilder&,
                                const GrPathProcessor*,
                                const GrPath*,
@@ -741,53 +304,17 @@
     virtual void onClear(const SkIRect* rect, GrColor color, bool canIgnoreRect,
                          GrRenderTarget* renderTarget) = 0;
 
-    /** The subclass will get a chance to copy the surface for falling back to the default
-        implementation, which simply draws a rectangle (and fails if dst isn't a render target). It
-        should assume that any clipping has already been performed on the rect and point. It won't
-        be called if the copy can be skipped. */
-    virtual bool onCopySurface(GrSurface* dst,
+    /** The subclass's copy surface implementation. It should assume that any clipping has already
+        been performed on the rect and point and that the GrGpu supports the copy. */
+    virtual void onCopySurface(GrSurface* dst,
                                GrSurface* src,
                                const SkIRect& srcRect,
                                const SkIPoint& dstPoint) = 0;
 
-    /** Indicates whether onCopySurface would succeed. It should assume that any clipping has
-        already been performed on the rect and point. It won't be called if the copy can be
-        skipped. */
-    virtual bool onCanCopySurface(const GrSurface* dst,
-                                  const GrSurface* src,
-                                  const SkIRect& srcRect,
-                                  const SkIPoint& dstPoint) = 0;
-    /**
-     * This will be called before allocating a texture to be a dst for onCopySurface. Only the
-     * dstDesc's config, flags, and origin need be set by the function. If the subclass cannot
-     * create a surface that would succeed its implementation of onCopySurface, it should return
-     * false. The base class will fall back to creating a render target to draw into using the src.
-     */
-    virtual bool onInitCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* dstDesc) = 0;
-
-    // helpers for reserving vertex and index space.
-    bool reserveVertexSpace(size_t vertexSize,
-                            int vertexCount,
-                            void** vertices);
-    bool reserveIndexSpace(int indexCount, void** indices);
-
-    // called by drawIndexed and drawNonIndexed. Use a negative indexCount to
-    // indicate non-indexed drawing.
-    bool checkDraw(const GrPipelineBuilder&,
-                   const GrGeometryProcessor*,
-                   GrPrimitiveType type,
-                   int startVertex,
-                   int startIndex,
-                   int vertexCount,
-                   int indexCount) const;
-    // called when setting a new vert/idx source to unref prev vb/ib
-    void releasePreviousVertexSource();
-    void releasePreviousIndexSource();
-
     // Check to see if this set of draw commands has been sent out
     virtual bool       isIssued(uint32_t drawID) { return true; }
     void getPathStencilSettingsForFilltype(GrPathRendering::FillType,
-                                           const GrStencilBuffer*,
+                                           const GrStencilAttachment*,
                                            GrStencilSettings*);
     virtual GrClipMaskManager* clipMaskManager() = 0;
     virtual bool setupClip(GrPipelineBuilder*,
@@ -796,16 +323,14 @@
                            GrScissorState*,
                            const SkRect* devBounds) = 0;
 
-    enum {
-        kPreallocGeoSrcStateStackCnt = 4,
-    };
-    SkSTArray<kPreallocGeoSrcStateStackCnt, GeometrySrcState, true> fGeoSrcStateStack;
     // The context owns us, not vice-versa, so this ptr is not ref'ed by DrawTarget.
     GrContext*                                                      fContext;
+    SkAutoTUnref<const GrDrawTargetCaps>                            fCaps;
     // To keep track that we always have at least as many debug marker adds as removes
     int                                                             fGpuTraceMarkerCount;
     GrTraceMarkerSet                                                fActiveTraceMarkers;
     GrTraceMarkerSet                                                fStoredTraceMarkers;
+    bool                                                            fFlushing;
 
     typedef SkRefCnt INHERITED;
 };
@@ -815,7 +340,8 @@
  */
 class GrClipTarget : public GrDrawTarget {
 public:
-    GrClipTarget(GrContext* context) : INHERITED(context) {
+    GrClipTarget(GrContext* context)
+        : INHERITED(context) {
         fClipMaskManager.setClipTarget(this);
     }
 
@@ -837,7 +363,7 @@
      * Release any resources that are cached but not currently in use. This
      * is intended to give an application some recourse when resources are low.
      */
-    void purgeResources() SK_OVERRIDE {
+    void purgeResources() override {
         // The clip mask manager can rebuild all its clip masks so just
         // get rid of them all.
         fClipMaskManager.purgeResources();
@@ -847,13 +373,13 @@
     GrClipMaskManager           fClipMaskManager;
 
 private:
-    GrClipMaskManager* clipMaskManager() SK_OVERRIDE { return &fClipMaskManager; }
+    GrClipMaskManager* clipMaskManager() override { return &fClipMaskManager; }
 
     virtual bool setupClip(GrPipelineBuilder*,
                            GrPipelineBuilder::AutoRestoreFragmentProcessors*,
                            GrPipelineBuilder::AutoRestoreStencil*,
                            GrScissorState* scissorState,
-                           const SkRect* devBounds) SK_OVERRIDE;
+                           const SkRect* devBounds) override;
 
     typedef GrDrawTarget INHERITED;
 };
diff --git a/src/gpu/GrDrawTargetCaps.h b/src/gpu/GrDrawTargetCaps.h
index 9a08eff..086b615 100644
--- a/src/gpu/GrDrawTargetCaps.h
+++ b/src/gpu/GrDrawTargetCaps.h
@@ -14,12 +14,9 @@
 #include "SkRefCnt.h"
 #include "SkString.h"
 
-/**
- * Represents the draw target capabilities.
- */
-class GrDrawTargetCaps : public SkRefCnt {
+class GrShaderCaps : public SkRefCnt {
 public:
-    SK_DECLARE_INST_COUNT(GrDrawTargetCaps)
+    SK_DECLARE_INST_COUNT(GrShaderCaps)
 
     /** Info about shader variable precision within a given shader stage. That is, this info
         is relevant to a float (or vecNf) variable declared with a GrSLPrecision
@@ -44,41 +41,95 @@
         int fLogRangeLow;
         /** floor(log2(|max_value|)) */
         int fLogRangeHigh;
-        /** Number of bits of precision. As defined in OpenGL (with names modified to reflect this 
+        /** Number of bits of precision. As defined in OpenGL (with names modified to reflect this
             struct) :
             """
-                If the smallest representable value greater than 1 is 1 + e, then fBits will
-                contain floor(log2(e)), and every value in the range [2^fLogRangeLow,
-                2^fLogRangeHigh] can be represented to at least one part in 2^fBits.
-            """  
+            If the smallest representable value greater than 1 is 1 + e, then fBits will
+            contain floor(log2(e)), and every value in the range [2^fLogRangeLow,
+            2^fLogRangeHigh] can be represented to at least one part in 2^fBits.
+            """
           */
         int fBits;
     };
 
-
-    GrDrawTargetCaps() : fUniqueID(CreateUniqueID()) {
+    GrShaderCaps() {
         this->reset();
     }
-    GrDrawTargetCaps(const GrDrawTargetCaps& other) : INHERITED(), fUniqueID(CreateUniqueID()) {
+    virtual ~GrShaderCaps() {}
+    GrShaderCaps(const GrShaderCaps& other) : INHERITED() {
         *this = other;
     }
+    GrShaderCaps& operator= (const GrShaderCaps&);
+
+    virtual void reset();
+    virtual SkString dump() const;
+
+    bool shaderDerivativeSupport() const { return fShaderDerivativeSupport; }
+    bool geometryShaderSupport() const { return fGeometryShaderSupport; }
+    bool pathRenderingSupport() const { return fPathRenderingSupport; }
+    bool dstReadInShaderSupport() const { return fDstReadInShaderSupport; }
+    bool dualSourceBlendingSupport() const { return fDualSourceBlendingSupport; }
+
+    /**
+    * Get the precision info for a variable of type kFloat_GrSLType, kVec2f_GrSLType, etc in a
+    * given shader type. If the shader type is not supported or the precision level is not
+    * supported in that shader type then the returned struct will report false when supported() is
+    * called.
+    */
+    const PrecisionInfo& getFloatShaderPrecisionInfo(GrShaderType shaderType,
+        GrSLPrecision precision) const {
+        return fFloatPrecisions[shaderType][precision];
+    };
+
+    /**
+    * Is there any difference between the float shader variable precision types? If this is true
+    * then unless the shader type is not supported, any call to getFloatShaderPrecisionInfo() would
+    * report the same info for all precisions in all shader types.
+    */
+    bool floatPrecisionVaries() const { return fShaderPrecisionVaries; }
+
+protected:
+    bool fShaderDerivativeSupport : 1;
+    bool fGeometryShaderSupport : 1;
+    bool fPathRenderingSupport : 1;
+    bool fDstReadInShaderSupport : 1;
+    bool fDualSourceBlendingSupport : 1;
+
+    bool fShaderPrecisionVaries;
+    PrecisionInfo fFloatPrecisions[kGrShaderTypeCount][kGrSLPrecisionCount];
+
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+/**
+ * Represents the draw target capabilities.
+ */
+class GrDrawTargetCaps : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(GrDrawTargetCaps)
+
+    GrDrawTargetCaps() {
+        fShaderCaps.reset(NULL);
+        this->reset();
+    }
+    GrDrawTargetCaps(const GrDrawTargetCaps& other) : INHERITED() {
+        *this = other;
+    }
+    virtual ~GrDrawTargetCaps() {}
     GrDrawTargetCaps& operator= (const GrDrawTargetCaps&);
 
     virtual void reset();
     virtual SkString dump() const;
 
+    GrShaderCaps* shaderCaps() const { return fShaderCaps; }
+
     bool npotTextureTileSupport() const { return fNPOTTextureTileSupport; }
     /** To avoid as-yet-unnecessary complexity we don't allow any partial support of MIP Maps (e.g.
         only for POT textures) */
     bool mipMapSupport() const { return fMipMapSupport; }
     bool twoSidedStencilSupport() const { return fTwoSidedStencilSupport; }
     bool stencilWrapOpsSupport() const { return  fStencilWrapOpsSupport; }
-    bool hwAALineSupport() const { return fHWAALineSupport; }
-    bool shaderDerivativeSupport() const { return fShaderDerivativeSupport; }
-    bool geometryShaderSupport() const { return fGeometryShaderSupport; }
-    bool dualSourceBlendingSupport() const { return fDualSourceBlendingSupport; }
-    bool pathRenderingSupport() const { return fPathRenderingSupport; }
-    bool dstReadInShaderSupport() const { return fDstReadInShaderSupport; }
     bool discardRenderTargetSupport() const { return fDiscardRenderTargetSupport; }
 #if GR_FORCE_GPU_TRACE_DEBUGGING
     bool gpuTracingSupport() const { return true; }
@@ -87,10 +138,35 @@
 #endif
     bool compressedTexSubImageSupport() const { return fCompressedTexSubImageSupport; }
     bool oversizedStencilSupport() const { return fOversizedStencilSupport; }
+    bool textureBarrierSupport() const { return fTextureBarrierSupport; }
 
     bool useDrawInsteadOfClear() const { return fUseDrawInsteadOfClear; }
 
     /**
+     * Indicates the capabilities of the fixed function blend unit.
+     */
+    enum BlendEquationSupport {
+        kBasic_BlendEquationSupport,             //<! Support to select the operator that
+                                                 //   combines src and dst terms.
+        kAdvanced_BlendEquationSupport,          //<! Additional fixed function support for specific
+                                                 //   SVG/PDF blend modes. Requires blend barriers.
+        kAdvancedCoherent_BlendEquationSupport,  //<! Advanced blend equation support that does not
+                                                 //   require blend barriers, and permits overlap.
+
+        kLast_BlendEquationSupport = kAdvancedCoherent_BlendEquationSupport
+    };
+
+    BlendEquationSupport blendEquationSupport() const { return fBlendEquationSupport; }
+
+    bool advancedBlendEquationSupport() const {
+        return fBlendEquationSupport >= kAdvanced_BlendEquationSupport;
+    }
+
+    bool advancedCoherentBlendEquationSupport() const {
+        return kAdvancedCoherent_BlendEquationSupport == fBlendEquationSupport;
+    }
+
+    /**
      * Indicates whether GPU->CPU memory mapping for GPU resources such as vertex buffers and
      * textures allows partial mappings or full mappings.
      */
@@ -125,50 +201,23 @@
         return fConfigTextureSupport[config];
     }
 
-    /**
-     * Get the precision info for a variable of type kFloat_GrSLType, kVec2f_GrSLType, etc in a
-     * given shader type. If the shader type is not supported or the precision level is not
-     * supported in that shader type then the returned struct will report false when supported() is
-     * called.
-     */
-    const PrecisionInfo& getFloatShaderPrecisionInfo(GrShaderType shaderType,
-                                                     GrSLPrecision precision) const {
-        return fFloatPrecisions[shaderType][precision];
-    };
-
-    /**
-     * Is there any difference between the float shader variable precision types? If this is true
-     * then unless the shader type is not supported, any call to getFloatShaderPrecisionInfo() would
-     * report the same info for all precisions in all shader types.
-     */
-    bool floatPrecisionVaries() const { return fShaderPrecisionVaries; }
-
-    /**
-     * Gets an id that is unique for this GrDrawTargetCaps object. It is static in that it does
-     * not change when the content of the GrDrawTargetCaps object changes. This will never return
-     * 0.
-     */
-    uint32_t getUniqueID() const { return fUniqueID; }
-
 protected:
+    SkAutoTUnref<GrShaderCaps>    fShaderCaps;
+
     bool fNPOTTextureTileSupport        : 1;
     bool fMipMapSupport                 : 1;
     bool fTwoSidedStencilSupport        : 1;
     bool fStencilWrapOpsSupport         : 1;
-    bool fHWAALineSupport               : 1;
-    bool fShaderDerivativeSupport       : 1;
-    bool fGeometryShaderSupport         : 1;
-    bool fDualSourceBlendingSupport     : 1;
-    bool fPathRenderingSupport          : 1;
-    bool fDstReadInShaderSupport        : 1;
     bool fDiscardRenderTargetSupport    : 1;
     bool fReuseScratchTextures          : 1;
     bool fGpuTracingSupport             : 1;
     bool fCompressedTexSubImageSupport  : 1;
     bool fOversizedStencilSupport       : 1;
+    bool fTextureBarrierSupport         : 1;
     // Driver workaround
     bool fUseDrawInsteadOfClear         : 1;
 
+    BlendEquationSupport fBlendEquationSupport;
     uint32_t fMapBufferFlags;
 
     int fMaxRenderTargetSize;
@@ -179,14 +228,7 @@
     bool fConfigRenderSupport[kGrPixelConfigCnt][2];
     bool fConfigTextureSupport[kGrPixelConfigCnt];
 
-    bool fShaderPrecisionVaries;
-    PrecisionInfo fFloatPrecisions[kGrShaderTypeCount][kGrSLPrecisionCount];
-
 private:
-    static uint32_t CreateUniqueID();
-
-    const uint32_t          fUniqueID;
-
     typedef SkRefCnt INHERITED;
 };
 
diff --git a/src/gpu/GrFlushToGpuDrawTarget.cpp b/src/gpu/GrFlushToGpuDrawTarget.cpp
deleted file mode 100644
index 23fa310..0000000
--- a/src/gpu/GrFlushToGpuDrawTarget.cpp
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrFlushToGpuDrawTarget.h"
-#include "GrContext.h"
-#include "GrFontCache.h"
-#include "GrGpu.h"
-#include "GrBufferAllocPool.h"
-
-GrFlushToGpuDrawTarget::GrFlushToGpuDrawTarget(GrGpu* gpu,
-                                               GrVertexBufferAllocPool* vertexPool,
-                                               GrIndexBufferAllocPool* indexPool)
-    : INHERITED(gpu->getContext())
-    , fGpu(SkRef(gpu))
-    , fVertexPool(vertexPool)
-    , fIndexPool(indexPool)
-    , fFlushing(false) {
-
-    fCaps.reset(SkRef(fGpu->caps()));
-
-    SkASSERT(vertexPool);
-    SkASSERT(indexPool);
-
-    GeometryPoolState& poolState = fGeoPoolStateStack.push_back();
-    poolState.fUsedPoolVertexBytes = 0;
-    poolState.fUsedPoolIndexBytes = 0;
-#ifdef SK_DEBUG
-    poolState.fPoolVertexBuffer = (GrVertexBuffer*)~0;
-    poolState.fPoolStartVertex = ~0;
-    poolState.fPoolIndexBuffer = (GrIndexBuffer*)~0;
-    poolState.fPoolStartIndex = ~0;
-#endif
-}
-
-GrFlushToGpuDrawTarget::~GrFlushToGpuDrawTarget() {
-    // This must be called by before the GrDrawTarget destructor
-    this->releaseGeometry();
-}
-
-void GrFlushToGpuDrawTarget::setDrawBuffers(DrawInfo* info, size_t vertexStride) {
-    GeometryPoolState& poolState = fGeoPoolStateStack.back();
-    if (kBuffer_GeometrySrcType == this->getGeomSrc().fVertexSrc) {
-        info->setVertexBuffer(this->getGeomSrc().fVertexBuffer);
-    } else {
-        // Update the bytes used since the last reserve-geom request.
-        size_t bytes = (info->vertexCount() + info->startVertex()) * vertexStride;
-        poolState.fUsedPoolVertexBytes = SkTMax(poolState.fUsedPoolVertexBytes, bytes);
-        info->setVertexBuffer(poolState.fPoolVertexBuffer);
-        info->adjustStartVertex(poolState.fPoolStartVertex);
-    }
-
-    if (info->isIndexed()) {
-        if (kBuffer_GeometrySrcType == this->getGeomSrc().fIndexSrc) {
-            info->setIndexBuffer(this->getGeomSrc().fIndexBuffer);
-        } else {
-            // Update the bytes used since the last reserve-geom request.
-            size_t bytes = (info->indexCount() + info->startIndex()) * sizeof(uint16_t);
-            poolState.fUsedPoolIndexBytes = SkTMax(poolState.fUsedPoolIndexBytes, bytes);
-            info->setIndexBuffer(poolState.fPoolIndexBuffer);
-            info->adjustStartIndex(poolState.fPoolStartIndex);
-        }
-    }
-}
-
-void GrFlushToGpuDrawTarget::reset() {
-    SkASSERT(1 == fGeoPoolStateStack.count());
-    this->resetVertexSource();
-    this->resetIndexSource();
-
-    fVertexPool->reset();
-    fIndexPool->reset();
-
-    this->onReset();
-}
-
-void GrFlushToGpuDrawTarget::flush() {
-    SkASSERT(kReserved_GeometrySrcType != this->getGeomSrc().fVertexSrc);
-    SkASSERT(kReserved_GeometrySrcType != this->getGeomSrc().fIndexSrc);
-
-    if (fFlushing) {
-        return;
-    }
-    fFlushing = true;
-
-    fGpu->getContext()->getFontCache()->updateTextures();
-
-    fGpu->saveActiveTraceMarkers();
-
-    this->onFlush();
-
-    fGpu->restoreActiveTraceMarkers();
-
-    fFlushing = false;
-    this->reset();
-}
-
-void GrFlushToGpuDrawTarget::willReserveVertexAndIndexSpace(int vertexCount,
-                                                            size_t vertexStride,
-                                                            int indexCount) {
-    // We use geometryHints() to know whether to flush the draw buffer. We
-    // can't flush if we are inside an unbalanced pushGeometrySource.
-    // Moreover, flushing blows away vertex and index data that was
-    // previously reserved. So if the vertex or index data is pulled from
-    // reserved space and won't be released by this request then we can't
-    // flush.
-    bool insideGeoPush = fGeoPoolStateStack.count() > 1;
-
-    bool unreleasedVertexSpace =
-        !vertexCount &&
-        kReserved_GeometrySrcType == this->getGeomSrc().fVertexSrc;
-
-    bool unreleasedIndexSpace =
-        !indexCount &&
-        kReserved_GeometrySrcType == this->getGeomSrc().fIndexSrc;
-
-    int vcount = vertexCount;
-    int icount = indexCount;
-
-    if (!insideGeoPush &&
-        !unreleasedVertexSpace &&
-        !unreleasedIndexSpace &&
-        this->geometryHints(vertexStride, &vcount, &icount)) {
-        this->flush();
-    }
-}
-
-bool GrFlushToGpuDrawTarget::geometryHints(size_t vertexStride,
-                                           int* vertexCount,
-                                           int* indexCount) const {
-    // we will recommend a flush if the data could fit in a single
-    // preallocated buffer but none are left and it can't fit
-    // in the current buffer (which may not be prealloced).
-    bool flush = false;
-    if (indexCount) {
-        int32_t currIndices = fIndexPool->currentBufferIndices();
-        if (*indexCount > currIndices &&
-            (!fIndexPool->preallocatedBuffersRemaining() &&
-             *indexCount <= fIndexPool->preallocatedBufferIndices())) {
-
-            flush = true;
-        }
-        *indexCount = currIndices;
-    }
-    if (vertexCount) {
-        int32_t currVertices = fVertexPool->currentBufferVertices(vertexStride);
-        if (*vertexCount > currVertices &&
-            (!fVertexPool->preallocatedBuffersRemaining() &&
-             *vertexCount <= fVertexPool->preallocatedBufferVertices(vertexStride))) {
-
-            flush = true;
-        }
-        *vertexCount = currVertices;
-    }
-    return flush;
-}
-
-bool GrFlushToGpuDrawTarget::onReserveVertexSpace(size_t vertexSize,
-                                                  int vertexCount,
-                                                  void** vertices) {
-    GeometryPoolState& poolState = fGeoPoolStateStack.back();
-    SkASSERT(vertexCount > 0);
-    SkASSERT(vertices);
-    SkASSERT(0 == poolState.fUsedPoolVertexBytes);
-
-    *vertices = fVertexPool->makeSpace(vertexSize,
-                                       vertexCount,
-                                       &poolState.fPoolVertexBuffer,
-                                       &poolState.fPoolStartVertex);
-    return SkToBool(*vertices);
-}
-
-bool GrFlushToGpuDrawTarget::onReserveIndexSpace(int indexCount, void** indices) {
-    GeometryPoolState& poolState = fGeoPoolStateStack.back();
-    SkASSERT(indexCount > 0);
-    SkASSERT(indices);
-    SkASSERT(0 == poolState.fUsedPoolIndexBytes);
-
-    *indices = fIndexPool->makeSpace(indexCount,
-                                     &poolState.fPoolIndexBuffer,
-                                     &poolState.fPoolStartIndex);
-    return SkToBool(*indices);
-}
-
-void GrFlushToGpuDrawTarget::releaseReservedVertexSpace() {
-    GeometryPoolState& poolState = fGeoPoolStateStack.back();
-    const GeometrySrcState& geoSrc = this->getGeomSrc();
-
-    // If we get a release vertex space call then our current source should either be reserved
-    // or array (which we copied into reserved space).
-    SkASSERT(kReserved_GeometrySrcType == geoSrc.fVertexSrc);
-
-    // When the caller reserved vertex buffer space we gave it back a pointer
-    // 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 = geoSrc.fVertexSize * geoSrc.fVertexCount;
-    fVertexPool->putBack(reservedVertexBytes - poolState.fUsedPoolVertexBytes);
-    poolState.fUsedPoolVertexBytes = 0;
-    poolState.fPoolVertexBuffer = NULL;
-    poolState.fPoolStartVertex = 0;
-}
-
-void GrFlushToGpuDrawTarget::releaseReservedIndexSpace() {
-    GeometryPoolState& poolState = fGeoPoolStateStack.back();
-    const GeometrySrcState& geoSrc = this->getGeomSrc();
-
-    // If we get a release index space call then our current source should either be reserved
-    // or array (which we copied into reserved space).
-    SkASSERT(kReserved_GeometrySrcType == geoSrc.fIndexSrc);
-
-    // Similar to releaseReservedVertexSpace we return any unused portion at
-    // the tail
-    size_t reservedIndexBytes = sizeof(uint16_t) * geoSrc.fIndexCount;
-    fIndexPool->putBack(reservedIndexBytes - poolState.fUsedPoolIndexBytes);
-    poolState.fUsedPoolIndexBytes = 0;
-    poolState.fPoolIndexBuffer = NULL;
-    poolState.fPoolStartIndex = 0;
-}
-
-void GrFlushToGpuDrawTarget::geometrySourceWillPush() {
-    GeometryPoolState& poolState = fGeoPoolStateStack.push_back();
-    poolState.fUsedPoolVertexBytes = 0;
-    poolState.fUsedPoolIndexBytes = 0;
-#ifdef SK_DEBUG
-    poolState.fPoolVertexBuffer = (GrVertexBuffer*)~0;
-    poolState.fPoolStartVertex = ~0;
-    poolState.fPoolIndexBuffer = (GrIndexBuffer*)~0;
-    poolState.fPoolStartIndex = ~0;
-#endif
-}
-
-void GrFlushToGpuDrawTarget::geometrySourceWillPop(const GeometrySrcState& restoredState) {
-    SkASSERT(fGeoPoolStateStack.count() > 1);
-    fGeoPoolStateStack.pop_back();
-    GeometryPoolState& poolState = fGeoPoolStateStack.back();
-    // we have to assume that any slack we had in our vertex/index data
-    // is now unreleasable because data may have been appended later in the
-    // pool.
-    if (kReserved_GeometrySrcType == restoredState.fVertexSrc) {
-        poolState.fUsedPoolVertexBytes = restoredState.fVertexSize * restoredState.fVertexCount;
-    }
-    if (kReserved_GeometrySrcType == restoredState.fIndexSrc) {
-        poolState.fUsedPoolIndexBytes = sizeof(uint16_t) * restoredState.fIndexCount;
-    }
-}
-
-bool GrFlushToGpuDrawTarget::onCanCopySurface(const GrSurface* dst,
-                                              const GrSurface* src,
-                                              const SkIRect& srcRect,
-                                              const SkIPoint& dstPoint) {
-    return getGpu()->canCopySurface(dst, src, srcRect, dstPoint);
-}
-
-bool GrFlushToGpuDrawTarget::onInitCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) {
-    return getGpu()->initCopySurfaceDstDesc(src, desc);
-}
diff --git a/src/gpu/GrFlushToGpuDrawTarget.h b/src/gpu/GrFlushToGpuDrawTarget.h
deleted file mode 100644
index 4c72f93..0000000
--- a/src/gpu/GrFlushToGpuDrawTarget.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrFlushToGpuDrawTarget_DEFINED
-#define GrFlushToGpuDrawTarget_DEFINED
-
-#include "GrDrawTarget.h"
-
-class GrIndexBufferAllocPool;
-class GrVertexBufferAllocPool;
-class GrGpu;
-
-/**
- * Base class for draw targets that accumulate index and vertex data in buffers for deferred.
- * When draw target clients reserve geometry this subclass will place that geometry into
- * preallocated vertex/index buffers in the order the requests are made (assuming the requests fit
- * in the preallocated buffers).
- */
-class GrFlushToGpuDrawTarget : public GrClipTarget {
-public:
-    GrFlushToGpuDrawTarget(GrGpu*, GrVertexBufferAllocPool*,GrIndexBufferAllocPool*);
-
-    ~GrFlushToGpuDrawTarget() SK_OVERRIDE;
-
-    /**
-     * Empties the draw buffer of any queued up draws. This must not be called while inside an
-     * unbalanced pushGeometrySource().
-     */
-    void reset();
-
-    /**
-     * This plays any queued up draws to its GrGpu 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.
-     */
-    void flush();
-
-    bool geometryHints(size_t vertexStride, int* vertexCount, int* indexCount) const SK_OVERRIDE;
-
-protected:
-    GrGpu* getGpu() { return fGpu; }
-    const GrGpu* getGpu() const{ return fGpu; }
-
-    GrVertexBufferAllocPool* getVertexAllocPool() { return fVertexPool; }
-    GrIndexBufferAllocPool* getIndexAllocPool() { return fIndexPool; }
-
-    // TODO all of this goes away when batch is everywhere
-    enum {
-        kGeoPoolStatePreAllocCnt = 4,
-    };
-
-    struct GeometryPoolState {
-        const GrVertexBuffer*   fPoolVertexBuffer;
-        int                     fPoolStartVertex;
-        const GrIndexBuffer*    fPoolIndexBuffer;
-        int                     fPoolStartIndex;
-        // caller may conservatively over reserve vertices / indices.
-        // we release unused space back to allocator if possible
-        // can only do this if there isn't an intervening pushGeometrySource()
-        size_t                  fUsedPoolVertexBytes;
-        size_t                  fUsedPoolIndexBytes;
-    };
-
-    typedef SkSTArray<kGeoPoolStatePreAllocCnt, GeometryPoolState> GeoPoolStateStack;
-    const GeoPoolStateStack& getGeoPoolStateStack() const { return fGeoPoolStateStack; }
-
-    void willReserveVertexAndIndexSpace(int vertexCount,
-                                        size_t vertexStride,
-                                        int indexCount) SK_OVERRIDE;
-
-private:
-    virtual void onReset() = 0;
-
-    virtual void onFlush() = 0;
-
-    void setDrawBuffers(DrawInfo*, size_t stride) SK_OVERRIDE;
-    bool onReserveVertexSpace(size_t vertexSize, int vertexCount, void** vertices) SK_OVERRIDE;
-    bool onReserveIndexSpace(int indexCount, void** indices) SK_OVERRIDE;
-    void releaseReservedVertexSpace() SK_OVERRIDE;
-    void releaseReservedIndexSpace() SK_OVERRIDE;
-    void geometrySourceWillPush() SK_OVERRIDE;
-    void geometrySourceWillPop(const GeometrySrcState& restoredState) SK_OVERRIDE;
-    bool onCanCopySurface(const GrSurface* dst,
-                          const GrSurface* src,
-                          const SkIRect& srcRect,
-                          const SkIPoint& dstPoint) SK_OVERRIDE;
-    bool onInitCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) SK_OVERRIDE;
-
-    GeoPoolStateStack                   fGeoPoolStateStack;
-    SkAutoTUnref<GrGpu>                 fGpu;
-    GrVertexBufferAllocPool*            fVertexPool;
-    GrIndexBufferAllocPool*             fIndexPool;
-    bool                                fFlushing;
-
-    typedef GrClipTarget INHERITED;
-};
-
-#endif
diff --git a/src/gpu/GrFontAtlasSizes.h b/src/gpu/GrFontAtlasSizes.h
index d5c5e28..8a3091c 100644
--- a/src/gpu/GrFontAtlasSizes.h
+++ b/src/gpu/GrFontAtlasSizes.h
@@ -9,6 +9,30 @@
 #ifndef GrFontAtlasSizes_DEFINED
 #define GrFontAtlasSizes_DEFINED
 
+// For debugging atlas which evict all of the time
+//#define DEBUG_CONSTANT_EVICT
+#ifdef DEBUG_CONSTANT_EVICT
+#define GR_FONT_ATLAS_TEXTURE_WIDTH    256//1024
+#define GR_FONT_ATLAS_A8_TEXTURE_WIDTH 256//2048
+#define GR_FONT_ATLAS_TEXTURE_HEIGHT   256//2048
+
+#define GR_FONT_ATLAS_PLOT_WIDTH       256
+#define GR_FONT_ATLAS_A8_PLOT_WIDTH    256//512
+#define GR_FONT_ATLAS_PLOT_HEIGHT      256
+
+#define GR_FONT_ATLAS_NUM_PLOTS_X     (GR_FONT_ATLAS_TEXTURE_WIDTH / GR_FONT_ATLAS_PLOT_WIDTH)
+#define GR_FONT_ATLAS_A8_NUM_PLOTS_X  (GR_FONT_ATLAS_A8_TEXTURE_WIDTH / GR_FONT_ATLAS_A8_PLOT_WIDTH)
+#define GR_FONT_ATLAS_NUM_PLOTS_Y     (GR_FONT_ATLAS_TEXTURE_HEIGHT / GR_FONT_ATLAS_PLOT_HEIGHT)
+
+// one over width and height
+#define GR_FONT_ATLAS_RECIP_WIDTH      "0.00390625"//"0.0009765625"
+#define GR_FONT_ATLAS_A8_RECIP_WIDTH   "0.00390625"//"0.00048828125"
+#define GR_FONT_ATLAS_RECIP_HEIGHT     "0.00390625"//"0.00048828125"
+
+// 1/(3*width)
+// only used for distance fields, which are A8
+#define GR_FONT_ATLAS_LCD_DELTA        "0.001302083"//"0.000162760417"
+#else
 #define GR_FONT_ATLAS_TEXTURE_WIDTH    1024
 #define GR_FONT_ATLAS_A8_TEXTURE_WIDTH 2048
 #define GR_FONT_ATLAS_TEXTURE_HEIGHT   2048
@@ -29,5 +53,5 @@
 // 1/(3*width)
 // only used for distance fields, which are A8
 #define GR_FONT_ATLAS_LCD_DELTA        "0.000162760417"
-
+#endif
 #endif
diff --git a/src/gpu/GrFontCache.cpp b/src/gpu/GrFontCache.cpp
deleted file mode 100644
index f5c1c22..0000000
--- a/src/gpu/GrFontCache.cpp
+++ /dev/null
@@ -1,349 +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.
- */
-
-#include "GrFontCache.h"
-#include "GrFontAtlasSizes.h"
-#include "GrGpu.h"
-#include "GrRectanizer.h"
-#include "GrSurfacePriv.h"
-#include "SkString.h"
-
-#include "SkDistanceFieldGen.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-#define FONT_CACHE_STATS 0
-#if FONT_CACHE_STATS
-static int g_PurgeCount = 0;
-#endif
-
-GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
-    gpu->ref();
-    for (int i = 0; i < kAtlasCount; ++i) {
-        fAtlases[i] = NULL;
-    }
-
-    fHead = fTail = NULL;
-}
-
-GrFontCache::~GrFontCache() {
-    SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
-    while (!iter.done()) {
-        SkDELETE(&(*iter));
-        ++iter;
-    }
-    for (int i = 0; i < kAtlasCount; ++i) {
-        delete fAtlases[i];
-    }
-    fGpu->unref();
-#if FONT_CACHE_STATS
-      SkDebugf("Num purges: %d\n", g_PurgeCount);
-#endif
-}
-
-static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
-    static const GrPixelConfig sPixelConfigs[] = {
-        kAlpha_8_GrPixelConfig,
-        kRGB_565_GrPixelConfig,
-        kSkia8888_GrPixelConfig
-    };
-    SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch);
-
-    return sPixelConfigs[format];
-}
-
-static int mask_format_to_atlas_index(GrMaskFormat format) {
-    static const int sAtlasIndices[] = {
-        GrFontCache::kA8_AtlasType,
-        GrFontCache::k565_AtlasType,
-        GrFontCache::k8888_AtlasType
-    };
-    SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
-
-    SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
-    return sAtlasIndices[format];
-}
-
-GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) {
-    GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, (this, scaler->getKey()));
-    fCache.add(strike);
-
-    if (fHead) {
-        fHead->fPrev = strike;
-    } else {
-        SkASSERT(NULL == fTail);
-        fTail = strike;
-    }
-    strike->fPrev = NULL;
-    strike->fNext = fHead;
-    fHead = strike;
-
-    return strike;
-}
-
-void GrFontCache::freeAll() {
-    SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
-    while (!iter.done()) {
-        SkDELETE(&(*iter));
-        ++iter;
-    }
-    fCache.rewind();
-    for (int i = 0; i < kAtlasCount; ++i) {
-        delete fAtlases[i];
-        fAtlases[i] = NULL;
-    }
-    fHead = NULL;
-    fTail = NULL;
-}
-
-void GrFontCache::purgeStrike(GrTextStrike* strike) {
-    fCache.remove(*(strike->fFontScalerKey));
-    this->detachStrikeFromList(strike);
-    delete strike;
-}
-
-
-GrPlot* GrFontCache::addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* usage,
-                                int width, int height, const void* image,
-                                SkIPoint16* loc) {
-    GrPixelConfig config = mask_format_to_pixel_config(format);
-    int atlasIndex = mask_format_to_atlas_index(format);
-    if (NULL == fAtlases[atlasIndex]) {
-        if (kA8_GrMaskFormat == format) {
-            SkISize textureSize = SkISize::Make(GR_FONT_ATLAS_A8_TEXTURE_WIDTH,
-                                                GR_FONT_ATLAS_TEXTURE_HEIGHT);
-            fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrSurfaceFlags,
-                                                        textureSize,
-                                                        GR_FONT_ATLAS_A8_NUM_PLOTS_X,
-                                                        GR_FONT_ATLAS_NUM_PLOTS_Y,
-                                                        true));
-        } else {
-            SkISize textureSize = SkISize::Make(GR_FONT_ATLAS_TEXTURE_WIDTH,
-                                                GR_FONT_ATLAS_TEXTURE_HEIGHT);
-            fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrSurfaceFlags,
-                                                        textureSize,
-                                                        GR_FONT_ATLAS_NUM_PLOTS_X,
-                                                        GR_FONT_ATLAS_NUM_PLOTS_Y,
-                                                        true));
-        }
-    }
-    return fAtlases[atlasIndex]->addToAtlas(usage, width, height, image, loc);
-}
-
-
-bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph) {
-    SkASSERT(preserveStrike);
-
-    int index = mask_format_to_atlas_index(glyph->fMaskFormat);
-    GrAtlas* atlas = fAtlases[index];
-    GrPlot* plot = atlas->getUnusedPlot();
-    if (NULL == plot) {
-        return false;
-    }
-    plot->resetRects();
-
-    GrTextStrike* strike = fHead;
-    while (strike) {
-        GrTextStrike* strikeToPurge = strike;
-        strike = strikeToPurge->fNext;
-        strikeToPurge->removePlot(plot);
-
-        // clear out any empty strikes (except this one)
-        if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty()) {
-            this->purgeStrike(strikeToPurge);
-        }
-    }
-
-#if FONT_CACHE_STATS
-    ++g_PurgeCount;
-#endif
-
-    return true;
-}
-
-#ifdef SK_DEBUG
-void GrFontCache::validate() const {
-    int count = fCache.count();
-    if (0 == count) {
-        SkASSERT(!fHead);
-        SkASSERT(!fTail);
-    } else if (1 == count) {
-        SkASSERT(fHead == fTail);
-    } else {
-        SkASSERT(fHead != fTail);
-    }
-
-    int count2 = 0;
-    const GrTextStrike* strike = fHead;
-    while (strike) {
-        count2 += 1;
-        strike = strike->fNext;
-    }
-    SkASSERT(count == count2);
-
-    count2 = 0;
-    strike = fTail;
-    while (strike) {
-        count2 += 1;
-        strike = strike->fPrev;
-    }
-    SkASSERT(count == count2);
-}
-#endif
-
-void GrFontCache::dump() const {
-    static int gDumpCount = 0;
-    for (int i = 0; i < kAtlasCount; ++i) {
-        if (fAtlases[i]) {
-            GrTexture* texture = fAtlases[i]->getTexture();
-            if (texture) {
-                SkString filename;
-#ifdef SK_BUILD_FOR_ANDROID
-                filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
-#else
-                filename.printf("fontcache_%d%d.png", gDumpCount, i);
-#endif
-                texture->surfacePriv().savePixels(filename.c_str());
-            }
-        }
-    }
-    ++gDumpCount;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#ifdef SK_DEBUG
-    static int gCounter;
-#endif
-
-/*
-    The text strike is specific to a given font/style/matrix setup, which is
-    represented by the GrHostFontScaler object we are given in getGlyph().
-
-    We map a 32bit glyphID to a GrGlyph record, which in turn points to a
-    atlas and a position within that texture.
- */
-
-GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key)
-    : fPool(9/*start allocations at 512 bytes*/) {
-    fFontScalerKey = key;
-    fFontScalerKey->ref();
-
-    fFontCache = cache;     // no need to ref, it won't go away before we do
-
-#ifdef SK_DEBUG
-//    SkDebugf(" GrTextStrike %p %d\n", this, gCounter);
-    gCounter += 1;
-#endif
-}
-
-GrTextStrike::~GrTextStrike() {
-    fFontScalerKey->unref();
-    SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
-    while (!iter.done()) {
-        (*iter).free();
-        ++iter;
-    }
-
-#ifdef SK_DEBUG
-    gCounter -= 1;
-//    SkDebugf("~GrTextStrike %p %d\n", this, gCounter);
-#endif
-}
-
-GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
-                                     GrFontScaler* scaler) {
-    SkIRect bounds;
-    if (fUseDistanceField) {
-        if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
-            return NULL;
-        }
-    } else {
-        if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
-            return NULL;
-        }
-    }
-    GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
-    
-    GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW);
-    glyph->init(packed, bounds, format);
-    fCache.add(glyph);
-    return glyph;
-}
-
-void GrTextStrike::removePlot(const GrPlot* plot) {
-    SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
-    while (!iter.done()) {
-        if (plot == (*iter).fPlot) {
-            (*iter).fPlot = NULL;
-        }
-        ++iter;
-    }
-
-    GrAtlas::RemovePlot(&fPlotUsage, plot);
-}
-
-bool GrTextStrike::glyphTooLargeForAtlas(GrGlyph* glyph) {
-    int width = glyph->fBounds.width();
-    int height = glyph->fBounds.height();
-    int pad = fUseDistanceField ? 2 * SK_DistanceFieldPad : 0;
-    int plotWidth = (kA8_GrMaskFormat == glyph->fMaskFormat) ? GR_FONT_ATLAS_A8_PLOT_WIDTH
-                                                             : GR_FONT_ATLAS_PLOT_WIDTH;
-    if (width + pad > plotWidth) {
-        return true;
-    }
-    if (height + pad > GR_FONT_ATLAS_PLOT_HEIGHT) {
-        return true;
-    }
-
-    return false;
-}
-
-bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
-#if 0   // testing hack to force us to flush our cache often
-    static int gCounter;
-    if ((++gCounter % 10) == 0) return false;
-#endif
-
-    SkASSERT(glyph);
-    SkASSERT(scaler);
-    SkASSERT(fCache.find(glyph->fPackedID));
-    SkASSERT(NULL == glyph->fPlot);
-
-    SkAutoUnref ar(SkSafeRef(scaler));
-
-    int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
-
-    size_t size = glyph->fBounds.area() * bytesPerPixel;
-    GrAutoMalloc<1024> storage(size);
-
-    if (fUseDistanceField) {
-        if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
-                                           glyph->height(),
-                                           storage.get())) {
-            return false;
-        }
-    } else {
-        if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
-                                         glyph->height(),
-                                         glyph->width() * bytesPerPixel,
-                                         storage.get())) {
-            return false;
-        }
-    }
-
-    GrPlot* plot = fFontCache->addToAtlas(glyph->fMaskFormat, &fPlotUsage,
-                                          glyph->width(), glyph->height(),
-                                          storage.get(), &glyph->fAtlasLocation);
-
-    if (NULL == plot) {
-        return false;
-    }
-
-    glyph->fPlot = plot;
-    return true;
-}
diff --git a/src/gpu/GrFontCache.h b/src/gpu/GrFontCache.h
deleted file mode 100644
index 6ee39d0..0000000
--- a/src/gpu/GrFontCache.h
+++ /dev/null
@@ -1,183 +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 GrTextStrike_DEFINED
-#define GrTextStrike_DEFINED
-
-#include "GrAtlas.h"
-#include "GrDrawTarget.h"
-#include "GrFontScaler.h"
-#include "GrGlyph.h"
-#include "SkTDynamicHash.h"
-#include "SkVarAlloc.h"
-
-class GrFontCache;
-class GrGpu;
-class GrFontPurgeListener;
-
-/**
- *  The textstrike maps a hostfontscaler instance to a dictionary of
- *  glyphid->strike
- */
-class GrTextStrike {
-public:
-    GrTextStrike(GrFontCache*, const GrFontDescKey* fontScalerKey);
-    ~GrTextStrike();
-
-    const GrFontDescKey* getFontScalerKey() const { return fFontScalerKey; }
-    GrFontCache* getFontCache() const { return fFontCache; }
-
-    inline GrGlyph* getGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler) {
-        GrGlyph* glyph = fCache.find(packed);
-        if (NULL == glyph) {
-            glyph = this->generateGlyph(packed, scaler);
-        }
-        return glyph;
-    }
-
-    // returns true if glyph (or glyph+padding for distance field)
-    // is too large to ever fit in texture atlas subregions (GrPlots)
-    bool glyphTooLargeForAtlas(GrGlyph*);
-    // returns true if glyph successfully added to texture atlas, false otherwise
-    bool addGlyphToAtlas(GrGlyph*, GrFontScaler*);
-
-    // testing
-    int countGlyphs() const { return fCache.count(); }
-
-    // remove any references to this plot
-    void removePlot(const GrPlot* plot);
-
-    static const GrFontDescKey& GetKey(const GrTextStrike& ts) {
-        return *(ts.fFontScalerKey);
-    }
-    static uint32_t Hash(const GrFontDescKey& key) {
-        return key.getHash();
-    }
-
-public:
-    // for easy removal from list
-    GrTextStrike*   fPrev;
-    GrTextStrike*   fNext;
-
-private:
-    SkTDynamicHash<GrGlyph, GrGlyph::PackedID> fCache;
-    const GrFontDescKey* fFontScalerKey;
-    SkVarAlloc fPool;
-
-    GrFontCache*    fFontCache;
-    bool            fUseDistanceField;
-
-    GrAtlas::ClientPlotUsage fPlotUsage;
-
-    GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
-
-    friend class GrFontCache;
-};
-
-class GrFontCache {
-public:
-    GrFontCache(GrGpu*);
-    ~GrFontCache();
-
-    inline GrTextStrike* getStrike(GrFontScaler* scaler, bool useDistanceField) {
-        this->validate();
-        
-        GrTextStrike* strike = fCache.find(*(scaler->getKey()));
-        if (NULL == strike) {
-            strike = this->generateStrike(scaler);
-        } else if (strike->fPrev) {
-            // Need to put the strike at the head of its dllist, since that is how
-            // we age the strikes for purging (we purge from the back of the list)
-            this->detachStrikeFromList(strike);
-            // attach at the head
-            fHead->fPrev = strike;
-            strike->fNext = fHead;
-            strike->fPrev = NULL;
-            fHead = strike;
-        }
-        strike->fUseDistanceField = useDistanceField;
-        this->validate();
-        return strike;
-    }
-
-    // add to texture atlas that matches this format
-    GrPlot* addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* usage,
-                       int width, int height, const void* image,
-                       SkIPoint16* loc);
-
-    void freeAll();
-
-    // make an unused plot available for this glyph
-    bool freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph);
-
-    // testing
-    int countStrikes() const { return fCache.count(); }
-    GrTextStrike* getHeadStrike() const { return fHead; }
-
-    void updateTextures() {
-        for (int i = 0; i < kAtlasCount; ++i) {
-            if (fAtlases[i]) {
-                fAtlases[i]->uploadPlotsToTexture();
-            }
-        }
-    }
-
-#ifdef SK_DEBUG
-    void validate() const;
-#else
-    void validate() const {}
-#endif
-
-    void dump() const;
-
-    enum AtlasType {
-        kA8_AtlasType,   //!< 1-byte per pixel
-        k565_AtlasType,  //!< 2-bytes per pixel
-        k8888_AtlasType, //!< 4-bytes per pixel
-
-        kLast_AtlasType = k8888_AtlasType
-    };
-    static const int kAtlasCount = kLast_AtlasType + 1;
-
-private:
-    friend class GrFontPurgeListener;
-
-    SkTDynamicHash<GrTextStrike, GrFontDescKey> fCache;
-    // for LRU
-    GrTextStrike* fHead;
-    GrTextStrike* fTail;
-
-    GrGpu*      fGpu;
-    GrAtlas*    fAtlases[kAtlasCount];
-
-    GrTextStrike* generateStrike(GrFontScaler*);
-    
-    inline void detachStrikeFromList(GrTextStrike* strike)  {
-        if (strike->fPrev) {
-            SkASSERT(fHead != strike);
-            strike->fPrev->fNext = strike->fNext;
-        } else {
-            SkASSERT(fHead == strike);
-            fHead = strike->fNext;
-        }
-        
-        if (strike->fNext) {
-            SkASSERT(fTail != strike);
-            strike->fNext->fPrev = strike->fPrev;
-        } else {
-            SkASSERT(fTail == strike);
-            fTail = strike->fPrev;
-        }
-    }
-    
-    void purgeStrike(GrTextStrike* strike);
-};
-
-#endif
diff --git a/src/gpu/GrGeometryProcessor.cpp b/src/gpu/GrGeometryProcessor.cpp
deleted file mode 100644
index 703c42e..0000000
--- a/src/gpu/GrGeometryProcessor.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrGeometryProcessor.h"
-
-#include "GrInvariantOutput.h"
-
-void GrGeometryProcessor::getInvariantOutputColor(GrInitInvariantOutput* out) const {
-    if (fHasVertexColor) {
-        if (fOpaqueVertexColors) {
-            out->setUnknownOpaqueFourComponents();
-        } else {
-            out->setUnknownFourComponents();
-        }
-    } else {
-        out->setKnownFourComponents(fColor);
-    }
-    this->onGetInvariantOutputColor(out);
-}
-
-void GrGeometryProcessor::getInvariantOutputCoverage(GrInitInvariantOutput* out) const {
-    this->onGetInvariantOutputCoverage(out);
-}
diff --git a/src/gpu/GrGeometryProcessor.h b/src/gpu/GrGeometryProcessor.h
index 7f8d041..eee286b 100644
--- a/src/gpu/GrGeometryProcessor.h
+++ b/src/gpu/GrGeometryProcessor.h
@@ -19,65 +19,28 @@
  */
 class GrGeometryProcessor : public GrPrimitiveProcessor {
 public:
-    // TODO the Hint can be handled in a much more clean way when we have deferred geometry or
-    // atleast bundles
-    GrGeometryProcessor(GrColor color,
-                        const SkMatrix& viewMatrix = SkMatrix::I(),
-                        const SkMatrix& localMatrix = SkMatrix::I(),
-                        bool opaqueVertexColors = false)
-        : INHERITED(viewMatrix, localMatrix, false)
-        , fColor(color)
-        , fOpaqueVertexColors(opaqueVertexColors)
+    GrGeometryProcessor()
+        : INHERITED(false)
         , fWillUseGeoShader(false)
-        , fHasVertexColor(false)
         , fHasLocalCoords(false) {}
 
     bool willUseGeoShader() const { return fWillUseGeoShader; }
 
-    /*
-     * In an ideal world, two GrGeometryProcessors with the same class id and texture accesses
-     * would ALWAYS be able to batch together.  If two GrGeometryProcesosrs are the same then we
-     * will only keep one of them.  The remaining GrGeometryProcessor then updates its
-     * GrBatchTracker to incorporate the draw information from the GrGeometryProcessor we discard.
-     * Any bundles associated with the discarded GrGeometryProcessor will be attached to the
-     * remaining GrGeometryProcessor.
-     */
+    // TODO delete this when paths are in batch
     bool canMakeEqual(const GrBatchTracker& mine,
                       const GrPrimitiveProcessor& that,
-                      const GrBatchTracker& theirs) const SK_OVERRIDE {
-        if (this->classID() != that.classID() || !this->hasSameTextureAccesses(that)) {
-            return false;
-        }
-
-        // TODO let the GPs decide this
-        if (!this->viewMatrix().cheapEqualTo(that.viewMatrix())) {
-            return false;
-        }
-
-        // TODO remove the hint
-        const GrGeometryProcessor& other = that.cast<GrGeometryProcessor>();
-        if (fHasVertexColor && fOpaqueVertexColors != other.fOpaqueVertexColors) {
-            return false;
-        }
-
-        // TODO this equality test should really be broken up, some of this can live on the batch
-        // tracker test and some of this should be in bundles
-        if (!this->onIsEqual(other)) {
-            return false;
-        }
-
-        return this->onCanMakeEqual(mine, other, theirs);
+                      const GrBatchTracker& theirs) const override {
+        SkFAIL("Unsupported\n");
+        return false;
     }
-    
-    // TODO we can remove color from the GrGeometryProcessor base class once we have bundles of
-    // primitive data
-    GrColor color() const { return fColor; }
 
-    // TODO this is a total hack until the gp can do deferred geometry
-    bool hasVertexColor() const { return fHasVertexColor; }
-
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE;
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE;
+    // TODO Delete when paths are in batch
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
+        SkFAIL("Unsupported\n");
+    }
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
+        SkFAIL("Unsupported\n");
+    }
 
 protected:
     /*
@@ -128,26 +91,12 @@
     void setWillUseGeoShader() { fWillUseGeoShader = true; }
 
     // TODO hack see above
-    void setHasVertexColor() { fHasVertexColor = true; }
     void setHasLocalCoords() { fHasLocalCoords = true; }
 
-    virtual void onGetInvariantOutputColor(GrInitInvariantOutput*) const {}
-    virtual void onGetInvariantOutputCoverage(GrInitInvariantOutput*) const = 0;
-
 private:
-    virtual bool onCanMakeEqual(const GrBatchTracker& mine,
-                                const GrGeometryProcessor& that,
-                                const GrBatchTracker& theirs) const = 0;
+    bool hasExplicitLocalCoords() const override { return fHasLocalCoords; }
 
-    // TODO delete this when we have more advanced equality testing via bundles and the BT
-    virtual bool onIsEqual(const GrGeometryProcessor&) const = 0;
-
-    bool hasExplicitLocalCoords() const SK_OVERRIDE { return fHasLocalCoords; }
-
-    GrColor fColor;
-    bool fOpaqueVertexColors;
     bool fWillUseGeoShader;
-    bool fHasVertexColor;
     bool fHasLocalCoords;
 
     typedef GrPrimitiveProcessor INHERITED;
diff --git a/src/gpu/GrGlyph.h b/src/gpu/GrGlyph.h
index 0e534d6..8750c76 100644
--- a/src/gpu/GrGlyph.h
+++ b/src/gpu/GrGlyph.h
@@ -8,6 +8,7 @@
 #ifndef GrGlyph_DEFINED
 #define GrGlyph_DEFINED
 
+#include "GrBatchAtlas.h"
 #include "GrRect.h"
 #include "GrTypes.h"
 
@@ -23,22 +24,32 @@
     - failed to get metrics
  */
 struct GrGlyph {
+    enum MaskStyle {
+        kCoverage_MaskStyle,
+        kDistance_MaskStyle
+    };
+    
     typedef uint32_t PackedID;
 
-    GrPlot*      fPlot;
-    SkPath*      fPath;
-    PackedID     fPackedID;
-    GrMaskFormat fMaskFormat;
-    GrIRect16    fBounds;
-    SkIPoint16   fAtlasLocation;
+    // TODO either plot or AtlasID will be valid, not both
+    GrBatchAtlas::AtlasID fID;
+    GrPlot*               fPlot;
+    SkPath*               fPath;
+    PackedID              fPackedID;
+    GrMaskFormat          fMaskFormat;
+    GrIRect16             fBounds;
+    SkIPoint16            fAtlasLocation;
+    bool                  fTooLargeForAtlas;
 
     void init(GrGlyph::PackedID packed, const SkIRect& bounds, GrMaskFormat format) {
+        fID = GrBatchAtlas::kInvalidAtlasID;
         fPlot = NULL;
         fPath = NULL;
         fPackedID = packed;
         fBounds.set(bounds);
         fMaskFormat = format;
         fAtlasLocation.set(0, 0);
+        fTooLargeForAtlas = GrBatchAtlas::GlyphTooLargeForAtlas(bounds.width(), bounds.height());
     }
 
     void free() {
@@ -60,10 +71,11 @@
         return (pos >> 14) & 3;
     }
 
-    static inline PackedID Pack(uint16_t glyphID, SkFixed x, SkFixed y) {
+    static inline PackedID Pack(uint16_t glyphID, SkFixed x, SkFixed y, MaskStyle ms) {
         x = ExtractSubPixelBitsFromFixed(x);
         y = ExtractSubPixelBitsFromFixed(y);
-        return (x << 18) | (y << 16) | glyphID;
+        int dfFlag = (ms == kDistance_MaskStyle) ? 0x1 : 0x0;
+        return (dfFlag << 20) | (x << 18) | (y << 16) | glyphID;
     }
 
     static inline SkFixed UnpackFixedX(PackedID packed) {
@@ -74,6 +86,10 @@
         return ((packed >> 16) & 3) << 14;
     }
 
+    static inline MaskStyle UnpackMaskStyle(PackedID packed) {
+        return ((packed >> 20) & 1) ? kDistance_MaskStyle : kCoverage_MaskStyle;
+    }
+    
     static inline uint16_t UnpackID(PackedID packed) {
         return (uint16_t)packed;
     }
@@ -83,7 +99,7 @@
     }
 
     static inline uint32_t Hash(GrGlyph::PackedID key) {
-        return SkChecksum::Murmur3(&key, sizeof(key));
+        return SkChecksum::Mix(key);
     }
 };
 
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 45a58af..b7d362c 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -9,36 +9,64 @@
 
 #include "GrGpu.h"
 
-#include "GrBufferAllocPool.h"
 #include "GrContext.h"
 #include "GrDrawTargetCaps.h"
 #include "GrGpuResourcePriv.h"
 #include "GrIndexBuffer.h"
 #include "GrResourceCache.h"
 #include "GrRenderTargetPriv.h"
-#include "GrStencilBuffer.h"
+#include "GrStencilAttachment.h"
 #include "GrVertexBuffer.h"
+#include "GrVertices.h"
+
+GrVertices& GrVertices::operator =(const GrVertices& di) {
+    fPrimitiveType  = di.fPrimitiveType;
+    fStartVertex    = di.fStartVertex;
+    fStartIndex     = di.fStartIndex;
+    fVertexCount    = di.fVertexCount;
+    fIndexCount     = di.fIndexCount;
+
+    fInstanceCount          = di.fInstanceCount;
+    fVerticesPerInstance    = di.fVerticesPerInstance;
+    fIndicesPerInstance     = di.fIndicesPerInstance;
+    fMaxInstancesPerDraw    = di.fMaxInstancesPerDraw;
+
+    fVertexBuffer.reset(di.vertexBuffer());
+    fIndexBuffer.reset(di.indexBuffer());
+
+    return *this;
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 
 GrGpu::GrGpu(GrContext* context)
     : fResetTimestamp(kExpiredTimestamp+1)
     , fResetBits(kAll_GrBackendState)
-    , fQuadIndexBuffer(NULL)
     , fGpuTraceMarkerCount(0)
     , fContext(context) {
 }
 
-GrGpu::~GrGpu() {
-    SkSafeSetNull(fQuadIndexBuffer);
-}
+GrGpu::~GrGpu() {}
 
 void GrGpu::contextAbandoned() {}
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrTexture* GrGpu::createTexture(const GrSurfaceDesc& desc, bool budgeted,
+static GrSurfaceOrigin resolve_origin(GrSurfaceOrigin origin, bool renderTarget) {
+    // By default, GrRenderTargets are GL's normal orientation so that they
+    // can be drawn to by the outside world without the client having
+    // to render upside down.
+    if (kDefault_GrSurfaceOrigin == origin) {
+        return renderTarget ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
+    } else {
+        return origin;
+    }
+}
+
+GrTexture* GrGpu::createTexture(const GrSurfaceDesc& origDesc, bool budgeted,
                                 const void* srcData, size_t rowBytes) {
+    GrSurfaceDesc desc = origDesc;
+
     if (!this->caps()->isConfigTexturable(desc.fConfig)) {
         return NULL;
     }
@@ -48,10 +76,38 @@
         return NULL;
     }
 
+    // We currently not support multisampled textures
+    if (!isRT && desc.fSampleCnt > 0) {
+        return NULL;
+    }
+
     GrTexture *tex = NULL;
+
+    if (isRT) {
+        int maxRTSize = this->caps()->maxRenderTargetSize();
+        if (desc.fWidth > maxRTSize || desc.fHeight > maxRTSize) {
+            return NULL;
+        }
+    } else {
+        int maxSize = this->caps()->maxTextureSize();
+        if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
+            return NULL;
+        }
+    }
+
+    GrGpuResource::LifeCycle lifeCycle = budgeted ? GrGpuResource::kCached_LifeCycle :
+                                                    GrGpuResource::kUncached_LifeCycle;
+
+    desc.fSampleCnt = SkTMin(desc.fSampleCnt, this->caps()->maxSampleCount());
+    // Attempt to catch un- or wrongly initialized sample counts;
+    SkASSERT(desc.fSampleCnt >= 0 && desc.fSampleCnt <= 64);
+
+    desc.fOrigin = resolve_origin(desc.fOrigin, isRT);
+
     if (GrPixelConfigIsCompressed(desc.fConfig)) {
         // We shouldn't be rendering into this
-        SkASSERT((desc.fFlags & kRenderTarget_GrSurfaceFlag) == 0);
+        SkASSERT(!isRT);
+        SkASSERT(0 == desc.fSampleCnt);
 
         if (!this->caps()->npotTextureTileSupport() &&
             (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight))) {
@@ -59,10 +115,10 @@
         }
 
         this->handleDirtyContext();
-        tex = this->onCreateCompressedTexture(desc, budgeted, srcData);
+        tex = this->onCreateCompressedTexture(desc, lifeCycle, srcData);
     } else {
         this->handleDirtyContext();
-        tex = this->onCreateTexture(desc, budgeted, srcData, rowBytes);
+        tex = this->onCreateTexture(desc, lifeCycle, srcData, rowBytes);
     }
     if (!this->caps()->reuseScratchTextures() && !isRT) {
         tex->resourcePriv().removeScratchKey();
@@ -76,29 +132,40 @@
     return tex;
 }
 
-bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt) {
-    SkASSERT(NULL == rt->renderTargetPriv().getStencilBuffer());
+bool GrGpu::attachStencilAttachmentToRenderTarget(GrRenderTarget* rt) {
+    SkASSERT(NULL == rt->renderTargetPriv().getStencilAttachment());
     GrUniqueKey sbKey;
 
     int width = rt->width();
     int height = rt->height();
+#if 0
     if (this->caps()->oversizedStencilSupport()) {
         width  = SkNextPow2(width);
         height = SkNextPow2(height);
     }
+#endif
 
-    GrStencilBuffer::ComputeSharedStencilBufferKey(width, height, rt->numSamples(), &sbKey);
-    SkAutoTUnref<GrStencilBuffer> sb(static_cast<GrStencilBuffer*>(
+    GrStencilAttachment::ComputeSharedStencilAttachmentKey(width, height, rt->numSamples(), &sbKey);
+    SkAutoTUnref<GrStencilAttachment> sb(static_cast<GrStencilAttachment*>(
         this->getContext()->getResourceCache()->findAndRefUniqueResource(sbKey)));
     if (sb) {
-        if (this->attachStencilBufferToRenderTarget(sb, rt)) {
-            rt->renderTargetPriv().didAttachStencilBuffer(sb);
+        if (this->attachStencilAttachmentToRenderTarget(sb, rt)) {
+            rt->renderTargetPriv().didAttachStencilAttachment(sb);
             return true;
         }
         return false;
     }
-    if (this->createStencilBufferForRenderTarget(rt, width, height)) {
-        GrStencilBuffer* sb = rt->renderTargetPriv().getStencilBuffer();
+    if (this->createStencilAttachmentForRenderTarget(rt, width, height)) {
+        // Right now we're clearing the stencil buffer here after it is
+        // attached to an RT for the first time. When we start matching
+        // stencil buffers with smaller color targets this will no longer
+        // be correct because it won't be guaranteed to clear the entire
+        // sb.
+        // We used to clear down in the GL subclass using a special purpose
+        // FBO. But iOS doesn't allow a stencil-only FBO. It reports unsupported
+        // FBO status.
+        this->clearStencil(rt);
+        GrStencilAttachment* sb = rt->renderTargetPriv().getStencilAttachment();
         sb->resourcePriv().setUniqueKey(sbKey);
         return true;
     } else {
@@ -114,7 +181,7 @@
     }
     // TODO: defer this and attach dynamically
     GrRenderTarget* tgt = tex->asRenderTarget();
-    if (tgt && !this->attachStencilBufferToRenderTarget(tgt)) {
+    if (tgt && !this->attachStencilAttachmentToRenderTarget(tgt)) {
         tex->unref();
         return NULL;
     } else {
@@ -137,39 +204,6 @@
     return this->onCreateIndexBuffer(size, dynamic);
 }
 
-GrIndexBuffer* GrGpu::createInstancedIndexBuffer(const uint16_t* pattern,
-                                                 int patternSize,
-                                                 int reps,
-                                                 int vertCount,
-                                                 bool isDynamic) {
-    size_t bufferSize = patternSize * reps * sizeof(uint16_t);
-    GrGpu* me = const_cast<GrGpu*>(this);
-    GrIndexBuffer* buffer = me->createIndexBuffer(bufferSize, isDynamic);
-    if (buffer) {
-        uint16_t* data = (uint16_t*) buffer->map();
-        bool useTempData = (NULL == data);
-        if (useTempData) {
-            data = SkNEW_ARRAY(uint16_t, reps * patternSize);
-        }
-        for (int i = 0; i < reps; ++i) {
-            int baseIdx = i * patternSize;
-            uint16_t baseVert = (uint16_t)(i * vertCount);
-            for (int j = 0; j < patternSize; ++j) {
-                data[baseIdx+j] = baseVert + pattern[j];
-            }
-        }
-        if (useTempData) {
-            if (!buffer->updateData(data, bufferSize)) {
-                SkFAIL("Can't get indices into buffer!");
-            }
-            SkDELETE_ARRAY(data);
-        } else {
-            buffer->unmap();
-        }
-    }
-    return buffer;
-}
-
 void GrGpu::clear(const SkIRect* rect,
                   GrColor color,
                   bool canIgnoreRect,
@@ -258,32 +292,13 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-static const int MAX_QUADS = 1 << 12; // max possible: (1 << 14) - 1;
-
-GR_STATIC_ASSERT(4 * MAX_QUADS <= 65535);
-
-static const uint16_t gQuadIndexPattern[] = {
-  0, 1, 2, 0, 2, 3
-};
-
-const GrIndexBuffer* GrGpu::getQuadIndexBuffer() const {
-    if (NULL == fQuadIndexBuffer || fQuadIndexBuffer->wasDestroyed()) {
-        SkSafeUnref(fQuadIndexBuffer);
-        GrGpu* me = const_cast<GrGpu*>(this);
-        fQuadIndexBuffer = me->createInstancedIndexBuffer(gQuadIndexPattern,
-                                                          6,
-                                                          MAX_QUADS,
-                                                          4);
-    }
-
-    return fQuadIndexBuffer;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-void GrGpu::draw(const DrawArgs& args, const GrDrawTarget::DrawInfo& info) {
+void GrGpu::draw(const DrawArgs& args, const GrVertices& vertices) {
     this->handleDirtyContext();
-    this->onDraw(args, info);
+    GrVertices::Iterator iter;
+    const GrNonInstancedVertices* verts = iter.init(vertices);
+    do {
+        this->onDraw(args, *verts);
+    } while ((verts = iter.next()));
 }
 
 void GrGpu::stencilPath(const GrPath* path, const StencilPathState& state) {
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 95bbc6e..f2ad10f 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -14,15 +14,15 @@
 #include "SkPath.h"
 
 class GrContext;
-class GrIndexBufferAllocPool;
+class GrNonInstancedVertices;
 class GrPath;
 class GrPathRange;
 class GrPathRenderer;
 class GrPathRendererChain;
 class GrPipeline;
 class GrPrimitiveProcessor;
-class GrStencilBuffer;
-class GrVertexBufferAllocPool;
+class GrStencilAttachment;
+class GrVertices;
 
 class GrGpu : public SkRefCnt {
 public:
@@ -36,7 +36,7 @@
     ////////////////////////////////////////////////////////////////////////////
 
     GrGpu(GrContext* context);
-    ~GrGpu() SK_OVERRIDE;
+    ~GrGpu() override;
 
     GrContext* getContext() { return fContext; }
     const GrContext* getContext() const { return fContext; }
@@ -119,34 +119,6 @@
     GrIndexBuffer* createIndexBuffer(size_t size, bool dynamic);
 
     /**
-     * Creates an index buffer for instance drawing with a specific pattern.
-     *
-     * @param pattern     the pattern to repeat
-     * @param patternSize size in bytes of the pattern
-     * @param reps        number of times to repeat the pattern
-     * @param vertCount   number of vertices the pattern references
-     * @param dynamic     hints whether the data will be frequently changed
-     *                    by either GrIndexBuffer::map() or
-     *                    GrIndexBuffer::updateData().
-     *
-     * @return The index buffer if successful, otherwise NULL.
-     */
-    GrIndexBuffer* createInstancedIndexBuffer(const uint16_t* pattern,
-                                              int patternSize,
-                                              int reps,
-                                              int vertCount,
-                                              bool isDynamic = false);
-
-    /**
-     * Returns an index buffer that can be used to render quads.
-     * Six indices per quad: 0, 1, 2, 0, 2, 3, etc.
-     * The max number of quads can be queried using GrIndexBuffer::maxQuads().
-     * Draw with kTriangles_GrPrimitiveType
-     * @ return the quad index buffer
-     */
-    const GrIndexBuffer* getQuadIndexBuffer() const;
-
-    /**
      * Resolves MSAA.
      */
     void resolveRenderTarget(GrRenderTarget* target);
@@ -304,8 +276,10 @@
                              const SkIRect& srcRect,
                              const SkIPoint& dstPoint) = 0;
 
+    // Called before certain draws in order to guarantee coherent results from dst reads.
+    virtual void xferBarrier(GrRenderTarget*, GrXferBarrierType) = 0;
+
     struct DrawArgs {
-        typedef GrDrawTarget::DrawInfo DrawInfo;
         DrawArgs(const GrPrimitiveProcessor* primProc,
                  const GrPipeline* pipeline,
                  const GrProgramDesc* desc,
@@ -322,7 +296,7 @@
         const GrBatchTracker* fBatchTracker;
     };
 
-    void draw(const DrawArgs&, const GrDrawTarget::DrawInfo&);
+    void draw(const DrawArgs&, const GrVertices&);
 
     /** None of these params are optional, pointers used just to avoid making copies. */
     struct StencilPathState {
@@ -358,7 +332,7 @@
             fShaderCompilations = 0;
             fTextureCreates = 0;
             fTextureUploads = 0;
-            fStencilBufferCreates = 0;
+            fStencilAttachmentCreates = 0;
         }
 
         int renderTargetBinds() const { return fRenderTargetBinds; }
@@ -369,7 +343,7 @@
         void incTextureCreates() { fTextureCreates++; }
         int textureUploads() const { return fTextureUploads; }
         void incTextureUploads() { fTextureUploads++; }
-        void incStencilBufferCreates() { fStencilBufferCreates++; }
+        void incStencilAttachmentCreates() { fStencilAttachmentCreates++; }
         void dump(SkString*);
 
     private:
@@ -377,14 +351,14 @@
         int fShaderCompilations;
         int fTextureCreates;
         int fTextureUploads;
-        int fStencilBufferCreates;
+        int fStencilAttachmentCreates;
 #else
         void dump(SkString*) {};
         void incRenderTargetBinds() {}
         void incShaderCompilations() {}
         void incTextureCreates() {}
         void incTextureUploads() {}
-        void incStencilBufferCreates() {}
+        void incStencilAttachmentCreates() {}
 #endif
     };
 
@@ -410,7 +384,7 @@
     void restoreActiveTraceMarkers();
 
     // Given a rt, find or create a stencil buffer and attach it
-    bool attachStencilBufferToRenderTarget(GrRenderTarget* target);
+    bool attachStencilAttachmentToRenderTarget(GrRenderTarget* target);
 
 protected:
     // Functions used to map clip-respecting stencil tests into normal
@@ -437,9 +411,13 @@
     virtual void onResetContext(uint32_t resetBits) = 0;
 
     // overridden by backend-specific derived class to create objects.
-    virtual GrTexture* onCreateTexture(const GrSurfaceDesc& desc, bool budgeted,
+    // Texture size and sample size will have already been validated in base class before
+    // onCreateTexture/CompressedTexture are called.
+    virtual GrTexture* onCreateTexture(const GrSurfaceDesc& desc,
+                                       GrGpuResource::LifeCycle lifeCycle,
                                        const void* srcData, size_t rowBytes) = 0;
-    virtual GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc, bool budgeted,
+    virtual GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc,
+                                                 GrGpuResource::LifeCycle lifeCycle,
                                                  const void* srcData) = 0;
     virtual GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) = 0;
     virtual GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) = 0;
@@ -456,7 +434,7 @@
     virtual void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) = 0;
 
     // overridden by backend-specific derived class to perform the draw call.
-    virtual void onDraw(const DrawArgs&, const GrDrawTarget::DrawInfo&) = 0;
+    virtual void onDraw(const DrawArgs&, const GrNonInstancedVertices&) = 0;
     virtual void onStencilPath(const GrPath*, const StencilPathState&) = 0;
 
     virtual void onDrawPath(const DrawArgs&, const GrPath*, const GrStencilSettings&) = 0;
@@ -488,10 +466,10 @@
     // 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*, int width, int height) = 0;
+    virtual bool createStencilAttachmentForRenderTarget(GrRenderTarget*, int width, int height) = 0;
 
     // attaches an existing SB to an existing RT.
-    virtual bool attachStencilBufferToRenderTarget(GrStencilBuffer*, GrRenderTarget*) = 0;
+    virtual bool attachStencilAttachmentToRenderTarget(GrStencilAttachment*, GrRenderTarget*) = 0;
 
     // clears target's entire stencil buffer to 0
     virtual void clearStencil(GrRenderTarget* target) = 0;
@@ -513,8 +491,6 @@
 
     ResetTimestamp                                                      fResetTimestamp;
     uint32_t                                                            fResetBits;
-    // these are mutable so they can be created on-demand
-    mutable GrIndexBuffer*                                              fQuadIndexBuffer;
     // To keep track that we always have at least as many debug marker adds as removes
     int                                                                 fGpuTraceMarkerCount;
     GrTraceMarkerSet                                                    fActiveTraceMarkers;
diff --git a/src/gpu/GrGpuFactory.cpp b/src/gpu/GrGpuFactory.cpp
index 4b09c14..74adf98 100644
--- a/src/gpu/GrGpuFactory.cpp
+++ b/src/gpu/GrGpuFactory.cpp
@@ -7,37 +7,49 @@
  */
 
 
-#include "GrTypes.h"
+#include "GrGpuFactory.h"
 
 #include "gl/GrGLConfig.h"
 
 #include "GrGpu.h"
 #include "gl/GrGLGpu.h"
 
-GrGpu* GrGpu::Create(GrBackend backend, GrBackendContext backendContext, GrContext* context) {
-
+static GrGpu* gl_gpu_create(GrBackendContext backendContext, GrContext* context) {
     const GrGLInterface* glInterface = NULL;
     SkAutoTUnref<const GrGLInterface> glInterfaceUnref;
 
-    if (kOpenGL_GrBackend == backend) {
-        glInterface = reinterpret_cast<const GrGLInterface*>(backendContext);
-        if (NULL == glInterface) {
-            glInterface = GrGLDefaultInterface();
-            // By calling GrGLDefaultInterface we've taken a ref on the
-            // returned object. We only want to hold that ref until after
-            // the GrGpu is constructed and has taken ownership.
-            glInterfaceUnref.reset(glInterface);
-        }
-        if (NULL == glInterface) {
+    glInterface = reinterpret_cast<const GrGLInterface*>(backendContext);
+    if (NULL == glInterface) {
+        glInterface = GrGLDefaultInterface();
+        // By calling GrGLDefaultInterface we've taken a ref on the
+        // returned object. We only want to hold that ref until after
+        // the GrGpu is constructed and has taken ownership.
+        glInterfaceUnref.reset(glInterface);
+    }
+    if (NULL == glInterface) {
 #ifdef SK_DEBUG
-            SkDebugf("No GL interface provided!\n");
+        SkDebugf("No GL interface provided!\n");
 #endif
-            return NULL;
-        }
-        GrGLContext ctx(glInterface);
-        if (ctx.isInitialized()) {
-            return SkNEW_ARGS(GrGLGpu, (ctx, context));
-        }
+        return NULL;
+    }
+    GrGLContext ctx(glInterface);
+    if (ctx.isInitialized()) {
+        return SkNEW_ARGS(GrGLGpu, (ctx, context));
     }
     return NULL;
 }
+
+static const int kMaxNumBackends = 4;
+static CreateGpuProc gGpuFactories[kMaxNumBackends] = {gl_gpu_create, NULL, NULL, NULL};
+
+GrGpuFactoryRegistrar::GrGpuFactoryRegistrar(int i, CreateGpuProc proc) {
+    gGpuFactories[i] = proc;
+}
+
+GrGpu* GrGpu::Create(GrBackend backend, GrBackendContext backendContext, GrContext* context) {
+    SkASSERT((int)backend < kMaxNumBackends);
+    if (!gGpuFactories[backend]) {
+        return NULL;
+    }
+    return (gGpuFactories[backend])(backendContext, context);
+}
diff --git a/src/gpu/GrGpuFactory.h b/src/gpu/GrGpuFactory.h
new file mode 100644
index 0000000..180f264
--- /dev/null
+++ b/src/gpu/GrGpuFactory.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGpuFactory_DEFINED
+#define GrGpuFactory_DEFINED
+
+#include "GrTypes.h"
+
+class GrGpu;
+class GrContext;
+
+typedef GrGpu* (*CreateGpuProc)(GrBackendContext, GrContext*);
+
+class GrGpuFactoryRegistrar {
+public:
+    GrGpuFactoryRegistrar(int i, CreateGpuProc proc);
+};
+
+#endif
diff --git a/src/gpu/GrGpuResource.cpp b/src/gpu/GrGpuResource.cpp
index a2fc7b3..52fe1e5 100644
--- a/src/gpu/GrGpuResource.cpp
+++ b/src/gpu/GrGpuResource.cpp
@@ -105,14 +105,39 @@
     get_resource_cache(fGpu)->resourceAccess().changeUniqueKey(this, key);
 }
 
-void GrGpuResource::notifyIsPurgeable() const {
+void GrGpuResource::notifyAllCntsAreZero(CntType lastCntTypeToReachZero) const {
     if (this->wasDestroyed()) {
         // We've already been removed from the cache. Goodbye cruel world!
         SkDELETE(this);
-    } else {
-        GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this);
-        get_resource_cache(fGpu)->resourceAccess().notifyPurgeable(mutableThis);
+        return;
     }
+
+    // We should have already handled this fully in notifyRefCntIsZero().
+    SkASSERT(kRef_CntType != lastCntTypeToReachZero);
+
+    GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this);
+    static const uint32_t kFlag =
+        GrResourceCache::ResourceAccess::kAllCntsReachedZero_RefNotificationFlag;
+    get_resource_cache(fGpu)->resourceAccess().notifyCntReachedZero(mutableThis, kFlag);
+}
+
+bool GrGpuResource::notifyRefCountIsZero() const {
+    if (this->wasDestroyed()) {
+        // handle this in notifyAllCntsAreZero().
+        return true;
+    }
+
+    GrGpuResource* mutableThis = const_cast<GrGpuResource*>(this);
+    uint32_t flags =
+        GrResourceCache::ResourceAccess::kRefCntReachedZero_RefNotificationFlag;
+    if (!this->internalHasPendingIO()) {
+        flags |= GrResourceCache::ResourceAccess::kAllCntsReachedZero_RefNotificationFlag;
+    }
+    get_resource_cache(fGpu)->resourceAccess().notifyCntReachedZero(mutableThis, flags);
+
+    // There is no need to call our notifyAllCntsAreZero function at this point since we already
+    // told the cache about the state of cnts.
+    return false;
 }
 
 void GrGpuResource::setScratchKey(const GrScratchKey& scratchKey) {
diff --git a/src/gpu/GrInOrderCommandBuilder.cpp b/src/gpu/GrInOrderCommandBuilder.cpp
new file mode 100644
index 0000000..e56607b
--- /dev/null
+++ b/src/gpu/GrInOrderCommandBuilder.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrInOrderCommandBuilder.h"
+
+#include "GrColor.h"
+#include "GrInOrderDrawBuffer.h"
+#include "GrTemplates.h"
+#include "SkPoint.h"
+
+static bool path_fill_type_is_winding(const GrStencilSettings& pathStencilSettings) {
+    static const GrStencilSettings::Face pathFace = GrStencilSettings::kFront_Face;
+    bool isWinding = kInvert_StencilOp != pathStencilSettings.passOp(pathFace);
+    if (isWinding) {
+        // Double check that it is in fact winding.
+        SkASSERT(kIncClamp_StencilOp == pathStencilSettings.passOp(pathFace));
+        SkASSERT(kIncClamp_StencilOp == pathStencilSettings.failOp(pathFace));
+        SkASSERT(0x1 != pathStencilSettings.writeMask(pathFace));
+        SkASSERT(!pathStencilSettings.isTwoSided());
+    }
+    return isWinding;
+}
+
+GrTargetCommands::Cmd* GrInOrderCommandBuilder::recordDrawBatch(State* state, GrBatch* batch) {
+    // Check if there is a Batch Draw we can batch with
+    if (!this->cmdBuffer()->empty() &&
+        Cmd::kDrawBatch_CmdType == this->cmdBuffer()->back().type()) {
+        DrawBatch* previous = static_cast<DrawBatch*>(&this->cmdBuffer()->back());
+        if (previous->fState == state && previous->fBatch->combineIfPossible(batch)) {
+            return NULL;
+        }
+    }
+
+    return GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(), DrawBatch, (state, batch,
+                                                                    this->batchTarget()));
+}
+
+GrTargetCommands::Cmd*
+GrInOrderCommandBuilder::recordStencilPath(const GrPipelineBuilder& pipelineBuilder,
+                                           const GrPathProcessor* pathProc,
+                                           const GrPath* path,
+                                           const GrScissorState& scissorState,
+                                           const GrStencilSettings& stencilSettings) {
+    StencilPath* sp = GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(), StencilPath,
+                                               (path, pipelineBuilder.getRenderTarget()));
+
+    sp->fScissor = scissorState;
+    sp->fUseHWAA = pipelineBuilder.isHWAntialias();
+    sp->fViewMatrix = pathProc->viewMatrix();
+    sp->fStencil = stencilSettings;
+    return sp;
+}
+
+GrTargetCommands::Cmd*
+GrInOrderCommandBuilder::recordDrawPath(State* state,
+                                        const GrPathProcessor* pathProc,
+                                        const GrPath* path,
+                                        const GrStencilSettings& stencilSettings) {
+    DrawPath* dp = GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(), DrawPath, (state, path));
+    dp->fStencilSettings = stencilSettings;
+    return dp;
+}
+
+GrTargetCommands::Cmd*
+GrInOrderCommandBuilder::recordDrawPaths(State* state,
+                                         GrInOrderDrawBuffer* iodb,
+                                         const GrPathProcessor* pathProc,
+                                         const GrPathRange* pathRange,
+                                         const void* indexValues,
+                                         GrDrawTarget::PathIndexType indexType,
+                                         const float transformValues[],
+                                         GrDrawTarget::PathTransformType transformType,
+                                         int count,
+                                         const GrStencilSettings& stencilSettings,
+                                         const GrDrawTarget::PipelineInfo& pipelineInfo) {
+    SkASSERT(pathRange);
+    SkASSERT(indexValues);
+    SkASSERT(transformValues);
+
+    char* savedIndices;
+    float* savedTransforms;
+
+    iodb->appendIndicesAndTransforms(indexValues, indexType,
+                                     transformValues, transformType,
+                                     count, &savedIndices, &savedTransforms);
+
+    if (!this->cmdBuffer()->empty() &&
+        Cmd::kDrawPaths_CmdType == this->cmdBuffer()->back().type()) {
+        // The previous command was also DrawPaths. Try to collapse this call into the one
+        // before. Note that stenciling all the paths at once, then covering, may not be
+        // equivalent to two separate draw calls if there is overlap. Blending won't work,
+        // and the combined calls may also cancel each other's winding numbers in some
+        // places. For now the winding numbers are only an issue if the fill is even/odd,
+        // because DrawPaths is currently only used for glyphs, and glyphs in the same
+        // font tend to all wind in the same direction.
+        DrawPaths* previous = static_cast<DrawPaths*>(&this->cmdBuffer()->back());
+        if (pathRange == previous->pathRange() &&
+            indexType == previous->fIndexType &&
+            transformType == previous->fTransformType &&
+            stencilSettings == previous->fStencilSettings &&
+            path_fill_type_is_winding(stencilSettings) &&
+            !pipelineInfo.willBlendWithDst(pathProc) &&
+            previous->fState == state) {
+                const int indexBytes = GrPathRange::PathIndexSizeInBytes(indexType);
+                const int xformSize = GrPathRendering::PathTransformSize(transformType);
+                if (&previous->fIndices[previous->fCount*indexBytes] == savedIndices &&
+                    (0 == xformSize ||
+                     &previous->fTransforms[previous->fCount*xformSize] == savedTransforms)) {
+                    // Fold this DrawPaths call into the one previous.
+                    previous->fCount += count;
+                    return NULL;
+                }
+        }
+    }
+
+    DrawPaths* dp = GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(), DrawPaths, (state, pathRange));
+    dp->fIndices = savedIndices;
+    dp->fIndexType = indexType;
+    dp->fTransforms = savedTransforms;
+    dp->fTransformType = transformType;
+    dp->fCount = count;
+    dp->fStencilSettings = stencilSettings;
+    return dp;
+}
diff --git a/src/gpu/GrInOrderCommandBuilder.h b/src/gpu/GrInOrderCommandBuilder.h
new file mode 100644
index 0000000..164db92
--- /dev/null
+++ b/src/gpu/GrInOrderCommandBuilder.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrInOrderCommandBuilder_DEFINED
+#define GrInOrderCommandBuilder_DEFINED
+
+#include "GrCommandBuilder.h"
+
+class GrInOrderCommandBuilder : public GrCommandBuilder {
+public:
+    typedef GrCommandBuilder::Cmd Cmd;
+    typedef GrCommandBuilder::State State;
+
+    GrInOrderCommandBuilder(GrGpu* gpu) : INHERITED(gpu) { }
+
+    Cmd* recordDrawBatch(State*, GrBatch*) override;
+    Cmd* recordStencilPath(const GrPipelineBuilder&,
+                           const GrPathProcessor*,
+                           const GrPath*,
+                           const GrScissorState&,
+                           const GrStencilSettings&) override;
+    Cmd* recordDrawPath(State*,
+                        const GrPathProcessor*,
+                        const GrPath*,
+                        const GrStencilSettings&) override;
+    Cmd* recordDrawPaths(State*,
+                         GrInOrderDrawBuffer*,
+                         const GrPathProcessor*,
+                         const GrPathRange*,
+                         const void*,
+                         GrDrawTarget::PathIndexType,
+                         const float transformValues[],
+                         GrDrawTarget::PathTransformType ,
+                         int,
+                         const GrStencilSettings&,
+                         const GrDrawTarget::PipelineInfo&) override;
+
+private:
+    typedef GrCommandBuilder INHERITED;
+
+};
+
+#endif
diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp
index 4d8fa5e..6b6490c 100644
--- a/src/gpu/GrInOrderDrawBuffer.cpp
+++ b/src/gpu/GrInOrderDrawBuffer.cpp
@@ -7,306 +7,27 @@
 
 #include "GrInOrderDrawBuffer.h"
 
-#include "GrDefaultGeoProcFactory.h"
-#include "GrTemplates.h"
-
-GrInOrderDrawBuffer::GrInOrderDrawBuffer(GrGpu* gpu,
-                                         GrVertexBufferAllocPool* vertexPool,
-                                         GrIndexBufferAllocPool* indexPool)
-    : INHERITED(gpu, vertexPool, indexPool)
-    , fCommands(gpu, vertexPool, indexPool)
+GrInOrderDrawBuffer::GrInOrderDrawBuffer(GrContext* context)
+    : INHERITED(context)
+    , fCommands(GrCommandBuilder::Create(context->getGpu(), false))
     , fPathIndexBuffer(kPathIdxBufferMinReserve * sizeof(char)/4)
     , fPathTransformBuffer(kPathXformBufferMinReserve * sizeof(float)/4)
+    , fPipelineBuffer(kPipelineBufferMinReserve)
     , fDrawID(0) {
-
-    SkASSERT(vertexPool);
-    SkASSERT(indexPool);
 }
 
 GrInOrderDrawBuffer::~GrInOrderDrawBuffer() {
     this->reset();
 }
 
-////////////////////////////////////////////////////////////////////////////////
-
-/** We always use per-vertex colors so that rects can be batched across color changes. Sometimes we
-    have explicit local coords and sometimes not. We *could* always provide explicit local coords
-    and just duplicate the positions when the caller hasn't provided a local coord rect, but we
-    haven't seen a use case which frequently switches between local rect and no local rect draws.
-
-    The color param is used to determine whether the opaque hint can be set on the draw state.
-    The caller must populate the vertex colors itself.
-
-    The vertex attrib order is always pos, color, [local coords].
- */
-static const GrGeometryProcessor* create_rect_gp(bool hasExplicitLocalCoords,
-                                                 GrColor color,
-                                                 const SkMatrix* localMatrix) {
-    uint32_t flags = GrDefaultGeoProcFactory::kPosition_GPType |
-                     GrDefaultGeoProcFactory::kColor_GPType;
-    flags |= hasExplicitLocalCoords ? GrDefaultGeoProcFactory::kLocalCoord_GPType : 0;
-    if (localMatrix) {
-        return GrDefaultGeoProcFactory::Create(flags, color, SkMatrix::I(), *localMatrix,
-                                               GrColorIsOpaque(color));
-    } else {
-        return GrDefaultGeoProcFactory::Create(flags, color, SkMatrix::I(), SkMatrix::I(),
-                                               GrColorIsOpaque(color));
-    }
-}
-
-class RectBatch : public GrBatch {
-public:
-    struct Geometry {
-        GrColor fColor;
-        SkMatrix fViewMatrix;
-        SkRect fRect;
-        bool fHasLocalRect;
-        bool fHasLocalMatrix;
-        SkRect fLocalRect;
-        SkMatrix fLocalMatrix;
-    };
-
-    static GrBatch* Create(const Geometry& geometry) {
-        return SkNEW_ARGS(RectBatch, (geometry));
-    }
-
-    const char* name() const SK_OVERRIDE { return "RectBatch"; }
-
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
-        // When this is called on a batch, there is only one geometry bundle
-        out->setKnownFourComponents(fGeoData[0].fColor);
-    }
-
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
-        out->setKnownSingleComponent(0xff);
-    }
-
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
-        // Handle any color overrides
-        if (init.fColorIgnored) {
-            fGeoData[0].fColor = GrColor_ILLEGAL;
-        } else if (GrColor_ILLEGAL != init.fOverrideColor) {
-            fGeoData[0].fColor = init.fOverrideColor;
-        }
-
-        // setup batch properties
-        fBatch.fColorIgnored = init.fColorIgnored;
-        fBatch.fColor = fGeoData[0].fColor;
-        fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
-        fBatch.fCoverageIgnored = init.fCoverageIgnored;
-    }
-
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
-        // Go to device coords to allow batching across matrix changes
-        SkMatrix invert = SkMatrix::I();
-
-        // if we have a local rect, then we apply the localMatrix directly to the localRect to
-        // generate vertex local coords
-        bool hasExplicitLocalCoords = this->hasLocalRect();
-        if (!hasExplicitLocalCoords) {
-            if (!this->viewMatrix().isIdentity() && !this->viewMatrix().invert(&invert)) {
-                SkDebugf("Could not invert\n");
-                return;
-            }
-
-            if (this->hasLocalMatrix()) {
-                invert.preConcat(this->localMatrix());
-            }
-        }
-
-        SkAutoTUnref<const GrGeometryProcessor> gp(create_rect_gp(hasExplicitLocalCoords,
-                                                                  this->color(),
-                                                                  &invert));
-
-        batchTarget->initDraw(gp, pipeline);
-
-        // TODO this is hacky, but the only way we have to initialize the GP is to use the
-        // GrPipelineInfo struct so we can generate the correct shader.  Once we have GrBatch
-        // everywhere we can remove this nastiness
-        GrPipelineInfo init;
-        init.fColorIgnored = fBatch.fColorIgnored;
-        init.fOverrideColor = GrColor_ILLEGAL;
-        init.fCoverageIgnored = fBatch.fCoverageIgnored;
-        init.fUsesLocalCoords = this->usesLocalCoords();
-        gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
-
-        size_t vertexStride = gp->getVertexStride();
-
-        SkASSERT(hasExplicitLocalCoords ?
-                 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr) :
-                 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
-
-        int instanceCount = fGeoData.count();
-        int vertexCount = kVertsPerRect * instanceCount;
-
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
-
-        void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              vertexCount,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices || !batchTarget->quadIndexBuffer()) {
-            SkDebugf("Could not allocate buffers\n");
-            return;
-        }
-
-        for (int i = 0; i < instanceCount; i++) {
-            const Geometry& args = fGeoData[i];
-
-            intptr_t offset = GrTCast<intptr_t>(vertices) + kVertsPerRect * i * vertexStride;
-            SkPoint* positions = GrTCast<SkPoint*>(offset);
-
-            positions->setRectFan(args.fRect.fLeft, args.fRect.fTop,
-                                  args.fRect.fRight, args.fRect.fBottom, vertexStride);
-            args.fViewMatrix.mapPointsWithStride(positions, vertexStride, kVertsPerRect);
-
-            if (args.fHasLocalRect) {
-                static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
-                SkPoint* coords = GrTCast<SkPoint*>(offset + kLocalOffset);
-                coords->setRectFan(args.fLocalRect.fLeft, args.fLocalRect.fTop,
-                                   args.fLocalRect.fRight, args.fLocalRect.fBottom,
-                                   vertexStride);
-                if (args.fHasLocalMatrix) {
-                    args.fLocalMatrix.mapPointsWithStride(coords, vertexStride, kVertsPerRect);
-                }
-            }
-
-            static const int kColorOffset = sizeof(SkPoint);
-            GrColor* vertColor = GrTCast<GrColor*>(offset + kColorOffset);
-            for (int j = 0; j < 4; ++j) {
-                *vertColor = args.fColor;
-                vertColor = (GrColor*) ((intptr_t) vertColor + vertexStride);
-            }
-        }
-
-        const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
-
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
-        drawInfo.setStartVertex(0);
-        drawInfo.setStartIndex(0);
-        drawInfo.setVerticesPerInstance(kVertsPerRect);
-        drawInfo.setIndicesPerInstance(kIndicesPerRect);
-        drawInfo.adjustStartVertex(firstVertex);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setIndexBuffer(quadIndexBuffer);
-
-        int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
-        while (instanceCount) {
-            drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
-            drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
-            drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
-
-            batchTarget->draw(drawInfo);
-
-            drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
-            instanceCount -= drawInfo.instanceCount();
-       }
-    }
-
-    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
-
-private:
-    RectBatch(const Geometry& geometry) {
-        this->initClassID<RectBatch>();
-        fGeoData.push_back(geometry);
-    }
-
-    GrColor color() const { return fBatch.fColor; }
-    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
-    bool colorIgnored() const { return fBatch.fColorIgnored; }
-    const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
-    const SkMatrix& localMatrix() const { return fGeoData[0].fLocalMatrix; }
-    bool hasLocalRect() const { return fGeoData[0].fHasLocalRect; }
-    bool hasLocalMatrix() const { return fGeoData[0].fHasLocalMatrix; }
-
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
-        RectBatch* that = t->cast<RectBatch>();
-
-        if (this->hasLocalRect() != that->hasLocalRect()) {
-            return false;
-        }
-
-        SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
-        if (!this->hasLocalRect() && this->usesLocalCoords()) {
-            if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
-                return false;
-            }
-
-            if (this->hasLocalMatrix() != that->hasLocalMatrix()) {
-                return false;
-            }
-
-            if (this->hasLocalMatrix() && !this->localMatrix().cheapEqualTo(that->localMatrix())) {
-                return false;
-            }
-        }
-
-        if (this->color() != that->color()) {
-            fBatch.fColor = GrColor_ILLEGAL;
-        }
-        fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
-        return true;
-    }
-
-    struct BatchTracker {
-        GrColor fColor;
-        bool fUsesLocalCoords;
-        bool fColorIgnored;
-        bool fCoverageIgnored;
-    };
-
-    const static int kVertsPerRect = 4;
-    const static int kIndicesPerRect = 6;
-
-    BatchTracker fBatch;
-    SkSTArray<1, Geometry, true> fGeoData;
-};
-
-void GrInOrderDrawBuffer::onDrawRect(GrPipelineBuilder* pipelineBuilder,
-                                     GrColor color,
-                                     const SkMatrix& viewMatrix,
-                                     const SkRect& rect,
-                                     const SkRect* localRect,
-                                     const SkMatrix* localMatrix) {
-    RectBatch::Geometry geometry;
-    geometry.fColor = color;
-    geometry.fViewMatrix = viewMatrix;
-    geometry.fRect = rect;
-
-    if (localRect) {
-        geometry.fHasLocalRect = true;
-        geometry.fLocalRect = *localRect;
-    } else {
-        geometry.fHasLocalRect = false;
-    }
-
-    if (localMatrix) {
-        geometry.fHasLocalMatrix = true;
-        geometry.fLocalMatrix = *localMatrix;
-    } else {
-        geometry.fHasLocalMatrix = false;
-    }
-
-    SkAutoTUnref<GrBatch> batch(RectBatch::Create(geometry));
-
-    SkRect bounds = rect;
-    viewMatrix.mapRect(&bounds);
-    this->drawBatch(pipelineBuilder, batch, &bounds);
-}
-
-void GrInOrderDrawBuffer::onDraw(const GrGeometryProcessor* gp,
-                                 const DrawInfo& info,
-                                 const PipelineInfo& pipelineInfo) {
-    GrTargetCommands::Cmd* cmd = fCommands.recordDraw(this, gp, info, pipelineInfo);
-    this->recordTraceMarkersIfNecessary(cmd);
-}
-
 void GrInOrderDrawBuffer::onDrawBatch(GrBatch* batch,
                                       const PipelineInfo& pipelineInfo) {
-    GrTargetCommands::Cmd* cmd = fCommands.recordDrawBatch(this, batch, pipelineInfo);
+    State* state = this->setupPipelineAndShouldDraw(batch, pipelineInfo);
+    if (!state) {
+        return;
+    }
+
+    GrTargetCommands::Cmd* cmd = fCommands->recordDrawBatch(state, batch);
     this->recordTraceMarkersIfNecessary(cmd);
 }
 
@@ -315,9 +36,9 @@
                                         const GrPath* path,
                                         const GrScissorState& scissorState,
                                         const GrStencilSettings& stencilSettings) {
-    GrTargetCommands::Cmd* cmd = fCommands.recordStencilPath(this, pipelineBuilder, 
-                                                             pathProc, path, scissorState,
-                                                             stencilSettings);
+    GrTargetCommands::Cmd* cmd = fCommands->recordStencilPath(pipelineBuilder,
+                                                              pathProc, path, scissorState,
+                                                              stencilSettings);
     this->recordTraceMarkersIfNecessary(cmd);
 }
 
@@ -325,9 +46,11 @@
                                      const GrPath* path,
                                      const GrStencilSettings& stencilSettings,
                                      const PipelineInfo& pipelineInfo) {
-    GrTargetCommands::Cmd* cmd = fCommands.recordDrawPath(this, pathProc,
-                                                          path, stencilSettings,
-                                                          pipelineInfo);
+    State* state = this->setupPipelineAndShouldDraw(pathProc, pipelineInfo);
+    if (!state) {
+        return;
+    }
+    GrTargetCommands::Cmd* cmd = fCommands->recordDrawPath(state, pathProc, path, stencilSettings);
     this->recordTraceMarkersIfNecessary(cmd);
 }
 
@@ -340,25 +63,27 @@
                                       int count,
                                       const GrStencilSettings& stencilSettings,
                                       const PipelineInfo& pipelineInfo) {
-    GrTargetCommands::Cmd* cmd = fCommands.recordDrawPaths(this, pathProc, pathRange,
-                                                           indices, indexType, transformValues,
-                                                           transformType, count,
-                                                           stencilSettings, pipelineInfo);
+    State* state = this->setupPipelineAndShouldDraw(pathProc, pipelineInfo);
+    if (!state) {
+        return;
+    }
+    GrTargetCommands::Cmd* cmd = fCommands->recordDrawPaths(state, this, pathProc, pathRange,
+                                                            indices, indexType, transformValues,
+                                                            transformType, count,
+                                                            stencilSettings, pipelineInfo);
     this->recordTraceMarkersIfNecessary(cmd);
 }
 
 void GrInOrderDrawBuffer::onClear(const SkIRect* rect, GrColor color,
                                   bool canIgnoreRect, GrRenderTarget* renderTarget) {
-    GrTargetCommands::Cmd* cmd = fCommands.recordClear(this, rect, color,
-                                                       canIgnoreRect, renderTarget);
+    GrTargetCommands::Cmd* cmd = fCommands->recordClear(rect, color, canIgnoreRect, renderTarget);
     this->recordTraceMarkersIfNecessary(cmd);
 }
 
 void GrInOrderDrawBuffer::clearStencilClip(const SkIRect& rect,
                                            bool insideClip,
                                            GrRenderTarget* renderTarget) {
-    GrTargetCommands::Cmd* cmd = fCommands.recordClearStencilClip(this, rect,
-                                                                  insideClip, renderTarget);
+    GrTargetCommands::Cmd* cmd = fCommands->recordClearStencilClip(rect, insideClip, renderTarget);
     this->recordTraceMarkersIfNecessary(cmd);
 }
 
@@ -367,30 +92,38 @@
         return;
     }
 
-    GrTargetCommands::Cmd* cmd = fCommands.recordDiscard(this, renderTarget);
+    GrTargetCommands::Cmd* cmd = fCommands->recordDiscard(renderTarget);
     this->recordTraceMarkersIfNecessary(cmd);
 }
 
 void GrInOrderDrawBuffer::onReset() {
-    fCommands.reset();
+    fCommands->reset();
     fPathIndexBuffer.rewind();
     fPathTransformBuffer.rewind();
     fGpuCmdMarkers.reset();
+
+    fPrevState.reset(NULL);
+    // Note, fPrevState points into fPipelineBuffer's allocation, so we have to reset first.
+    // Furthermore, we have to reset fCommands before fPipelineBuffer too.
+    if (fDrawID % kPipelineBufferHighWaterMark) {
+        fPipelineBuffer.rewind();
+    } else {
+        fPipelineBuffer.reset();
+    }
 }
 
 void GrInOrderDrawBuffer::onFlush() {
-    fCommands.flush(this);
+    fCommands->flush(this);
     ++fDrawID;
 }
 
-bool GrInOrderDrawBuffer::onCopySurface(GrSurface* dst,
+void GrInOrderDrawBuffer::onCopySurface(GrSurface* dst,
                                         GrSurface* src,
                                         const SkIRect& srcRect,
                                         const SkIPoint& dstPoint) {
-    GrTargetCommands::Cmd* cmd = fCommands.recordCopySurface(this, dst, src,
-                                                             srcRect, dstPoint);
+    SkASSERT(this->getGpu()->canCopySurface(dst, src, srcRect, dstPoint));
+    GrTargetCommands::Cmd* cmd = fCommands->recordCopySurface(dst, src, srcRect, dstPoint);
     this->recordTraceMarkersIfNecessary(cmd);
-    return SkToBool(cmd);
 }
 
 void GrInOrderDrawBuffer::recordTraceMarkersIfNecessary(GrTargetCommands::Cmd* cmd) {
@@ -408,10 +141,56 @@
     }
 }
 
-void GrInOrderDrawBuffer::willReserveVertexAndIndexSpace(int vertexCount,
-                                                         size_t vertexStride,
-                                                         int indexCount) {
-    fCommands.closeBatch();
+GrTargetCommands::State*
+GrInOrderDrawBuffer::setupPipelineAndShouldDraw(const GrPrimitiveProcessor* primProc,
+                                                const GrDrawTarget::PipelineInfo& pipelineInfo) {
+    State* state = this->allocState(primProc);
+    this->setupPipeline(pipelineInfo, state->pipelineLocation());
 
-    this->INHERITED::willReserveVertexAndIndexSpace(vertexCount, vertexStride, indexCount);
+    if (state->getPipeline()->mustSkip()) {
+        this->unallocState(state);
+        return NULL;
+    }
+
+    state->fPrimitiveProcessor->initBatchTracker(&state->fBatchTracker,
+                                                 state->getPipeline()->getInitBatchTracker());
+
+    if (fPrevState && fPrevState->fPrimitiveProcessor.get() &&
+        fPrevState->fPrimitiveProcessor->canMakeEqual(fPrevState->fBatchTracker,
+                                                      *state->fPrimitiveProcessor,
+                                                      state->fBatchTracker) &&
+        fPrevState->getPipeline()->isEqual(*state->getPipeline())) {
+        this->unallocState(state);
+    } else {
+        fPrevState.reset(state);
+    }
+
+    this->recordTraceMarkersIfNecessary(
+            fCommands->recordXferBarrierIfNecessary(*fPrevState->getPipeline(), *this->caps()));
+    return fPrevState;
+}
+
+GrTargetCommands::State*
+GrInOrderDrawBuffer::setupPipelineAndShouldDraw(GrBatch* batch,
+                                                const GrDrawTarget::PipelineInfo& pipelineInfo) {
+    State* state = this->allocState();
+    this->setupPipeline(pipelineInfo, state->pipelineLocation());
+
+    if (state->getPipeline()->mustSkip()) {
+        this->unallocState(state);
+        return NULL;
+    }
+
+    batch->initBatchTracker(state->getPipeline()->getInitBatchTracker());
+
+    if (fPrevState && !fPrevState->fPrimitiveProcessor.get() &&
+        fPrevState->getPipeline()->isEqual(*state->getPipeline())) {
+        this->unallocState(state);
+    } else {
+        fPrevState.reset(state);
+    }
+
+    this->recordTraceMarkersIfNecessary(
+            fCommands->recordXferBarrierIfNecessary(*fPrevState->getPipeline(), *this->caps()));
+    return fPrevState;
 }
diff --git a/src/gpu/GrInOrderDrawBuffer.h b/src/gpu/GrInOrderDrawBuffer.h
index 464fdcb..b3de192 100644
--- a/src/gpu/GrInOrderDrawBuffer.h
+++ b/src/gpu/GrInOrderDrawBuffer.h
@@ -8,8 +8,8 @@
 #ifndef GrInOrderDrawBuffer_DEFINED
 #define GrInOrderDrawBuffer_DEFINED
 
-#include "GrFlushToGpuDrawTarget.h"
-#include "GrTargetCommands.h"
+#include "GrDrawTarget.h"
+#include "GrCommandBuilder.h"
 #include "SkChunkAlloc.h"
 
 /**
@@ -22,38 +22,28 @@
  * in the GrGpu object that the buffer is played back into. The buffer requires VB and IB pools to
  * store geometry.
  */
-class GrInOrderDrawBuffer : public GrFlushToGpuDrawTarget {
+class GrInOrderDrawBuffer : public GrClipTarget {
 public:
 
     /**
      * Creates a GrInOrderDrawBuffer
      *
-     * @param gpu        the gpu object that this draw buffer flushes to.
-     * @param vertexPool pool where vertices for queued draws will be saved when
-     *                   the vertex source is either reserved or array.
-     * @param indexPool  pool where indices for queued draws will be saved when
-     *                   the index source is either reserved or array.
+     * @param context    the context object that owns this draw buffer.
      */
-    GrInOrderDrawBuffer(GrGpu* gpu,
-                        GrVertexBufferAllocPool* vertexPool,
-                        GrIndexBufferAllocPool* indexPool);
+    GrInOrderDrawBuffer(GrContext* context);
 
-    ~GrInOrderDrawBuffer() SK_OVERRIDE;
+    ~GrInOrderDrawBuffer() override;
 
     // tracking for draws
-    DrawToken getCurrentDrawToken() SK_OVERRIDE { return DrawToken(this, fDrawID); }
+    DrawToken getCurrentDrawToken() override { return DrawToken(this, fDrawID); }
 
     void clearStencilClip(const SkIRect& rect,
                           bool insideClip,
-                          GrRenderTarget* renderTarget) SK_OVERRIDE;
+                          GrRenderTarget* renderTarget) override;
 
-    void discard(GrRenderTarget*) SK_OVERRIDE;
+    void discard(GrRenderTarget*) override;
 
 protected:
-    void willReserveVertexAndIndexSpace(int vertexCount,
-                                        size_t vertexStride,
-                                        int indexCount) SK_OVERRIDE;
-
     void appendIndicesAndTransforms(const void* indexValues, PathIndexType indexType, 
                                     const float* transformValues, PathTransformType transformType,
                                     int count, char** indicesLocation, float** xformsLocation) {
@@ -74,46 +64,36 @@
         }
     }
 
-    bool canConcatToIndexBuffer(const GrIndexBuffer** ib) {
-        const GrDrawTarget::GeometrySrcState& geomSrc = this->getGeomSrc();
-
-        // we only attempt to concat 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 false;
-        }
-
-        *ib = geomSrc.fIndexBuffer;
-        return true;
-    }
-
 private:
+    friend class GrInOrderCommandBuilder;
     friend class GrTargetCommands;
 
-    void onReset() SK_OVERRIDE;
-    void onFlush() SK_OVERRIDE;
+    typedef GrTargetCommands::State State;
+
+    State* allocState(const GrPrimitiveProcessor* primProc = NULL) {
+        void* allocation = fPipelineBuffer.alloc(sizeof(State), SkChunkAlloc::kThrow_AllocFailType);
+        return SkNEW_PLACEMENT_ARGS(allocation, State, (primProc));
+    }
+
+    void unallocState(State* state) {
+        state->unref();
+        fPipelineBuffer.unalloc(state);
+    }
+
+    void onReset() override;
+    void onFlush() override;
 
     // overrides from GrDrawTarget
-    void onDraw(const GrGeometryProcessor*, const DrawInfo&, const PipelineInfo&) SK_OVERRIDE;
-    void onDrawBatch(GrBatch*, const PipelineInfo&) SK_OVERRIDE;
-    void onDrawRect(GrPipelineBuilder*,
-                    GrColor,
-                    const SkMatrix& viewMatrix,
-                    const SkRect& rect,
-                    const SkRect* localRect,
-                    const SkMatrix* localMatrix) SK_OVERRIDE;
-
+    void onDrawBatch(GrBatch*, const PipelineInfo&) override;
     void onStencilPath(const GrPipelineBuilder&,
                        const GrPathProcessor*,
                        const GrPath*,
                        const GrScissorState&,
-                       const GrStencilSettings&) SK_OVERRIDE;
+                       const GrStencilSettings&) override;
     void onDrawPath(const GrPathProcessor*,
                     const GrPath*,
                     const GrStencilSettings&,
-                    const PipelineInfo&) SK_OVERRIDE;
+                    const PipelineInfo&) override;
     void onDrawPaths(const GrPathProcessor*,
                      const GrPathRange*,
                      const void* indices,
@@ -122,19 +102,15 @@
                      PathTransformType,
                      int count,
                      const GrStencilSettings&,
-                     const PipelineInfo&) SK_OVERRIDE;
+                     const PipelineInfo&) override;
     void onClear(const SkIRect* rect,
                  GrColor color,
                  bool canIgnoreRect,
-                 GrRenderTarget* renderTarget) SK_OVERRIDE;
-    bool onCopySurface(GrSurface* dst,
+                 GrRenderTarget* renderTarget) override;
+    void onCopySurface(GrSurface* dst,
                        GrSurface* src,
                        const SkIRect& srcRect,
-                       const SkIPoint& dstPoint) SK_OVERRIDE;
-
-    // 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&);
+                       const SkIPoint& dstPoint) override;
 
     // We lazily record clip changes in order to skip clips that have no effect.
     void recordClipIfNecessary();
@@ -144,21 +120,33 @@
         SkASSERT(index < fGpuCmdMarkers.count());
         return fGpuCmdMarkers[index].toString();
     }
-    bool isIssued(uint32_t drawID) SK_OVERRIDE { return drawID != fDrawID; }
+    bool isIssued(uint32_t drawID) override { return drawID != fDrawID; }
+
+    State* SK_WARN_UNUSED_RESULT setupPipelineAndShouldDraw(const GrPrimitiveProcessor*,
+                                                            const GrDrawTarget::PipelineInfo&);
+    State* SK_WARN_UNUSED_RESULT setupPipelineAndShouldDraw(GrBatch*,
+                                                            const GrDrawTarget::PipelineInfo&);
 
     // TODO: Use a single allocator for commands and records
     enum {
         kPathIdxBufferMinReserve     = 2 * 64,  // 64 uint16_t's
         kPathXformBufferMinReserve   = 2 * 64,  // 64 two-float transforms
+        kPipelineBufferMinReserve    = 32 * sizeof(State),
     };
 
-    GrTargetCommands                    fCommands;
+    // every 100 flushes we should reset our fPipelineBuffer to prevent us from holding at a
+    // highwater mark
+    static const int kPipelineBufferHighWaterMark = 100;
+
+    SkAutoTDelete<GrCommandBuilder>     fCommands;
     SkTArray<GrTraceMarkerSet, false>   fGpuCmdMarkers;
     SkChunkAlloc                        fPathIndexBuffer;
     SkChunkAlloc                        fPathTransformBuffer;
+    SkChunkAlloc                        fPipelineBuffer;
     uint32_t                            fDrawID;
+    SkAutoTUnref<State>                 fPrevState;
 
-    typedef GrFlushToGpuDrawTarget INHERITED;
+    typedef GrClipTarget INHERITED;
 };
 
 #endif
diff --git a/src/gpu/GrLayerCache.cpp b/src/gpu/GrLayerCache.cpp
index 1e09d0e..aea1c9b 100644
--- a/src/gpu/GrLayerCache.cpp
+++ b/src/gpu/GrLayerCache.cpp
@@ -248,12 +248,12 @@
     }
 
     // TODO: make the test for exact match depend on the image filters themselves
-    GrContext::ScratchTexMatch usage = GrContext::kApprox_ScratchTexMatch;
+    GrTextureProvider::ScratchTexMatch usage = GrTextureProvider::kApprox_ScratchTexMatch;
     if (layer->fFilter) {
-        usage = GrContext::kExact_ScratchTexMatch;
+        usage = GrTextureProvider::kExact_ScratchTexMatch;
     }
 
-    SkAutoTUnref<GrTexture> tex(fContext->refScratchTexture(desc, usage));
+    SkAutoTUnref<GrTexture> tex(fContext->textureProvider()->refScratchTexture(desc, usage));
     if (!tex) {
         return false;
     }
diff --git a/src/gpu/GrMemoryPool.cpp b/src/gpu/GrMemoryPool.cpp
index 5009f20..e59ed83 100644
--- a/src/gpu/GrMemoryPool.cpp
+++ b/src/gpu/GrMemoryPool.cpp
@@ -15,11 +15,13 @@
 
 GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) {
     SkDEBUGCODE(fAllocationCnt = 0);
+    SkDEBUGCODE(fAllocBlockCnt = 0);
 
     minAllocSize = SkTMax<size_t>(minAllocSize, 1 << 10);
     fMinAllocSize = GrSizeAlignUp(minAllocSize + kPerAllocPad, kAlignment),
     fPreallocSize = GrSizeAlignUp(preallocSize + kPerAllocPad, kAlignment);
     fPreallocSize = SkTMax(fPreallocSize, fMinAllocSize);
+    fSize = fPreallocSize;
 
     fHead = CreateBlock(fPreallocSize);
     fTail = fHead;
@@ -50,6 +52,8 @@
         SkASSERT(NULL == fTail->fNext);
         fTail->fNext = block;
         fTail = block;
+        fSize += block->fSize;
+        SkDEBUGCODE(++fAllocBlockCnt);
     }
     SkASSERT(fTail->fFreeSize >= size);
     intptr_t ptr = fTail->fCurrPtr;
@@ -61,6 +65,7 @@
     fTail->fCurrPtr += size;
     fTail->fFreeSize -= size;
     fTail->fLiveCount += 1;
+
     SkDEBUGCODE(++fAllocationCnt);
     VALIDATE;
     return reinterpret_cast<void*>(ptr);
@@ -73,8 +78,7 @@
     if (1 == block->fLiveCount) {
         // the head block is special, it is reset rather than deleted
         if (fHead == block) {
-            fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) +
-                                kHeaderSize;
+            fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) + kHeaderSize;
             fHead->fLiveCount = 0;
             fHead->fFreeSize = fPreallocSize;
         } else {
@@ -88,7 +92,9 @@
                 SkASSERT(fTail == block);
                 fTail = prev;
             }
+            fSize -= block->fSize;
             DeleteBlock(block);
+            SkDEBUGCODE(fAllocBlockCnt--);
         }
     } else {
         --block->fLiveCount;
@@ -103,14 +109,16 @@
 }
 
 GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) {
+    size_t paddedSize = size + kHeaderSize;
     BlockHeader* block =
-        reinterpret_cast<BlockHeader*>(sk_malloc_throw(size + kHeaderSize));
+        reinterpret_cast<BlockHeader*>(sk_malloc_throw(paddedSize));
     // we assume malloc gives us aligned memory
     SkASSERT(!(reinterpret_cast<intptr_t>(block) % kAlignment));
     block->fLiveCount = 0;
     block->fFreeSize = size;
     block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize;
     block->fPrevPtr = 0; // gcc warns on assigning NULL to an intptr_t.
+    block->fSize = paddedSize;
     return block;
 }
 
@@ -157,5 +165,6 @@
     } while ((block = block->fNext));
     SkASSERT(allocCount == fAllocationCnt);
     SkASSERT(prev == fTail);
+    SkASSERT(fAllocBlockCnt != 0 || fSize == fPreallocSize);
 #endif
 }
diff --git a/src/gpu/GrMemoryPool.h b/src/gpu/GrMemoryPool.h
index 5ab8958..4de641d 100644
--- a/src/gpu/GrMemoryPool.h
+++ b/src/gpu/GrMemoryPool.h
@@ -43,6 +43,11 @@
      */
     bool isEmpty() const { return fTail == fHead && !fHead->fLiveCount; }
 
+    /**
+     * Returns the total allocated size of the GrMemoryPool
+     */
+    size_t size() const { return fSize; }
+
 private:
     struct BlockHeader;
 
@@ -60,6 +65,7 @@
         intptr_t     fCurrPtr;   ///< ptr to the start of blocks free space.
         intptr_t     fPrevPtr;   ///< ptr to the last allocation made
         size_t       fFreeSize;  ///< amount of free space left in the block.
+        size_t       fSize;      ///< total allocated size of the block
     };
 
     enum {
@@ -68,12 +74,14 @@
         kHeaderSize   = GR_CT_ALIGN_UP(sizeof(BlockHeader), kAlignment),
         kPerAllocPad  = GR_CT_ALIGN_UP(sizeof(BlockHeader*), kAlignment),
     };
+    size_t                            fSize;
     size_t                            fPreallocSize;
     size_t                            fMinAllocSize;
     BlockHeader*                      fHead;
     BlockHeader*                      fTail;
 #ifdef SK_DEBUG
     int                               fAllocationCnt;
+    int                               fAllocBlockCnt;
 #endif
 };
 
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index 20ae77c..0a7b3f8 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -9,13 +9,14 @@
 
 #include "GrBatch.h"
 #include "GrBatchTarget.h"
-#include "GrBufferAllocPool.h"
+#include "GrBatchTest.h"
 #include "GrDrawTarget.h"
 #include "GrGeometryProcessor.h"
-#include "GrGpu.h"
 #include "GrInvariantOutput.h"
 #include "GrPipelineBuilder.h"
 #include "GrProcessor.h"
+#include "GrResourceProvider.h"
+#include "GrVertexBuffer.h"
 #include "SkRRect.h"
 #include "SkStrokeRec.h"
 #include "SkTLazy.h"
@@ -76,9 +77,11 @@
 
     const Attribute* inPosition() const { return fInPosition; }
     const Attribute* inCircleEdge() const { return fInCircleEdge; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& localMatrix() const { return fLocalMatrix; }
     virtual ~CircleEdgeEffect() {}
 
-    const char* name() const SK_OVERRIDE { return "CircleEdge"; }
+    const char* name() const override { return "CircleEdge"; }
 
     inline bool isStroked() const { return fStroke; }
 
@@ -88,7 +91,7 @@
                     const GrBatchTracker&)
             : fColor(GrColor_ILLEGAL) {}
 
-        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
+        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
             const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
             GrGLGPBuilder* pb = args.fPB;
             const BatchTracker& local = args.fBT.cast<BatchTracker>();
@@ -106,13 +109,13 @@
                                         &fColorUniform);
 
             // Setup position
-            this->setupPosition(pb, gpArgs, ce.inPosition()->fName, ce.viewMatrix());
+            this->setupPosition(pb, gpArgs, ce.inPosition()->fName);
 
             // emit transforms
             this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
-                                 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);;
+                                 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);
 
-            GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+            GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
             fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
             fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
             if (ce.isStroked()) {
@@ -126,21 +129,18 @@
 
         static void GenKey(const GrGeometryProcessor& gp,
                            const GrBatchTracker& bt,
-                           const GrGLCaps&,
+                           const GrGLSLCaps&,
                            GrProcessorKeyBuilder* b) {
             const BatchTracker& local = bt.cast<BatchTracker>();
-            const CircleEdgeEffect& circleEffect = gp.cast<CircleEdgeEffect>();
-            uint16_t key = circleEffect.isStroked() ? 0x1 : 0x0;
-            key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
-            key |= ComputePosKey(gp.viewMatrix()) << 2;
+            const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
+            uint16_t key = ce.isStroked() ? 0x1 : 0x0;
+            key |= local.fUsesLocalCoords && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
             b->add32(key << 16 | local.fInputColorType);
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
                              const GrPrimitiveProcessor& gp,
-                             const GrBatchTracker& bt) SK_OVERRIDE {
-            this->setUniformViewMatrix(pdman, gp.viewMatrix());
-
+                             const GrBatchTracker& bt) override {
             const BatchTracker& local = bt.cast<BatchTracker>();
             if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
                 GrGLfloat c[4];
@@ -150,6 +150,13 @@
             }
         }
 
+        void setTransformData(const GrPrimitiveProcessor& primProc,
+                              const GrGLProgramDataManager& pdman,
+                              int index,
+                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
+            this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
+        }
+
     private:
         GrColor fColor;
         UniformHandle fColorUniform;
@@ -157,36 +164,26 @@
     };
 
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GLProcessor::GenKey(*this, bt, caps, b);
     }
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE {
+                                                     const GrGLSLCaps&) const override {
         return SkNEW_ARGS(GLProcessor, (*this, bt));
     }
 
-    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
+    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
         BatchTracker* local = bt->cast<BatchTracker>();
         local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
         local->fUsesLocalCoords = init.fUsesLocalCoords;
     }
 
-    bool onCanMakeEqual(const GrBatchTracker& m,
-                        const GrGeometryProcessor& that,
-                        const GrBatchTracker& t) const SK_OVERRIDE {
-        const BatchTracker& mine = m.cast<BatchTracker>();
-        const BatchTracker& theirs = t.cast<BatchTracker>();
-        return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                       that, theirs.fUsesLocalCoords) &&
-               CanCombineOutput(mine.fInputColorType, mine.fColor,
-                                theirs.fInputColorType, theirs.fColor);
-    }
-
 private:
     CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
-        : INHERITED(color, SkMatrix::I(), localMatrix) {
+        : fColor(color)
+        , fLocalMatrix(localMatrix) {
         this->initClassID<CircleEdgeEffect>();
         fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
         fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
@@ -194,21 +191,14 @@
         fStroke = stroke;
     }
 
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
-        const CircleEdgeEffect& cee = other.cast<CircleEdgeEffect>();
-        return cee.fStroke == fStroke;
-    }
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
-        out->setUnknownSingleComponent();
-    }
-
     struct BatchTracker {
         GrGPInput fInputColorType;
         GrColor fColor;
         bool fUsesLocalCoords;
     };
 
+    GrColor fColor;
+    SkMatrix fLocalMatrix;
     const Attribute* fInPosition;
     const Attribute* fInCircleEdge;
     bool fStroke;
@@ -226,7 +216,7 @@
                                                   GrTexture* textures[]) {
     return CircleEdgeEffect::Create(GrRandomColor(random),
                                     random->nextBool(),
-                                    GrProcessorUnitTest::TestMatrix(random));
+                                    GrTest::TestMatrix(random));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -247,11 +237,13 @@
 
     virtual ~EllipseEdgeEffect() {}
 
-    const char* name() const SK_OVERRIDE { return "EllipseEdge"; }
+    const char* name() const override { return "EllipseEdge"; }
 
     const Attribute* inPosition() const { return fInPosition; }
     const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
     const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& localMatrix() const { return fLocalMatrix; }
 
     inline bool isStroked() const { return fStroke; }
 
@@ -261,7 +253,7 @@
                     const GrBatchTracker&)
             : fColor(GrColor_ILLEGAL) {}
 
-        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
+        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
             const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
             GrGLGPBuilder* pb = args.fPB;
             const BatchTracker& local = args.fBT.cast<BatchTracker>();
@@ -285,14 +277,14 @@
                                         &fColorUniform);
 
             // Setup position
-            this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
+            this->setupPosition(pb, gpArgs, ee.inPosition()->fName);
 
             // emit transforms
             this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
                                  ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
 
             // for outer curve
-            GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+            GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
             fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
                                    ellipseRadii.fsIn());
             fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
@@ -320,20 +312,18 @@
 
         static void GenKey(const GrGeometryProcessor& gp,
                            const GrBatchTracker& bt,
-                           const GrGLCaps&,
+                           const GrGLSLCaps&,
                            GrProcessorKeyBuilder* b) {
             const BatchTracker& local = bt.cast<BatchTracker>();
-            const EllipseEdgeEffect& ellipseEffect = gp.cast<EllipseEdgeEffect>();
-            uint16_t key = ellipseEffect.isStroked() ? 0x1 : 0x0;
-            key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
-            key |= ComputePosKey(gp.viewMatrix()) << 2;
+            const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
+            uint16_t key = ee.isStroked() ? 0x1 : 0x0;
+            key |= local.fUsesLocalCoords && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
             b->add32(key << 16 | local.fInputColorType);
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
                              const GrPrimitiveProcessor& gp,
-                             const GrBatchTracker& bt) SK_OVERRIDE {
-            this->setUniformViewMatrix(pdman, gp.viewMatrix());
+                             const GrBatchTracker& bt) override {
 
             const BatchTracker& local = bt.cast<BatchTracker>();
             if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
@@ -344,6 +334,13 @@
             }
         }
 
+        void setTransformData(const GrPrimitiveProcessor& primProc,
+                              const GrGLProgramDataManager& pdman,
+                              int index,
+                              const SkTArray<const GrCoordTransform*, true>& transforms) override {
+            this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
+        }
+
     private:
         GrColor fColor;
         UniformHandle fColorUniform;
@@ -352,36 +349,26 @@
     };
 
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GLProcessor::GenKey(*this, bt, caps, b);
     }
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE {
+                                                     const GrGLSLCaps&) const override {
         return SkNEW_ARGS(GLProcessor, (*this, bt));
     }
 
-    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
+    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
         BatchTracker* local = bt->cast<BatchTracker>();
         local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
         local->fUsesLocalCoords = init.fUsesLocalCoords;
     }
 
-    bool onCanMakeEqual(const GrBatchTracker& m,
-                        const GrGeometryProcessor& that,
-                        const GrBatchTracker& t) const SK_OVERRIDE {
-        const BatchTracker& mine = m.cast<BatchTracker>();
-        const BatchTracker& theirs = t.cast<BatchTracker>();
-        return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                       that, theirs.fUsesLocalCoords) &&
-               CanCombineOutput(mine.fInputColorType, mine.fColor,
-                                theirs.fInputColorType, theirs.fColor);
-    }
-
 private:
     EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
-        : INHERITED(color, SkMatrix::I(), localMatrix) {
+        : fColor(color)
+        , fLocalMatrix(localMatrix) {
         this->initClassID<EllipseEdgeEffect>();
         fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
         fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
@@ -391,15 +378,6 @@
         fStroke = stroke;
     }
 
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
-        const EllipseEdgeEffect& eee = other.cast<EllipseEdgeEffect>();
-        return eee.fStroke == fStroke;
-    }
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
-        out->setUnknownSingleComponent();
-    }
-
     struct BatchTracker {
         GrGPInput fInputColorType;
         GrColor fColor;
@@ -409,6 +387,8 @@
     const Attribute* fInPosition;
     const Attribute* fInEllipseOffset;
     const Attribute* fInEllipseRadii;
+    GrColor fColor;
+    SkMatrix fLocalMatrix;
     bool fStroke;
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
@@ -424,7 +404,7 @@
                                                    GrTexture* textures[]) {
     return EllipseEdgeEffect::Create(GrRandomColor(random),
                                      random->nextBool(),
-                                     GrProcessorUnitTest::TestMatrix(random));
+                                     GrTest::TestMatrix(random));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -448,11 +428,13 @@
 
     virtual ~DIEllipseEdgeEffect() {}
 
-    const char* name() const SK_OVERRIDE { return "DIEllipseEdge"; }
+    const char* name() const override { return "DIEllipseEdge"; }
 
     const Attribute* inPosition() const { return fInPosition; }
     const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
     const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& viewMatrix() const { return fViewMatrix; }
 
     inline Mode getMode() const { return fMode; }
 
@@ -462,7 +444,7 @@
                     const GrBatchTracker&)
             : fColor(GrColor_ILLEGAL) {}
 
-        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
+        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
             const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
             GrGLGPBuilder* pb = args.fPB;
             const BatchTracker& local = args.fBT.cast<BatchTracker>();
@@ -490,9 +472,9 @@
 
             // emit transforms
             this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
-                                 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
+                                 args.fTransformsIn, args.fTransformsOut);
 
-            GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+            GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
             SkAssertResult(fsBuilder->enableFeature(
                     GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
             // for outer curve
@@ -535,20 +517,20 @@
 
         static void GenKey(const GrGeometryProcessor& gp,
                            const GrBatchTracker& bt,
-                           const GrGLCaps&,
+                           const GrGLSLCaps&,
                            GrProcessorKeyBuilder* b) {
             const BatchTracker& local = bt.cast<BatchTracker>();
             const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
             uint16_t key = ellipseEffect.getMode();
-            key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 8 : 0x0;
-            key |= ComputePosKey(gp.viewMatrix()) << 9;
+            key |= ComputePosKey(ellipseEffect.viewMatrix()) << 9;
             b->add32(key << 16 | local.fInputColorType);
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
                              const GrPrimitiveProcessor& gp,
-                             const GrBatchTracker& bt) SK_OVERRIDE {
-            this->setUniformViewMatrix(pdman, gp.viewMatrix());
+                             const GrBatchTracker& bt) override {
+            const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
+            this->setUniformViewMatrix(pdman, dee.viewMatrix());
 
             const BatchTracker& local = bt.cast<BatchTracker>();
             if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
@@ -567,36 +549,26 @@
     };
 
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GLProcessor::GenKey(*this, bt, caps, b);
     }
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE {
+                                                     const GrGLSLCaps&) const override {
         return SkNEW_ARGS(GLProcessor, (*this, bt));
     }
 
-    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE {
+    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
         BatchTracker* local = bt->cast<BatchTracker>();
         local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
         local->fUsesLocalCoords = init.fUsesLocalCoords;
     }
 
-    bool onCanMakeEqual(const GrBatchTracker& m,
-                        const GrGeometryProcessor& that,
-                        const GrBatchTracker& t) const SK_OVERRIDE {
-        const BatchTracker& mine = m.cast<BatchTracker>();
-        const BatchTracker& theirs = t.cast<BatchTracker>();
-        return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                       that, theirs.fUsesLocalCoords) &&
-               CanCombineOutput(mine.fInputColorType, mine.fColor,
-                                theirs.fInputColorType, theirs.fColor);
-    }
-
 private:
     DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
-        : INHERITED(color, viewMatrix) {
+        : fColor(color)
+        , fViewMatrix(viewMatrix) {
         this->initClassID<DIEllipseEdgeEffect>();
         fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
         fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
@@ -606,15 +578,6 @@
         fMode = mode;
     }
 
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
-        const DIEllipseEdgeEffect& eee = other.cast<DIEllipseEdgeEffect>();
-        return eee.fMode == fMode;
-    }
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
-        out->setUnknownSingleComponent();
-    }
-
     struct BatchTracker {
         GrGPInput fInputColorType;
         GrColor fColor;
@@ -624,6 +587,8 @@
     const Attribute* fInPosition;
     const Attribute* fInEllipseOffsets0;
     const Attribute* fInEllipseOffsets1;
+    GrColor fColor;
+    SkMatrix fViewMatrix;
     Mode fMode;
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
@@ -638,17 +603,12 @@
                                                      const GrDrawTargetCaps&,
                                                      GrTexture* textures[]) {
     return DIEllipseEdgeEffect::Create(GrRandomColor(random),
-                                       GrProcessorUnitTest::TestMatrix(random),
+                                       GrTest::TestMatrix(random),
                                        (Mode)(random->nextRangeU(0,2)));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void GrOvalRenderer::reset() {
-    SkSafeSetNull(fRRectIndexBuffer);
-    SkSafeSetNull(fStrokeRRectIndexBuffer);
-}
-
 bool GrOvalRenderer::drawOval(GrDrawTarget* target,
                               GrPipelineBuilder* pipelineBuilder,
                               GrColor color,
@@ -657,8 +617,7 @@
                               const SkRect& oval,
                               const SkStrokeRec& stroke)
 {
-    bool useCoverageAA = useAA &&
-        !pipelineBuilder->getRenderTarget()->isMultisampled();
+    bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
 
     if (!useCoverageAA) {
         return false;
@@ -668,7 +627,7 @@
     if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
         this->drawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
     // if we have shader derivative support, render as device-independent
-    } else if (target->caps()->shaderDerivativeSupport()) {
+    } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
         return this->drawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
                                    stroke);
     // otherwise axis-aligned ellipses only
@@ -695,22 +654,20 @@
         SkRect fDevBounds;
     };
 
-    static GrBatch* Create(const Geometry& geometry) {
-        return SkNEW_ARGS(CircleBatch, (geometry));
-    }
+    static GrBatch* Create(const Geometry& geometry) { return SkNEW_ARGS(CircleBatch, (geometry)); }
 
-    const char* name() const SK_OVERRIDE { return "CircleBatch"; }
+    const char* name() const override { return "CircleBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
 
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -726,7 +683,7 @@
         fBatch.fCoverageIgnored = init.fCoverageIgnored;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         SkMatrix invert;
         if (!this->viewMatrix().invert(&invert)) {
             return;
@@ -750,32 +707,22 @@
         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
 
         int instanceCount = fGeoData.count();
-        int vertexCount = kVertsPerCircle * instanceCount;
         size_t vertexStride = gp->getVertexStride();
         SkASSERT(vertexStride == sizeof(CircleVertex));
-
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
-
-        void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              vertexCount,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices || !batchTarget->quadIndexBuffer()) {
-            SkDebugf("Could not allocate buffers\n");
+        QuadHelper helper;
+        CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget, vertexStride,
+                                                                          instanceCount));
+        if (!verts) {
             return;
         }
 
-        CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
-
         for (int i = 0; i < instanceCount; i++) {
-            Geometry& args = fGeoData[i];
+            Geometry& geom = fGeoData[i];
 
-            SkScalar innerRadius = args.fInnerRadius;
-            SkScalar outerRadius = args.fOuterRadius;
+            SkScalar innerRadius = geom.fInnerRadius;
+            SkScalar outerRadius = geom.fOuterRadius;
 
-            const SkRect& bounds = args.fDevBounds;
+            const SkRect& bounds = geom.fDevBounds;
 
             // The inner radius in the vertex data must be specified in normalized space.
             innerRadius = innerRadius / outerRadius;
@@ -799,33 +746,9 @@
             verts[3].fOuterRadius = outerRadius;
             verts[3].fInnerRadius = innerRadius;
 
-            verts += kVertsPerCircle;
+            verts += kVerticesPerQuad;
         }
-
-        const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
-
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
-        drawInfo.setStartVertex(0);
-        drawInfo.setStartIndex(0);
-        drawInfo.setVerticesPerInstance(kVertsPerCircle);
-        drawInfo.setIndicesPerInstance(kIndicesPerCircle);
-        drawInfo.adjustStartVertex(firstVertex);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setIndexBuffer(quadIndexBuffer);
-
-        int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
-
-        while (instanceCount) {
-            drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
-            drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
-            drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
-
-            batchTarget->draw(drawInfo);
-
-            drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
-            instanceCount -= drawInfo.instanceCount();
-        }
+        helper.issueDraw(batchTarget);
     }
 
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
@@ -834,9 +757,11 @@
     CircleBatch(const Geometry& geometry) {
         this->initClassID<CircleBatch>();
         fGeoData.push_back(geometry);
+
+        this->setBounds(geometry.fDevBounds);
     }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         CircleBatch* that = t->cast<CircleBatch>();
 
         // TODO use vertex color to avoid breaking batches
@@ -854,6 +779,7 @@
         }
 
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
         return true;
     }
 
@@ -870,20 +796,15 @@
         bool fCoverageIgnored;
     };
 
-    static const int kVertsPerCircle = 4;
-    static const int kIndicesPerCircle = 6;
-
     BatchTracker fBatch;
     SkSTArray<1, Geometry, true> fGeoData;
 };
 
-void GrOvalRenderer::drawCircle(GrDrawTarget* target,
-                                GrPipelineBuilder* pipelineBuilder,
-                                GrColor color,
-                                const SkMatrix& viewMatrix,
-                                bool useCoverageAA,
-                                const SkRect& circle,
-                                const SkStrokeRec& stroke) {
+static GrBatch* create_circle_batch(GrColor color,
+                                    const SkMatrix& viewMatrix,
+                                    bool useCoverageAA,
+                                    const SkRect& circle,
+                                    const SkStrokeRec& stroke) {
     SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
     viewMatrix.mapPoints(&center, 1);
     SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
@@ -917,23 +838,28 @@
     outerRadius += SK_ScalarHalf;
     innerRadius -= SK_ScalarHalf;
 
-    SkRect bounds = SkRect::MakeLTRB(
-        center.fX - outerRadius,
-        center.fY - outerRadius,
-        center.fX + outerRadius,
-        center.fY + outerRadius
-    );
-
     CircleBatch::Geometry geometry;
     geometry.fViewMatrix = viewMatrix;
     geometry.fColor = color;
     geometry.fInnerRadius = innerRadius;
     geometry.fOuterRadius = outerRadius;
     geometry.fStroke = isStrokeOnly && innerRadius > 0;
-    geometry.fDevBounds = bounds;
+    geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
+                                           center.fX + outerRadius, center.fY + outerRadius);
 
-    SkAutoTUnref<GrBatch> batch(CircleBatch::Create(geometry));
-    target->drawBatch(pipelineBuilder, batch, &bounds);
+    return CircleBatch::Create(geometry);
+}
+
+void GrOvalRenderer::drawCircle(GrDrawTarget* target,
+                                GrPipelineBuilder* pipelineBuilder,
+                                GrColor color,
+                                const SkMatrix& viewMatrix,
+                                bool useCoverageAA,
+                                const SkRect& circle,
+                                const SkStrokeRec& stroke) {
+    SkAutoTUnref<GrBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
+                                                    stroke));
+    target->drawBatch(pipelineBuilder, batch);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -955,17 +881,17 @@
         return SkNEW_ARGS(EllipseBatch, (geometry));
     }
 
-    const char* name() const SK_OVERRIDE { return "EllipseBatch"; }
+    const char* name() const override { return "EllipseBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -981,7 +907,7 @@
         fBatch.fCoverageIgnored = init.fCoverageIgnored;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         SkMatrix invert;
         if (!this->viewMatrix().invert(&invert)) {
             return;
@@ -1005,38 +931,28 @@
         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
 
         int instanceCount = fGeoData.count();
-        int vertexCount = kVertsPerEllipse * instanceCount;
+        QuadHelper helper;
         size_t vertexStride = gp->getVertexStride();
         SkASSERT(vertexStride == sizeof(EllipseVertex));
-
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
-
-        void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              vertexCount,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices || !batchTarget->quadIndexBuffer()) {
-            SkDebugf("Could not allocate buffers\n");
+        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
+            helper.init(batchTarget, vertexStride, instanceCount));
+        if (!verts) {
             return;
         }
 
-        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
-
         for (int i = 0; i < instanceCount; i++) {
-            Geometry& args = fGeoData[i];
+            Geometry& geom = fGeoData[i];
 
-            SkScalar xRadius = args.fXRadius;
-            SkScalar yRadius = args.fYRadius;
+            SkScalar xRadius = geom.fXRadius;
+            SkScalar yRadius = geom.fYRadius;
 
             // Compute the reciprocals of the radii here to save time in the shader
             SkScalar xRadRecip = SkScalarInvert(xRadius);
             SkScalar yRadRecip = SkScalarInvert(yRadius);
-            SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
-            SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
+            SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
+            SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
 
-            const SkRect& bounds = args.fDevBounds;
+            const SkRect& bounds = geom.fDevBounds;
 
             // The inner radius in the vertex data must be specified in normalized space.
             verts[0].fPos = SkPoint::Make(bounds.fLeft,  bounds.fTop);
@@ -1059,33 +975,9 @@
             verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
             verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
 
-            verts += kVertsPerEllipse;
+            verts += kVerticesPerQuad;
         }
-
-        const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
-
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
-        drawInfo.setStartVertex(0);
-        drawInfo.setStartIndex(0);
-        drawInfo.setVerticesPerInstance(kVertsPerEllipse);
-        drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
-        drawInfo.adjustStartVertex(firstVertex);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setIndexBuffer(quadIndexBuffer);
-
-        int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
-
-        while (instanceCount) {
-            drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
-            drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
-            drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
-
-            batchTarget->draw(drawInfo);
-
-            drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
-            instanceCount -= drawInfo.instanceCount();
-        }
+        helper.issueDraw(batchTarget);
     }
 
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
@@ -1094,9 +986,11 @@
     EllipseBatch(const Geometry& geometry) {
         this->initClassID<EllipseBatch>();
         fGeoData.push_back(geometry);
+
+        this->setBounds(geometry.fDevBounds);
     }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         EllipseBatch* that = t->cast<EllipseBatch>();
 
         // TODO use vertex color to avoid breaking batches
@@ -1114,6 +1008,7 @@
         }
 
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
         return true;
     }
 
@@ -1130,20 +1025,15 @@
         bool fCoverageIgnored;
     };
 
-    static const int kVertsPerEllipse = 4;
-    static const int kIndicesPerEllipse = 6;
-
     BatchTracker fBatch;
     SkSTArray<1, Geometry, true> fGeoData;
 };
 
-bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
-                                 GrPipelineBuilder* pipelineBuilder,
-                                 GrColor color,
-                                 const SkMatrix& viewMatrix,
-                                 bool useCoverageAA,
-                                 const SkRect& ellipse,
-                                 const SkStrokeRec& stroke) {
+static GrBatch* create_ellipse_batch(GrColor color,
+                                     const SkMatrix& viewMatrix,
+                                     bool useCoverageAA,
+                                     const SkRect& ellipse,
+                                     const SkStrokeRec& stroke) {
 #ifdef SK_DEBUG
     {
         // we should have checked for this previously
@@ -1187,13 +1077,13 @@
         // we only handle thick strokes for near-circular ellipses
         if (scaledStroke.length() > SK_ScalarHalf &&
             (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
-            return false;
+            return NULL;
         }
 
         // we don't handle it if curvature of the stroke is less than curvature of the ellipse
         if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
             scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
-            return false;
+            return NULL;
         }
 
         // this is legit only if scale & translation (which should be the case at the moment)
@@ -1212,13 +1102,6 @@
     xRadius += SK_ScalarHalf;
     yRadius += SK_ScalarHalf;
 
-    SkRect bounds = SkRect::MakeLTRB(
-        center.fX - xRadius,
-        center.fY - yRadius,
-        center.fX + xRadius,
-        center.fY + yRadius
-    );
-
     EllipseBatch::Geometry geometry;
     geometry.fViewMatrix = viewMatrix;
     geometry.fColor = color;
@@ -1227,11 +1110,26 @@
     geometry.fInnerXRadius = innerXRadius;
     geometry.fInnerYRadius = innerYRadius;
     geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
-    geometry.fDevBounds = bounds;
+    geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
+                                           center.fX + xRadius, center.fY + yRadius);
 
-    SkAutoTUnref<GrBatch> batch(EllipseBatch::Create(geometry));
-    target->drawBatch(pipelineBuilder, batch, &bounds);
+    return EllipseBatch::Create(geometry);
+}
 
+bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
+                                 GrPipelineBuilder* pipelineBuilder,
+                                 GrColor color,
+                                 const SkMatrix& viewMatrix,
+                                 bool useCoverageAA,
+                                 const SkRect& ellipse,
+                                 const SkStrokeRec& stroke) {
+    SkAutoTUnref<GrBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
+                                                     stroke));
+    if (!batch) {
+        return false;
+    }
+
+    target->drawBatch(pipelineBuilder, batch);
     return true;
 }
 
@@ -1249,24 +1147,24 @@
         SkScalar fGeoDx;
         SkScalar fGeoDy;
         DIEllipseEdgeEffect::Mode fMode;
-        SkRect fDevBounds;
+        SkRect fBounds;
     };
 
-    static GrBatch* Create(const Geometry& geometry) {
-        return SkNEW_ARGS(DIEllipseBatch, (geometry));
+    static GrBatch* Create(const Geometry& geometry, const SkRect& bounds) {
+        return SkNEW_ARGS(DIEllipseBatch, (geometry, bounds));
     }
 
-    const char* name() const SK_OVERRIDE { return "DIEllipseBatch"; }
+    const char* name() const override { return "DIEllipseBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -1282,7 +1180,7 @@
         fBatch.fCoverageIgnored = init.fCoverageIgnored;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         // Setup geometry processor
         SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
                                                                          this->viewMatrix(),
@@ -1301,39 +1199,29 @@
         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
 
         int instanceCount = fGeoData.count();
-        int vertexCount = kVertsPerEllipse * instanceCount;
         size_t vertexStride = gp->getVertexStride();
         SkASSERT(vertexStride == sizeof(DIEllipseVertex));
-
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
-
-        void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              vertexCount,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices || !batchTarget->quadIndexBuffer()) {
-            SkDebugf("Could not allocate buffers\n");
+        QuadHelper helper;
+        DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
+            helper.init(batchTarget, vertexStride, instanceCount));
+        if (!verts) {
             return;
         }
 
-        DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(vertices);
-
         for (int i = 0; i < instanceCount; i++) {
-            Geometry& args = fGeoData[i];
+            Geometry& geom = fGeoData[i];
 
-            SkScalar xRadius = args.fXRadius;
-            SkScalar yRadius = args.fYRadius;
+            SkScalar xRadius = geom.fXRadius;
+            SkScalar yRadius = geom.fYRadius;
 
-            const SkRect& bounds = args.fDevBounds;
+            const SkRect& bounds = geom.fBounds;
 
             // This adjusts the "radius" to include the half-pixel border
-            SkScalar offsetDx = SkScalarDiv(args.fGeoDx, xRadius);
-            SkScalar offsetDy = SkScalarDiv(args.fGeoDy, yRadius);
+            SkScalar offsetDx = geom.fGeoDx / xRadius;
+            SkScalar offsetDy = geom.fGeoDy / yRadius;
 
-            SkScalar innerRatioX = SkScalarDiv(xRadius, args.fInnerXRadius);
-            SkScalar innerRatioY = SkScalarDiv(yRadius, args.fInnerYRadius);
+            SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
+            SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
 
             verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
             verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
@@ -1351,44 +1239,22 @@
             verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
             verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
 
-            verts += kVertsPerEllipse;
+            verts += kVerticesPerQuad;
         }
-
-        const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
-
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
-        drawInfo.setStartVertex(0);
-        drawInfo.setStartIndex(0);
-        drawInfo.setVerticesPerInstance(kVertsPerEllipse);
-        drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
-        drawInfo.adjustStartVertex(firstVertex);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setIndexBuffer(quadIndexBuffer);
-
-        int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
-
-        while (instanceCount) {
-            drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
-            drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
-            drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
-
-            batchTarget->draw(drawInfo);
-
-            drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
-            instanceCount -= drawInfo.instanceCount();
-        }
+        helper.issueDraw(batchTarget);
     }
 
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
 
 private:
-    DIEllipseBatch(const Geometry& geometry) {
+    DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) {
         this->initClassID<DIEllipseBatch>();
         fGeoData.push_back(geometry);
+
+        this->setBounds(bounds);
     }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         DIEllipseBatch* that = t->cast<DIEllipseBatch>();
 
         // TODO use vertex color to avoid breaking batches
@@ -1400,12 +1266,13 @@
             return false;
         }
 
-        SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
-        if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+        // TODO rewrite to allow positioning on CPU
+        if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
             return false;
         }
 
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
         return true;
     }
 
@@ -1422,20 +1289,15 @@
         bool fCoverageIgnored;
     };
 
-    static const int kVertsPerEllipse = 4;
-    static const int kIndicesPerEllipse = 6;
-
     BatchTracker fBatch;
     SkSTArray<1, Geometry, true> fGeoData;
 };
 
-bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
-                                   GrPipelineBuilder* pipelineBuilder,
-                                   GrColor color,
-                                   const SkMatrix& viewMatrix,
-                                   bool useCoverageAA,
-                                   const SkRect& ellipse,
-                                   const SkStrokeRec& stroke) {
+static GrBatch* create_diellipse_batch(GrColor color,
+                                       const SkMatrix& viewMatrix,
+                                       bool useCoverageAA,
+                                       const SkRect& ellipse,
+                                       const SkStrokeRec& stroke) {
     SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
     SkScalar xRadius = SkScalarHalf(ellipse.width());
     SkScalar yRadius = SkScalarHalf(ellipse.height());
@@ -1460,13 +1322,13 @@
         // we only handle thick strokes for near-circular ellipses
         if (strokeWidth > SK_ScalarHalf &&
             (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
-            return false;
+            return NULL;
         }
 
         // we don't handle it if curvature of the stroke is less than curvature of the ellipse
         if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
             strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
-            return false;
+            return NULL;
         }
 
         // set inner radius (if needed)
@@ -1488,15 +1350,8 @@
     SkScalar b = viewMatrix[SkMatrix::kMSkewX];
     SkScalar c = viewMatrix[SkMatrix::kMSkewY];
     SkScalar d = viewMatrix[SkMatrix::kMScaleY];
-    SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
-    SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
-
-    SkRect bounds = SkRect::MakeLTRB(
-        center.fX - xRadius - geoDx,
-        center.fY - yRadius - geoDy,
-        center.fX + xRadius + geoDx,
-        center.fY + yRadius + geoDy
-    );
+    SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
+    SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
 
     DIEllipseBatch::Geometry geometry;
     geometry.fViewMatrix = viewMatrix;
@@ -1508,11 +1363,27 @@
     geometry.fGeoDx = geoDx;
     geometry.fGeoDy = geoDy;
     geometry.fMode = mode;
-    geometry.fDevBounds = bounds;
+    geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
+                                        center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
 
-    SkAutoTUnref<GrBatch> batch(DIEllipseBatch::Create(geometry));
-    target->drawBatch(pipelineBuilder, batch, &bounds);
+    SkRect devBounds = geometry.fBounds;
+    viewMatrix.mapRect(&devBounds);
+    return DIEllipseBatch::Create(geometry, devBounds);
+}
 
+bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
+                                   GrPipelineBuilder* pipelineBuilder,
+                                   GrColor color,
+                                   const SkMatrix& viewMatrix,
+                                   bool useCoverageAA,
+                                   const SkRect& ellipse,
+                                   const SkStrokeRec& stroke) {
+    SkAutoTUnref<GrBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
+                                                       stroke));
+    if (!batch) {
+        return false;
+    }
+    target->drawBatch(pipelineBuilder, batch);
     return true;
 }
 
@@ -1541,23 +1412,21 @@
 static const int kVertsPerRRect = 16;
 static const int kNumRRectsInIndexBuffer = 256;
 
-GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(bool isStrokeOnly) {
-    if (isStrokeOnly) {
-        if (NULL == fStrokeRRectIndexBuffer) {
-            fStrokeRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
-                                                                       kIndicesPerStrokeRRect,
-                                                                       kNumRRectsInIndexBuffer,
-                                                                       kVertsPerRRect);
-        }
-        return fStrokeRRectIndexBuffer;
+GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
+GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
+static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
+                                                   GrResourceProvider* resourceProvider) {
+    GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
+    GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
+    if (strokeOnly) {
+        return resourceProvider->refOrCreateInstancedIndexBuffer(
+            gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
+            gStrokeRRectOnlyIndexBufferKey);
     } else {
-        if (NULL == fRRectIndexBuffer) {
-            fRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
-                                                                 kIndicesPerRRect,
-                                                                 kNumRRectsInIndexBuffer,
-                                                                 kVertsPerRRect);
-        }
-        return fRRectIndexBuffer;
+        return resourceProvider->refOrCreateInstancedIndexBuffer(
+            gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
+            gRRectOnlyIndexBufferKey);
+
     }
 }
 
@@ -1639,21 +1508,21 @@
         SkRect fDevBounds;
     };
 
-    static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
-        return SkNEW_ARGS(RRectCircleRendererBatch, (geometry, indexBuffer));
+    static GrBatch* Create(const Geometry& geometry) {
+        return SkNEW_ARGS(RRectCircleRendererBatch, (geometry));
     }
 
-    const char* name() const SK_OVERRIDE { return "RRectCircleBatch"; }
+    const char* name() const override { return "RRectCircleBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -1669,7 +1538,7 @@
         fBatch.fCoverageIgnored = init.fCoverageIgnored;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         // reset to device coordinates
         SkMatrix invert;
         if (!this->viewMatrix().invert(&invert)) {
@@ -1695,25 +1564,23 @@
         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
 
         int instanceCount = fGeoData.count();
-        int vertexCount = kVertsPerRRect * instanceCount;
         size_t vertexStride = gp->getVertexStride();
         SkASSERT(vertexStride == sizeof(CircleVertex));
 
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
+        // drop out the middle quad if we're stroked
+        int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
+        SkAutoTUnref<const GrIndexBuffer> indexBuffer(
+            ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
 
-        void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              vertexCount,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices) {
+        InstancedHelper helper;
+        CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget,
+            kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
+            indicesPerInstance, instanceCount));
+        if (!verts || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
-        CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
-
         for (int i = 0; i < instanceCount; i++) {
             Geometry& args = fGeoData[i];
 
@@ -1758,45 +1625,20 @@
             }
         }
 
-        // drop out the middle quad if we're stroked
-        int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
-                                        SK_ARRAY_COUNT(gRRectIndices);
-
-
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
-        drawInfo.setStartVertex(0);
-        drawInfo.setStartIndex(0);
-        drawInfo.setVerticesPerInstance(kVertsPerRRect);
-        drawInfo.setIndicesPerInstance(indexCnt);
-        drawInfo.adjustStartVertex(firstVertex);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setIndexBuffer(fIndexBuffer);
-
-        int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
-
-        while (instanceCount) {
-            drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
-            drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
-            drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
-
-            batchTarget->draw(drawInfo);
-
-            drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
-            instanceCount -= drawInfo.instanceCount();
-        }
+        helper.issueDraw(batchTarget);
     }
 
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
 
 private:
-    RRectCircleRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
-        : fIndexBuffer(indexBuffer) {
+    RRectCircleRendererBatch(const Geometry& geometry) {
         this->initClassID<RRectCircleRendererBatch>();
         fGeoData.push_back(geometry);
+
+        this->setBounds(geometry.fDevBounds);
     }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
 
         // TODO use vertex color to avoid breaking batches
@@ -1814,6 +1656,7 @@
         }
 
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
         return true;
     }
 
@@ -1832,7 +1675,6 @@
 
     BatchTracker fBatch;
     SkSTArray<1, Geometry, true> fGeoData;
-    const GrIndexBuffer* fIndexBuffer;
 };
 
 class RRectEllipseRendererBatch : public GrBatch {
@@ -1848,21 +1690,21 @@
         SkRect fDevBounds;
     };
 
-    static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
-        return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry, indexBuffer));
+    static GrBatch* Create(const Geometry& geometry) {
+        return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry));
     }
 
-    const char* name() const SK_OVERRIDE { return "RRectEllipseRendererBatch"; }
+    const char* name() const override { return "RRectEllipseRendererBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -1878,7 +1720,7 @@
         fBatch.fCoverageIgnored = init.fCoverageIgnored;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         // reset to device coordinates
         SkMatrix invert;
         if (!this->viewMatrix().invert(&invert)) {
@@ -1904,25 +1746,23 @@
         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
 
         int instanceCount = fGeoData.count();
-        int vertexCount = kVertsPerRRect * instanceCount;
         size_t vertexStride = gp->getVertexStride();
         SkASSERT(vertexStride == sizeof(EllipseVertex));
 
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
+        // drop out the middle quad if we're stroked
+        int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
+        SkAutoTUnref<const GrIndexBuffer> indexBuffer(
+            ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider()));
 
-        void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              vertexCount,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices) {
+        InstancedHelper helper;
+        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
+            helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
+            kVertsPerRRect, indicesPerInstance, instanceCount));
+        if (!verts || !indexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
-        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
-
         for (int i = 0; i < instanceCount; i++) {
             Geometry& args = fGeoData[i];
 
@@ -1977,45 +1817,20 @@
                 verts++;
             }
         }
-
-        // drop out the middle quad if we're stroked
-        int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
-                                        SK_ARRAY_COUNT(gRRectIndices);
-
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
-        drawInfo.setStartVertex(0);
-        drawInfo.setStartIndex(0);
-        drawInfo.setVerticesPerInstance(kVertsPerRRect);
-        drawInfo.setIndicesPerInstance(indexCnt);
-        drawInfo.adjustStartVertex(firstVertex);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setIndexBuffer(fIndexBuffer);
-
-        int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
-
-        while (instanceCount) {
-            drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
-            drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
-            drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
-
-            batchTarget->draw(drawInfo);
-
-            drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
-            instanceCount -= drawInfo.instanceCount();
-        }
+        helper.issueDraw(batchTarget);
     }
 
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
 
 private:
-    RRectEllipseRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
-        : fIndexBuffer(indexBuffer) {
+    RRectEllipseRendererBatch(const Geometry& geometry) {
         this->initClassID<RRectEllipseRendererBatch>();
         fGeoData.push_back(geometry);
+
+        this->setBounds(geometry.fDevBounds);
     }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
 
         // TODO use vertex color to avoid breaking batches
@@ -2033,6 +1848,7 @@
         }
 
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
         return true;
     }
 
@@ -2051,33 +1867,17 @@
 
     BatchTracker fBatch;
     SkSTArray<1, Geometry, true> fGeoData;
-    const GrIndexBuffer* fIndexBuffer;
 };
 
-bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
-                               GrPipelineBuilder* pipelineBuilder,
-                               GrColor color,
-                               const SkMatrix& viewMatrix,
-                               bool useAA,
-                               const SkRRect& rrect,
-                               const SkStrokeRec& stroke) {
-    if (rrect.isOval()) {
-        return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
-                              stroke);
-    }
+static GrBatch* create_rrect_batch(GrColor color,
+                                   const SkMatrix& viewMatrix,
+                                   const SkRRect& rrect,
+                                   const SkStrokeRec& stroke) {
+    SkASSERT(viewMatrix.rectStaysRect());
+    SkASSERT(rrect.isSimple());
+    SkASSERT(!rrect.isOval());
 
-    bool useCoverageAA = useAA &&
-        !pipelineBuilder->getRenderTarget()->isMultisampled();
-
-    // only anti-aliased rrects for now
-    if (!useCoverageAA) {
-        return false;
-    }
-
-    if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
-        return false;
-    }
-
+    // RRect batchs only handle simple, but not too simple, rrects
     // do any matrix crunching before we reset the draw state for device coords
     const SkRect& rrectBounds = rrect.getBounds();
     SkRect bounds;
@@ -2111,7 +1911,7 @@
 
         // if half of strokewidth is greater than radius, we don't handle that right now
         if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
-            return false;
+            return NULL;
         }
     }
 
@@ -2121,13 +1921,7 @@
     // We could consider falling back to rect rendering here, since a tiny radius is
     // indistinguishable from a square corner.
     if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
-        return false;
-    }
-
-    GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(isStrokeOnly);
-    if (NULL == indexBuffer) {
-        SkDebugf("Failed to create index buffer!\n");
-        return false;
+        return NULL;
     }
 
     // if the corners are circles, use the circle renderer
@@ -2170,9 +1964,7 @@
         geometry.fStroke = isStrokeOnly;
         geometry.fDevBounds = bounds;
 
-        SkAutoTUnref<GrBatch> batch(RRectCircleRendererBatch::Create(geometry, indexBuffer));
-        target->drawBatch(pipelineBuilder, batch, &bounds);
-
+        return RRectCircleRendererBatch::Create(geometry);
     // otherwise we use the ellipse renderer
     } else {
         SkScalar innerXRadius = 0.0f;
@@ -2187,13 +1979,13 @@
             // we only handle thick strokes for near-circular ellipses
             if (scaledStroke.length() > SK_ScalarHalf &&
                 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
-                return false;
+                return NULL;
             }
 
             // we don't handle it if curvature of the stroke is less than curvature of the ellipse
             if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
                 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
-                return false;
+                return NULL;
             }
 
             // this is legit only if scale & translation (which should be the case at the moment)
@@ -2222,8 +2014,77 @@
         geometry.fStroke = isStrokeOnly;
         geometry.fDevBounds = bounds;
 
-        SkAutoTUnref<GrBatch> batch(RRectEllipseRendererBatch::Create(geometry, indexBuffer));
-        target->drawBatch(pipelineBuilder, batch, &bounds);
+        return RRectEllipseRendererBatch::Create(geometry);
     }
+}
+
+bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
+                               GrPipelineBuilder* pipelineBuilder,
+                               GrColor color,
+                               const SkMatrix& viewMatrix,
+                               bool useAA,
+                               const SkRRect& rrect,
+                               const SkStrokeRec& stroke) {
+    if (rrect.isOval()) {
+        return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
+                              stroke);
+    }
+
+    bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
+
+    // only anti-aliased rrects for now
+    if (!useCoverageAA) {
+        return false;
+    }
+
+    if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
+        return false;
+    }
+
+    SkAutoTUnref<GrBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke));
+    if (!batch) {
+        return false;
+    }
+
+    target->drawBatch(pipelineBuilder, batch);
     return true;
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef GR_TEST_UTILS
+
+BATCH_TEST_DEFINE(CircleBatch) {
+    SkMatrix viewMatrix = GrTest::TestMatrix(random);
+    GrColor color = GrRandomColor(random);
+    bool useCoverageAA = random->nextBool();
+    SkRect circle = GrTest::TestSquare(random);
+    return create_circle_batch(color, viewMatrix, useCoverageAA, circle,
+                               GrTest::TestStrokeRec(random));
+}
+
+BATCH_TEST_DEFINE(EllipseBatch) {
+    SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
+    GrColor color = GrRandomColor(random);
+    SkRect ellipse = GrTest::TestSquare(random);
+    return create_ellipse_batch(color, viewMatrix, true, ellipse,
+                                GrTest::TestStrokeRec(random));
+}
+
+BATCH_TEST_DEFINE(DIEllipseBatch) {
+    SkMatrix viewMatrix = GrTest::TestMatrix(random);
+    GrColor color = GrRandomColor(random);
+    bool useCoverageAA = random->nextBool();
+    SkRect ellipse = GrTest::TestSquare(random);
+    return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
+                                  GrTest::TestStrokeRec(random));
+}
+
+BATCH_TEST_DEFINE(RRectBatch) {
+    SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
+    GrColor color = GrRandomColor(random);
+    const SkRRect& rrect = GrTest::TestRRectSimple(random);
+    return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
+}
+
+#endif
diff --git a/src/gpu/GrOvalRenderer.h b/src/gpu/GrOvalRenderer.h
index 9d861c5..57ce2a5 100644
--- a/src/gpu/GrOvalRenderer.h
+++ b/src/gpu/GrOvalRenderer.h
@@ -24,16 +24,6 @@
 public:
     SK_DECLARE_INST_COUNT(GrOvalRenderer)
 
-    GrOvalRenderer(GrGpu* gpu)
-        : fGpu(gpu)
-        , fRRectIndexBuffer(NULL)
-        , fStrokeRRectIndexBuffer(NULL) {}
-    ~GrOvalRenderer() {
-        this->reset();
-    }
-
-    void reset();
-
     bool drawOval(GrDrawTarget*,
                   GrPipelineBuilder*,
                   GrColor,
@@ -79,12 +69,6 @@
                     const SkRect& circle,
                     const SkStrokeRec& stroke);
 
-    GrIndexBuffer* rRectIndexBuffer(bool isStrokeOnly);
-
-    GrGpu*         fGpu;
-    GrIndexBuffer* fRRectIndexBuffer;
-    GrIndexBuffer* fStrokeRRectIndexBuffer;
-
     typedef SkRefCnt INHERITED;
 };
 
diff --git a/src/gpu/GrPath.h b/src/gpu/GrPath.h
index 27bbdc0..ab8c51f 100644
--- a/src/gpu/GrPath.h
+++ b/src/gpu/GrPath.h
@@ -31,7 +31,7 @@
     static uint64_t ComputeStrokeKey(const SkStrokeRec&);
 
     bool isEqualTo(const SkPath& path, const SkStrokeRec& stroke) {
-        return fSkPath == path && fStroke == stroke;
+        return fSkPath == path && fStroke.hasEqualEffect(stroke);
     }
 
     const SkRect& getBounds() const { return fBounds; }
diff --git a/src/gpu/GrPathProcessor.cpp b/src/gpu/GrPathProcessor.cpp
index f6c1640..5c07a8b 100644
--- a/src/gpu/GrPathProcessor.cpp
+++ b/src/gpu/GrPathProcessor.cpp
@@ -1,3 +1,10 @@
+/*
+* 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 "GrPathProcessor.h"
 
 #include "gl/GrGLPathProcessor.h"
@@ -6,8 +13,10 @@
 GrPathProcessor::GrPathProcessor(GrColor color,
                                  const SkMatrix& viewMatrix,
                                  const SkMatrix& localMatrix)
-    : INHERITED(viewMatrix, localMatrix, true)
-    , fColor(color) {
+    : INHERITED(true)
+    , fColor(color)
+    , fViewMatrix(viewMatrix)
+    , fLocalMatrix(localMatrix) {
     this->initClassID<GrPathProcessor>();
 }
 
@@ -41,7 +50,8 @@
         return false;
     }
 
-    if (!this->viewMatrix().cheapEqualTo(that.viewMatrix())) {
+    const GrPathProcessor& other = that.cast<GrPathProcessor>();
+    if (!this->viewMatrix().cheapEqualTo(other.viewMatrix())) {
         return false;
     }
 
@@ -56,18 +66,13 @@
 }
 
 void GrPathProcessor::getGLProcessorKey(const GrBatchTracker& bt,
-                                        const GrGLCaps& caps,
+                                        const GrGLSLCaps& caps,
                                         GrProcessorKeyBuilder* b) const {
     GrGLPathProcessor::GenKey(*this, bt, caps, b);
 }
 
 GrGLPrimitiveProcessor* GrPathProcessor::createGLInstance(const GrBatchTracker& bt,
-                                                          const GrGLCaps& caps) const {
-    SkASSERT(caps.nvprSupport() != GrGLCaps::kNone_NvprSupport);
-    if (caps.nvprSupport() == GrGLCaps::kLegacy_NvprSupport) {
-        return SkNEW_ARGS(GrGLLegacyPathProcessor, (*this, bt,
-                                                    caps.maxFixedFunctionTextureCoords()));
-    } else {
-        return SkNEW_ARGS(GrGLNormalPathProcessor, (*this, bt));
-    }
+                                                          const GrGLSLCaps& caps) const {
+    SkASSERT(caps.pathRenderingSupport());
+    return SkNEW_ARGS(GrGLPathProcessor, (*this, bt));
 }
diff --git a/src/gpu/GrPathProcessor.h b/src/gpu/GrPathProcessor.h
index 03d3907..e34d0ce 100644
--- a/src/gpu/GrPathProcessor.h
+++ b/src/gpu/GrPathProcessor.h
@@ -29,35 +29,71 @@
         return SkNEW_ARGS(GrPathProcessor, (color, viewMatrix, localMatrix));
     }
 
-    void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const SK_OVERRIDE;
+    void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const override;
 
     bool canMakeEqual(const GrBatchTracker& mine,
                       const GrPrimitiveProcessor& that,
-                      const GrBatchTracker& theirs) const SK_OVERRIDE;
+                      const GrBatchTracker& theirs) const override;
 
-    const char* name() const SK_OVERRIDE { return "PathProcessor"; }
+    const char* name() const override { return "PathProcessor"; }
 
     GrColor color() const { return fColor; }
+    const SkMatrix& viewMatrix() const { return fViewMatrix; }
+    const SkMatrix& localMatrix() const { return fLocalMatrix; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE;
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE;
 
-    bool willUseGeoShader() const SK_OVERRIDE { return false; }
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override;
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override;
+
+    bool willUseGeoShader() const override { return false; }
 
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps& caps) const SK_OVERRIDE;
-
-protected:
-    GrPathProcessor(GrColor color, const SkMatrix& viewMatrix, const SkMatrix& localMatrix);
+                                                     const GrGLSLCaps& caps) const override;
 
 private:
-    bool hasExplicitLocalCoords() const SK_OVERRIDE { return false; }
+    GrPathProcessor(GrColor color, const SkMatrix& viewMatrix, const SkMatrix& localMatrix);
+
+    /*
+     * CanCombineOutput will return true if two draws are 'batchable' from a color perspective.
+     * TODO is this really necessary?
+     */
+    static bool CanCombineOutput(GrGPInput left, GrColor lColor, GrGPInput right, GrColor rColor) {
+        if (left != right) {
+            return false;
+        }
+
+        if (kUniform_GrGPInput == left && lColor != rColor) {
+            return false;
+        }
+
+        return true;
+    }
+
+    static bool CanCombineLocalMatrices(const GrPrimitiveProcessor& l,
+                                        bool leftUsesLocalCoords,
+                                        const GrPrimitiveProcessor& r,
+                                        bool rightUsesLocalCoords) {
+        if (leftUsesLocalCoords != rightUsesLocalCoords) {
+            return false;
+        }
+
+        const GrPathProcessor& left = l.cast<GrPathProcessor>();
+        const GrPathProcessor& right = r.cast<GrPathProcessor>();
+        if (leftUsesLocalCoords && !left.localMatrix().cheapEqualTo(right.localMatrix())) {
+            return false;
+        }
+        return true;
+    }
+
+    bool hasExplicitLocalCoords() const override { return false; }
 
     GrColor fColor;
+    const SkMatrix fViewMatrix;
+    const SkMatrix fLocalMatrix;
 
     typedef GrPrimitiveProcessor INHERITED;
 };
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index 5144597..a53d51d 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -12,9 +12,9 @@
 #include "GrDrawTarget.h"
 #include "GrPathRendererChain.h"
 #include "GrStencil.h"
+#include "GrStrokeInfo.h"
 
 #include "SkDrawProcs.h"
-#include "SkStrokeRec.h"
 #include "SkTArray.h"
 
 class SkPath;
@@ -84,7 +84,7 @@
     StencilSupport getStencilSupport(const GrDrawTarget* target,
                                      const GrPipelineBuilder* pipelineBuilder,
                                      const SkPath& path,
-                                     const SkStrokeRec& stroke) const {
+                                     const GrStrokeInfo& stroke) const {
         SkASSERT(!path.isInverseFillType());
         return this->onGetStencilSupport(target, pipelineBuilder, path, stroke);
     }
@@ -107,7 +107,7 @@
                              const GrPipelineBuilder* pipelineBuilder,
                              const SkMatrix& viewMatrix,
                              const SkPath& path,
-                             const SkStrokeRec& rec,
+                             const GrStrokeInfo& rec,
                              bool antiAlias) const = 0;
     /**
      * Draws the path into the draw target. If getStencilSupport() would return kNoRestriction then
@@ -125,7 +125,7 @@
                   GrColor color,
                   const SkMatrix& viewMatrix,
                   const SkPath& path,
-                  const SkStrokeRec& stroke,
+                  const GrStrokeInfo& stroke,
                   bool antiAlias) {
         SkASSERT(!path.isEmpty());
         SkASSERT(this->canDrawPath(target, ds, viewMatrix, path, stroke, antiAlias));
@@ -147,7 +147,7 @@
                      GrPipelineBuilder* ds,
                      const SkMatrix& viewMatrix,
                      const SkPath& path,
-                     const SkStrokeRec& stroke) {
+                     const GrStrokeInfo& stroke) {
         SkASSERT(!path.isEmpty());
         SkASSERT(kNoSupport_StencilSupport != this->getStencilSupport(target, ds, path, stroke));
         this->onStencilPath(target, ds, viewMatrix, path, stroke);
@@ -155,16 +155,19 @@
 
     // Helper for determining if we can treat a thin stroke as a hairline w/ coverage.
     // If we can, we draw lots faster (raster device does this same test).
-    static bool IsStrokeHairlineOrEquivalent(const SkStrokeRec& stroke, const SkMatrix& matrix,
+    static bool IsStrokeHairlineOrEquivalent(const GrStrokeInfo& stroke, const SkMatrix& matrix,
                                              SkScalar* outCoverage) {
-        if (stroke.isHairlineStyle()) {
+        if (stroke.isDashed()) {
+            return false;
+        }
+        if (stroke.getStrokeRec().isHairlineStyle()) {
             if (outCoverage) {
                 *outCoverage = SK_Scalar1;
             }
             return true;
         }
-        return stroke.getStyle() == SkStrokeRec::kStroke_Style &&
-            SkDrawTreatAAStrokeAsHairline(stroke.getWidth(), matrix, outCoverage);
+        return stroke.getStrokeRec().getStyle() == SkStrokeRec::kStroke_Style &&
+            SkDrawTreatAAStrokeAsHairline(stroke.getStrokeRec().getWidth(), matrix, outCoverage);
     }
 
 protected:
@@ -174,7 +177,7 @@
     virtual StencilSupport onGetStencilSupport(const GrDrawTarget*,
                                                const GrPipelineBuilder*,
                                                const SkPath&,
-                                               const SkStrokeRec&) const {
+                                               const GrStrokeInfo&) const {
         return kNoRestriction_StencilSupport;
     }
 
@@ -186,7 +189,7 @@
                             GrColor,
                             const SkMatrix& viewMatrix,
                             const SkPath&,
-                            const SkStrokeRec&,
+                            const GrStrokeInfo&,
                             bool antiAlias) = 0;
 
     /**
@@ -197,7 +200,7 @@
                                GrPipelineBuilder* pipelineBuilder,
                                const SkMatrix& viewMatrix,
                                const SkPath& path,
-                               const SkStrokeRec& stroke) {
+                               const GrStrokeInfo& stroke) {
         GR_STATIC_CONST_SAME_STENCIL(kIncrementStencil,
                                      kReplace_StencilOp,
                                      kReplace_StencilOp,
diff --git a/src/gpu/GrPathRendererChain.cpp b/src/gpu/GrPathRendererChain.cpp
index d11763b..915bdd2 100644
--- a/src/gpu/GrPathRendererChain.cpp
+++ b/src/gpu/GrPathRendererChain.cpp
@@ -35,7 +35,7 @@
                                                      const GrPipelineBuilder* pipelineBuilder,
                                                      const SkMatrix& viewMatrix,
                                                      const SkPath& path,
-                                                     const SkStrokeRec& stroke,
+                                                     const GrStrokeInfo& stroke,
                                                      DrawType drawType,
                                                      StencilSupport* stencilSupport) {
     if (!fInit) {
diff --git a/src/gpu/GrPathRendering.cpp b/src/gpu/GrPathRendering.cpp
index 456f6d8..fba55d5 100644
--- a/src/gpu/GrPathRendering.cpp
+++ b/src/gpu/GrPathRendering.cpp
@@ -24,11 +24,11 @@
         SkDescriptor::Free(fDesc);
     }
 
-    int getNumPaths() SK_OVERRIDE {
+    int getNumPaths() override {
         return fScalerContext->getGlyphCount();
     }
 
-    void generatePath(int glyphID, SkPath* out) SK_OVERRIDE {
+    void generatePath(int glyphID, SkPath* out) override {
         SkGlyph skGlyph;
         skGlyph.initWithGlyphID(glyphID);
         fScalerContext->getMetrics(&skGlyph);
@@ -37,7 +37,7 @@
         out->transform(fFlipMatrix); // Load glyphs with the inverted y-direction.
     }
 
-    bool isEqualTo(const SkDescriptor& desc) const SK_OVERRIDE {
+    bool isEqualTo(const SkDescriptor& desc) const override {
         return fDesc->equals(desc);
     }
 
diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp
index 2a90666..3e2c3bf 100644
--- a/src/gpu/GrPathUtils.cpp
+++ b/src/gpu/GrPathUtils.cpp
@@ -29,8 +29,7 @@
             stretch = SkMaxScalar(stretch, mat.mapRadius(SK_Scalar1));
         }
     }
-    srcTol = SkScalarDiv(srcTol, stretch);
-    return srcTol;
+    return srcTol / stretch;
 }
 
 static const int MAX_POINTS_PER_CURVE = 1 << 10;
@@ -51,7 +50,7 @@
         // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
         // points.
         // 2^(log4(x)) = sqrt(x);
-        SkScalar divSqrt = SkScalarSqrt(SkScalarDiv(d, tol));
+        SkScalar divSqrt = SkScalarSqrt(d / tol);
         if (((SkScalar)SK_MaxS32) <= divSqrt) {
             return MAX_POINTS_PER_CURVE;
         } else {
@@ -107,11 +106,11 @@
     if (d <= tol) {
         return 1;
     } else {
-        SkScalar divSqrt = SkScalarSqrt(SkScalarDiv(d, tol));
+        SkScalar divSqrt = SkScalarSqrt(d / tol);
         if (((SkScalar)SK_MaxS32) <= divSqrt) {
             return MAX_POINTS_PER_CURVE;
         } else {
-            int temp = SkScalarCeilToInt(SkScalarSqrt(SkScalarDiv(d, tol)));
+            int temp = SkScalarCeilToInt(SkScalarSqrt(d / tol));
             int pow2 = GrNextPow2(temp);
             // Because of NaNs & INFs we can wind up with a degenerate temp
             // such that pow2 comes out negative. Also, our point generator
diff --git a/src/gpu/GrPathUtils.h b/src/gpu/GrPathUtils.h
index c15fd7a..250b0a2 100644
--- a/src/gpu/GrPathUtils.h
+++ b/src/gpu/GrPathUtils.h
@@ -168,5 +168,12 @@
     // If you transform the points the lines will also need to be transformed. This can be done
     // by mapping the lines with the inverse-transpose of the matrix used to map the points.
     void getCubicKLM(const SkPoint p[4], SkScalar klm[9]);
+
+    // When tessellating curved paths into linear segments, this defines the maximum distance
+    // in screen space which a segment may deviate from the mathmatically correct value.
+    // Above this value, the segment will be subdivided.
+    // This value was chosen to approximate the supersampling accuracy of the raster path (16
+    // samples, or one quarter pixel).
+    static const SkScalar kDefaultTolerance = SkDoubleToScalar(0.25);
 };
 #endif
diff --git a/src/gpu/GrPipeline.cpp b/src/gpu/GrPipeline.cpp
index 906426c..551882f 100644
--- a/src/gpu/GrPipeline.cpp
+++ b/src/gpu/GrPipeline.cpp
@@ -64,6 +64,9 @@
     if (pipelineBuilder.isDither()) {
         fFlags |= kDither_Flag;
     }
+    if (pipelineBuilder.snapVerticesToPixelCenters()) {
+        fFlags |= kSnapVertices_Flag;
+    }
 
     int firstColorStageIdx = colorPOI.firstEffectiveStageIndex();
 
@@ -71,9 +74,6 @@
     // GrPipelineBuilder's coverageProcInfo (like color above) to set this initial information.
     int firstCoverageStageIdx = 0;
 
-    GrXferProcessor::BlendInfo blendInfo;
-    fXferProcessor->getBlendInfo(&blendInfo);
-
     this->adjustProgramFromOptimizations(pipelineBuilder, optFlags, colorPOI, coveragePOI,
                                          &firstColorStageIdx, &firstCoverageStageIdx);
 
@@ -112,13 +112,15 @@
                                                 const GrProcOptInfo& coveragePOI,
                                                 int* firstColorStageIdx,
                                                 int* firstCoverageStageIdx) {
-    fReadsFragPosition = false;
+    fReadsFragPosition = fXferProcessor->willReadFragmentPosition();
 
     if ((flags & GrXferProcessor::kIgnoreColor_OptFlag) ||
         (flags & GrXferProcessor::kOverrideColor_OptFlag)) {
         *firstColorStageIdx = pipelineBuilder.numColorFragmentStages();
     } else {
-        fReadsFragPosition = colorPOI.readsFragPosition();
+        if (coveragePOI.readsFragPosition()) {
+            fReadsFragPosition = true;
+        }
     }
 
     if (flags & GrXferProcessor::kIgnoreCoverage_OptFlag) {
diff --git a/src/gpu/GrPipeline.h b/src/gpu/GrPipeline.h
index 2cb5aab..fdba0d7 100644
--- a/src/gpu/GrPipeline.h
+++ b/src/gpu/GrPipeline.h
@@ -80,6 +80,7 @@
 
     bool isDitherState() const { return SkToBool(fFlags & kDither_Flag); }
     bool isHWAntialiasState() const { return SkToBool(fFlags & kHWAA_Flag); }
+    bool snapVerticesToPixelCenters() const { return SkToBool(fFlags & kSnapVertices_Flag); }
     // Skip any draws that refer to this pipeline (they should be a no-op).
     bool mustSkip() const { return NULL == this->getRenderTarget(); }
 
@@ -119,6 +120,7 @@
     enum Flags {
         kDither_Flag            = 0x1,
         kHWAA_Flag              = 0x2,
+        kSnapVertices_Flag      = 0x4,
     };
 
     typedef GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> RenderTarget;
diff --git a/src/gpu/GrPipelineBuilder.cpp b/src/gpu/GrPipelineBuilder.cpp
index 2a17c28..3b722e2 100644
--- a/src/gpu/GrPipelineBuilder.cpp
+++ b/src/gpu/GrPipelineBuilder.cpp
@@ -7,6 +7,7 @@
 
 #include "GrPipelineBuilder.h"
 
+#include "GrBatch.h"
 #include "GrBlend.h"
 #include "GrPaint.h"
 #include "GrPipeline.h"
@@ -15,7 +16,7 @@
 #include "effects/GrPorterDuffXferProcessor.h"
 
 GrPipelineBuilder::GrPipelineBuilder()
-    : fFlagBits(0x0)
+    : fFlags(0x0)
     , fDrawFace(kBoth_DrawFace)
     , fColorProcInfoValid(false)
     , fCoverageProcInfoValid(false)
@@ -26,7 +27,7 @@
 
 GrPipelineBuilder& GrPipelineBuilder::operator=(const GrPipelineBuilder& that) {
     fRenderTarget.reset(SkSafeRef(that.fRenderTarget.get()));
-    fFlagBits = that.fFlagBits;
+    fFlags = that.fFlags;
     fStencilSettings = that.fStencilSettings;
     fDrawFace = that.fDrawFace;
     fXPFactory.reset(SkRef(that.getXPFactory()));
@@ -68,12 +69,13 @@
     // These have no equivalent in GrPaint, set them to defaults
     fDrawFace = kBoth_DrawFace;
     fStencilSettings.setDisabled();
-    fFlagBits = 0;
+    fFlags = 0;
 
     fClip = clip;
 
-    this->setState(GrPipelineBuilder::kDither_StateBit, paint.isDither());
-    this->setState(GrPipelineBuilder::kHWAntialias_StateBit, paint.isAntiAlias());
+    this->setState(GrPipelineBuilder::kDither_Flag, paint.isDither());
+    this->setState(GrPipelineBuilder::kHWAntialias_Flag,
+                   rt->isMultisampled() && paint.isAntiAlias());
 
     fColorProcInfoValid = false;
     fCoverageProcInfoValid = false;
@@ -115,14 +117,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-// Some blend modes allow folding a fractional coverage value into the color's alpha channel, while
-// others will blend incorrectly.
-bool GrPipelineBuilder::canTweakAlphaForCoverage() const {
-    return this->getXPFactory()->canTweakAlphaForCoverage();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 GrPipelineBuilder::~GrPipelineBuilder() {
     SkASSERT(0 == fBlockEffectRemovalCnt);
 }
diff --git a/src/gpu/GrPipelineBuilder.h b/src/gpu/GrPipelineBuilder.h
index 8578572..fa085f0 100644
--- a/src/gpu/GrPipelineBuilder.h
+++ b/src/gpu/GrPipelineBuilder.h
@@ -8,7 +8,6 @@
 #ifndef GrPipelineBuilder_DEFINED
 #define GrPipelineBuilder_DEFINED
 
-#include "GrBatch.h"
 #include "GrBlend.h"
 #include "GrClip.h"
 #include "GrDrawTargetCaps.h"
@@ -24,6 +23,7 @@
 #include "effects/GrPorterDuffXferProcessor.h"
 #include "effects/GrSimpleTextureEffect.h"
 
+class GrBatch;
 class GrDrawTargetCaps;
 class GrPaint;
 class GrTexture;
@@ -139,13 +139,6 @@
     ////
 
     /**
-     * Determines whether multiplying the computed per-pixel color by the pixel's fractional
-     * coverage before the blend will give the correct final destination color. In general it
-     * will not as coverage is applied after blending.
-     */
-    bool canTweakAlphaForCoverage() const;
-
-    /**
      * This function returns true if the render target destination pixel values will be read for
      * blending during draw.
      */
@@ -275,49 +268,56 @@
      *  Flags that affect rendering. Controlled using enable/disableState(). All
      *  default to disabled.
      */
-    enum StateBits {
+    enum Flags {
         /**
          * Perform dithering. TODO: Re-evaluate whether we need this bit
          */
-        kDither_StateBit        = 0x01,
+        kDither_Flag        = 0x01,
         /**
          * Perform HW anti-aliasing. This means either HW FSAA, if supported by the render target,
          * or smooth-line rendering if a line primitive is drawn and line smoothing is supported by
          * the 3D API.
          */
-        kHWAntialias_StateBit   = 0x02,
+        kHWAntialias_Flag   = 0x02,
 
-        kLast_StateBit = kHWAntialias_StateBit,
+        /**
+         * Modifies the vertex shader so that vertices will be positioned at pixel centers.
+         */
+        kSnapVerticesToPixelCenters_Flag = 0x04,
+
+        kLast_Flag = kSnapVerticesToPixelCenters_Flag,
     };
 
-    bool isDither() const { return 0 != (fFlagBits & kDither_StateBit); }
-    bool isHWAntialias() const { return 0 != (fFlagBits & kHWAntialias_StateBit); }
+    bool isDither() const { return SkToBool(fFlags & kDither_Flag); }
+    bool isHWAntialias() const { return SkToBool(fFlags & kHWAntialias_Flag); }
+    bool snapVerticesToPixelCenters() const {
+        return SkToBool(fFlags & kSnapVerticesToPixelCenters_Flag); }
 
     /**
      * Enable render state settings.
      *
-     * @param stateBits bitfield of StateBits specifying the states to enable
+     * @param flags bitfield of Flags specifying the states to enable
      */
-    void enableState(uint32_t stateBits) { fFlagBits |= stateBits; }
-
+    void enableState(uint32_t flags) { fFlags |= flags; }
+        
     /**
      * Disable render state settings.
      *
-     * @param stateBits bitfield of StateBits specifying the states to disable
+     * @param flags bitfield of Flags specifying the states to disable
      */
-    void disableState(uint32_t stateBits) { fFlagBits &= ~(stateBits); }
+    void disableState(uint32_t flags) { fFlags &= ~(flags); }
 
     /**
-     * Enable or disable stateBits based on a boolean.
+     * Enable or disable flags based on a boolean.
      *
-     * @param stateBits bitfield of StateBits to enable or disable
+     * @param flags bitfield of Flags to enable or disable
      * @param enable    if true enable stateBits, otherwise disable
      */
-    void setState(uint32_t stateBits, bool enable) {
+    void setState(uint32_t flags, bool enable) {
         if (enable) {
-            this->enableState(stateBits);
+            this->enableState(flags);
         } else {
-            this->disableState(stateBits);
+            this->disableState(flags);
         }
     }
 
@@ -422,7 +422,7 @@
     typedef SkSTArray<4, GrFragmentStage> FragmentStageArray;
 
     SkAutoTUnref<GrRenderTarget>            fRenderTarget;
-    uint32_t                                fFlagBits;
+    uint32_t                                fFlags;
     GrStencilSettings                       fStencilSettings;
     DrawFace                                fDrawFace;
     mutable SkAutoTUnref<const GrXPFactory> fXPFactory;
diff --git a/src/gpu/GrPrimitiveProcessor.h b/src/gpu/GrPrimitiveProcessor.h
index 45668e9..2e25bdb 100644
--- a/src/gpu/GrPrimitiveProcessor.h
+++ b/src/gpu/GrPrimitiveProcessor.h
@@ -66,10 +66,8 @@
     SkAlignedSStorage<kMaxSize> fData;
 };
 
-class GrIndexBufferAllocPool;
-class GrGLCaps;
+class GrGLSLCaps;
 class GrGLPrimitiveProcessor;
-class GrVertexBufferAllocPool;
 
 struct GrInitInvariantOutput;
 
@@ -104,11 +102,6 @@
  */
 class GrPrimitiveProcessor : public GrProcessor {
 public:
-    // TODO let the PrimProc itself set this in its setData call, this should really live on the
-    // bundle of primitive data
-    const SkMatrix& viewMatrix() const { return fViewMatrix; }
-    const SkMatrix& localMatrix() const { return fLocalMatrix; }
-
     virtual void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const = 0;
 
     virtual bool canMakeEqual(const GrBatchTracker& mine,
@@ -163,7 +156,7 @@
      * processor's GL backend implementation.
      */
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
+                                   const GrGLSLCaps& caps,
                                    GrProcessorKeyBuilder* b) const = 0;
 
 
@@ -171,49 +164,16 @@
         for the given GrProcessor; caller is responsible for deleting
         the object. */
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps& caps) const = 0;
+                                                     const GrGLSLCaps& caps) const = 0;
 
     bool isPathRendering() const { return fIsPathRendering; }
 
 protected:
-    GrPrimitiveProcessor(const SkMatrix& viewMatrix, const SkMatrix& localMatrix,
-                         bool isPathRendering)
+    GrPrimitiveProcessor(bool isPathRendering)
         : fNumAttribs(0)
         , fVertexStride(0)
-        , fViewMatrix(viewMatrix)
-        , fLocalMatrix(localMatrix)
         , fIsPathRendering(isPathRendering) {}
 
-    /*
-     * CanCombineOutput will return true if two draws are 'batchable' from a color perspective.
-     * TODO remove this when GPs can upgrade to attribute color
-     */
-    static bool CanCombineOutput(GrGPInput left, GrColor lColor, GrGPInput right, GrColor rColor) {
-        if (left != right) {
-            return false;
-        }
-
-        if (kUniform_GrGPInput == left && lColor != rColor) {
-            return false;
-        }
-
-        return true;
-    }
-
-    static bool CanCombineLocalMatrices(const GrPrimitiveProcessor& left,
-                                        bool leftUsesLocalCoords,
-                                        const GrPrimitiveProcessor& right,
-                                        bool rightUsesLocalCoords) {
-        if (leftUsesLocalCoords != rightUsesLocalCoords) {
-            return false;
-        }
-
-        if (leftUsesLocalCoords && !left.localMatrix().cheapEqualTo(right.localMatrix())) {
-            return false;
-        }
-        return true;
-    }
-
     Attribute fAttribs[kMaxVertexAttribs];
     int fNumAttribs;
     size_t fVertexStride;
@@ -221,8 +181,6 @@
 private:
     virtual bool hasExplicitLocalCoords() const = 0;
 
-    const SkMatrix fViewMatrix;
-    SkMatrix fLocalMatrix;
     bool fIsPathRendering;
 
     typedef GrProcessor INHERITED;
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index 3b8cb67..23f0ed1 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -12,7 +12,7 @@
 #include "GrInvariantOutput.h"
 #include "GrMemoryPool.h"
 #include "GrXferProcessor.h"
-#include "SkMutex.h"
+#include "SkSpinlock.h"
 
 #if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
 
@@ -76,38 +76,18 @@
 
 #endif
 
-namespace GrProcessorUnitTest {
-const SkMatrix& TestMatrix(SkRandom* random) {
-    static SkMatrix gMatrices[5];
-    static bool gOnce;
-    if (!gOnce) {
-        gMatrices[0].reset();
-        gMatrices[1].setTranslate(SkIntToScalar(-100), SkIntToScalar(100));
-        gMatrices[2].setRotate(SkIntToScalar(17));
-        gMatrices[3].setRotate(SkIntToScalar(185));
-        gMatrices[3].postTranslate(SkIntToScalar(66), SkIntToScalar(-33));
-        gMatrices[3].postScale(SkIntToScalar(2), SK_ScalarHalf);
-        gMatrices[4].setRotate(SkIntToScalar(215));
-        gMatrices[4].set(SkMatrix::kMPersp0, 0.00013f);
-        gMatrices[4].set(SkMatrix::kMPersp1, -0.000039f);
-        gOnce = true;
-    }
-    return gMatrices[random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gMatrices)))];
-}
-}
 
-
-// We use a global pool protected by a mutex. Chrome may use the same GrContext on different
-// threads. The GrContext is not used concurrently on different threads and there is a memory
-// barrier between accesses of a context on different threads. Also, there may be multiple
+// We use a global pool protected by a mutex(spinlock). Chrome may use the same GrContext on
+// different threads. The GrContext is not used concurrently on different threads and there is a
+// memory barrier between accesses of a context on different threads. Also, there may be multiple
 // GrContexts and those contexts may be in use concurrently on different threads.
 namespace {
-SK_DECLARE_STATIC_MUTEX(gProcessorPoolMutex);
+SK_DECLARE_STATIC_SPINLOCK(gProcessorSpinlock);
 class MemoryPoolAccessor {
 public:
-    MemoryPoolAccessor() { gProcessorPoolMutex.acquire(); }
+    MemoryPoolAccessor() { gProcessorSpinlock.acquire(); }
 
-    ~MemoryPoolAccessor() { gProcessorPoolMutex.release(); }
+    ~MemoryPoolAccessor() { gProcessorSpinlock.release(); }
 
     GrMemoryPool* pool() const {
         static GrMemoryPool gPool(4096, 4096);
diff --git a/src/gpu/GrProgramDesc.h b/src/gpu/GrProgramDesc.h
index 66e0e06..b306f02 100644
--- a/src/gpu/GrProgramDesc.h
+++ b/src/gpu/GrProgramDesc.h
@@ -12,8 +12,6 @@
 #include "GrTypesPriv.h"
 #include "SkChecksum.h"
 
-class GrGLGpu;
-
 /** This class describes a program to generate. It also serves as a program cache key. Very little
     of this is GL-specific. The GL-specific parts could be factored out into a subclass. */
 class GrProgramDesc {
@@ -58,10 +56,11 @@
         uint8_t                     fFragPosKey;   // set by GrGLShaderBuilder if there are
                                                    // effects that read the fragment position.
                                                    // Otherwise, 0.
-
+        uint8_t                     fSnapVerticesToPixelCenters;
         int8_t                      fColorEffectCnt;
         int8_t                      fCoverageEffectCnt;
     };
+    GR_STATIC_ASSERT(sizeof(KeyHeader) == 4);
 
     int numColorEffects() const {
         return this->header().fColorEffectCnt;
@@ -76,7 +75,7 @@
     // This should really only be used internally, base classes should return their own headers
     const KeyHeader& header() const { return *this->atOffset<KeyHeader, kHeaderOffset>(); }
 
-private:
+protected:
     template<typename T, size_t OFFSET> T* atOffset() {
         return reinterpret_cast<T*>(reinterpret_cast<intptr_t>(fKey.begin()) + OFFSET);
     }
@@ -117,9 +116,11 @@
                         kMaxPreallocProcessors * sizeof(uint32_t) * kIntsPerProcessor,
     };
 
-    SkSTArray<kPreAllocSize, uint8_t, true> fKey;
+    SkSTArray<kPreAllocSize, uint8_t, true>& key() { return fKey; }
+    const SkSTArray<kPreAllocSize, uint8_t, true>& key() const { return fKey; }
 
-    friend class GrGLProgramDescBuilder;
+private:
+    SkSTArray<kPreAllocSize, uint8_t, true> fKey;
 };
 
 #endif
diff --git a/src/gpu/GrRectBatch.cpp b/src/gpu/GrRectBatch.cpp
new file mode 100644
index 0000000..100aafc
--- /dev/null
+++ b/src/gpu/GrRectBatch.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrRectBatch.h"
+
+#include "GrBatch.h"
+#include "GrBatchTarget.h"
+#include "GrBatchTest.h"
+#include "GrDefaultGeoProcFactory.h"
+#include "GrPrimitiveProcessor.h"
+#include "GrTemplates.h"
+
+/** We always use per-vertex colors so that rects can be batched across color changes. Sometimes we
+    have explicit local coords and sometimes not. We *could* always provide explicit local coords
+    and just duplicate the positions when the caller hasn't provided a local coord rect, but we
+    haven't seen a use case which frequently switches between local rect and no local rect draws.
+
+    The color param is used to determine whether the opaque hint can be set on the draw state.
+    The caller must populate the vertex colors itself.
+
+    The vertex attrib order is always pos, color, [local coords].
+ */
+static const GrGeometryProcessor* create_rect_gp(bool hasExplicitLocalCoords,
+                                                 GrColor color,
+                                                 const SkMatrix* localMatrix) {
+    uint32_t flags = GrDefaultGeoProcFactory::kPosition_GPType |
+                     GrDefaultGeoProcFactory::kColor_GPType;
+    flags |= hasExplicitLocalCoords ? GrDefaultGeoProcFactory::kLocalCoord_GPType : 0;
+    if (localMatrix) {
+        return GrDefaultGeoProcFactory::Create(flags, color, SkMatrix::I(), *localMatrix);
+    } else {
+        return GrDefaultGeoProcFactory::Create(flags, color, SkMatrix::I(), SkMatrix::I());
+    }
+}
+
+class RectBatch : public GrBatch {
+public:
+    struct Geometry {
+        SkMatrix fViewMatrix;
+        SkRect fRect;
+        SkRect fLocalRect;
+        SkMatrix fLocalMatrix;
+        GrColor fColor;
+        bool fHasLocalRect;
+        bool fHasLocalMatrix;
+    };
+
+    static GrBatch* Create(const Geometry& geometry) {
+        return SkNEW_ARGS(RectBatch, (geometry));
+    }
+
+    const char* name() const override { return "RectBatch"; }
+
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
+        // When this is called on a batch, there is only one geometry bundle
+        out->setKnownFourComponents(fGeoData[0].fColor);
+    }
+
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
+        out->setKnownSingleComponent(0xff);
+    }
+
+    void initBatchTracker(const GrPipelineInfo& init) override {
+        // Handle any color overrides
+        if (init.fColorIgnored) {
+            fGeoData[0].fColor = GrColor_ILLEGAL;
+        } else if (GrColor_ILLEGAL != init.fOverrideColor) {
+            fGeoData[0].fColor = init.fOverrideColor;
+        }
+
+        // setup batch properties
+        fBatch.fColorIgnored = init.fColorIgnored;
+        fBatch.fColor = fGeoData[0].fColor;
+        fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
+        fBatch.fCoverageIgnored = init.fCoverageIgnored;
+    }
+
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
+        // Go to device coords to allow batching across matrix changes
+        SkMatrix invert = SkMatrix::I();
+
+        // if we have a local rect, then we apply the localMatrix directly to the localRect to
+        // generate vertex local coords
+        bool hasExplicitLocalCoords = this->hasLocalRect();
+        if (!hasExplicitLocalCoords) {
+            if (!this->viewMatrix().isIdentity() && !this->viewMatrix().invert(&invert)) {
+                SkDebugf("Could not invert\n");
+                return;
+            }
+
+            if (this->hasLocalMatrix()) {
+                invert.preConcat(this->localMatrix());
+            }
+        }
+
+        SkAutoTUnref<const GrGeometryProcessor> gp(create_rect_gp(hasExplicitLocalCoords,
+                                                                  this->color(),
+                                                                  &invert));
+
+        batchTarget->initDraw(gp, pipeline);
+
+        // TODO this is hacky, but the only way we have to initialize the GP is to use the
+        // GrPipelineInfo struct so we can generate the correct shader.  Once we have GrBatch
+        // everywhere we can remove this nastiness
+        GrPipelineInfo init;
+        init.fColorIgnored = fBatch.fColorIgnored;
+        init.fOverrideColor = GrColor_ILLEGAL;
+        init.fCoverageIgnored = fBatch.fCoverageIgnored;
+        init.fUsesLocalCoords = this->usesLocalCoords();
+        gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
+
+        int instanceCount = fGeoData.count();
+        size_t vertexStride = gp->getVertexStride();
+        SkASSERT(hasExplicitLocalCoords ?
+                 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr) :
+                 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
+        QuadHelper helper;
+        void* vertices = helper.init(batchTarget, vertexStride, instanceCount);
+
+        if (!vertices) {
+            return;
+        }
+
+
+        for (int i = 0; i < instanceCount; i++) {
+            const Geometry& geom = fGeoData[i];
+
+            intptr_t offset = GrTCast<intptr_t>(vertices) + kVerticesPerQuad * i * vertexStride;
+            SkPoint* positions = GrTCast<SkPoint*>(offset);
+
+            positions->setRectFan(geom.fRect.fLeft, geom.fRect.fTop,
+                                  geom.fRect.fRight, geom.fRect.fBottom, vertexStride);
+            geom.fViewMatrix.mapPointsWithStride(positions, vertexStride, kVerticesPerQuad);
+
+            if (geom.fHasLocalRect) {
+                static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
+                SkPoint* coords = GrTCast<SkPoint*>(offset + kLocalOffset);
+                coords->setRectFan(geom.fLocalRect.fLeft, geom.fLocalRect.fTop,
+                                   geom.fLocalRect.fRight, geom.fLocalRect.fBottom,
+                                   vertexStride);
+                if (geom.fHasLocalMatrix) {
+                    geom.fLocalMatrix.mapPointsWithStride(coords, vertexStride, kVerticesPerQuad);
+                }
+            }
+
+            static const int kColorOffset = sizeof(SkPoint);
+            GrColor* vertColor = GrTCast<GrColor*>(offset + kColorOffset);
+            for (int j = 0; j < 4; ++j) {
+                *vertColor = geom.fColor;
+                vertColor = (GrColor*) ((intptr_t) vertColor + vertexStride);
+            }
+        }
+
+        helper.issueDraw(batchTarget);
+    }
+
+    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+    RectBatch(const Geometry& geometry) {
+        this->initClassID<RectBatch>();
+        fGeoData.push_back(geometry);
+
+        fBounds = geometry.fRect;
+        geometry.fViewMatrix.mapRect(&fBounds);
+    }
+
+    GrColor color() const { return fBatch.fColor; }
+    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+    bool colorIgnored() const { return fBatch.fColorIgnored; }
+    const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
+    const SkMatrix& localMatrix() const { return fGeoData[0].fLocalMatrix; }
+    bool hasLocalRect() const { return fGeoData[0].fHasLocalRect; }
+    bool hasLocalMatrix() const { return fGeoData[0].fHasLocalMatrix; }
+
+    bool onCombineIfPossible(GrBatch* t) override {
+        RectBatch* that = t->cast<RectBatch>();
+
+        if (this->hasLocalRect() != that->hasLocalRect()) {
+            return false;
+        }
+
+        SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
+        if (!this->hasLocalRect() && this->usesLocalCoords()) {
+            if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+                return false;
+            }
+
+            if (this->hasLocalMatrix() != that->hasLocalMatrix()) {
+                return false;
+            }
+
+            if (this->hasLocalMatrix() && !this->localMatrix().cheapEqualTo(that->localMatrix())) {
+                return false;
+            }
+        }
+
+        if (this->color() != that->color()) {
+            fBatch.fColor = GrColor_ILLEGAL;
+        }
+        fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
+        return true;
+    }
+
+    struct BatchTracker {
+        GrColor fColor;
+        bool fUsesLocalCoords;
+        bool fColorIgnored;
+        bool fCoverageIgnored;
+    };
+
+    BatchTracker fBatch;
+    SkSTArray<1, Geometry, true> fGeoData;
+};
+
+namespace GrRectBatch {
+
+GrBatch* Create(GrColor color,
+                const SkMatrix& viewMatrix,
+                const SkRect& rect,
+                const SkRect* localRect,
+                const SkMatrix* localMatrix) {
+    RectBatch::Geometry geometry;
+    geometry.fColor = color;
+    geometry.fViewMatrix = viewMatrix;
+    geometry.fRect = rect;
+
+    if (localRect) {
+        geometry.fHasLocalRect = true;
+        geometry.fLocalRect = *localRect;
+    } else {
+        geometry.fHasLocalRect = false;
+    }
+
+    if (localMatrix) {
+        geometry.fHasLocalMatrix = true;
+        geometry.fLocalMatrix = *localMatrix;
+    } else {
+        geometry.fHasLocalMatrix = false;
+    }
+
+    return RectBatch::Create(geometry);
+}
+
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef GR_TEST_UTILS
+
+BATCH_TEST_DEFINE(RectBatch) {
+    GrColor color = GrRandomColor(random);
+
+    SkRect rect = GrTest::TestRect(random);
+    SkRect localRect;
+    bool hasLocalRect = random->nextBool();
+    bool hasLocalMatrix = random->nextBool();
+
+    SkMatrix viewMatrix;
+    SkMatrix localMatrix;
+    if (hasLocalRect) {
+        viewMatrix = GrTest::TestMatrixInvertible(random);
+        localRect = GrTest::TestRect(random);
+    } else {
+        viewMatrix = GrTest::TestMatrix(random);
+    }
+
+    if (hasLocalMatrix) {
+        localMatrix = GrTest::TestMatrix(random);
+    }
+
+    return GrRectBatch::Create(color, viewMatrix, rect,
+                               hasLocalRect ? &localRect : NULL,
+                               hasLocalMatrix ? &localMatrix : NULL);
+}
+
+#endif
diff --git a/src/gpu/GrRectBatch.h b/src/gpu/GrRectBatch.h
new file mode 100644
index 0000000..5844cc3
--- /dev/null
+++ b/src/gpu/GrRectBatch.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrRectBatch_DEFINED
+#define GrRectBatch_DEFINED
+
+#include "GrColor.h"
+
+class GrBatch;
+class SkMatrix;
+struct SkRect;
+
+/*
+ * A factory for returning batches which can draw rectangles.  Right now this only handles non-AA
+ * rects
+ */
+namespace GrRectBatch {
+
+GrBatch* Create(GrColor color,
+                const SkMatrix& viewMatrix,
+                const SkRect& rect,
+                const SkRect* localRect,
+                const SkMatrix* localMatrix);
+
+};
+
+#endif
diff --git a/src/gpu/GrRectanizer_pow2.h b/src/gpu/GrRectanizer_pow2.h
index 59ef864..451589d 100644
--- a/src/gpu/GrRectanizer_pow2.h
+++ b/src/gpu/GrRectanizer_pow2.h
@@ -24,15 +24,15 @@
 
     virtual ~GrRectanizerPow2() { }
 
-    void reset() SK_OVERRIDE {
+    void reset() override {
         fNextStripY = 0;
         fAreaSoFar = 0;
         sk_bzero(fRows, sizeof(fRows));
     }
 
-    bool addRect(int w, int h, SkIPoint16* loc) SK_OVERRIDE;
+    bool addRect(int w, int h, SkIPoint16* loc) override;
 
-    float percentFull() const SK_OVERRIDE {
+    float percentFull() const override {
         return fAreaSoFar / ((float)this->width() * this->height());
     }
 
diff --git a/src/gpu/GrRectanizer_skyline.h b/src/gpu/GrRectanizer_skyline.h
index c9f9255..a06bba0 100644
--- a/src/gpu/GrRectanizer_skyline.h
+++ b/src/gpu/GrRectanizer_skyline.h
@@ -21,7 +21,7 @@
 
     virtual ~GrRectanizerSkyline() { }
 
-    void reset() SK_OVERRIDE{
+    void reset() override{
         fAreaSoFar = 0;
         fSkyline.reset();
         SkylineSegment* seg = fSkyline.append(1);
@@ -30,9 +30,9 @@
         seg->fWidth = this->width();
     }
 
-    bool addRect(int w, int h, SkIPoint16* loc) SK_OVERRIDE;
+    bool addRect(int w, int h, SkIPoint16* loc) override;
 
-    float percentFull() const SK_OVERRIDE {
+    float percentFull() const override {
         return fAreaSoFar / ((float)this->width() * this->height());
     }
 
diff --git a/src/gpu/GrRenderTarget.cpp b/src/gpu/GrRenderTarget.cpp
index 042e19f..d539c90 100644
--- a/src/gpu/GrRenderTarget.cpp
+++ b/src/gpu/GrRenderTarget.cpp
@@ -12,7 +12,7 @@
 #include "GrContext.h"
 #include "GrGpu.h"
 #include "GrRenderTargetPriv.h"
-#include "GrStencilBuffer.h"
+#include "GrStencilAttachment.h"
 
 void GrRenderTarget::discard() {
     // go through context so that all necessary flushing occurs
@@ -48,29 +48,29 @@
 }
 
 void GrRenderTarget::onRelease() {
-    this->renderTargetPriv().didAttachStencilBuffer(NULL);
+    this->renderTargetPriv().didAttachStencilAttachment(NULL);
 
     INHERITED::onRelease();
 }
 
 void GrRenderTarget::onAbandon() {
-    this->renderTargetPriv().didAttachStencilBuffer(NULL);
+    this->renderTargetPriv().didAttachStencilAttachment(NULL);
 
     INHERITED::onAbandon();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void GrRenderTargetPriv::didAttachStencilBuffer(GrStencilBuffer* stencilBuffer) {
-    SkRefCnt_SafeAssign(fRenderTarget->fStencilBuffer, stencilBuffer);
+void GrRenderTargetPriv::didAttachStencilAttachment(GrStencilAttachment* stencilAttachment) {
+    SkRefCnt_SafeAssign(fRenderTarget->fStencilAttachment, stencilAttachment);
 }
 
-GrStencilBuffer* GrRenderTargetPriv::attachStencilBuffer() const {
-    if (fRenderTarget->fStencilBuffer) {
-        return fRenderTarget->fStencilBuffer;
+GrStencilAttachment* GrRenderTargetPriv::attachStencilAttachment() const {
+    if (fRenderTarget->fStencilAttachment) {
+        return fRenderTarget->fStencilAttachment;
     }
     if (!fRenderTarget->wasDestroyed() && fRenderTarget->canAttemptStencilAttachment()) {
-        fRenderTarget->getGpu()->attachStencilBufferToRenderTarget(fRenderTarget);
+        fRenderTarget->getGpu()->attachStencilAttachmentToRenderTarget(fRenderTarget);
     }
-    return fRenderTarget->fStencilBuffer;
+    return fRenderTarget->fStencilAttachment;
 }
diff --git a/src/gpu/GrRenderTargetPriv.h b/src/gpu/GrRenderTargetPriv.h
index 92ee30e..59262f4 100644
--- a/src/gpu/GrRenderTargetPriv.h
+++ b/src/gpu/GrRenderTargetPriv.h
@@ -16,17 +16,17 @@
 class GrRenderTargetPriv {
 public:
     /**
-     * GrStencilBuffer is not part of the public API.
+     * GrStencilAttachment is not part of the public API.
      */
-    GrStencilBuffer* getStencilBuffer() const { return fRenderTarget->fStencilBuffer; }
+    GrStencilAttachment* getStencilAttachment() const { return fRenderTarget->fStencilAttachment; }
 
     /**
      * If this render target already has a stencil buffer, return it. Otherwise attempt to attach
      * one.
      */
-    GrStencilBuffer* attachStencilBuffer() const;
+    GrStencilAttachment* attachStencilAttachment() const;
 
-    void didAttachStencilBuffer(GrStencilBuffer*);
+    void didAttachStencilAttachment(GrStencilAttachment*);
 
 private:
     explicit GrRenderTargetPriv(GrRenderTarget* renderTarget) : fRenderTarget(renderTarget) {}
diff --git a/src/gpu/GrReorderCommandBuilder.cpp b/src/gpu/GrReorderCommandBuilder.cpp
new file mode 100644
index 0000000..3ba95d7
--- /dev/null
+++ b/src/gpu/GrReorderCommandBuilder.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrReorderCommandBuilder.h"
+
+static bool intersect(const SkRect& a, const SkRect& b) {
+    SkASSERT(a.fLeft <= a.fRight && a.fTop <= a.fBottom &&
+             b.fLeft <= b.fRight && b.fTop <= b.fBottom);
+    return a.fLeft < b.fRight && b.fLeft < a.fRight &&
+           a.fTop < b.fBottom && b.fTop < a.fBottom;
+}
+
+GrTargetCommands::Cmd* GrReorderCommandBuilder::recordDrawBatch(State* state, GrBatch* batch) {
+    // Check if there is a Batch Draw we can batch with by linearly searching back until we either
+    // 1) check every draw
+    // 2) intersect with something
+    // 3) find a 'blocker'
+    if (!this->cmdBuffer()->empty()) {
+        GrTargetCommands::CmdBuffer::ReverseIter reverseIter(*this->cmdBuffer());
+
+        do {
+            if (Cmd::kDrawBatch_CmdType == reverseIter->type()) {
+                DrawBatch* previous = static_cast<DrawBatch*>(reverseIter.get());
+
+                if (previous->fState->getPipeline()->isEqual(*state->getPipeline()) &&
+                    previous->fBatch->combineIfPossible(batch)) {
+                    return NULL;
+                }
+
+                if (intersect(previous->fBatch->bounds(), batch->bounds())) {
+                    break;
+                }
+            } else {
+                // TODO temporary until we can navigate the other types of commands
+                break;
+            }
+        } while (reverseIter.previous());
+    }
+
+    return GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(), DrawBatch, (state, batch,
+                                                                    this->batchTarget()));
+}
diff --git a/src/gpu/GrReorderCommandBuilder.h b/src/gpu/GrReorderCommandBuilder.h
new file mode 100644
index 0000000..99a2c11
--- /dev/null
+++ b/src/gpu/GrReorderCommandBuilder.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrReorderCommandBuilder_DEFINED
+#define GrReorderCommandBuilder_DEFINED
+
+#include "GrCommandBuilder.h"
+
+class GrReorderCommandBuilder : public GrCommandBuilder {
+public:
+    typedef GrCommandBuilder::Cmd Cmd;
+    typedef GrCommandBuilder::State State;
+
+    GrReorderCommandBuilder(GrGpu* gpu) : INHERITED(gpu) {}
+
+    Cmd* recordDrawBatch(State*, GrBatch*) override;
+    Cmd* recordStencilPath(const GrPipelineBuilder&,
+                           const GrPathProcessor*,
+                           const GrPath*,
+                           const GrScissorState&,
+                           const GrStencilSettings&) override {
+        SkFAIL("Unsupported\n");
+        return NULL;
+    }
+
+    Cmd* recordDrawPath(State*,
+                        const GrPathProcessor*,
+                        const GrPath*,
+                        const GrStencilSettings&) override {
+        SkFAIL("Unsupported\n");
+        return NULL;
+    }
+
+    Cmd* recordDrawPaths(State*,
+                         GrInOrderDrawBuffer*,
+                         const GrPathProcessor*,
+                         const GrPathRange*,
+                         const void*,
+                         GrDrawTarget::PathIndexType,
+                         const float transformValues[],
+                         GrDrawTarget::PathTransformType ,
+                         int,
+                         const GrStencilSettings&,
+                         const GrDrawTarget::PipelineInfo&) override {
+        SkFAIL("Unsupported\n");
+        return NULL;
+    }
+
+private:
+    typedef GrCommandBuilder INHERITED;
+
+};
+
+#endif
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index f8b1a12..ae9c68f 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -40,6 +40,7 @@
 
     return static_cast<Domain>(domain);
 }
+
 uint32_t GrResourceKeyHash(const uint32_t* data, size_t size) {
     return SkChecksum::Compute(data, size);
 }
@@ -56,13 +57,12 @@
 
  //////////////////////////////////////////////////////////////////////////////
 
-static const int kDefaultMaxCount = 2 * (1 << 12);
-static const size_t kDefaultMaxSize = 96 * (1 << 20);
 
 GrResourceCache::GrResourceCache()
     : fTimestamp(0)
     , fMaxCount(kDefaultMaxCount)
     , fMaxBytes(kDefaultMaxSize)
+    , fMaxUnusedFlushes(kDefaultMaxUnusedFlushes)
 #if GR_CACHE_STATS
     , fHighWaterCount(0)
     , fHighWaterBytes(0)
@@ -73,20 +73,49 @@
     , fBudgetedCount(0)
     , fBudgetedBytes(0)
     , fOverBudgetCB(NULL)
-    , fOverBudgetData(NULL) {
+    , fOverBudgetData(NULL)
+    , fFlushTimestamps(NULL)
+    , fLastFlushTimestampIndex(0){
     SkDEBUGCODE(fCount = 0;)
+    SkDEBUGCODE(fNewlyPurgeableResourceForValidation = NULL;)
+    this->resetFlushTimestamps();
 }
 
 GrResourceCache::~GrResourceCache() {
     this->releaseAll();
+    SkDELETE_ARRAY(fFlushTimestamps);
 }
 
-void GrResourceCache::setLimits(int count, size_t bytes) {
+void GrResourceCache::setLimits(int count, size_t bytes, int maxUnusedFlushes) {
     fMaxCount = count;
     fMaxBytes = bytes;
+    fMaxUnusedFlushes = maxUnusedFlushes;
+    this->resetFlushTimestamps();
     this->purgeAsNeeded();
 }
 
+void GrResourceCache::resetFlushTimestamps() {
+    SkDELETE_ARRAY(fFlushTimestamps);
+
+    // We assume this number is a power of two when wrapping indices into the timestamp array.
+    fMaxUnusedFlushes = SkNextPow2(fMaxUnusedFlushes);
+
+    // Since our implementation is to store the timestamps of the last fMaxUnusedFlushes flush calls
+    // we just turn the feature off if that array would be large.
+    static const int kMaxSupportedTimestampHistory = 128;
+
+    if (fMaxUnusedFlushes > kMaxSupportedTimestampHistory) {
+        fFlushTimestamps = NULL;
+        return;
+    }
+
+    fFlushTimestamps = SkNEW_ARRAY(uint32_t, fMaxUnusedFlushes);
+    fLastFlushTimestampIndex = 0;
+    // Set all the historical flush timestamps to initially be at the beginning of time (timestamp
+    // 0).
+    sk_bzero(fFlushTimestamps, fMaxUnusedFlushes * sizeof(uint32_t));
+}
+
 void GrResourceCache::insertResource(GrGpuResource* resource) {
     SkASSERT(resource);
     SkASSERT(!this->isInCache(resource));
@@ -247,8 +276,8 @@
 }
 
 void GrResourceCache::removeUniqueKey(GrGpuResource* resource) {
-    // Someone has a ref to this resource in order to invalidate it. When the ref count reaches
-    // zero we will get a notifyPurgable() and figure out what to do with it.
+    // Someone has a ref to this resource in order to have removed the key. When the ref count
+    // reaches zero we will get a ref cnt notification and figure out what to do with it.
     if (resource->getUniqueKey().isValid()) {
         SkASSERT(resource == fUniqueHash.find(resource->getUniqueKey()));
         fUniqueHash.remove(resource->getUniqueKey());
@@ -307,11 +336,34 @@
     this->validate();
 }
 
-void GrResourceCache::notifyPurgeable(GrGpuResource* resource) {
+void GrResourceCache::notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
     SkASSERT(resource);
+    SkASSERT(!resource->wasDestroyed());
+    SkASSERT(flags);
     SkASSERT(this->isInCache(resource));
-    SkASSERT(resource->isPurgeable());
+    // This resource should always be in the nonpurgeable array when this function is called. It
+    // will be moved to the queue if it is newly purgeable.
+    SkASSERT(fNonpurgeableResources[*resource->cacheAccess().accessCacheIndex()] == resource);
 
+    if (SkToBool(ResourceAccess::kRefCntReachedZero_RefNotificationFlag & flags)) {
+#ifdef SK_DEBUG
+        // When the timestamp overflows validate() is called. validate() checks that resources in
+        // the nonpurgeable array are indeed not purgeable. However, the movement from the array to
+        // the purgeable queue happens just below in this function. So we mark it as an exception.
+        if (resource->isPurgeable()) {
+            fNewlyPurgeableResourceForValidation = resource;
+        }
+#endif
+        resource->cacheAccess().setTimestamp(this->getNextTimestamp());
+        SkDEBUGCODE(fNewlyPurgeableResourceForValidation = NULL);
+    }
+
+    if (!SkToBool(ResourceAccess::kAllCntsReachedZero_RefNotificationFlag & flags)) {
+        SkASSERT(!resource->isPurgeable());
+        return;
+    }
+
+    SkASSERT(resource->isPurgeable());
     this->removeFromNonpurgeableArray(resource);
     fPurgeableQueue.insert(resource);
 
@@ -391,25 +443,43 @@
     this->validate();
 }
 
-void GrResourceCache::internalPurgeAsNeeded() {
-    SkASSERT(this->overBudget());
+void GrResourceCache::purgeAsNeeded() {
+    SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
+    fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
+    if (invalidKeyMsgs.count()) {
+        this->processInvalidUniqueKeys(invalidKeyMsgs);
+    }
 
-    bool stillOverbudget = true;
-    while (fPurgeableQueue.count()) {
+    if (fFlushTimestamps) {
+        // Assuming kNumFlushesToDeleteUnusedResource is a power of 2.
+        SkASSERT(SkIsPow2(fMaxUnusedFlushes));
+        int oldestFlushIndex = (fLastFlushTimestampIndex + 1) & (fMaxUnusedFlushes - 1);
+
+        uint32_t oldestAllowedTimestamp = fFlushTimestamps[oldestFlushIndex];
+        while (fPurgeableQueue.count()) {
+            uint32_t oldestResourceTimestamp = fPurgeableQueue.peek()->cacheAccess().timestamp();
+            if (oldestAllowedTimestamp < oldestResourceTimestamp) {
+                break;
+            }
+            GrGpuResource* resource = fPurgeableQueue.peek();
+            SkASSERT(resource->isPurgeable());
+            resource->cacheAccess().release();
+        }
+    }
+
+    bool stillOverbudget = this->overBudget();
+    while (stillOverbudget && fPurgeableQueue.count()) {
         GrGpuResource* resource = fPurgeableQueue.peek();
         SkASSERT(resource->isPurgeable());
         resource->cacheAccess().release();
-        if (!this->overBudget()) {
-            stillOverbudget = false;
-            break;
-        }
+        stillOverbudget = this->overBudget();
     }
 
     this->validate();
 
     if (stillOverbudget) {
         // Despite the purge we're still over budget. Call our over budget callback. If this frees
-        // any resources then we'll get notifyPurgeable() calls and take appropriate action.
+        // any resources then we'll get notified and take appropriate action.
         (*fOverBudgetCB)(fOverBudgetData);
         this->validate();
     }
@@ -433,7 +503,7 @@
         GrGpuResource* resource = this->findAndRefUniqueResource(msgs[i].key());
         if (resource) {
             resource->resourcePriv().removeUniqueKey();
-            resource->unref(); // will call notifyPurgeable, if it is indeed now purgeable.
+            resource->unref(); // If this resource is now purgeable, the cache will be notified.
         }
     }
 }
@@ -518,11 +588,26 @@
 
             // count should be the next timestamp we return.
             SkASSERT(fTimestamp == SkToU32(count));
+            
+            // The historical timestamps of flushes are now invalid.
+            this->resetFlushTimestamps();
         }        
     }
     return fTimestamp++;
 }
 
+void GrResourceCache::notifyFlushOccurred() {
+    if (fFlushTimestamps) {
+        SkASSERT(SkIsPow2(fMaxUnusedFlushes));
+        fLastFlushTimestampIndex = (fLastFlushTimestampIndex + 1) & (fMaxUnusedFlushes - 1);
+        // get the timestamp before accessing fFlushTimestamps because getNextTimestamp will
+        // reallocate fFlushTimestamps on timestamp overflow.
+        uint32_t timestamp = this->getNextTimestamp();
+        fFlushTimestamps[fLastFlushTimestampIndex] = timestamp;
+        this->purgeAsNeeded();
+    }
+}
+
 #ifdef SK_DEBUG
 void GrResourceCache::validate() const {
     // Reduce the frequency of validations for large resource counts.
@@ -586,7 +671,8 @@
     Stats stats(this);
 
     for (int i = 0; i < fNonpurgeableResources.count(); ++i) {
-        SkASSERT(!fNonpurgeableResources[i]->isPurgeable());
+        SkASSERT(!fNonpurgeableResources[i]->isPurgeable() ||
+                 fNewlyPurgeableResourceForValidation == fNonpurgeableResources[i]);
         SkASSERT(*fNonpurgeableResources[i]->cacheAccess().accessCacheIndex() == i);
         SkASSERT(!fNonpurgeableResources[i]->wasDestroyed());
         stats.update(fNonpurgeableResources[i]);
@@ -615,7 +701,7 @@
     SkASSERT(stats.fContent == fUniqueHash.count());
     SkASSERT(stats.fScratch + stats.fCouldBeScratch == fScratchMap.count());
 
-    // This assertion is not currently valid because we can be in recursive notifyIsPurgeable()
+    // This assertion is not currently valid because we can be in recursive notifyCntReachedZero()
     // calls. This will be fixed when subresource registration is explicit.
     // bool overBudget = budgetedBytes > fMaxBytes || budgetedCount > fMaxCount;
     // SkASSERT(!overBudget || locked == count || fPurging);
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index 8331bf5..5483e19 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -38,20 +38,39 @@
  * A unique key always takes precedence over a scratch key when a resource has both types of keys.
  * If a resource has neither key type then it will be deleted as soon as the last reference to it
  * is dropped.
+ *
+ * When proactive purging is enabled, on every flush, the timestamp of that flush is stored in a
+ * n-sized ring buffer. When purging occurs each purgeable resource's timestamp is compared to the
+ * timestamp of the n-th prior flush. If the resource's last use timestamp is older than the old
+ * flush then the resource is proactively purged even when the cache is under budget. By default
+ * this feature is disabled, though it can be enabled by calling GrResourceCache::setLimits.
  */
 class GrResourceCache {
 public:
     GrResourceCache();
     ~GrResourceCache();
 
+    // Default maximum number of budgeted resources in the cache.
+    static const int    kDefaultMaxCount            = 2 * (1 << 12);
+    // Default maximum number of bytes of gpu memory of budgeted resources in the cache.
+    static const size_t kDefaultMaxSize             = 96 * (1 << 20);
+    // Default number of flushes a budgeted resources can go unused in the cache before it is
+    // purged. Large values disable the feature (as the ring buffer of flush timestamps would be
+    // large). This is currently the default until we decide to enable this feature
+    // of the cache by default.
+    static const int    kDefaultMaxUnusedFlushes    = 1024;
+
     /** Used to access functionality needed by GrGpuResource for lifetime management. */
     class ResourceAccess;
     ResourceAccess resourceAccess();
 
     /**
-     * Sets the cache limits in terms of number of resources and max gpu memory byte size.
+     * Sets the cache limits in terms of number of resources, max gpu memory byte size, and number
+     * of GrContext flushes that a resource can be unused before it is evicted. The latter value is
+     * a suggestion and there is no promise that a resource will be purged immediately after it
+     * hasn't been used in maxUnusedFlushes flushes.
      */
-    void setLimits(int count, size_t bytes);
+    void setLimits(int count, size_t bytes, int maxUnusedFlushes = kDefaultMaxUnusedFlushes);
 
     /**
      * Returns the number of resources.
@@ -136,17 +155,7 @@
 
     /** Purges resources to become under budget and processes resources with invalidated unique
         keys. */
-    void purgeAsNeeded() {
-        SkTArray<GrUniqueKeyInvalidatedMessage> invalidKeyMsgs;
-        fInvalidUniqueKeyInbox.poll(&invalidKeyMsgs);
-        if (invalidKeyMsgs.count()) {
-            this->processInvalidUniqueKeys(invalidKeyMsgs);
-        }
-        if (fBudgetedCount <= fMaxCount && fBudgetedBytes <= fMaxBytes) {
-            return;
-        }
-        this->internalPurgeAsNeeded();
-    }
+    void purgeAsNeeded();
 
     /** Purges all resources that don't have external owners. */
     void purgeAllUnlocked();
@@ -166,6 +175,8 @@
         fOverBudgetCB = overBudgetCB;
         fOverBudgetData = data;
     }
+    
+    void notifyFlushOccurred();
 
 #if GR_GPU_STATS
     void dumpStats(SkString*) const;
@@ -180,7 +191,7 @@
     ////
     void insertResource(GrGpuResource*);
     void removeResource(GrGpuResource*);
-    void notifyPurgeable(GrGpuResource*);
+    void notifyCntReachedZero(GrGpuResource*, uint32_t flags);
     void didChangeGpuMemorySize(const GrGpuResource*, size_t oldSize);
     void changeUniqueKey(GrGpuResource*, const GrUniqueKey&);
     void removeUniqueKey(GrGpuResource*);
@@ -189,7 +200,7 @@
     void refAndMakeResourceMRU(GrGpuResource*);
     /// @}
 
-    void internalPurgeAsNeeded();
+    void resetFlushTimestamps();
     void processInvalidUniqueKeys(const SkTArray<GrUniqueKeyInvalidatedMessage>&);
     void addToNonpurgeableArray(GrGpuResource*);
     void removeFromNonpurgeableArray(GrGpuResource*);
@@ -251,6 +262,7 @@
     // our budget, used in purgeAsNeeded()
     int                                 fMaxCount;
     size_t                              fMaxBytes;
+    int                                 fMaxUnusedFlushes;
 
 #if GR_CACHE_STATS
     int                                 fHighWaterCount;
@@ -270,7 +282,16 @@
     PFOverBudgetCB                      fOverBudgetCB;
     void*                               fOverBudgetData;
 
+    // We keep track of the "timestamps" of the last n flushes. If a resource hasn't been used in
+    // that time then we well preemptively purge it to reduce memory usage.
+    uint32_t*                           fFlushTimestamps;
+    int                                 fLastFlushTimestampIndex;
+
     InvalidUniqueKeyInbox               fInvalidUniqueKeyInbox;
+
+    // This resource is allowed to be in the nonpurgeable array for the sake of validate() because
+    // we're in the midst of converting it to purgeable status.
+    SkDEBUGCODE(GrGpuResource*          fNewlyPurgeableResourceForValidation;)
 };
 
 class GrResourceCache::ResourceAccess {
@@ -290,9 +311,26 @@
     void removeResource(GrGpuResource* resource) { fCache->removeResource(resource); }
 
     /**
-     * Called by GrGpuResources when they detects that they are newly purgeable.
+     * Notifications that should be sent to the cache when the ref/io cnt status of resources
+     * changes.
      */
-    void notifyPurgeable(GrGpuResource* resource) { fCache->notifyPurgeable(resource); }
+    enum RefNotificationFlags {
+        /** All types of refs on the resource have reached zero. */
+        kAllCntsReachedZero_RefNotificationFlag = 0x1,
+        /** The normal (not pending IO type) ref cnt has reached zero. */
+        kRefCntReachedZero_RefNotificationFlag  = 0x2,
+    };
+    /**
+     * Called by GrGpuResources when they detect that their ref/io cnts have reached zero. When the
+     * normal ref cnt reaches zero the flags that are set should be:
+     *     a) kRefCntReachedZero if a pending IO cnt is still non-zero.
+     *     b) (kRefCntReachedZero | kAllCntsReachedZero) when all pending IO cnts are also zero.
+     * kAllCntsReachedZero is set by itself if a pending IO cnt is decremented to zero and all the
+     * the other cnts are already zero.
+     */
+    void notifyCntReachedZero(GrGpuResource* resource, uint32_t flags) {
+        fCache->notifyCntReachedZero(resource, flags);
+    }
 
     /**
      * Called by GrGpuResources when their sizes change.
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
new file mode 100644
index 0000000..8fc3272
--- /dev/null
+++ b/src/gpu/GrResourceProvider.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrResourceProvider.h"
+
+#include "GrGpu.h"
+#include "GrResourceCache.h"
+#include "GrResourceKey.h"
+#include "GrVertexBuffer.h"
+
+GR_DECLARE_STATIC_UNIQUE_KEY(gQuadIndexBufferKey);
+
+GrResourceProvider::GrResourceProvider(GrGpu* gpu, GrResourceCache* cache) : INHERITED(gpu, cache) {
+    GR_DEFINE_STATIC_UNIQUE_KEY(gQuadIndexBufferKey);
+    fQuadIndexBufferKey = gQuadIndexBufferKey;
+}
+
+const GrIndexBuffer* GrResourceProvider::createInstancedIndexBuffer(const uint16_t* pattern,
+                                                                    int patternSize,
+                                                                    int reps,
+                                                                    int vertCount,
+                                                                    const GrUniqueKey& key) {
+    size_t bufferSize = patternSize * reps * sizeof(uint16_t);
+
+    GrIndexBuffer* buffer = this->gpu()->createIndexBuffer(bufferSize, /* dynamic = */ false);
+    if (!buffer) {
+        return NULL;
+    }
+    uint16_t* data = (uint16_t*) buffer->map();
+    bool useTempData = (NULL == data);
+    if (useTempData) {
+        data = SkNEW_ARRAY(uint16_t, reps * patternSize);
+    }
+    for (int i = 0; i < reps; ++i) {
+        int baseIdx = i * patternSize;
+        uint16_t baseVert = (uint16_t)(i * vertCount);
+        for (int j = 0; j < patternSize; ++j) {
+            data[baseIdx+j] = baseVert + pattern[j];
+        }
+    }
+    if (useTempData) {
+        if (!buffer->updateData(data, bufferSize)) {
+            buffer->unref();
+            return NULL;
+        }
+        SkDELETE_ARRAY(data);
+    } else {
+        buffer->unmap();
+    }
+    this->assignUniqueKeyToResource(key, buffer);
+    return buffer;
+}
+
+const GrIndexBuffer* GrResourceProvider::createQuadIndexBuffer() {
+    static const int kMaxQuads = 1 << 12; // max possible: (1 << 14) - 1;
+    GR_STATIC_ASSERT(4 * kMaxQuads <= 65535);
+    static const uint16_t kPattern[] = { 0, 1, 2, 0, 2, 3 };
+
+    return this->createInstancedIndexBuffer(kPattern, 6, kMaxQuads, 4, fQuadIndexBufferKey);
+}
+
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
new file mode 100644
index 0000000..0d80cdd
--- /dev/null
+++ b/src/gpu/GrResourceProvider.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrResourceProvider_DEFINED
+#define GrResourceProvider_DEFINED
+
+#include "GrTextureProvider.h"
+
+class GrIndexBuffer;
+class GrVertexBuffer;
+
+/**
+ * An extension of the texture provider for arbitrary resource types. This class is intended for
+ * use within the Gr code base, not by clients or extensions (e.g. third party GrProcessor
+ * derivatives).
+ */
+class GrResourceProvider : public GrTextureProvider {
+public:
+
+    GrResourceProvider(GrGpu* gpu, GrResourceCache* cache);
+
+    template <typename T> T* findAndRefTByUniqueKey(const GrUniqueKey& key) {
+        return static_cast<T*>(this->findAndRefResourceByUniqueKey(key));
+    }
+
+    /**
+     * Either finds and refs, or creates an index buffer for instanced drawing with a specific
+     * pattern if the index buffer is not found. If the return is non-null, the caller owns
+     * a ref on the returned GrIndexBuffer.
+     *
+     * @param pattern     the pattern of indices to repeat
+     * @param patternSize size in bytes of the pattern
+     * @param reps        number of times to repeat the pattern
+     * @param vertCount   number of vertices the pattern references
+     * @param key         Key to be assigned to the index buffer.
+     *
+     * @return The index buffer if successful, otherwise NULL.
+     */
+    const GrIndexBuffer* refOrCreateInstancedIndexBuffer(const uint16_t* pattern,
+                                                         int patternSize,
+                                                         int reps,
+                                                         int vertCount,
+                                                         const GrUniqueKey& key) {
+        if (GrIndexBuffer* buffer = this->findAndRefTByUniqueKey<GrIndexBuffer>(key)) {
+            return buffer;
+        }
+        return this->createInstancedIndexBuffer(pattern, patternSize, reps, vertCount, key);
+    }
+
+    /**
+     * Returns an index buffer that can be used to render quads.
+     * Six indices per quad: 0, 1, 2, 0, 2, 3, etc.
+     * The max number of quads can be queried using GrIndexBuffer::maxQuads().
+     * Draw with kTriangles_GrPrimitiveType
+     * @ return the quad index buffer
+     */
+    const GrIndexBuffer* refQuadIndexBuffer() {
+        if (GrIndexBuffer* buffer =
+            this->findAndRefTByUniqueKey<GrIndexBuffer>(fQuadIndexBufferKey)) {
+            return buffer;
+        }
+        return this->createQuadIndexBuffer();
+    }
+
+
+    using GrTextureProvider::assignUniqueKeyToResource;
+    using GrTextureProvider::findAndRefResourceByUniqueKey;
+    using GrTextureProvider::abandon;
+
+private:
+    const GrIndexBuffer* createInstancedIndexBuffer(const uint16_t* pattern,
+                                                    int patternSize,
+                                                    int reps,
+                                                    int vertCount,
+                                                    const GrUniqueKey& key);
+
+    const GrIndexBuffer* createQuadIndexBuffer();
+
+    GrUniqueKey fQuadIndexBufferKey;
+
+    typedef GrTextureProvider INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index ff52912..510b85a 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -248,7 +248,8 @@
         SkASSERT(fContext->getGpu()->caps()->isConfigTexturable(desc.fConfig));
     }
 
-    return fContext->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch);
+    return fContext->textureProvider()->refScratchTexture(
+        desc, GrTextureProvider::kApprox_ScratchTexMatch);
 }
 
 void GrSWMaskHelper::sendTextureData(GrTexture *texture, const GrSurfaceDesc& desc,
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index 466ad81..34a1af1 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -9,18 +9,21 @@
 #include "GrSoftwarePathRenderer.h"
 #include "GrContext.h"
 #include "GrSWMaskHelper.h"
+#include "GrVertexBuffer.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 bool GrSoftwarePathRenderer::canDrawPath(const GrDrawTarget*,
                                          const GrPipelineBuilder*,
                                          const SkMatrix& viewMatrix,
                                          const SkPath&,
-                                         const SkStrokeRec&,
+                                         const GrStrokeInfo& stroke,
                                          bool antiAlias) const {
     if (NULL == fContext) {
         return false;
     }
-
+    if (stroke.isDashed()) {
+        return false;
+    }
     return true;
 }
 
@@ -28,7 +31,7 @@
 GrSoftwarePathRenderer::onGetStencilSupport(const GrDrawTarget*,
                                             const GrPipelineBuilder*,
                                             const SkPath&,
-                                            const SkStrokeRec&) const {
+                                            const GrStrokeInfo&) const {
     return GrPathRenderer::kNoSupport_StencilSupport;
 }
 
@@ -119,9 +122,8 @@
                                         GrColor color,
                                         const SkMatrix& viewMatrix,
                                         const SkPath& path,
-                                        const SkStrokeRec& stroke,
+                                        const GrStrokeInfo& stroke,
                                         bool antiAlias) {
-
     if (NULL == fContext) {
         return false;
     }
@@ -137,7 +139,7 @@
     }
 
     SkAutoTUnref<GrTexture> texture(
-            GrSWMaskHelper::DrawPathMaskToTexture(fContext, path, stroke,
+            GrSWMaskHelper::DrawPathMaskToTexture(fContext, path, stroke.getStrokeRec(),
                                                   devPathBounds,
                                                   antiAlias, &viewMatrix));
     if (NULL == texture) {
diff --git a/src/gpu/GrSoftwarePathRenderer.h b/src/gpu/GrSoftwarePathRenderer.h
index ed897cc..6e2055a 100644
--- a/src/gpu/GrSoftwarePathRenderer.h
+++ b/src/gpu/GrSoftwarePathRenderer.h
@@ -27,21 +27,21 @@
                              const GrPipelineBuilder*,
                              const SkMatrix& viewMatrix,
                              const SkPath&,
-                             const SkStrokeRec&,
-                             bool antiAlias) const SK_OVERRIDE;
+                             const GrStrokeInfo&,
+                             bool antiAlias) const override;
 protected:
     virtual StencilSupport onGetStencilSupport(const GrDrawTarget*,
                                                const GrPipelineBuilder*,
                                                const SkPath&,
-                                               const SkStrokeRec&) const SK_OVERRIDE;
+                                               const GrStrokeInfo&) const override;
 
     virtual bool onDrawPath(GrDrawTarget*,
                             GrPipelineBuilder*,
                             GrColor,
                             const SkMatrix& viewMatrix,
                             const SkPath&,
-                            const SkStrokeRec&,
-                            bool antiAlias) SK_OVERRIDE;
+                            const GrStrokeInfo&,
+                            bool antiAlias) override;
 
 private:
     GrContext*     fContext;
diff --git a/src/gpu/GrStencilAndCoverPathRenderer.cpp b/src/gpu/GrStencilAndCoverPathRenderer.cpp
index aed1743..33b988e 100644
--- a/src/gpu/GrStencilAndCoverPathRenderer.cpp
+++ b/src/gpu/GrStencilAndCoverPathRenderer.cpp
@@ -14,7 +14,8 @@
 #include "GrPath.h"
 #include "GrRenderTarget.h"
 #include "GrRenderTargetPriv.h"
-#include "SkStrokeRec.h"
+#include "GrResourceProvider.h"
+#include "GrStrokeInfo.h"
 
 /*
  * For now paths only natively support winding and even odd fill types
@@ -35,7 +36,7 @@
 GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrContext* context) {
     SkASSERT(context);
     SkASSERT(context->getGpu());
-    if (context->getGpu()->caps()->pathRenderingSupport()) {
+    if (context->getGpu()->caps()->shaderCaps()->pathRenderingSupport()) {
         return SkNEW_ARGS(GrStencilAndCoverPathRenderer, (context->getGpu()));
     } else {
         return NULL;
@@ -43,7 +44,7 @@
 }
 
 GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrGpu* gpu) {
-    SkASSERT(gpu->caps()->pathRenderingSupport());
+    SkASSERT(gpu->caps()->shaderCaps()->pathRenderingSupport());
     fGpu = gpu;
     gpu->ref();
 }
@@ -56,18 +57,19 @@
                                                 const GrPipelineBuilder* pipelineBuilder,
                                                 const SkMatrix& viewMatrix,
                                                 const SkPath& path,
-                                                const SkStrokeRec& stroke,
+                                                const GrStrokeInfo& stroke,
                                                 bool antiAlias) const {
-    return !stroke.isHairlineStyle() &&
-           !antiAlias && // doesn't do per-path AA, relies on the target having MSAA
-           pipelineBuilder->getStencil().isDisabled();
+    return !stroke.getStrokeRec().isHairlineStyle() &&
+        !stroke.isDashed() &&
+        !antiAlias && // doesn't do per-path AA, relies on the target having MSAA
+        pipelineBuilder->getStencil().isDisabled();
 }
 
 GrPathRenderer::StencilSupport
 GrStencilAndCoverPathRenderer::onGetStencilSupport(const GrDrawTarget*,
                                                    const GrPipelineBuilder*,
                                                    const SkPath&,
-                                                   const SkStrokeRec&) const {
+                                                   const GrStrokeInfo&) const {
     return GrPathRenderer::kStencilOnly_StencilSupport;
 }
 
@@ -75,10 +77,11 @@
     GrContext* ctx = gpu->getContext();
     GrUniqueKey key;
     GrPath::ComputeKey(skPath, stroke, &key);
-    SkAutoTUnref<GrPath> path(static_cast<GrPath*>(ctx->findAndRefCachedResource(key)));
+    SkAutoTUnref<GrPath> path(
+        static_cast<GrPath*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key)));
     if (NULL == path || !path->isEqualTo(skPath, stroke)) {
         path.reset(gpu->pathRendering()->createPath(skPath, stroke));
-        ctx->addResourceToCache(key, path);
+        ctx->resourceProvider()->assignUniqueKeyToResource(key, path);
     }
     return path.detach();
 }
@@ -87,10 +90,10 @@
                                                   GrPipelineBuilder* pipelineBuilder,
                                                   const SkMatrix& viewMatrix,
                                                   const SkPath& path,
-                                                  const SkStrokeRec& stroke) {
+                                                  const GrStrokeInfo& stroke) {
     SkASSERT(!path.isInverseFillType());
     SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(GrColor_WHITE, viewMatrix));
-    SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke));
+    SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke.getStrokeRec()));
     target->stencilPath(pipelineBuilder, pp, p, convert_skpath_filltype(path.getFillType()));
 }
 
@@ -99,14 +102,14 @@
                                                GrColor color,
                                                const SkMatrix& viewMatrix,
                                                const SkPath& path,
-                                               const SkStrokeRec& stroke,
+                                               const GrStrokeInfo& stroke,
                                                bool antiAlias) {
     SkASSERT(!antiAlias);
-    SkASSERT(!stroke.isHairlineStyle());
-
+    SkASSERT(!stroke.getStrokeRec().isHairlineStyle());
+    SkASSERT(!stroke.isDashed());
     SkASSERT(pipelineBuilder->getStencil().isDisabled());
 
-    SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke));
+    SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke.getStrokeRec()));
 
     if (path.isInverseFillType()) {
         GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass,
diff --git a/src/gpu/GrStencilAndCoverPathRenderer.h b/src/gpu/GrStencilAndCoverPathRenderer.h
index cb4012f..7e3c005 100644
--- a/src/gpu/GrStencilAndCoverPathRenderer.h
+++ b/src/gpu/GrStencilAndCoverPathRenderer.h
@@ -29,28 +29,28 @@
                              const GrPipelineBuilder*,
                              const SkMatrix& viewMatrix,
                              const SkPath&,
-                             const SkStrokeRec&,
-                             bool antiAlias) const SK_OVERRIDE;
+                             const GrStrokeInfo&,
+                             bool antiAlias) const override;
 
 protected:
     virtual StencilSupport onGetStencilSupport(const GrDrawTarget*,
                                                const GrPipelineBuilder*,
                                                const SkPath&,
-                                               const SkStrokeRec&) const SK_OVERRIDE;
+                                               const GrStrokeInfo&) const override;
 
     virtual bool onDrawPath(GrDrawTarget*,
                             GrPipelineBuilder*,
                             GrColor,
                             const SkMatrix& viewMatrix,
                             const SkPath&,
-                            const SkStrokeRec&,
-                            bool antiAlias) SK_OVERRIDE;
+                            const GrStrokeInfo&,
+                            bool antiAlias) override;
 
     virtual void onStencilPath(GrDrawTarget*,
                                GrPipelineBuilder*,
                                const SkMatrix& viewMatrix,
                                const SkPath&,
-                               const SkStrokeRec&) SK_OVERRIDE;
+                               const GrStrokeInfo&) override;
 
 private:
     GrStencilAndCoverPathRenderer(GrGpu*);
diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp
index e43387e..a2abfa7 100644
--- a/src/gpu/GrStencilAndCoverTextContext.cpp
+++ b/src/gpu/GrStencilAndCoverTextContext.cpp
@@ -6,11 +6,12 @@
  */
 
 #include "GrStencilAndCoverTextContext.h"
-#include "GrBitmapTextContext.h"
+#include "GrAtlasTextContext.h"
 #include "GrDrawTarget.h"
 #include "GrGpu.h"
 #include "GrPath.h"
 #include "GrPathRange.h"
+#include "GrResourceProvider.h"
 #include "SkAutoKern.h"
 #include "SkDraw.h"
 #include "SkDrawProcs.h"
@@ -20,19 +21,22 @@
 #include "SkTextMapStateProc.h"
 #include "SkTextFormatParams.h"
 
-GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
-    GrContext* context, const SkDeviceProperties& properties)
-    : GrTextContext(context, properties)
+GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
+                                                           SkGpuDevice* gpuDevice,
+                                                           const SkDeviceProperties& properties)
+    : GrTextContext(context, gpuDevice, properties)
     , fStroke(SkStrokeRec::kFill_InitStyle)
     , fQueuedGlyphCount(0)
     , fFallbackGlyphsIdx(kGlyphBufferSize) {
 }
 
-GrStencilAndCoverTextContext* GrStencilAndCoverTextContext::Create(GrContext* context,
-                                                                 const SkDeviceProperties& props) {
+GrStencilAndCoverTextContext*
+GrStencilAndCoverTextContext::Create(GrContext* context, SkGpuDevice* gpuDevice,
+                                     const SkDeviceProperties& props) {
     GrStencilAndCoverTextContext* textContext = SkNEW_ARGS(GrStencilAndCoverTextContext,
-                                                           (context, props));
-    textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
+                                                           (context, gpuDevice, props));
+    textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuDevice, props,
+                                                                   false);
 
     return textContext;
 }
@@ -40,27 +44,31 @@
 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
 }
 
-bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) {
-    if (paint.getRasterizer()) {
+bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt,
+                                           const GrClip& clip,
+                                           const GrPaint& paint,
+                                           const SkPaint& skPaint,
+                                           const SkMatrix& viewMatrix) {
+    if (skPaint.getRasterizer()) {
         return false;
     }
-    if (paint.getMaskFilter()) {
+    if (skPaint.getMaskFilter()) {
         return false;
     }
-    if (paint.getPathEffect()) {
+    if (skPaint.getPathEffect()) {
         return false;
     }
 
     // No hairlines unless we can map the 1 px width to the object space.
-    if (paint.getStyle() == SkPaint::kStroke_Style
-        && paint.getStrokeWidth() == 0
+    if (skPaint.getStyle() == SkPaint::kStroke_Style
+        && skPaint.getStrokeWidth() == 0
         && viewMatrix.hasPerspective()) {
         return false;
     }
 
     // No color bitmap fonts.
     SkScalerContext::Rec    rec;
-    SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
+    SkScalerContext::MakeRec(skPaint, &fDeviceProperties, NULL, &rec);
     return rec.getFormat() != SkMask::kARGB32_Format;
 }
 
@@ -71,7 +79,8 @@
                                               const SkMatrix& viewMatrix,
                                               const char text[],
                                               size_t byteLength,
-                                              SkScalar x, SkScalar y) {
+                                              SkScalar x, SkScalar y,
+                                              const SkIRect& regionClipBounds) {
     SkASSERT(byteLength == 0 || text != NULL);
 
     if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
@@ -93,7 +102,8 @@
     // will turn off the use of device-space glyphs when perspective transforms
     // are in use.
 
-    this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix);
+    this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix,
+               regionClipBounds);
 
     // Transform our starting point.
     if (fUsingDeviceSpaceGlyphs) {
@@ -143,13 +153,13 @@
     SkFixed fy = SkScalarToFixed(y);
     while (text < stop) {
         const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
-        fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
+        fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
         if (glyph.fWidth) {
             this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
         }
 
-        fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
-        fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio);
+        fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
+        fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
     }
 
     this->finish();
@@ -164,7 +174,8 @@
                                                  size_t byteLength,
                                                  const SkScalar pos[],
                                                  int scalarsPerPosition,
-                                                 const SkPoint& offset) {
+                                                 const SkPoint& offset,
+                                                 const SkIRect& regionClipBounds) {
     SkASSERT(byteLength == 0 || text != NULL);
     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
 
@@ -181,7 +192,8 @@
     // transform is not part of SkPaint::measureText API, and thus we use the
     // same glyphs as what were measured.
 
-    this->init(rt, clip, paint, skPaint, byteLength, kMaxPerformance_RenderMode, viewMatrix);
+    this->init(rt, clip, paint, skPaint, byteLength, kMaxPerformance_RenderMode, viewMatrix,
+               regionClipBounds);
 
     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
 
@@ -224,10 +236,10 @@
     builder.finish();
 
     SkAutoTUnref<GrPathRange> glyphs(
-        static_cast<GrPathRange*>(ctx->findAndRefCachedResource(key)));
+        static_cast<GrPathRange*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key)));
     if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) {
         glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
-        ctx->addResourceToCache(key, glyphs);
+        ctx->resourceProvider()->assignUniqueKeyToResource(key, glyphs);
     }
 
     return glyphs.detach();
@@ -239,8 +251,9 @@
                                         const SkPaint& skPaint,
                                         size_t textByteLength,
                                         RenderMode renderMode,
-                                        const SkMatrix& viewMatrix) {
-    GrTextContext::init(rt, clip, paint, skPaint);
+                                        const SkMatrix& viewMatrix,
+                                        const SkIRect& regionClipBounds) {
+    GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds);
 
     fContextInitialMatrix = viewMatrix;
     fViewMatrix = viewMatrix;
@@ -449,7 +462,7 @@
                                           fViewMatrix, (char*)&fGlyphIndices[fFallbackGlyphsIdx],
                                           2 * fallbackGlyphCount,
                                           get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]),
-                                          2, SkPoint::Make(0, 0));
+                                          2, SkPoint::Make(0, 0), fRegionClipBounds);
 
         fFallbackGlyphsIdx = kGlyphBufferSize;
     }
diff --git a/src/gpu/GrStencilAndCoverTextContext.h b/src/gpu/GrStencilAndCoverTextContext.h
index d7871dc..d3d2470 100644
--- a/src/gpu/GrStencilAndCoverTextContext.h
+++ b/src/gpu/GrStencilAndCoverTextContext.h
@@ -23,7 +23,8 @@
  */
 class GrStencilAndCoverTextContext : public GrTextContext {
 public:
-    static GrStencilAndCoverTextContext* Create(GrContext*, const SkDeviceProperties&);
+    static GrStencilAndCoverTextContext* Create(GrContext*, SkGpuDevice*,
+                                                const SkDeviceProperties&);
 
     virtual ~GrStencilAndCoverTextContext();
 
@@ -67,22 +68,24 @@
     SkMatrix                                            fLocalMatrix;
     bool                                                fUsingDeviceSpaceGlyphs;
 
-    GrStencilAndCoverTextContext(GrContext*, const SkDeviceProperties&);
+    GrStencilAndCoverTextContext(GrContext*, SkGpuDevice*, const SkDeviceProperties&);
 
-    bool canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) SK_OVERRIDE;
+    bool canDraw(const GrRenderTarget*, const GrClip&, const GrPaint&,
+                 const SkPaint&, const SkMatrix& viewMatrix) override;
 
-    virtual void onDrawText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
-                            const SkMatrix& viewMatrix,
-                            const char text[], size_t byteLength,
-                            SkScalar x, SkScalar y) SK_OVERRIDE;
-    virtual void onDrawPosText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
-                               const SkMatrix& viewMatrix,
-                               const char text[], size_t byteLength,
-                               const SkScalar pos[], int scalarsPerPosition,
-                               const SkPoint& offset) SK_OVERRIDE;
+    void onDrawText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
+                    const SkMatrix& viewMatrix,
+                    const char text[], size_t byteLength,
+                    SkScalar x, SkScalar y, const SkIRect& regionClipBounds) override;
+    void onDrawPosText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
+                       const SkMatrix& viewMatrix,
+                       const char text[], size_t byteLength,
+                       const SkScalar pos[], int scalarsPerPosition,
+                       const SkPoint& offset, const SkIRect& regionClipBounds) override;
 
     void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
-              size_t textByteLength, RenderMode, const SkMatrix& viewMatrix);
+              size_t textByteLength, RenderMode, const SkMatrix& viewMatrix,
+              const SkIRect& regionClipBounds);
     bool mapToFallbackContext(SkMatrix* inverse);
     void appendGlyph(const SkGlyph&, const SkPoint&);
     void flush();
diff --git a/src/gpu/GrStencilBuffer.cpp b/src/gpu/GrStencilAttachment.cpp
similarity index 64%
rename from src/gpu/GrStencilBuffer.cpp
rename to src/gpu/GrStencilAttachment.cpp
index 3f2b28f..9b07070 100644
--- a/src/gpu/GrStencilBuffer.cpp
+++ b/src/gpu/GrStencilAttachment.cpp
@@ -6,11 +6,11 @@
  * found in the LICENSE file.
  */
 
-#include "GrStencilBuffer.h"
+#include "GrStencilAttachment.h"
 #include "GrResourceKey.h"
 
-void GrStencilBuffer::ComputeSharedStencilBufferKey(int width, int height, int sampleCnt,
-                                                    GrUniqueKey* key) {
+void GrStencilAttachment::ComputeSharedStencilAttachmentKey(int width, int height, int sampleCnt,
+                                                            GrUniqueKey* key) {
     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
     GrUniqueKey::Builder builder(key, kDomain, 3);
     builder[0] = width;
diff --git a/src/gpu/GrStencilBuffer.h b/src/gpu/GrStencilAttachment.h
similarity index 79%
rename from src/gpu/GrStencilBuffer.h
rename to src/gpu/GrStencilAttachment.h
index 0589935..ba9781d 100644
--- a/src/gpu/GrStencilBuffer.h
+++ b/src/gpu/GrStencilAttachment.h
@@ -7,8 +7,8 @@
  */
 
 
-#ifndef GrStencilBuffer_DEFINED
-#define GrStencilBuffer_DEFINED
+#ifndef GrStencilAttachment_DEFINED
+#define GrStencilAttachment_DEFINED
 
 #include "GrClip.h"
 #include "GrGpuResource.h"
@@ -16,11 +16,11 @@
 class GrRenderTarget;
 class GrResourceKey;
 
-class GrStencilBuffer : public GrGpuResource {
+class GrStencilAttachment : public GrGpuResource {
 public:
-    SK_DECLARE_INST_COUNT(GrStencilBuffer);
+    SK_DECLARE_INST_COUNT(GrStencilAttachment);
 
-    virtual ~GrStencilBuffer() {
+    virtual ~GrStencilAttachment() {
         // TODO: allow SB to be purged and detach itself from rts
     }
 
@@ -49,11 +49,12 @@
 
     // We create a unique stencil buffer at each width, height and sampleCnt and share it for
     // all render targets that require a stencil with those params.
-    static void ComputeSharedStencilBufferKey(int width, int height, int sampleCnt,
-                                              GrUniqueKey* key);
+    static void ComputeSharedStencilAttachmentKey(int width, int height, int sampleCnt,
+                                                  GrUniqueKey* key);
 
 protected:
-    GrStencilBuffer(GrGpu* gpu, LifeCycle lifeCycle, int width, int height, int bits, int sampleCnt)
+    GrStencilAttachment(GrGpu* gpu, LifeCycle lifeCycle, int width, int height, int bits,
+                        int sampleCnt)
         : GrGpuResource(gpu, lifeCycle)
         , fWidth(width)
         , fHeight(height)
diff --git a/src/gpu/GrStrokeInfo.cpp b/src/gpu/GrStrokeInfo.cpp
new file mode 100644
index 0000000..be8e560
--- /dev/null
+++ b/src/gpu/GrStrokeInfo.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrStrokeInfo.h"
+
+#include "SkDashPathPriv.h"
+
+bool GrStrokeInfo::applyDash(SkPath* dst, GrStrokeInfo* dstStrokeInfo, const SkPath& src) const {
+    if (this->isDashed()) {
+        SkPathEffect::DashInfo info;
+        info.fIntervals = fIntervals.get();
+        info.fCount = fIntervals.count();
+        info.fPhase = fDashPhase;
+        SkStrokeRec strokeRec = fStroke;
+        if (SkDashPath::FilterDashPath(dst, src, &strokeRec, NULL, info)) {
+            dstStrokeInfo->fStroke = strokeRec;
+            dstStrokeInfo->removeDash();
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/src/gpu/GrStrokeInfo.h b/src/gpu/GrStrokeInfo.h
index 2bd056e..e1349a7 100644
--- a/src/gpu/GrStrokeInfo.h
+++ b/src/gpu/GrStrokeInfo.h
@@ -12,10 +12,9 @@
 #include "SkPathEffect.h"
 
 /*
- * GrStrokeInfo encapsulates the data objects that hold all the pertinent infomation
- * regarding the stroke. The two objects are SkStrokeRec which holds information on fill style,
- * width, miter, cap, and join. The second object is DashInfo. This holds information about the
- * dash like intervals, count, and phase.
+ * GrStrokeInfo encapsulates all the pertinent infomation regarding the stroke. The SkStrokeRec
+ * which holds information on fill style, width, miter, cap, and join. It also holds information
+ * about the dash like intervals, count, and phase.
  */
 class GrStrokeInfo {
 public: 
@@ -23,11 +22,11 @@
         fStroke(style), fDashType(SkPathEffect::kNone_DashType) {}
 
     GrStrokeInfo(const GrStrokeInfo& src, bool includeDash = true) : fStroke(src.fStroke) {
-        if (includeDash) {
-            fDashInfo = src.fDashInfo;
+        if (includeDash && src.isDashed()) {
             fDashType = src.fDashType;
-            fIntervals.reset(src.dashCount());
-            memcpy(fIntervals.get(), src.fIntervals.get(), src.dashCount() * sizeof(SkScalar));
+            fDashPhase = src.fDashPhase;
+            fIntervals.reset(src.getDashCount());
+            memcpy(fIntervals.get(), src.fIntervals.get(), fIntervals.count() * sizeof(SkScalar));
         } else {
             fDashType = SkPathEffect::kNone_DashType;
         }
@@ -38,12 +37,24 @@
         this->init(paint);
     }
 
-
     explicit GrStrokeInfo(const SkPaint& paint) :
         fStroke(paint), fDashType(SkPathEffect::kNone_DashType) {
         this->init(paint);
     }
 
+    GrStrokeInfo& operator=(const GrStrokeInfo& other) {
+        if (other.isDashed()) {
+            fDashType = other.fDashType;
+            fDashPhase = other.fDashPhase;
+            fIntervals.reset(other.getDashCount());
+            memcpy(fIntervals.get(), other.fIntervals.get(), fIntervals.count() * sizeof(SkScalar));
+        } else {
+            this->removeDash();
+        }
+        fStroke = other.fStroke;
+        return *this;
+    }
+
     const SkStrokeRec& getStrokeRec() const { return fStroke; }
 
     SkStrokeRec* getStrokeRecPtr() { return &fStroke; }
@@ -51,37 +62,74 @@
     void setFillStyle() { fStroke.setFillStyle(); }
 
     /*
-     * This functions takes in a patheffect and fills in fDashInfo with the various dashing
-     * information if the path effect is a Dash type. Returns true if the path effect is a
-     * dashed effect and we are stroking, otherwise it retruns false.
+     * This functions takes in a patheffect and updates the dashing information if the path effect
+     * is a Dash type. Returns true if the path effect is a dashed effect and we are stroking,
+     * otherwise it returns false.
      */
     bool setDashInfo(const SkPathEffect* pe) {
         if (pe && !fStroke.isFillStyle()) {
-            fDashInfo.fIntervals = NULL;
-            fDashType = pe->asADash(&fDashInfo);
+            SkPathEffect::DashInfo dashInfo;
+            fDashType = pe->asADash(&dashInfo);
             if (SkPathEffect::kDash_DashType == fDashType) {
-                fIntervals.reset(fDashInfo.fCount);
-                fDashInfo.fIntervals = fIntervals.get();
-                pe->asADash(&fDashInfo);
+                fIntervals.reset(dashInfo.fCount);
+                dashInfo.fIntervals = fIntervals.get();
+                pe->asADash(&dashInfo);
+                fDashPhase = dashInfo.fPhase;
                 return true;
             }
         }
         return false;
     }
 
+    /*
+     * Like the above, but sets with an explicit SkPathEffect::DashInfo
+     */
+    bool setDashInfo(const SkPathEffect::DashInfo& info) {
+        if (!fStroke.isFillStyle()) {
+            SkASSERT(!fStroke.isFillStyle());
+            fDashType = SkPathEffect::kDash_DashType;
+            fDashPhase = info.fPhase;
+            fIntervals.reset(info.fCount);
+            for (int i = 0; i < fIntervals.count(); i++) {
+                fIntervals[i] = info.fIntervals[i];
+            }
+            return true;
+        }
+        return false;
+    }
+
     bool isDashed() const {
         return (!fStroke.isFillStyle() && SkPathEffect::kDash_DashType == fDashType);
     }
 
-    int32_t dashCount() const {
-        return fDashInfo.fCount;
+    bool isFillStyle() const { return fStroke.isFillStyle(); }
+
+    int32_t getDashCount() const {
+        SkASSERT(this->isDashed());
+        return fIntervals.count();
+    }
+
+    SkScalar getDashPhase() const {
+        SkASSERT(this->isDashed());
+        return fDashPhase;
+    }
+
+    const SkScalar* getDashIntervals() const {
+        SkASSERT(this->isDashed());
+        return fIntervals.get();
     }
 
     void removeDash() {
         fDashType = SkPathEffect::kNone_DashType;
     }
-    
-    const SkPathEffect::DashInfo& getDashInfo() const { return fDashInfo; }
+
+    /** Applies the dash to a path, if the stroke info has dashing.
+     * @return true if the dashing was applied (dst and dstStrokeInfo will be modified).
+     *         false if the stroke info did not have dashing. The dst and dstStrokeInfo
+     *               will be unmodified. The stroking in the SkStrokeRec might still
+     *               be applicable.
+     */
+    bool applyDash(SkPath* dst, GrStrokeInfo* dstStrokeInfo, const SkPath& src) const;
 
 private:
 
@@ -92,7 +140,7 @@
 
     SkStrokeRec            fStroke;
     SkPathEffect::DashType fDashType;
-    SkPathEffect::DashInfo fDashInfo;
+    SkScalar               fDashPhase;
     SkAutoSTArray<2, SkScalar> fIntervals;
 };
 
diff --git a/src/gpu/GrTRecorder.h b/src/gpu/GrTRecorder.h
index bddf197..1c49c86 100644
--- a/src/gpu/GrTRecorder.h
+++ b/src/gpu/GrTRecorder.h
@@ -46,6 +46,7 @@
 template<typename TBase, typename TAlign> class GrTRecorder : SkNoncopyable {
 public:
     class Iter;
+    class ReverseIter;
 
     /**
      * Create a recorder.
@@ -162,6 +163,7 @@
                               const GrTRecorderAllocWrapper<UItem>&);
 
     friend class Iter;
+    friend class ReverseIter;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -182,7 +184,7 @@
         fLastItem = NULL;
         return;
     }
-    if (!fTailBlock->fBack) {
+    while (!fTailBlock->fBack) {
         // We popped the last entry in a block that isn't the head block. Move back a block but
         // don't free it since we'll probably grow into it shortly.
         fTailBlock = fTailBlock->fPrev;
@@ -239,6 +241,15 @@
     return rawPtr;
 }
 
+/**
+ * Iterates through a recorder from front to back. The initial state of the iterator is
+ * to not have the front item loaded yet; next() must be called first. Usage model:
+ *
+ *    GrTRecorder<TBase, TAlign>::Iter iter(recorder);
+ *    while (iter.next()) {
+ *        iter->doSomething();
+ *    }
+ */
 template<typename TBase, typename TAlign>
 class GrTRecorder<TBase, TAlign>::Iter {
 public:
@@ -273,6 +284,55 @@
     TBase*    fItem;
 };
 
+/**
+ * Iterates through a recorder in reverse, from back to front. This version mirrors "Iter",
+ * so the initial state is to have recorder.back() loaded already. (Note that this will
+ * assert if the recorder is empty.) Usage model:
+ *
+ *    GrTRecorder<TBase, TAlign>::ReverseIter reverseIter(recorder);
+ *    do {
+ *        reverseIter->doSomething();
+ *    } while (reverseIter.previous());
+ */
+template<typename TBase, typename TAlign>
+class GrTRecorder<TBase, TAlign>::ReverseIter {
+public:
+    ReverseIter(GrTRecorder& recorder)
+        : fBlock(recorder.fTailBlock),
+          fItem(&recorder.back()) {
+        Header* lastHeader = reinterpret_cast<Header*>(
+            reinterpret_cast<TAlign*>(fItem) - length_of<Header>::kValue);
+        fPosition = fBlock->fBack - lastHeader->fTotalLength;
+    }
+
+    bool previous() {
+        Header* header = reinterpret_cast<Header*>(&(*fBlock)[fPosition]);
+
+        while (0 == fPosition) {
+            if (!fBlock->fPrev) {
+                // We've reached the front of the recorder.
+                return false;
+            }
+            fBlock = fBlock->fPrev;
+            fPosition = fBlock->fBack;
+        }
+
+        fPosition -= header->fPrevLength;
+        SkASSERT(fPosition >= 0);
+
+        fItem = reinterpret_cast<TBase*>(&(*fBlock)[fPosition + length_of<Header>::kValue]);
+        return true;
+    }
+
+    TBase* get() const { return fItem; }
+    TBase* operator->() const { return this->get(); }
+
+private:
+    MemBlock* fBlock;
+    int       fPosition;
+    TBase*    fItem;
+};
+
 template<typename TBase, typename TAlign>
 void GrTRecorder<TBase, TAlign>::reset() {
     Iter iter(*this);
diff --git a/src/gpu/GrTargetCommands.cpp b/src/gpu/GrTargetCommands.cpp
index 9b968dc..096d429 100644
--- a/src/gpu/GrTargetCommands.cpp
+++ b/src/gpu/GrTargetCommands.cpp
@@ -7,278 +7,11 @@
 
 #include "GrTargetCommands.h"
 
-#include "GrColor.h"
-#include "GrDefaultGeoProcFactory.h"
 #include "GrInOrderDrawBuffer.h"
-#include "GrTemplates.h"
-#include "SkPoint.h"
-
-void GrTargetCommands::closeBatch() {
-    if (fDrawBatch) {
-        fBatchTarget.resetNumberOfDraws();
-        fDrawBatch->execute(NULL, fPrevState);
-        fDrawBatch->fBatch->setNumberOfDraws(fBatchTarget.numberOfDraws());
-        fDrawBatch = NULL;
-    }
-}
-
-static bool path_fill_type_is_winding(const GrStencilSettings& pathStencilSettings) {
-    static const GrStencilSettings::Face pathFace = GrStencilSettings::kFront_Face;
-    bool isWinding = kInvert_StencilOp != pathStencilSettings.passOp(pathFace);
-    if (isWinding) {
-        // Double check that it is in fact winding.
-        SkASSERT(kIncClamp_StencilOp == pathStencilSettings.passOp(pathFace));
-        SkASSERT(kIncClamp_StencilOp == pathStencilSettings.failOp(pathFace));
-        SkASSERT(0x1 != pathStencilSettings.writeMask(pathFace));
-        SkASSERT(!pathStencilSettings.isTwoSided());
-    }
-    return isWinding;
-}
-
-int GrTargetCommands::concatInstancedDraw(GrInOrderDrawBuffer* iodb,
-                                          const GrDrawTarget::DrawInfo& info) {
-    SkASSERT(!fCmdBuffer.empty());
-    SkASSERT(info.isInstanced());
-
-    const GrIndexBuffer* ib;
-    if (!iodb->canConcatToIndexBuffer(&ib)) {
-        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 (Cmd::kDraw_CmdType != fCmdBuffer.back().type()) {
-        return 0;
-    }
-
-    Draw* draw = static_cast<Draw*>(&fCmdBuffer.back());
-
-    if (!draw->fInfo.isInstanced() ||
-        draw->fInfo.primitiveType() != info.primitiveType() ||
-        draw->fInfo.verticesPerInstance() != info.verticesPerInstance() ||
-        draw->fInfo.indicesPerInstance() != info.indicesPerInstance() ||
-        draw->fInfo.vertexBuffer() != info.vertexBuffer() ||
-        draw->fInfo.indexBuffer() != ib) {
-        return 0;
-    }
-    if (draw->fInfo.startVertex() + draw->fInfo.vertexCount() != info.startVertex()) {
-        return 0;
-    }
-
-    // how many instances can be concat'ed onto draw given the size of the index buffer
-    int instancesToConcat = iodb->indexCountInCurrentSource() / info.indicesPerInstance();
-    instancesToConcat -= draw->fInfo.instanceCount();
-    instancesToConcat = SkTMin(instancesToConcat, info.instanceCount());
-
-    draw->fInfo.adjustInstanceCount(instancesToConcat);
-
-    // update last fGpuCmdMarkers to include any additional trace markers that have been added
-    iodb->recordTraceMarkersIfNecessary(draw);
-    return instancesToConcat;
-}
-
-GrTargetCommands::Cmd* GrTargetCommands::recordDraw(
-                                                  GrInOrderDrawBuffer* iodb,
-                                                  const GrGeometryProcessor* gp,
-                                                  const GrDrawTarget::DrawInfo& info,
-                                                  const GrDrawTarget::PipelineInfo& pipelineInfo) {
-    SkASSERT(info.vertexBuffer() && (!info.isIndexed() || info.indexBuffer()));
-    this->closeBatch();
-
-    if (!this->setupPipelineAndShouldDraw(iodb, gp, pipelineInfo)) {
-        return NULL;
-    }
-
-    Draw* draw;
-    if (info.isInstanced()) {
-        int instancesConcated = this->concatInstancedDraw(iodb, info);
-        if (info.instanceCount() > instancesConcated) {
-            draw = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, Draw, (info));
-            draw->fInfo.adjustInstanceCount(-instancesConcated);
-        } else {
-            return NULL;
-        }
-    } else {
-        draw = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, Draw, (info));
-    }
-
-    return draw;
-}
-
-GrTargetCommands::Cmd* GrTargetCommands::recordDrawBatch(
-                                                  GrInOrderDrawBuffer* iodb,
-                                                  GrBatch* batch,
-                                                  const GrDrawTarget::PipelineInfo& pipelineInfo) {
-    if (!this->setupPipelineAndShouldDraw(iodb, batch, pipelineInfo)) {
-        return NULL;
-    }
-
-    // Check if there is a Batch Draw we can batch with
-    if (Cmd::kDrawBatch_CmdType != fCmdBuffer.back().type() || !fDrawBatch) {
-        fDrawBatch = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, DrawBatch, (batch, &fBatchTarget));
-        return fDrawBatch;
-    }
-
-    SkASSERT(&fCmdBuffer.back() == fDrawBatch);
-    if (!fDrawBatch->fBatch->combineIfPossible(batch)) {
-        this->closeBatch();
-        fDrawBatch = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, DrawBatch, (batch, &fBatchTarget));
-    }
-
-    return fDrawBatch;
-}
-
-GrTargetCommands::Cmd* GrTargetCommands::recordStencilPath(
-                                                        GrInOrderDrawBuffer* iodb,
-                                                        const GrPipelineBuilder& pipelineBuilder,
-                                                        const GrPathProcessor* pathProc,
-                                                        const GrPath* path,
-                                                        const GrScissorState& scissorState,
-                                                        const GrStencilSettings& stencilSettings) {
-    this->closeBatch();
-
-    StencilPath* sp = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, StencilPath,
-                                               (path, pipelineBuilder.getRenderTarget()));
-
-    sp->fScissor = scissorState;
-    sp->fUseHWAA = pipelineBuilder.isHWAntialias();
-    sp->fViewMatrix = pathProc->viewMatrix();
-    sp->fStencil = stencilSettings;
-    return sp;
-}
-
-GrTargetCommands::Cmd* GrTargetCommands::recordDrawPath(
-                                                  GrInOrderDrawBuffer* iodb,
-                                                  const GrPathProcessor* pathProc,
-                                                  const GrPath* path,
-                                                  const GrStencilSettings& stencilSettings,
-                                                  const GrDrawTarget::PipelineInfo& pipelineInfo) {
-    this->closeBatch();
-
-    // TODO: Only compare the subset of GrPipelineBuilder relevant to path covering?
-    if (!this->setupPipelineAndShouldDraw(iodb, pathProc, pipelineInfo)) {
-        return NULL;
-    }
-    DrawPath* dp = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, DrawPath, (path));
-    dp->fStencilSettings = stencilSettings;
-    return dp;
-}
-
-GrTargetCommands::Cmd* GrTargetCommands::recordDrawPaths(
-                                                  GrInOrderDrawBuffer* iodb,
-                                                  const GrPathProcessor* pathProc,
-                                                  const GrPathRange* pathRange,
-                                                  const void* indexValues,
-                                                  GrDrawTarget::PathIndexType indexType,
-                                                  const float transformValues[],
-                                                  GrDrawTarget::PathTransformType transformType,
-                                                  int count,
-                                                  const GrStencilSettings& stencilSettings,
-                                                  const GrDrawTarget::PipelineInfo& pipelineInfo) {
-    SkASSERT(pathRange);
-    SkASSERT(indexValues);
-    SkASSERT(transformValues);
-    this->closeBatch();
-
-    if (!this->setupPipelineAndShouldDraw(iodb, pathProc, pipelineInfo)) {
-        return NULL;
-    }
-
-    char* savedIndices;
-    float* savedTransforms;
-    
-    iodb->appendIndicesAndTransforms(indexValues, indexType,
-                                     transformValues, transformType,
-                                     count, &savedIndices, &savedTransforms);
-
-    if (Cmd::kDrawPaths_CmdType == fCmdBuffer.back().type()) {
-        // The previous command was also DrawPaths. Try to collapse this call into the one
-        // before. Note that stenciling all the paths at once, then covering, may not be
-        // equivalent to two separate draw calls if there is overlap. Blending won't work,
-        // and the combined calls may also cancel each other's winding numbers in some
-        // places. For now the winding numbers are only an issue if the fill is even/odd,
-        // because DrawPaths is currently only used for glyphs, and glyphs in the same
-        // font tend to all wind in the same direction.
-        DrawPaths* previous = static_cast<DrawPaths*>(&fCmdBuffer.back());
-        if (pathRange == previous->pathRange() &&
-            indexType == previous->fIndexType &&
-            transformType == previous->fTransformType &&
-            stencilSettings == previous->fStencilSettings &&
-            path_fill_type_is_winding(stencilSettings) &&
-            !pipelineInfo.willBlendWithDst(pathProc)) {
-                const int indexBytes = GrPathRange::PathIndexSizeInBytes(indexType);
-                const int xformSize = GrPathRendering::PathTransformSize(transformType);
-                if (&previous->fIndices[previous->fCount*indexBytes] == savedIndices &&
-                    (0 == xformSize ||
-                     &previous->fTransforms[previous->fCount*xformSize] == savedTransforms)) {
-                    // Fold this DrawPaths call into the one previous.
-                    previous->fCount += count;
-                    return NULL;
-                }
-        }
-    }
-
-    DrawPaths* dp = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, DrawPaths, (pathRange));
-    dp->fIndices = savedIndices;
-    dp->fIndexType = indexType;
-    dp->fTransforms = savedTransforms;
-    dp->fTransformType = transformType;
-    dp->fCount = count;
-    dp->fStencilSettings = stencilSettings;
-    return dp;
-}
-
-GrTargetCommands::Cmd* GrTargetCommands::recordClear(GrInOrderDrawBuffer* iodb,
-                                                     const SkIRect* rect, 
-                                                     GrColor color,
-                                                     bool canIgnoreRect,
-                                                     GrRenderTarget* renderTarget) {
-    SkASSERT(renderTarget);
-    this->closeBatch();
-
-    SkIRect r;
-    if (NULL == rect) {
-        // We could do something smart and remove previous draws and clears to
-        // the current render target. If we get that smart we have to make sure
-        // those draws aren't read before this clear (render-to-texture).
-        r.setLTRB(0, 0, renderTarget->width(), renderTarget->height());
-        rect = &r;
-    }
-    Clear* clr = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, Clear, (renderTarget));
-    GrColorIsPMAssert(color);
-    clr->fColor = color;
-    clr->fRect = *rect;
-    clr->fCanIgnoreRect = canIgnoreRect;
-    return clr;
-}
-
-GrTargetCommands::Cmd* GrTargetCommands::recordClearStencilClip(GrInOrderDrawBuffer* iodb,
-                                                                const SkIRect& rect,
-                                                                bool insideClip,
-                                                                GrRenderTarget* renderTarget) {
-    SkASSERT(renderTarget);
-    this->closeBatch();
-
-    ClearStencilClip* clr = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, ClearStencilClip, (renderTarget));
-    clr->fRect = rect;
-    clr->fInsideClip = insideClip;
-    return clr;
-}
-
-GrTargetCommands::Cmd* GrTargetCommands::recordDiscard(GrInOrderDrawBuffer* iodb,
-                                                       GrRenderTarget* renderTarget) {
-    SkASSERT(renderTarget);
-    this->closeBatch();
-
-    Clear* clr = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, Clear, (renderTarget));
-    clr->fColor = GrColor_ILLEGAL;
-    return clr;
-}
 
 void GrTargetCommands::reset() {
     fCmdBuffer.reset();
-    fPrevState = NULL;
-    fDrawBatch = NULL;
+    fBatchTarget.reset();
 }
 
 void GrTargetCommands::flush(GrInOrderDrawBuffer* iodb) {
@@ -286,20 +19,23 @@
         return;
     }
 
-    // TODO this is temporary while batch is being rolled out
-    this->closeBatch();
-    iodb->getVertexAllocPool()->unmap();
-    iodb->getIndexAllocPool()->unmap();
+    GrGpu* gpu = iodb->getGpu();
+
+    // Loop over all batches and generate geometry
+    CmdBuffer::Iter genIter(fCmdBuffer);
+    while (genIter.next()) {
+        if (Cmd::kDrawBatch_CmdType == genIter->type()) {
+            DrawBatch* db = reinterpret_cast<DrawBatch*>(genIter.get());
+            fBatchTarget.resetNumberOfDraws();
+            db->fBatch->generateGeometry(&fBatchTarget, db->fState->getPipeline());
+            db->fBatch->setNumberOfDraws(fBatchTarget.numberOfDraws());
+        }
+    }
+
     fBatchTarget.preFlush();
 
-    // Updated every time we find a set state cmd to reflect the current state in the playback
-    // stream.
-    SetState* currentState = NULL;
-
     CmdBuffer::Iter iter(fCmdBuffer);
 
-    GrGpu* gpu = iodb->getGpu();
-
     while (iter.next()) {
         GrGpuTraceMarker newMarker("", -1);
         SkString traceString;
@@ -309,43 +45,16 @@
             gpu->addGpuTraceMarker(&newMarker);
         }
 
-        // TODO temporary hack
-        if (Cmd::kDrawBatch_CmdType == iter->type()) {
-            DrawBatch* db = reinterpret_cast<DrawBatch*>(iter.get());
-            fBatchTarget.flushNext(db->fBatch->numberOfDraws());
-
-            if (iter->isTraced()) {
-                gpu->removeGpuTraceMarker(&newMarker);
-            }
-            continue;
-        }
-
-        if (Cmd::kSetState_CmdType == iter->type()) {
-            SetState* ss = reinterpret_cast<SetState*>(iter.get());
-
-            ss->execute(gpu, currentState);
-            currentState = ss;
-        } else {
-            iter->execute(gpu, currentState);
-        }
-
+        iter->execute(gpu);
         if (iter->isTraced()) {
             gpu->removeGpuTraceMarker(&newMarker);
         }
     }
 
-    // TODO see copious notes about hack
     fBatchTarget.postFlush();
 }
 
-void GrTargetCommands::Draw::execute(GrGpu* gpu, const SetState* state) {
-    SkASSERT(state);
-    DrawArgs args(state->fPrimitiveProcessor.get(), state->getPipeline(), &state->fDesc,
-                  &state->fBatchTracker);
-    gpu->draw(args, fInfo);
-}
-
-void GrTargetCommands::StencilPath::execute(GrGpu* gpu, const SetState*) {
+void GrTargetCommands::StencilPath::execute(GrGpu* gpu) {
     GrGpu::StencilPathState state;
     state.fRenderTarget = fRenderTarget.get();
     state.fScissor = &fScissor;
@@ -356,37 +65,36 @@
     gpu->stencilPath(this->path(), state);
 }
 
-void GrTargetCommands::DrawPath::execute(GrGpu* gpu, const SetState* state) {
-    SkASSERT(state);
-    DrawArgs args(state->fPrimitiveProcessor.get(), state->getPipeline(), &state->fDesc,
-                  &state->fBatchTracker);
+void GrTargetCommands::DrawPath::execute(GrGpu* gpu) {
+    if (!fState->fCompiled) {
+        gpu->buildProgramDesc(&fState->fDesc, *fState->fPrimitiveProcessor, *fState->getPipeline(),
+                              fState->fBatchTracker);
+        fState->fCompiled = true;
+    }
+    DrawArgs args(fState->fPrimitiveProcessor.get(), fState->getPipeline(),
+                  &fState->fDesc, &fState->fBatchTracker);
     gpu->drawPath(args, this->path(), fStencilSettings);
 }
 
-void GrTargetCommands::DrawPaths::execute(GrGpu* gpu, const SetState* state) {
-    SkASSERT(state);
-    DrawArgs args(state->fPrimitiveProcessor.get(), state->getPipeline(), &state->fDesc,
-                  &state->fBatchTracker);
+void GrTargetCommands::DrawPaths::execute(GrGpu* gpu) {
+    if (!fState->fCompiled) {
+        gpu->buildProgramDesc(&fState->fDesc, *fState->fPrimitiveProcessor, *fState->getPipeline(),
+                              fState->fBatchTracker);
+        fState->fCompiled = true;
+    }
+    DrawArgs args(fState->fPrimitiveProcessor.get(), fState->getPipeline(),
+                  &fState->fDesc, &fState->fBatchTracker);
     gpu->drawPaths(args, this->pathRange(),
                    fIndices, fIndexType,
                    fTransforms, fTransformType,
                    fCount, fStencilSettings);
 }
 
-void GrTargetCommands::DrawBatch::execute(GrGpu*, const SetState* state) {
-    SkASSERT(state);
-    fBatch->generateGeometry(fBatchTarget, state->getPipeline());
+void GrTargetCommands::DrawBatch::execute(GrGpu*) {
+    fBatchTarget->flushNext(fBatch->numberOfDraws());
 }
 
-void GrTargetCommands::SetState::execute(GrGpu* gpu, const SetState*) {
-    // TODO sometimes we have a prim proc, othertimes we have a GrBatch.  Eventually we
-    // will only have GrBatch and we can delete this
-    if (fPrimitiveProcessor) {
-        gpu->buildProgramDesc(&fDesc, *fPrimitiveProcessor, *getPipeline(), fBatchTracker);
-    }
-}
-
-void GrTargetCommands::Clear::execute(GrGpu* gpu, const SetState*) {
+void GrTargetCommands::Clear::execute(GrGpu* gpu) {
     if (GrColor_ILLEGAL == fColor) {
         gpu->discard(this->renderTarget());
     } else {
@@ -394,77 +102,14 @@
     }
 }
 
-void GrTargetCommands::ClearStencilClip::execute(GrGpu* gpu, const SetState*) {
+void GrTargetCommands::ClearStencilClip::execute(GrGpu* gpu) {
     gpu->clearStencilClip(fRect, fInsideClip, this->renderTarget());
 }
 
-void GrTargetCommands::CopySurface::execute(GrGpu* gpu, const SetState*) {
+void GrTargetCommands::CopySurface::execute(GrGpu* gpu) {
     gpu->copySurface(this->dst(), this->src(), fSrcRect, fDstPoint);
 }
 
-GrTargetCommands::Cmd* GrTargetCommands::recordCopySurface(GrInOrderDrawBuffer* iodb,
-                                                           GrSurface* dst,
-                                                           GrSurface* src,
-                                                           const SkIRect& srcRect,
-                                                           const SkIPoint& dstPoint) {
-    if (iodb->getGpu()->canCopySurface(dst, src, srcRect, dstPoint)) {
-        this->closeBatch();
-        CopySurface* cs = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, CopySurface, (dst, src));
-        cs->fSrcRect = srcRect;
-        cs->fDstPoint = dstPoint;
-        return cs;
-    }
-    return NULL;
+void GrTargetCommands::XferBarrier::execute(GrGpu* gpu) {
+    gpu->xferBarrier(fRenderTarget.get(), fBarrierType);
 }
-
-bool GrTargetCommands::setupPipelineAndShouldDraw(GrInOrderDrawBuffer* iodb,
-                                                  const GrPrimitiveProcessor* primProc,
-                                                  const GrDrawTarget::PipelineInfo& pipelineInfo) {
-    SetState* ss = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, SetState, (primProc));
-    iodb->setupPipeline(pipelineInfo, ss->pipelineLocation()); 
-
-    if (ss->getPipeline()->mustSkip()) {
-        fCmdBuffer.pop_back();
-        return false;
-    }
-
-    ss->fPrimitiveProcessor->initBatchTracker(&ss->fBatchTracker,
-                                              ss->getPipeline()->getInitBatchTracker());
-
-    if (fPrevState && fPrevState->fPrimitiveProcessor.get() &&
-        fPrevState->fPrimitiveProcessor->canMakeEqual(fPrevState->fBatchTracker,
-                                                      *ss->fPrimitiveProcessor,
-                                                      ss->fBatchTracker) &&
-        fPrevState->getPipeline()->isEqual(*ss->getPipeline())) {
-        fCmdBuffer.pop_back();
-    } else {
-        fPrevState = ss;
-        iodb->recordTraceMarkersIfNecessary(ss);
-    }
-    return true;
-}
-
-bool GrTargetCommands::setupPipelineAndShouldDraw(GrInOrderDrawBuffer* iodb,
-                                                  GrBatch* batch,
-                                                  const GrDrawTarget::PipelineInfo& pipelineInfo) {
-    SetState* ss = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, SetState, ());
-    iodb->setupPipeline(pipelineInfo, ss->pipelineLocation()); 
-
-    if (ss->getPipeline()->mustSkip()) {
-        fCmdBuffer.pop_back();
-        return false;
-    }
-
-    batch->initBatchTracker(ss->getPipeline()->getInitBatchTracker());
-
-    if (fPrevState && !fPrevState->fPrimitiveProcessor.get() &&
-        fPrevState->getPipeline()->isEqual(*ss->getPipeline())) {
-        fCmdBuffer.pop_back();
-    } else {
-        this->closeBatch();
-        fPrevState = ss;
-        iodb->recordTraceMarkersIfNecessary(ss);
-    }
-    return true;
-}
-
diff --git a/src/gpu/GrTargetCommands.h b/src/gpu/GrTargetCommands.h
index 035fbeb..2f9909b 100644
--- a/src/gpu/GrTargetCommands.h
+++ b/src/gpu/GrTargetCommands.h
@@ -8,6 +8,7 @@
 #ifndef GrTargetCommands_DEFINED
 #define GrTargetCommands_DEFINED
 
+#include "GrBatch.h"
 #include "GrBatchTarget.h"
 #include "GrDrawTarget.h"
 #include "GrGpu.h"
@@ -19,39 +20,32 @@
 #include "SkTypes.h"
 
 class GrInOrderDrawBuffer;
-class GrVertexBufferAllocPool;
-class GrIndexBufferAllocPool;
+
 
 class GrTargetCommands : ::SkNoncopyable {
-    struct SetState;
-
 public:
-    GrTargetCommands(GrGpu* gpu,
-                     GrVertexBufferAllocPool* vertexPool,
-                     GrIndexBufferAllocPool* indexPool)
+    GrTargetCommands(GrGpu* gpu)
         : fCmdBuffer(kCmdBufferInitialSizeInBytes)
-        , fPrevState(NULL)
-        , fBatchTarget(gpu, vertexPool, indexPool)
-        , fDrawBatch(NULL) {
+        , fBatchTarget(gpu) {
     }
 
     class Cmd : ::SkNoncopyable {
     public:
         enum CmdType {
-            kDraw_CmdType              = 1,
-            kStencilPath_CmdType       = 2,
-            kSetState_CmdType          = 3,
-            kClear_CmdType             = 4,
-            kCopySurface_CmdType       = 5,
-            kDrawPath_CmdType          = 6,
-            kDrawPaths_CmdType         = 7,
-            kDrawBatch_CmdType         = 8,
+            kStencilPath_CmdType       = 1,
+            kSetState_CmdType          = 2,
+            kClear_CmdType             = 3,
+            kCopySurface_CmdType       = 4,
+            kDrawPath_CmdType          = 5,
+            kDrawPaths_CmdType         = 6,
+            kDrawBatch_CmdType         = 7,
+            kXferBarrier_CmdType       = 8,
         };
 
         Cmd(CmdType type) : fMarkerID(-1), fType(type) {}
         virtual ~Cmd() {}
 
-        virtual void execute(GrGpu*, const SetState*) = 0;
+        virtual void execute(GrGpu*) = 0;
 
         CmdType type() const { return fType; }
 
@@ -68,87 +62,58 @@
     void reset();
     void flush(GrInOrderDrawBuffer*);
 
-    Cmd* recordClearStencilClip(GrInOrderDrawBuffer*,
-                                const SkIRect& rect,
-                                bool insideClip,
-                                GrRenderTarget* renderTarget);
-
-    Cmd* recordDiscard(GrInOrderDrawBuffer*, GrRenderTarget*);
-
-    Cmd* recordDraw(GrInOrderDrawBuffer*,
-                    const GrGeometryProcessor*,
-                    const GrDrawTarget::DrawInfo&,
-                    const GrDrawTarget::PipelineInfo&);
-    Cmd* recordDrawBatch(GrInOrderDrawBuffer*,
-                         GrBatch*,
-                         const GrDrawTarget::PipelineInfo&);
-    void recordDrawRect(GrInOrderDrawBuffer*,
-                        GrPipelineBuilder*,
-                        GrColor,
-                        const SkMatrix& viewMatrix,
-                        const SkRect& rect,
-                        const SkRect* localRect,
-                        const SkMatrix* localMatrix);
-    Cmd* recordStencilPath(GrInOrderDrawBuffer*,
-                           const GrPipelineBuilder&,
-                           const GrPathProcessor*,
-                           const GrPath*,
-                           const GrScissorState&,
-                           const GrStencilSettings&);
-    Cmd* recordDrawPath(GrInOrderDrawBuffer*,
-                        const GrPathProcessor*,
-                        const GrPath*,
-                        const GrStencilSettings&,
-                        const GrDrawTarget::PipelineInfo&);
-    Cmd* recordDrawPaths(GrInOrderDrawBuffer*,
-                         const GrPathProcessor*,
-                         const GrPathRange*,
-                         const void*,
-                         GrDrawTarget::PathIndexType,
-                         const float transformValues[],
-                         GrDrawTarget::PathTransformType ,
-                         int,
-                         const GrStencilSettings&,
-                         const GrDrawTarget::PipelineInfo&);
-    Cmd* recordClear(GrInOrderDrawBuffer*,
-                     const SkIRect* rect,
-                     GrColor,
-                     bool canIgnoreRect,
-                     GrRenderTarget*);
-    Cmd* recordCopySurface(GrInOrderDrawBuffer*,
-                           GrSurface* dst,
-                           GrSurface* src,
-                           const SkIRect& srcRect,
-                           const SkIPoint& dstPoint);
-
-protected:
-    void willReserveVertexAndIndexSpace(int vertexCount,
-                                        size_t vertexStride,
-                                        int indexCount);
-
 private:
-    friend class GrInOrderDrawBuffer;
+    friend class GrCommandBuilder;
+    friend class GrInOrderDrawBuffer; // This goes away when State becomes just a pipeline
+    friend class GrReorderCommandBuilder;
 
     typedef GrGpu::DrawArgs DrawArgs;
 
-    // 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(GrInOrderDrawBuffer*, const GrDrawTarget::DrawInfo&);
+    void recordXferBarrierIfNecessary(const GrPipeline&, GrInOrderDrawBuffer*);
 
-    bool SK_WARN_UNUSED_RESULT setupPipelineAndShouldDraw(GrInOrderDrawBuffer*,
-                                                          const GrPrimitiveProcessor*,
-                                                          const GrDrawTarget::PipelineInfo&);
-    bool SK_WARN_UNUSED_RESULT setupPipelineAndShouldDraw(GrInOrderDrawBuffer*,
-                                                          GrBatch*,
-                                                          const GrDrawTarget::PipelineInfo&);
+    // TODO: This can be just a pipeline once paths are in batch, and it should live elsewhere
+    struct State : public SkNVRefCnt<State> {
+        // TODO get rid of the prim proc parameter when we use batch everywhere
+        State(const GrPrimitiveProcessor* primProc = NULL)
+            : fPrimitiveProcessor(primProc)
+            , fCompiled(false) {}
 
-    struct Draw : public Cmd {
-        Draw(const GrDrawTarget::DrawInfo& info) : Cmd(kDraw_CmdType), fInfo(info) {}
+        ~State() { reinterpret_cast<GrPipeline*>(fPipeline.get())->~GrPipeline(); }
 
-        void execute(GrGpu*, const SetState*) SK_OVERRIDE;
+        // This function is only for getting the location in memory where we will create our
+        // pipeline object.
+        GrPipeline* pipelineLocation() { return reinterpret_cast<GrPipeline*>(fPipeline.get()); }
 
-        GrDrawTarget::DrawInfo     fInfo;
+        const GrPipeline* getPipeline() const {
+            return reinterpret_cast<const GrPipeline*>(fPipeline.get());
+        }
+        GrRenderTarget* getRenderTarget() const {
+            return this->getPipeline()->getRenderTarget();
+        }
+        const GrXferProcessor* getXferProcessor() const {
+            return this->getPipeline()->getXferProcessor();
+        }
+
+        void operator delete(void* p) {}
+        void* operator new(size_t) {
+            SkFAIL("All States are created by placement new.");
+            return sk_malloc_throw(0);
+        }
+
+        void* operator new(size_t, void* p) { return p; }
+        void operator delete(void* target, void* placement) {
+            ::operator delete(target, placement);
+        }
+
+        typedef GrPendingProgramElement<const GrPrimitiveProcessor> ProgramPrimitiveProcessor;
+        ProgramPrimitiveProcessor               fPrimitiveProcessor;
+        SkAlignedSStorage<sizeof(GrPipeline)>   fPipeline;
+        GrProgramDesc                           fDesc;
+        GrBatchTracker                          fBatchTracker;
+        bool                                    fCompiled;
     };
+    // TODO remove this when State is just a pipeline
+    friend SkNVRefCnt<State>;
 
     struct StencilPath : public Cmd {
         StencilPath(const GrPath* path, GrRenderTarget* rt)
@@ -158,7 +123,7 @@
 
         const GrPath* path() const { return fPath.get(); }
 
-        void execute(GrGpu*, const SetState*) SK_OVERRIDE;
+        void execute(GrGpu*) override;
 
         SkMatrix                                                fViewMatrix;
         bool                                                    fUseHWAA;
@@ -170,25 +135,32 @@
     };
 
     struct DrawPath : public Cmd {
-        DrawPath(const GrPath* path) : Cmd(kDrawPath_CmdType), fPath(path) {}
+        DrawPath(State* state, const GrPath* path)
+            : Cmd(kDrawPath_CmdType)
+            , fState(SkRef(state))
+            , fPath(path) {}
 
         const GrPath* path() const { return fPath.get(); }
 
-        void execute(GrGpu*, const SetState*) SK_OVERRIDE;
+        void execute(GrGpu*) override;
 
+        SkAutoTUnref<State>     fState;
         GrStencilSettings       fStencilSettings;
-
     private:
         GrPendingIOResource<const GrPath, kRead_GrIOType> fPath;
     };
 
     struct DrawPaths : public Cmd {
-        DrawPaths(const GrPathRange* pathRange) : Cmd(kDrawPaths_CmdType), fPathRange(pathRange) {}
+        DrawPaths(State* state, const GrPathRange* pathRange)
+            : Cmd(kDrawPaths_CmdType)
+            , fState(SkRef(state))
+            , fPathRange(pathRange) {}
 
         const GrPathRange* pathRange() const { return fPathRange.get();  }
 
-        void execute(GrGpu*, const SetState*) SK_OVERRIDE;
+        void execute(GrGpu*) override;
 
+        SkAutoTUnref<State>             fState;
         char*                           fIndices;
         GrDrawTarget::PathIndexType     fIndexType;
         float*                          fTransforms;
@@ -206,7 +178,7 @@
 
         GrRenderTarget* renderTarget() const { return fRenderTarget.get(); }
 
-        void execute(GrGpu*, const SetState*) SK_OVERRIDE;
+        void execute(GrGpu*) override;
 
         SkIRect fRect;
         GrColor fColor;
@@ -222,7 +194,7 @@
 
         GrRenderTarget* renderTarget() const { return fRenderTarget.get(); }
 
-        void execute(GrGpu*, const SetState*) SK_OVERRIDE;
+        void execute(GrGpu*) override;
 
         SkIRect fRect;
         bool    fInsideClip;
@@ -241,7 +213,7 @@
         GrSurface* dst() const { return fDst.get(); }
         GrSurface* src() const { return fSrc.get(); }
 
-        void execute(GrGpu*, const SetState*) SK_OVERRIDE;
+        void execute(GrGpu*) override;
 
         SkIPoint    fDstPoint;
         SkIRect     fSrcRect;
@@ -251,63 +223,48 @@
         GrPendingIOResource<GrSurface, kRead_GrIOType> fSrc;
     };
 
-    // TODO: rename to SetPipeline once pp, batch tracker, and desc are removed
-    struct SetState : public Cmd {
-        // TODO get rid of the prim proc parameter when we use batch everywhere
-        SetState(const GrPrimitiveProcessor* primProc = NULL)
-        : Cmd(kSetState_CmdType)
-        , fPrimitiveProcessor(primProc) {}
-
-        ~SetState() { reinterpret_cast<GrPipeline*>(fPipeline.get())->~GrPipeline(); }
-
-        // This function is only for getting the location in memory where we will create our
-        // pipeline object.
-        GrPipeline* pipelineLocation() { return reinterpret_cast<GrPipeline*>(fPipeline.get()); }
-
-        const GrPipeline* getPipeline() const {
-            return reinterpret_cast<const GrPipeline*>(fPipeline.get());
-        }
-
-        void execute(GrGpu*, const SetState*) SK_OVERRIDE;
-
-        typedef GrPendingProgramElement<const GrPrimitiveProcessor> ProgramPrimitiveProcessor;
-        ProgramPrimitiveProcessor               fPrimitiveProcessor;
-        SkAlignedSStorage<sizeof(GrPipeline)>   fPipeline;
-        GrProgramDesc                           fDesc;
-        GrBatchTracker                          fBatchTracker;
-    };
-
     struct DrawBatch : public Cmd {
-        DrawBatch(GrBatch* batch, GrBatchTarget* batchTarget) 
+        DrawBatch(State* state, GrBatch* batch, GrBatchTarget* batchTarget)
             : Cmd(kDrawBatch_CmdType)
+            , fState(SkRef(state))
             , fBatch(SkRef(batch))
             , fBatchTarget(batchTarget) {
             SkASSERT(!batch->isUsed());
         }
 
-        void execute(GrGpu*, const SetState*) SK_OVERRIDE;
+        void execute(GrGpu*) override;
 
-        // TODO it wouldn't be too hard to let batches allocate in the cmd buffer
+        SkAutoTUnref<State>    fState;
         SkAutoTUnref<GrBatch>  fBatch;
 
     private:
         GrBatchTarget*         fBatchTarget;
     };
 
-     static const int kCmdBufferInitialSizeInBytes = 8 * 1024;
+    struct XferBarrier : public Cmd {
+        XferBarrier(GrRenderTarget* rt)
+            : Cmd(kXferBarrier_CmdType)
+            , fRenderTarget(rt) {
+        }
 
-     typedef void* TCmdAlign; // This wouldn't be enough align if a command used long double.
-     typedef GrTRecorder<Cmd, TCmdAlign> CmdBuffer;
+        void execute(GrGpu*) override;
 
-     CmdBuffer                           fCmdBuffer;
-     SetState*                           fPrevState;
-     GrBatchTarget                       fBatchTarget;
-     // TODO hack until batch is everywhere
-     GrTargetCommands::DrawBatch*        fDrawBatch;
+        GrXferBarrierType   fBarrierType;
 
-     // This will go away when everything uses batch.  However, in the short term anything which
-     // might be put into the GrInOrderDrawBuffer needs to make sure it closes the last batch
-     void closeBatch();
+    private:
+        GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> fRenderTarget;
+    };
+
+    static const int kCmdBufferInitialSizeInBytes = 8 * 1024;
+
+    typedef void* TCmdAlign; // This wouldn't be enough align if a command used long double.
+    typedef GrTRecorder<Cmd, TCmdAlign> CmdBuffer;
+
+    CmdBuffer* cmdBuffer() { return &fCmdBuffer; }
+    GrBatchTarget* batchTarget() { return &fBatchTarget; }
+
+    CmdBuffer                           fCmdBuffer;
+    GrBatchTarget                       fBatchTarget;
 };
 
 #endif
diff --git a/src/gpu/GrTessellatingPathRenderer.cpp b/src/gpu/GrTessellatingPathRenderer.cpp
index 1170e1c..0fd8afd 100644
--- a/src/gpu/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/GrTessellatingPathRenderer.cpp
@@ -9,8 +9,10 @@
 
 #include "GrBatch.h"
 #include "GrBatchTarget.h"
+#include "GrBatchTest.h"
 #include "GrDefaultGeoProcFactory.h"
 #include "GrPathUtils.h"
+#include "GrVertices.h"
 #include "SkChunkAlloc.h"
 #include "SkGeometry.h"
 
@@ -67,21 +69,16 @@
  * Only type 2 vertices (see paper) require the O(N) lookups, and these are much less
  * frequent. There may be other data structures worth investigating, however.
  *
- * Note that there is a compile-time flag (SWEEP_IN_X) which changes the orientation of the
- * line sweep algorithms. When SWEEP_IN_X is unset, we sort vertices based on increasing
- * Y coordinate, and secondarily by increasing X coordinate. When SWEEP_IN_X is set, we sort by
- * increasing X coordinate, but secondarily by *decreasing* Y coordinate. This is so that the
- * "left" and "right" orientation in the code remains correct (edges to the left are increasing
- * in Y; edges to the right are decreasing in Y). That is, the setting rotates 90 degrees
- * counterclockwise, rather that transposing.
- *
- * The choice is arbitrary, but most test cases are wider than they are tall, so the
- * default is to sweep in X. In the future, we may want to make this a runtime parameter
- * and base it on the aspect ratio of the clip bounds.
+ * Note that the orientation of the line sweep algorithms is determined by the aspect ratio of the
+ * path bounds. When the path is taller than it is wide, we sort vertices based on increasing Y
+ * coordinate, and secondarily by increasing X coordinate. When the path is wider than it is tall,
+ * we sort by increasing X coordinate, but secondarily by *decreasing* Y coordinate. This is so
+ * that the "left" and "right" orientation in the code remains correct (edges to the left are
+ * increasing in Y; edges to the right are decreasing in Y). That is, the setting rotates 90
+ * degrees counterclockwise, rather that transposing.
  */
 #define LOGGING_ENABLED 0
 #define WIREFRAME 0
-#define SWEEP_IN_X 1
 
 #if LOGGING_ENABLED
 #define LOG printf
@@ -165,29 +162,35 @@
 
 /***************************************************************************************/
 
-bool sweep_lt(const SkPoint& a, const SkPoint& b) {
-#if SWEEP_IN_X
+typedef bool (*CompareFunc)(const SkPoint& a, const SkPoint& b);
+
+struct Comparator {
+    CompareFunc sweep_lt;
+    CompareFunc sweep_gt;
+};
+
+bool sweep_lt_horiz(const SkPoint& a, const SkPoint& b) {
     return a.fX == b.fX ? a.fY > b.fY : a.fX < b.fX;
-#else
+}
+
+bool sweep_lt_vert(const SkPoint& a, const SkPoint& b) {
     return a.fY == b.fY ? a.fX < b.fX : a.fY < b.fY;
-#endif
 }
 
-bool sweep_gt(const SkPoint& a, const SkPoint& b) {
-#if SWEEP_IN_X
+bool sweep_gt_horiz(const SkPoint& a, const SkPoint& b) {
     return a.fX == b.fX ? a.fY < b.fY : a.fX > b.fX;
-#else
+}
+
+bool sweep_gt_vert(const SkPoint& a, const SkPoint& b) {
     return a.fY == b.fY ? a.fX > b.fX : a.fY > b.fY;
-#endif
 }
 
-inline void* emit_vertex(Vertex* v, void* data) {
-    SkPoint* d = static_cast<SkPoint*>(data);
-    *d++ = v->fPoint;
-    return d;
+inline SkPoint* emit_vertex(Vertex* v, SkPoint* data) {
+    *data++ = v->fPoint;
+    return data;
 }
 
-void* emit_triangle(Vertex* v0, Vertex* v1, Vertex* v2, void* data) {
+SkPoint* emit_triangle(Vertex* v0, Vertex* v1, Vertex* v2, SkPoint* data) {
 #if WIREFRAME
     data = emit_vertex(v0, data);
     data = emit_vertex(v1, data);
@@ -203,6 +206,12 @@
     return data;
 }
 
+struct EdgeList {
+    EdgeList() : fHead(NULL), fTail(NULL) {}
+    Edge* fHead;
+    Edge* fTail;
+};
+
 /**
  * An Edge joins a top Vertex to a bottom Vertex. Edge ordering for the list of "edges above" and
  * "edge below" a vertex as well as for the active edge list is handled by isLeftOf()/isRightOf().
@@ -292,8 +301,8 @@
         p->fY = SkDoubleToScalar(fTop->fPoint.fY + s * fDY);
         return true;
     }
-    bool isActive(Edge** activeEdges) const {
-        return activeEdges && (fLeft || fRight || *activeEdges == this);
+    bool isActive(EdgeList* activeEdges) const {
+        return activeEdges && (fLeft || fRight || activeEdges->fHead == this);
     }
 };
 
@@ -350,14 +359,11 @@
             return done;
         }
 
-        void* emit(void* data) {
+        SkPoint* emit(SkPoint* data) {
             Vertex* first = fHead;
             Vertex* v = first->fNext;
             while (v != fTail) {
                 SkASSERT(v && v->fPrev && v->fNext);
-#ifdef SK_DEBUG
-                validate();
-#endif
                 Vertex* prev = v->fPrev;
                 Vertex* curr = v;
                 Vertex* next = v->fNext;
@@ -376,27 +382,10 @@
                     }
                 } else {
                     v = v->fNext;
-                    SkASSERT(v != fTail);
                 }
             }
             return data;
         }
-
-#ifdef SK_DEBUG
-        void validate() {
-            int winding = sweep_lt(fHead->fPoint, fTail->fPoint) ? 1 : -1;
-            Vertex* top = winding < 0 ? fTail : fHead;
-            Vertex* bottom = winding < 0 ? fHead : fTail;
-            Edge e(top, bottom, winding);
-            for (Vertex* v = fHead->fNext; v != fTail; v = v->fNext) {
-                if (fSide == kRight_Side) {
-                    SkASSERT(!e.isRightOf(v));
-                } else if (fSide == Poly::kLeft_Side) {
-                    SkASSERT(!e.isLeftOf(v));
-                }
-            }
-        }
-#endif
     };
     Poly* addVertex(Vertex* v, Side side, SkChunkAlloc& alloc) {
         LOG("addVertex() to %d at %g (%g, %g), %s side\n", fID, v->fID, v->fPoint.fX, v->fPoint.fY,
@@ -410,9 +399,6 @@
             fActive = ALLOC_NEW(MonotonePoly, (), alloc);
         }
         if (fActive->addVertex(v, side, alloc)) {
-#ifdef SK_DEBUG
-            fActive->validate();
-#endif
             if (fTail) {
                 fActive->fPrev = fTail;
                 fTail->fNext = fActive;
@@ -441,7 +427,7 @@
         }
         addVertex(v, fActive->fSide == kLeft_Side ? kRight_Side : kLeft_Side, alloc);
     }
-    void* emit(void *data) {
+    SkPoint* emit(SkPoint *data) {
         if (fCount < 3) {
             return data;
         }
@@ -477,67 +463,6 @@
     return poly;
 }
 
-#ifdef SK_DEBUG
-void validate_edges(Edge* head) {
-    for (Edge* e = head; e != NULL; e = e->fRight) {
-        SkASSERT(e->fTop != e->fBottom);
-        if (e->fLeft) {
-            SkASSERT(e->fLeft->fRight == e);
-            if (sweep_gt(e->fTop->fPoint, e->fLeft->fTop->fPoint)) {
-                SkASSERT(e->fLeft->isLeftOf(e->fTop));
-            }
-            if (sweep_lt(e->fBottom->fPoint, e->fLeft->fBottom->fPoint)) {
-                SkASSERT(e->fLeft->isLeftOf(e->fBottom));
-            }
-        } else {
-            SkASSERT(e == head);
-        }
-        if (e->fRight) {
-            SkASSERT(e->fRight->fLeft == e);
-            if (sweep_gt(e->fTop->fPoint, e->fRight->fTop->fPoint)) {
-                SkASSERT(e->fRight->isRightOf(e->fTop));
-            }
-            if (sweep_lt(e->fBottom->fPoint, e->fRight->fBottom->fPoint)) {
-                SkASSERT(e->fRight->isRightOf(e->fBottom));
-            }
-        }
-    }
-}
-
-void validate_connectivity(Vertex* v) {
-    for (Edge* e = v->fFirstEdgeAbove; e != NULL; e = e->fNextEdgeAbove) {
-        SkASSERT(e->fBottom == v);
-        if (e->fPrevEdgeAbove) {
-            SkASSERT(e->fPrevEdgeAbove->fNextEdgeAbove == e);
-            SkASSERT(e->fPrevEdgeAbove->isLeftOf(e->fTop));
-        } else {
-            SkASSERT(e == v->fFirstEdgeAbove);
-        }
-        if (e->fNextEdgeAbove) {
-            SkASSERT(e->fNextEdgeAbove->fPrevEdgeAbove == e);
-            SkASSERT(e->fNextEdgeAbove->isRightOf(e->fTop));
-        } else {
-            SkASSERT(e == v->fLastEdgeAbove);
-        }
-    }
-    for (Edge* e = v->fFirstEdgeBelow; e != NULL; e = e->fNextEdgeBelow) {
-        SkASSERT(e->fTop == v);
-        if (e->fPrevEdgeBelow) {
-            SkASSERT(e->fPrevEdgeBelow->fNextEdgeBelow == e);
-            SkASSERT(e->fPrevEdgeBelow->isLeftOf(e->fBottom));
-        } else {
-            SkASSERT(e == v->fFirstEdgeBelow);
-        }
-        if (e->fNextEdgeBelow) {
-            SkASSERT(e->fNextEdgeBelow->fPrevEdgeBelow == e);
-            SkASSERT(e->fNextEdgeBelow->isRightOf(e->fBottom));
-        } else {
-            SkASSERT(e == v->fLastEdgeBelow);
-        }
-    }
-}
-#endif
-
 Vertex* append_point_to_contour(const SkPoint& p, Vertex* prev, Vertex** head,
                                 SkChunkAlloc& alloc) {
     Vertex* v = ALLOC_NEW(Vertex, (p), alloc);
@@ -641,7 +566,7 @@
                 SkScalar weight = iter.conicWeight();
                 const SkPoint* quadPts = converter.computeQuads(pts, weight, toleranceSqd);
                 for (int i = 0; i < converter.countQuads(); ++i) {
-                    int pointsLeft = GrPathUtils::quadraticPointCount(quadPts, toleranceSqd);
+                    int pointsLeft = GrPathUtils::quadraticPointCount(quadPts, tolerance);
                     prev = generate_quadratic_points(quadPts[0], quadPts[1], quadPts[2],
                                                      toleranceSqd, prev, &head, pointsLeft, alloc);
                     quadPts += 2;
@@ -662,13 +587,13 @@
                 break;
             }
             case SkPath::kQuad_Verb: {
-                int pointsLeft = GrPathUtils::quadraticPointCount(pts, toleranceSqd);
+                int pointsLeft = GrPathUtils::quadraticPointCount(pts, tolerance);
                 prev = generate_quadratic_points(pts[0], pts[1], pts[2], toleranceSqd, prev,
                                                  &head, pointsLeft, alloc);
                 break;
             }
             case SkPath::kCubic_Verb: {
-                int pointsLeft = GrPathUtils::cubicPointCount(pts, toleranceSqd);
+                int pointsLeft = GrPathUtils::cubicPointCount(pts, tolerance);
                 prev = generate_cubic_points(pts[0], pts[1], pts[2], pts[3],
                                 toleranceSqd, prev, &head, pointsLeft, alloc);
                 break;
@@ -709,54 +634,54 @@
     }
 }
 
-Edge* new_edge(Vertex* prev, Vertex* next, SkChunkAlloc& alloc) {
-    int winding = sweep_lt(prev->fPoint, next->fPoint) ? 1 : -1;
+Edge* new_edge(Vertex* prev, Vertex* next, SkChunkAlloc& alloc, Comparator& c) {
+    int winding = c.sweep_lt(prev->fPoint, next->fPoint) ? 1 : -1;
     Vertex* top = winding < 0 ? next : prev;
     Vertex* bottom = winding < 0 ? prev : next;
     return ALLOC_NEW(Edge, (top, bottom, winding), alloc);
 }
 
-void remove_edge(Edge* edge, Edge** head) {
+void remove_edge(Edge* edge, EdgeList* edges) {
     LOG("removing edge %g -> %g\n", edge->fTop->fID, edge->fBottom->fID);
-    SkASSERT(edge->isActive(head));
-    remove<Edge, &Edge::fLeft, &Edge::fRight>(edge, head, NULL);
+    SkASSERT(edge->isActive(edges));
+    remove<Edge, &Edge::fLeft, &Edge::fRight>(edge, &edges->fHead, &edges->fTail);
 }
 
-void insert_edge(Edge* edge, Edge* prev, Edge** head) {
+void insert_edge(Edge* edge, Edge* prev, EdgeList* edges) {
     LOG("inserting edge %g -> %g\n", edge->fTop->fID, edge->fBottom->fID);
-    SkASSERT(!edge->isActive(head));
-    Edge* next = prev ? prev->fRight : *head;
-    insert<Edge, &Edge::fLeft, &Edge::fRight>(edge, prev, next, head, NULL);
+    SkASSERT(!edge->isActive(edges));
+    Edge* next = prev ? prev->fRight : edges->fHead;
+    insert<Edge, &Edge::fLeft, &Edge::fRight>(edge, prev, next, &edges->fHead, &edges->fTail);
 }
 
-void find_enclosing_edges(Vertex* v, Edge* head, Edge** left, Edge** right) {
+void find_enclosing_edges(Vertex* v, EdgeList* edges, Edge** left, Edge** right) {
     if (v->fFirstEdgeAbove) {
         *left = v->fFirstEdgeAbove->fLeft;
         *right = v->fLastEdgeAbove->fRight;
         return;
     }
-    Edge* prev = NULL;
-    Edge* next;
-    for (next = head; next != NULL; next = next->fRight) {
-        if (next->isRightOf(v)) {
+    Edge* next = NULL;
+    Edge* prev;
+    for (prev = edges->fTail; prev != NULL; prev = prev->fLeft) {
+        if (prev->isLeftOf(v)) {
             break;
         }
-        prev = next;
+        next = prev;
     }
     *left = prev;
     *right = next;
     return;
 }
 
-void find_enclosing_edges(Edge* edge, Edge* head, Edge** left, Edge** right) {
+void find_enclosing_edges(Edge* edge, EdgeList* edges, Comparator& c, Edge** left, Edge** right) {
     Edge* prev = NULL;
     Edge* next;
-    for (next = head; next != NULL; next = next->fRight) {
-        if ((sweep_gt(edge->fTop->fPoint, next->fTop->fPoint) && next->isRightOf(edge->fTop)) ||
-            (sweep_gt(next->fTop->fPoint, edge->fTop->fPoint) && edge->isLeftOf(next->fTop)) ||
-            (sweep_lt(edge->fBottom->fPoint, next->fBottom->fPoint) &&
+    for (next = edges->fHead; next != NULL; next = next->fRight) {
+        if ((c.sweep_gt(edge->fTop->fPoint, next->fTop->fPoint) && next->isRightOf(edge->fTop)) ||
+            (c.sweep_gt(next->fTop->fPoint, edge->fTop->fPoint) && edge->isLeftOf(next->fTop)) ||
+            (c.sweep_lt(edge->fBottom->fPoint, next->fBottom->fPoint) &&
              next->isRightOf(edge->fBottom)) ||
-            (sweep_lt(next->fBottom->fPoint, edge->fBottom->fPoint) &&
+            (c.sweep_lt(next->fBottom->fPoint, edge->fBottom->fPoint) &&
              edge->isLeftOf(next->fBottom))) {
             break;
         }
@@ -767,7 +692,7 @@
     return;
 }
 
-void fix_active_state(Edge* edge, Edge** activeEdges) {
+void fix_active_state(Edge* edge, EdgeList* activeEdges, Comparator& c) {
     if (edge->isActive(activeEdges)) {
         if (edge->fBottom->fProcessed || !edge->fTop->fProcessed) {
             remove_edge(edge, activeEdges);
@@ -775,15 +700,14 @@
     } else if (edge->fTop->fProcessed && !edge->fBottom->fProcessed) {
         Edge* left;
         Edge* right;
-        find_enclosing_edges(edge, *activeEdges, &left, &right);
+        find_enclosing_edges(edge, activeEdges, c, &left, &right);
         insert_edge(edge, left, activeEdges);
     }
 }
 
-void insert_edge_above(Edge* edge, Vertex* v) {
+void insert_edge_above(Edge* edge, Vertex* v, Comparator& c) {
     if (edge->fTop->fPoint == edge->fBottom->fPoint ||
-        sweep_gt(edge->fTop->fPoint, edge->fBottom->fPoint)) {
-        SkASSERT(false);
+        c.sweep_gt(edge->fTop->fPoint, edge->fBottom->fPoint)) {
         return;
     }
     LOG("insert edge (%g -> %g) above vertex %g\n", edge->fTop->fID, edge->fBottom->fID, v->fID);
@@ -799,10 +723,9 @@
         edge, prev, next, &v->fFirstEdgeAbove, &v->fLastEdgeAbove);
 }
 
-void insert_edge_below(Edge* edge, Vertex* v) {
+void insert_edge_below(Edge* edge, Vertex* v, Comparator& c) {
     if (edge->fTop->fPoint == edge->fBottom->fPoint ||
-        sweep_gt(edge->fTop->fPoint, edge->fBottom->fPoint)) {
-        SkASSERT(false);
+        c.sweep_gt(edge->fTop->fPoint, edge->fBottom->fPoint)) {
         return;
     }
     LOG("insert edge (%g -> %g) below vertex %g\n", edge->fTop->fID, edge->fBottom->fID, v->fID);
@@ -832,39 +755,39 @@
         edge, &edge->fTop->fFirstEdgeBelow, &edge->fTop->fLastEdgeBelow);
 }
 
-void erase_edge_if_zero_winding(Edge* edge, Edge** head) {
+void erase_edge_if_zero_winding(Edge* edge, EdgeList* edges) {
     if (edge->fWinding != 0) {
         return;
     }
     LOG("erasing edge (%g -> %g)\n", edge->fTop->fID, edge->fBottom->fID);
     remove_edge_above(edge);
     remove_edge_below(edge);
-    if (edge->isActive(head)) {
-        remove_edge(edge, head);
+    if (edge->isActive(edges)) {
+        remove_edge(edge, edges);
     }
 }
 
-void merge_collinear_edges(Edge* edge, Edge** activeEdges);
+void merge_collinear_edges(Edge* edge, EdgeList* activeEdges, Comparator& c);
 
-void set_top(Edge* edge, Vertex* v, Edge** activeEdges) {
+void set_top(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c) {
     remove_edge_below(edge);
     edge->fTop = v;
     edge->recompute();
-    insert_edge_below(edge, v);
-    fix_active_state(edge, activeEdges);
-    merge_collinear_edges(edge, activeEdges);
+    insert_edge_below(edge, v, c);
+    fix_active_state(edge, activeEdges, c);
+    merge_collinear_edges(edge, activeEdges, c);
 }
 
-void set_bottom(Edge* edge, Vertex* v, Edge** activeEdges) {
+void set_bottom(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c) {
     remove_edge_above(edge);
     edge->fBottom = v;
     edge->recompute();
-    insert_edge_above(edge, v);
-    fix_active_state(edge, activeEdges);
-    merge_collinear_edges(edge, activeEdges);
+    insert_edge_above(edge, v, c);
+    fix_active_state(edge, activeEdges, c);
+    merge_collinear_edges(edge, activeEdges, c);
 }
 
-void merge_edges_above(Edge* edge, Edge* other, Edge** activeEdges) {
+void merge_edges_above(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c) {
     if (coincident(edge->fTop->fPoint, other->fTop->fPoint)) {
         LOG("merging coincident above edges (%g, %g) -> (%g, %g)\n",
             edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
@@ -873,18 +796,18 @@
         erase_edge_if_zero_winding(other, activeEdges);
         edge->fWinding = 0;
         erase_edge_if_zero_winding(edge, activeEdges);
-    } else if (sweep_lt(edge->fTop->fPoint, other->fTop->fPoint)) {
+    } else if (c.sweep_lt(edge->fTop->fPoint, other->fTop->fPoint)) {
         other->fWinding += edge->fWinding;
         erase_edge_if_zero_winding(other, activeEdges);
-        set_bottom(edge, other->fTop, activeEdges);
+        set_bottom(edge, other->fTop, activeEdges, c);
     } else {
         edge->fWinding += other->fWinding;
         erase_edge_if_zero_winding(edge, activeEdges);
-        set_bottom(other, edge->fTop, activeEdges);
+        set_bottom(other, edge->fTop, activeEdges, c);
     }
 }
 
-void merge_edges_below(Edge* edge, Edge* other, Edge** activeEdges) {
+void merge_edges_below(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c) {
     if (coincident(edge->fBottom->fPoint, other->fBottom->fPoint)) {
         LOG("merging coincident below edges (%g, %g) -> (%g, %g)\n",
             edge->fTop->fPoint.fX, edge->fTop->fPoint.fY,
@@ -893,105 +816,107 @@
         erase_edge_if_zero_winding(other, activeEdges);
         edge->fWinding = 0;
         erase_edge_if_zero_winding(edge, activeEdges);
-    } else if (sweep_lt(edge->fBottom->fPoint, other->fBottom->fPoint)) {
+    } else if (c.sweep_lt(edge->fBottom->fPoint, other->fBottom->fPoint)) {
         edge->fWinding += other->fWinding;
         erase_edge_if_zero_winding(edge, activeEdges);
-        set_top(other, edge->fBottom, activeEdges);
+        set_top(other, edge->fBottom, activeEdges, c);
     } else {
         other->fWinding += edge->fWinding;
         erase_edge_if_zero_winding(other, activeEdges);
-        set_top(edge, other->fBottom, activeEdges);
+        set_top(edge, other->fBottom, activeEdges, c);
     }
 }
 
-void merge_collinear_edges(Edge* edge, Edge** activeEdges) {
+void merge_collinear_edges(Edge* edge, EdgeList* activeEdges, Comparator& c) {
     if (edge->fPrevEdgeAbove && (edge->fTop == edge->fPrevEdgeAbove->fTop ||
                                  !edge->fPrevEdgeAbove->isLeftOf(edge->fTop))) {
-        merge_edges_above(edge, edge->fPrevEdgeAbove, activeEdges);
+        merge_edges_above(edge, edge->fPrevEdgeAbove, activeEdges, c);
     } else if (edge->fNextEdgeAbove && (edge->fTop == edge->fNextEdgeAbove->fTop ||
                                         !edge->isLeftOf(edge->fNextEdgeAbove->fTop))) {
-        merge_edges_above(edge, edge->fNextEdgeAbove, activeEdges);
+        merge_edges_above(edge, edge->fNextEdgeAbove, activeEdges, c);
     }
     if (edge->fPrevEdgeBelow && (edge->fBottom == edge->fPrevEdgeBelow->fBottom ||
                                  !edge->fPrevEdgeBelow->isLeftOf(edge->fBottom))) {
-        merge_edges_below(edge, edge->fPrevEdgeBelow, activeEdges);
+        merge_edges_below(edge, edge->fPrevEdgeBelow, activeEdges, c);
     } else if (edge->fNextEdgeBelow && (edge->fBottom == edge->fNextEdgeBelow->fBottom ||
                                         !edge->isLeftOf(edge->fNextEdgeBelow->fBottom))) {
-        merge_edges_below(edge, edge->fNextEdgeBelow, activeEdges);
+        merge_edges_below(edge, edge->fNextEdgeBelow, activeEdges, c);
     }
 }
 
-void split_edge(Edge* edge, Vertex* v, Edge** activeEdges, SkChunkAlloc& alloc);
+void split_edge(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c, SkChunkAlloc& alloc);
 
-void cleanup_active_edges(Edge* edge, Edge** activeEdges, SkChunkAlloc& alloc) {
+void cleanup_active_edges(Edge* edge, EdgeList* activeEdges, Comparator& c, SkChunkAlloc& alloc) {
     Vertex* top = edge->fTop;
     Vertex* bottom = edge->fBottom;
     if (edge->fLeft) {
         Vertex* leftTop = edge->fLeft->fTop;
         Vertex* leftBottom = edge->fLeft->fBottom;
-        if (sweep_gt(top->fPoint, leftTop->fPoint) && !edge->fLeft->isLeftOf(top)) {
-            split_edge(edge->fLeft, edge->fTop, activeEdges, alloc);
-        } else if (sweep_gt(leftTop->fPoint, top->fPoint) && !edge->isRightOf(leftTop)) {
-            split_edge(edge, leftTop, activeEdges, alloc);
-        } else if (sweep_lt(bottom->fPoint, leftBottom->fPoint) && !edge->fLeft->isLeftOf(bottom)) {
-            split_edge(edge->fLeft, bottom, activeEdges, alloc);
-        } else if (sweep_lt(leftBottom->fPoint, bottom->fPoint) && !edge->isRightOf(leftBottom)) {
-            split_edge(edge, leftBottom, activeEdges, alloc);
+        if (c.sweep_gt(top->fPoint, leftTop->fPoint) && !edge->fLeft->isLeftOf(top)) {
+            split_edge(edge->fLeft, edge->fTop, activeEdges, c, alloc);
+        } else if (c.sweep_gt(leftTop->fPoint, top->fPoint) && !edge->isRightOf(leftTop)) {
+            split_edge(edge, leftTop, activeEdges, c, alloc);
+        } else if (c.sweep_lt(bottom->fPoint, leftBottom->fPoint) &&
+                   !edge->fLeft->isLeftOf(bottom)) {
+            split_edge(edge->fLeft, bottom, activeEdges, c, alloc);
+        } else if (c.sweep_lt(leftBottom->fPoint, bottom->fPoint) && !edge->isRightOf(leftBottom)) {
+            split_edge(edge, leftBottom, activeEdges, c, alloc);
         }
     }
     if (edge->fRight) {
         Vertex* rightTop = edge->fRight->fTop;
         Vertex* rightBottom = edge->fRight->fBottom;
-        if (sweep_gt(top->fPoint, rightTop->fPoint) && !edge->fRight->isRightOf(top)) {
-            split_edge(edge->fRight, top, activeEdges, alloc);
-        } else if (sweep_gt(rightTop->fPoint, top->fPoint) && !edge->isLeftOf(rightTop)) {
-            split_edge(edge, rightTop, activeEdges, alloc);
-        } else if (sweep_lt(bottom->fPoint, rightBottom->fPoint) &&
+        if (c.sweep_gt(top->fPoint, rightTop->fPoint) && !edge->fRight->isRightOf(top)) {
+            split_edge(edge->fRight, top, activeEdges, c, alloc);
+        } else if (c.sweep_gt(rightTop->fPoint, top->fPoint) && !edge->isLeftOf(rightTop)) {
+            split_edge(edge, rightTop, activeEdges, c, alloc);
+        } else if (c.sweep_lt(bottom->fPoint, rightBottom->fPoint) &&
                    !edge->fRight->isRightOf(bottom)) {
-            split_edge(edge->fRight, bottom, activeEdges, alloc);
-        } else if (sweep_lt(rightBottom->fPoint, bottom->fPoint) &&
+            split_edge(edge->fRight, bottom, activeEdges, c, alloc);
+        } else if (c.sweep_lt(rightBottom->fPoint, bottom->fPoint) &&
                    !edge->isLeftOf(rightBottom)) {
-            split_edge(edge, rightBottom, activeEdges, alloc);
+            split_edge(edge, rightBottom, activeEdges, c, alloc);
         }
     }
 }
 
-void split_edge(Edge* edge, Vertex* v, Edge** activeEdges, SkChunkAlloc& alloc) {
+void split_edge(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c, SkChunkAlloc& alloc) {
     LOG("splitting edge (%g -> %g) at vertex %g (%g, %g)\n",
         edge->fTop->fID, edge->fBottom->fID,
         v->fID, v->fPoint.fX, v->fPoint.fY);
-    if (sweep_lt(v->fPoint, edge->fTop->fPoint)) {
-        set_top(edge, v, activeEdges);
-    } else if (sweep_gt(v->fPoint, edge->fBottom->fPoint)) {
-        set_bottom(edge, v, activeEdges);
+    if (c.sweep_lt(v->fPoint, edge->fTop->fPoint)) {
+        set_top(edge, v, activeEdges, c);
+    } else if (c.sweep_gt(v->fPoint, edge->fBottom->fPoint)) {
+        set_bottom(edge, v, activeEdges, c);
     } else {
         Edge* newEdge = ALLOC_NEW(Edge, (v, edge->fBottom, edge->fWinding), alloc);
-        insert_edge_below(newEdge, v);
-        insert_edge_above(newEdge, edge->fBottom);
-        set_bottom(edge, v, activeEdges);
-        cleanup_active_edges(edge, activeEdges, alloc);
-        fix_active_state(newEdge, activeEdges);
-        merge_collinear_edges(newEdge, activeEdges);
+        insert_edge_below(newEdge, v, c);
+        insert_edge_above(newEdge, edge->fBottom, c);
+        set_bottom(edge, v, activeEdges, c);
+        cleanup_active_edges(edge, activeEdges, c, alloc);
+        fix_active_state(newEdge, activeEdges, c);
+        merge_collinear_edges(newEdge, activeEdges, c);
     }
 }
 
-void merge_vertices(Vertex* src, Vertex* dst, Vertex** head, SkChunkAlloc& alloc) {
+void merge_vertices(Vertex* src, Vertex* dst, Vertex** head, Comparator& c, SkChunkAlloc& alloc) {
     LOG("found coincident verts at %g, %g; merging %g into %g\n", src->fPoint.fX, src->fPoint.fY,
         src->fID, dst->fID);
     for (Edge* edge = src->fFirstEdgeAbove; edge;) {
         Edge* next = edge->fNextEdgeAbove;
-        set_bottom(edge, dst, NULL);
+        set_bottom(edge, dst, NULL, c);
         edge = next;
     }
     for (Edge* edge = src->fFirstEdgeBelow; edge;) {
         Edge* next = edge->fNextEdgeBelow;
-        set_top(edge, dst, NULL);
+        set_top(edge, dst, NULL, c);
         edge = next;
     }
     remove<Vertex, &Vertex::fPrev, &Vertex::fNext>(src, head, NULL);
 }
 
-Vertex* check_for_intersection(Edge* edge, Edge* other, Edge** activeEdges, SkChunkAlloc& alloc) {
+Vertex* check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c,
+                               SkChunkAlloc& alloc) {
     SkPoint p;
     if (!edge || !other) {
         return NULL;
@@ -999,24 +924,24 @@
     if (edge->intersect(*other, &p)) {
         Vertex* v;
         LOG("found intersection, pt is %g, %g\n", p.fX, p.fY);
-        if (p == edge->fTop->fPoint || sweep_lt(p, edge->fTop->fPoint)) {
-            split_edge(other, edge->fTop, activeEdges, alloc);
+        if (p == edge->fTop->fPoint || c.sweep_lt(p, edge->fTop->fPoint)) {
+            split_edge(other, edge->fTop, activeEdges, c, alloc);
             v = edge->fTop;
-        } else if (p == edge->fBottom->fPoint || sweep_gt(p, edge->fBottom->fPoint)) {
-            split_edge(other, edge->fBottom, activeEdges, alloc);
+        } else if (p == edge->fBottom->fPoint || c.sweep_gt(p, edge->fBottom->fPoint)) {
+            split_edge(other, edge->fBottom, activeEdges, c, alloc);
             v = edge->fBottom;
-        } else if (p == other->fTop->fPoint || sweep_lt(p, other->fTop->fPoint)) {
-            split_edge(edge, other->fTop, activeEdges, alloc);
+        } else if (p == other->fTop->fPoint || c.sweep_lt(p, other->fTop->fPoint)) {
+            split_edge(edge, other->fTop, activeEdges, c, alloc);
             v = other->fTop;
-        } else if (p == other->fBottom->fPoint || sweep_gt(p, other->fBottom->fPoint)) {
-            split_edge(edge, other->fBottom, activeEdges, alloc);
+        } else if (p == other->fBottom->fPoint || c.sweep_gt(p, other->fBottom->fPoint)) {
+            split_edge(edge, other->fBottom, activeEdges, c, alloc);
             v = other->fBottom;
         } else {
             Vertex* nextV = edge->fTop;
-            while (sweep_lt(p, nextV->fPoint)) {
+            while (c.sweep_lt(p, nextV->fPoint)) {
                 nextV = nextV->fPrev;
             }
-            while (sweep_lt(nextV->fPoint, p)) {
+            while (c.sweep_lt(nextV->fPoint, p)) {
                 nextV = nextV->fNext;
             }
             Vertex* prevV = nextV->fPrev;
@@ -1037,12 +962,9 @@
                 prevV->fNext = v;
                 nextV->fPrev = v;
             }
-            split_edge(edge, v, activeEdges, alloc);
-            split_edge(other, v, activeEdges, alloc);
+            split_edge(edge, v, activeEdges, c, alloc);
+            split_edge(other, v, activeEdges, c, alloc);
         }
-#ifdef SK_DEBUG
-        validate_connectivity(v);
-#endif
         return v;
     }
     return NULL;
@@ -1072,34 +994,34 @@
     }
 }
 
-void merge_coincident_vertices(Vertex** vertices, SkChunkAlloc& alloc) {
+void merge_coincident_vertices(Vertex** vertices, Comparator& c, SkChunkAlloc& alloc) {
     for (Vertex* v = (*vertices)->fNext; v != NULL; v = v->fNext) {
-        if (sweep_lt(v->fPoint, v->fPrev->fPoint)) {
+        if (c.sweep_lt(v->fPoint, v->fPrev->fPoint)) {
             v->fPoint = v->fPrev->fPoint;
         }
         if (coincident(v->fPrev->fPoint, v->fPoint)) {
-            merge_vertices(v->fPrev, v, vertices, alloc);
+            merge_vertices(v->fPrev, v, vertices, c, alloc);
         }
     }
 }
 
 // Stage 2: convert the contours to a mesh of edges connecting the vertices.
 
-Vertex* build_edges(Vertex** contours, int contourCnt, SkChunkAlloc& alloc) {
+Vertex* build_edges(Vertex** contours, int contourCnt, Comparator& c, SkChunkAlloc& alloc) {
     Vertex* vertices = NULL;
     Vertex* prev = NULL;
     for (int i = 0; i < contourCnt; ++i) {
         for (Vertex* v = contours[i]; v != NULL;) {
             Vertex* vNext = v->fNext;
-            Edge* edge = new_edge(v->fPrev, v, alloc);
+            Edge* edge = new_edge(v->fPrev, v, alloc, c);
             if (edge->fWinding > 0) {
-                insert_edge_below(edge, v->fPrev);
-                insert_edge_above(edge, v);
+                insert_edge_below(edge, v->fPrev, c);
+                insert_edge_above(edge, v, c);
             } else {
-                insert_edge_below(edge, v);
-                insert_edge_above(edge, v->fPrev);
+                insert_edge_below(edge, v, c);
+                insert_edge_above(edge, v->fPrev, c);
             }
-            merge_collinear_edges(edge, NULL);
+            merge_collinear_edges(edge, NULL, c);
             if (prev) {
                 prev->fNext = v;
                 v->fPrev = prev;
@@ -1117,9 +1039,9 @@
     return vertices;
 }
 
-// Stage 3: sort the vertices by increasing Y (or X if SWEEP_IN_X is on).
+// Stage 3: sort the vertices by increasing sweep direction.
 
-Vertex* sorted_merge(Vertex* a, Vertex* b);
+Vertex* sorted_merge(Vertex* a, Vertex* b, Comparator& c);
 
 void front_back_split(Vertex* v, Vertex** pFront, Vertex** pBack) {
     Vertex* fast;
@@ -1146,7 +1068,7 @@
     }
 }
 
-void merge_sort(Vertex** head) {
+void merge_sort(Vertex** head, Comparator& c) {
     if (!*head || !(*head)->fNext) {
         return;
     }
@@ -1155,37 +1077,49 @@
     Vertex* b;
     front_back_split(*head, &a, &b);
 
-    merge_sort(&a);
-    merge_sort(&b);
+    merge_sort(&a, c);
+    merge_sort(&b, c);
 
-    *head = sorted_merge(a, b);
+    *head = sorted_merge(a, b, c);
 }
 
-Vertex* sorted_merge(Vertex* a, Vertex* b) {
-    if (!a) {
-        return b;
-    } else if (!b) {
-        return a;
-    }
+inline void append_vertex(Vertex* v, Vertex** head, Vertex** tail) {
+    insert<Vertex, &Vertex::fPrev, &Vertex::fNext>(v, *tail, NULL, head, tail);
+}
 
-    Vertex* result = NULL;
+inline void append_vertex_list(Vertex* v, Vertex** head, Vertex** tail) {
+    insert<Vertex, &Vertex::fPrev, &Vertex::fNext>(v, *tail, v->fNext, head, tail);
+}
 
-    if (sweep_lt(a->fPoint, b->fPoint)) {
-        result = a;
-        result->fNext = sorted_merge(a->fNext, b);
-    } else {
-        result = b;
-        result->fNext = sorted_merge(a, b->fNext);
+Vertex* sorted_merge(Vertex* a, Vertex* b, Comparator& c) {
+    Vertex* head = NULL;
+    Vertex* tail = NULL;
+
+    while (a && b) {
+        if (c.sweep_lt(a->fPoint, b->fPoint)) {
+            Vertex* next = a->fNext;
+            append_vertex(a, &head, &tail);
+            a = next;
+        } else {
+            Vertex* next = b->fNext;
+            append_vertex(b, &head, &tail);
+            b = next;
+        }
     }
-    result->fNext->fPrev = result;
-    return result;
+    if (a) {
+        append_vertex_list(a, &head, &tail);
+    }
+    if (b) {
+        append_vertex_list(b, &head, &tail);
+    }
+    return head;
 }
 
 // Stage 4: Simplify the mesh by inserting new vertices at intersecting edges.
 
-void simplify(Vertex* vertices, SkChunkAlloc& alloc) {
+void simplify(Vertex* vertices, Comparator& c, SkChunkAlloc& alloc) {
     LOG("simplifying complex polygons\n");
-    Edge* activeEdges = NULL;
+    EdgeList activeEdges;
     for (Vertex* v = vertices; v != NULL; v = v->fNext) {
         if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) {
             continue;
@@ -1193,30 +1127,27 @@
 #if LOGGING_ENABLED
         LOG("\nvertex %g: (%g,%g)\n", v->fID, v->fPoint.fX, v->fPoint.fY);
 #endif
-#ifdef SK_DEBUG
-        validate_connectivity(v);
-#endif
         Edge* leftEnclosingEdge = NULL;
         Edge* rightEnclosingEdge = NULL;
         bool restartChecks;
         do {
             restartChecks = false;
-            find_enclosing_edges(v, activeEdges, &leftEnclosingEdge, &rightEnclosingEdge);
+            find_enclosing_edges(v, &activeEdges, &leftEnclosingEdge, &rightEnclosingEdge);
             if (v->fFirstEdgeBelow) {
                 for (Edge* edge = v->fFirstEdgeBelow; edge != NULL; edge = edge->fNextEdgeBelow) {
-                    if (check_for_intersection(edge, leftEnclosingEdge, &activeEdges, alloc)) {
+                    if (check_for_intersection(edge, leftEnclosingEdge, &activeEdges, c, alloc)) {
                         restartChecks = true;
                         break;
                     }
-                    if (check_for_intersection(edge, rightEnclosingEdge, &activeEdges, alloc)) {
+                    if (check_for_intersection(edge, rightEnclosingEdge, &activeEdges, c, alloc)) {
                         restartChecks = true;
                         break;
                     }
                 }
             } else {
                 if (Vertex* pv = check_for_intersection(leftEnclosingEdge, rightEnclosingEdge,
-                                                        &activeEdges, alloc)) {
-                    if (sweep_lt(pv->fPoint, v->fPoint)) {
+                                                        &activeEdges, c, alloc)) {
+                    if (c.sweep_lt(pv->fPoint, v->fPoint)) {
                         v = pv;
                     }
                     restartChecks = true;
@@ -1224,11 +1155,6 @@
 
             }
         } while (restartChecks);
-        SkASSERT(!leftEnclosingEdge || leftEnclosingEdge->isLeftOf(v));
-        SkASSERT(!rightEnclosingEdge || rightEnclosingEdge->isRightOf(v));
-#ifdef SK_DEBUG
-        validate_edges(activeEdges);
-#endif
         for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) {
             remove_edge(e, &activeEdges);
         }
@@ -1245,7 +1171,7 @@
 
 Poly* tessellate(Vertex* vertices, SkChunkAlloc& alloc) {
     LOG("tessellating simple polygons\n");
-    Edge* activeEdges = NULL;
+    EdgeList activeEdges;
     Poly* polys = NULL;
     for (Vertex* v = vertices; v != NULL; v = v->fNext) {
         if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) {
@@ -1254,17 +1180,9 @@
 #if LOGGING_ENABLED
         LOG("\nvertex %g: (%g,%g)\n", v->fID, v->fPoint.fX, v->fPoint.fY);
 #endif
-#ifdef SK_DEBUG
-        validate_connectivity(v);
-#endif
         Edge* leftEnclosingEdge = NULL;
         Edge* rightEnclosingEdge = NULL;
-        find_enclosing_edges(v, activeEdges, &leftEnclosingEdge, &rightEnclosingEdge);
-        SkASSERT(!leftEnclosingEdge || leftEnclosingEdge->isLeftOf(v));
-        SkASSERT(!rightEnclosingEdge || rightEnclosingEdge->isRightOf(v));
-#ifdef SK_DEBUG
-        validate_edges(activeEdges);
-#endif
+        find_enclosing_edges(v, &activeEdges, &leftEnclosingEdge, &rightEnclosingEdge);
         Poly* leftPoly = NULL;
         Poly* rightPoly = NULL;
         if (v->fFirstEdgeAbove) {
@@ -1356,12 +1274,9 @@
             }
             v->fLastEdgeBelow->fRightPoly = rightPoly;
         }
-#ifdef SK_DEBUG
-        validate_edges(activeEdges);
-#endif
 #if LOGGING_ENABLED
         LOG("\nactive edges:\n");
-        for (Edge* e = activeEdges; e != NULL; e = e->fRight) {
+        for (Edge* e = activeEdges.fHead; e != NULL; e = e->fRight) {
             LOG("%g -> %g, lpoly %d, rpoly %d\n", e->fTop->fID, e->fBottom->fID,
                 e->fLeftPoly ? e->fLeftPoly->fID : -1, e->fRightPoly ? e->fRightPoly->fID : -1);
         }
@@ -1372,7 +1287,7 @@
 
 // This is a driver function which calls stages 2-5 in turn.
 
-Poly* contours_to_polys(Vertex** contours, int contourCnt, SkChunkAlloc& alloc) {
+Poly* contours_to_polys(Vertex** contours, int contourCnt, Comparator& c, SkChunkAlloc& alloc) {
 #if LOGGING_ENABLED
     for (int i = 0; i < contourCnt; ++i) {
         Vertex* v = contours[i];
@@ -1384,28 +1299,28 @@
     }
 #endif
     sanitize_contours(contours, contourCnt);
-    Vertex* vertices = build_edges(contours, contourCnt, alloc);
+    Vertex* vertices = build_edges(contours, contourCnt, c, alloc);
     if (!vertices) {
         return NULL;
     }
 
     // Sort vertices in Y (secondarily in X).
-    merge_sort(&vertices);
-    merge_coincident_vertices(&vertices, alloc);
+    merge_sort(&vertices, c);
+    merge_coincident_vertices(&vertices, c, alloc);
 #if LOGGING_ENABLED
     for (Vertex* v = vertices; v != NULL; v = v->fNext) {
         static float gID = 0.0f;
         v->fID = gID++;
     }
 #endif
-    simplify(vertices, alloc);
+    simplify(vertices, c, alloc);
     return tessellate(vertices, alloc);
 }
 
 // Stage 6: Triangulate the monotone polygons into a vertex buffer.
 
-void* polys_to_triangles(Poly* polys, SkPath::FillType fillType, void* data) {
-    void* d = data;
+SkPoint* polys_to_triangles(Poly* polys, SkPath::FillType fillType, SkPoint* data) {
+    SkPoint* d = data;
     for (Poly* poly = polys; poly; poly = poly->fNext) {
         if (apply_fill_type(fillType, poly->fWinding)) {
             d = poly->emit(d);
@@ -1423,7 +1338,7 @@
                                                             const GrDrawTarget*,
                                                             const GrPipelineBuilder*,
                                                             const SkPath&,
-                                                            const SkStrokeRec&) const {
+                                                            const GrStrokeInfo&) const {
     return GrPathRenderer::kNoSupport_StencilSupport;
 }
 
@@ -1431,7 +1346,7 @@
                                              const GrPipelineBuilder* pipelineBuilder,
                                              const SkMatrix& viewMatrix,
                                              const SkPath& path,
-                                             const SkStrokeRec& stroke,
+                                             const GrStrokeInfo& stroke,
                                              bool antiAlias) const {
     // This path renderer can draw all fill styles, but does not do antialiasing. It can do convex
     // and concave paths, but we'll leave the convex ones to simpler algorithms.
@@ -1448,17 +1363,17 @@
         return SkNEW_ARGS(TessellatingPathBatch, (color, path, viewMatrix, clipBounds));
     }
 
-    const char* name() const SK_OVERRIDE { return "TessellatingPathBatch"; }
+    const char* name() const override { return "TessellatingPathBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         out->setKnownFourComponents(fColor);
     }
 
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fColor = GrColor_ILLEGAL;
@@ -1468,8 +1383,18 @@
         fPipelineInfo = init;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
-        SkScalar tol = GrPathUtils::scaleToleranceToSrc(SK_Scalar1, fViewMatrix, fPath.getBounds());
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
+        SkRect pathBounds = fPath.getBounds();
+        Comparator c;
+        if (pathBounds.width() > pathBounds.height()) {
+            c.sweep_lt = sweep_lt_horiz;
+            c.sweep_gt = sweep_gt_horiz;
+        } else {
+            c.sweep_lt = sweep_lt_vert;
+            c.sweep_gt = sweep_gt_vert;
+        }
+        SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance;
+        SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix, pathBounds);
         int contourCnt;
         int maxPts = GrPathUtils::worstCasePointCount(fPath, &contourCnt, tol);
         if (maxPts <= 0) {
@@ -1500,50 +1425,45 @@
         SkChunkAlloc alloc(maxPts * (3 * sizeof(Vertex) + sizeof(Edge)));
         path_to_contours(fPath, tol, fClipBounds, contours.get(), alloc);
         Poly* polys;
-        polys = contours_to_polys(contours.get(), contourCnt, alloc);
+        polys = contours_to_polys(contours.get(), contourCnt, c, alloc);
         int count = 0;
         for (Poly* poly = polys; poly; poly = poly->fNext) {
             if (apply_fill_type(fillType, poly->fWinding) && poly->fCount >= 3) {
                 count += (poly->fCount - 2) * (WIREFRAME ? 6 : 3);
             }
         }
+        if (0 == count) {
+            return;
+        }
 
         size_t stride = gp->getVertexStride();
+        SkASSERT(stride == sizeof(SkPoint));
         const GrVertexBuffer* vertexBuffer;
         int firstVertex;
-        void* vertices = batchTarget->vertexPool()->makeSpace(stride,
-                                                              count,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices) {
+        SkPoint* verts = static_cast<SkPoint*>(
+            batchTarget->makeVertSpace(stride, count, &vertexBuffer, &firstVertex));
+        if (!verts) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
         LOG("emitting %d verts\n", count);
-        void* end = polys_to_triangles(polys, fillType, vertices);
-        int actualCount = static_cast<int>(
-            (static_cast<char*>(end) - static_cast<char*>(vertices)) / stride);
+        SkPoint* end = polys_to_triangles(polys, fillType, verts);
+        int actualCount = static_cast<int>(end - verts);
         LOG("actual count: %d\n", actualCount);
         SkASSERT(actualCount <= count);
 
         GrPrimitiveType primitiveType = WIREFRAME ? kLines_GrPrimitiveType
                                                   : kTriangles_GrPrimitiveType;
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(primitiveType);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setStartVertex(firstVertex);
-        drawInfo.setVertexCount(actualCount);
-        drawInfo.setStartIndex(0);
-        drawInfo.setIndexCount(0);
-        batchTarget->draw(drawInfo);
+        GrVertices vertices;
+        vertices.init(primitiveType, vertexBuffer, firstVertex, actualCount);
+        batchTarget->draw(vertices);
 
         batchTarget->putBackVertices((size_t)(count - actualCount), stride);
         return;
     }
 
-    bool onCombineIfPossible(GrBatch*) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch*) override {
         return false;
     }
 
@@ -1557,6 +1477,9 @@
       , fViewMatrix(viewMatrix)
       , fClipBounds(clipBounds) {
         this->initClassID<TessellatingPathBatch>();
+
+        fBounds = path.getBounds();
+        viewMatrix.mapRect(&fBounds);
     }
 
     GrColor        fColor;
@@ -1571,7 +1494,7 @@
                                             GrColor color,
                                             const SkMatrix& viewM,
                                             const SkPath& path,
-                                            const SkStrokeRec& stroke,
+                                            const GrStrokeInfo&,
                                             bool antiAlias) {
     SkASSERT(!antiAlias);
     const GrRenderTarget* rt = pipelineBuilder->getRenderTarget();
@@ -1592,3 +1515,23 @@
 
     return true;
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef GR_TEST_UTILS
+
+BATCH_TEST_DEFINE(TesselatingPathBatch) {
+    GrColor color = GrRandomColor(random);
+    SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
+    SkPath path = GrTest::TestPath(random);
+    SkRect clipBounds = GrTest::TestRect(random);
+    SkMatrix vmi;
+    bool result = viewMatrix.invert(&vmi);
+    if (!result) {
+        SkFAIL("Cannot invert matrix\n");
+    }
+    vmi.mapRect(&clipBounds);
+    return TessellatingPathBatch::Create(color, path, viewMatrix, clipBounds);
+}
+
+#endif
diff --git a/src/gpu/GrTessellatingPathRenderer.h b/src/gpu/GrTessellatingPathRenderer.h
index 3262c9a..79cafc9 100644
--- a/src/gpu/GrTessellatingPathRenderer.h
+++ b/src/gpu/GrTessellatingPathRenderer.h
@@ -22,22 +22,22 @@
                      const GrPipelineBuilder*,
                      const SkMatrix&,
                      const SkPath&,
-                     const SkStrokeRec&,
-                     bool antiAlias) const SK_OVERRIDE;
+                     const GrStrokeInfo&,
+                     bool antiAlias) const override;
 protected:
 
     StencilSupport onGetStencilSupport(const GrDrawTarget*,
                                        const GrPipelineBuilder*,
                                        const SkPath&,
-                                       const SkStrokeRec&) const SK_OVERRIDE;
+                                       const GrStrokeInfo&) const override;
 
     bool onDrawPath(GrDrawTarget*,
                     GrPipelineBuilder*,
                     GrColor,
                     const SkMatrix& viewMatrix,
                     const SkPath&,
-                    const SkStrokeRec&,
-                    bool antiAlias) SK_OVERRIDE;
+                    const GrStrokeInfo&,
+                    bool antiAlias) override;
 
     typedef GrPathRenderer INHERITED;
 };
diff --git a/src/gpu/GrTest.cpp b/src/gpu/GrTest.cpp
index cefd015..0b3d030 100644
--- a/src/gpu/GrTest.cpp
+++ b/src/gpu/GrTest.cpp
@@ -18,8 +18,6 @@
 
     fContext.reset(SkRef(ctx));
     fDrawTarget.reset(SkRef(target));
-
-    SkNEW_IN_TLAZY(&fAGP, GrDrawTarget::AutoGeometryPush, (target));
 }
 
 void GrContext::getTestTarget(GrTestTarget* tar) {
@@ -71,7 +69,7 @@
     out->appendf("Shader Compilations: %d\n", fShaderCompilations);
     out->appendf("Textures Created: %d\n", fTextureCreates);
     out->appendf("Texture Uploads: %d\n", fTextureUploads);
-    out->appendf("Stencil Buffer Creates: %d\n", fStencilBufferCreates);
+    out->appendf("Stencil Buffer Creates: %d\n", fStencilAttachmentCreates);
 }
 #endif
 
@@ -133,7 +131,6 @@
 // Code for the mock context. It's built on a mock GrGpu class that does nothing.
 ////
 
-#include "GrBufferAllocPool.h"
 #include "GrInOrderDrawBuffer.h"
 #include "GrGpu.h"
 
@@ -142,8 +139,8 @@
 class MockGpu : public GrGpu {
 public:
     MockGpu(GrContext* context) : INHERITED(context) { fCaps.reset(SkNEW(GrDrawTargetCaps)); }
-    ~MockGpu() SK_OVERRIDE {}
-    bool canWriteTexturePixels(const GrTexture*, GrPixelConfig srcConfig) const SK_OVERRIDE {
+    ~MockGpu() override {}
+    bool canWriteTexturePixels(const GrTexture*, GrPixelConfig srcConfig) const override {
         return true;
     }
 
@@ -151,60 +148,62 @@
                                    int left, int top,
                                    int width, int height,
                                    GrPixelConfig config,
-                                   size_t rowBytes) const SK_OVERRIDE { return false; }
+                                   size_t rowBytes) const override { return false; }
     void buildProgramDesc(GrProgramDesc*,const GrPrimitiveProcessor&,
                           const GrPipeline&,
-                          const GrBatchTracker&) const SK_OVERRIDE {}
+                          const GrBatchTracker&) const override {}
 
-    void discard(GrRenderTarget*) SK_OVERRIDE {}
+    void discard(GrRenderTarget*) override {}
 
     bool canCopySurface(const GrSurface* dst,
                         const GrSurface* src,
                         const SkIRect& srcRect,
-                        const SkIPoint& dstPoint) SK_OVERRIDE { return false; };
+                        const SkIPoint& dstPoint) override { return false; };
 
     bool copySurface(GrSurface* dst,
                      GrSurface* src,
                      const SkIRect& srcRect,
-                     const SkIPoint& dstPoint) SK_OVERRIDE { return false; };
+                     const SkIPoint& dstPoint) override { return false; };
 
-    bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) SK_OVERRIDE {
+    bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) override {
         return false;
     }
 
+    void xferBarrier(GrRenderTarget*, GrXferBarrierType) override {}
+
 private:
-    void onResetContext(uint32_t resetBits) SK_OVERRIDE {}
+    void onResetContext(uint32_t resetBits) override {}
 
-    GrTexture* onCreateTexture(const GrSurfaceDesc& desc, bool budgeted, const void* srcData,
-                               size_t rowBytes) SK_OVERRIDE {
+    GrTexture* onCreateTexture(const GrSurfaceDesc& desc, GrGpuResource::LifeCycle lifeCycle,
+                               const void* srcData, size_t rowBytes) override {
         return NULL;
     }
 
-    GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc, bool budgeted,
-                                         const void* srcData) SK_OVERRIDE {
+    GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc, GrGpuResource::LifeCycle,
+                                         const void* srcData) override {
         return NULL;
     }
 
-    GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) SK_OVERRIDE { return NULL; }
+    GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) override { return NULL; }
 
-    GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) SK_OVERRIDE {
+    GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) override {
         return NULL;
     }
 
-    GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) SK_OVERRIDE { return NULL; }
+    GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) override { return NULL; }
 
-    GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) SK_OVERRIDE { return NULL; }
+    GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) override { return NULL; }
 
     void onClear(GrRenderTarget*, const SkIRect* rect, GrColor color,
-                         bool canIgnoreRect) SK_OVERRIDE {}
+                         bool canIgnoreRect) override {}
 
-    void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) SK_OVERRIDE {}
+    void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) override {}
 
-    void onDraw(const DrawArgs&, const GrDrawTarget::DrawInfo&) SK_OVERRIDE {}
+    void onDraw(const DrawArgs&, const GrNonInstancedVertices&) override {}
 
-    void onStencilPath(const GrPath* path, const StencilPathState& state) SK_OVERRIDE {}
+    void onStencilPath(const GrPath* path, const StencilPathState& state) override {}
 
-    void onDrawPath(const DrawArgs&, const GrPath*, const GrStencilSettings&) SK_OVERRIDE {}
+    void onDrawPath(const DrawArgs&, const GrPath*, const GrStencilSettings&) override {}
 
     void onDrawPaths(const DrawArgs&,
                      const GrPathRange*,
@@ -213,38 +212,38 @@
                      const float transformValues[],
                      GrDrawTarget::PathTransformType,
                      int count,
-                     const GrStencilSettings&) SK_OVERRIDE {}
+                     const GrStencilSettings&) override {}
 
     bool onReadPixels(GrRenderTarget* target,
                       int left, int top, int width, int height,
                       GrPixelConfig,
                       void* buffer,
-                      size_t rowBytes) SK_OVERRIDE {
+                      size_t rowBytes) override {
         return false;
     }
 
     bool onWriteTexturePixels(GrTexture* texture,
                               int left, int top, int width, int height,
                               GrPixelConfig config, const void* buffer,
-                              size_t rowBytes) SK_OVERRIDE {
+                              size_t rowBytes) override {
         return false;
     }
 
-    void onResolveRenderTarget(GrRenderTarget* target) SK_OVERRIDE { return; }
+    void onResolveRenderTarget(GrRenderTarget* target) override { return; }
 
-    bool createStencilBufferForRenderTarget(GrRenderTarget*, int width, int height) SK_OVERRIDE {
+    bool createStencilAttachmentForRenderTarget(GrRenderTarget*, int width, int height) override {
         return false;
     }
 
-    bool attachStencilBufferToRenderTarget(GrStencilBuffer*, GrRenderTarget*) SK_OVERRIDE {
+    bool attachStencilAttachmentToRenderTarget(GrStencilAttachment*, GrRenderTarget*) override {
         return false;
     }
 
-    void clearStencil(GrRenderTarget* target) SK_OVERRIDE  {}
+    void clearStencil(GrRenderTarget* target) override  {}
 
-    void didAddGpuTraceMarker() SK_OVERRIDE {}
+    void didAddGpuTraceMarker() override {}
 
-    void didRemoveGpuTraceMarker() SK_OVERRIDE {}
+    void didRemoveGpuTraceMarker() override {}
 
     typedef GrGpu INHERITED;
 };
@@ -266,10 +265,6 @@
     // these objects are required for any of tests that use this context. TODO: make stop allocating
     // resources in the buffer pools.
     SkDELETE(fDrawBuffer);
-    SkDELETE(fDrawBufferVBAllocPool);
-    SkDELETE(fDrawBufferIBAllocPool);
-
     fDrawBuffer = NULL;
-    fDrawBufferVBAllocPool = NULL;
-    fDrawBufferIBAllocPool = NULL;
+
 }
diff --git a/src/gpu/GrTest.h b/src/gpu/GrTest.h
index 47122c5..cf9a53e 100644
--- a/src/gpu/GrTest.h
+++ b/src/gpu/GrTest.h
@@ -24,8 +24,6 @@
     GrDrawTarget* target() { return fDrawTarget.get(); }
 
 private:
-    SkTLazy<GrDrawTarget::AutoGeometryPush> fAGP;
-
     SkAutoTUnref<GrDrawTarget>              fDrawTarget;
     SkAutoTUnref<GrContext>                 fContext;
 };
diff --git a/src/gpu/GrTestBatch.h b/src/gpu/GrTestBatch.h
index 677abaf..a165f4a 100644
--- a/src/gpu/GrTestBatch.h
+++ b/src/gpu/GrTestBatch.h
@@ -9,6 +9,7 @@
 #define GrTestBatch_DEFINED
 
 #include "GrBatch.h"
+#include "GrVertexBuffer.h"
 
 /*
  * A simple batch only for testing purposes which actually doesn't batch at all, but can fit into
@@ -20,22 +21,18 @@
         GrColor fColor;
     };
 
-    virtual const char* name() const SK_OVERRIDE = 0;
+    virtual const char* name() const override = 0;
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
-        if (fGeometryProcessor->hasVertexColor()) {
-            out->setUnknownFourComponents();
-        } else {
-            out->setKnownFourComponents(fGeometryProcessor->color());
-        }
+        out->setKnownFourComponents(this->geoData(0)->fColor);
     }
 
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             this->geoData(0)->fColor = GrColor_ILLEGAL;
@@ -50,7 +47,7 @@
         fBatch.fCoverageIgnored = init.fCoverageIgnored;
     }
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         batchTarget->initDraw(fGeometryProcessor, pipeline);
 
         // TODO this is hacky, but the only way we have to initialize the GP is to use the
@@ -67,16 +64,19 @@
     }
 
 protected:
-    GrTestBatch(const GrGeometryProcessor* gp) {
+    GrTestBatch(const GrGeometryProcessor* gp, const SkRect& bounds) {
         fGeometryProcessor.reset(SkRef(gp));
+
+        this->setBounds(bounds);
     }
 
     const GrGeometryProcessor* geometryProcessor() const { return fGeometryProcessor; }
 
 private:
     virtual Geometry* geoData(int index) = 0;
+    virtual const Geometry* geoData(int index) const = 0;
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         return false;
     }
 
diff --git a/src/gpu/GrTestUtils.cpp b/src/gpu/GrTestUtils.cpp
new file mode 100644
index 0000000..b690d7e
--- /dev/null
+++ b/src/gpu/GrTestUtils.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrTestUtils.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkRRect.h"
+
+#ifdef GR_TEST_UTILS
+
+static const SkMatrix& test_matrix(SkRandom* random, bool includePerspective) {
+    static SkMatrix gMatrices[5];
+    static const int kPerspectiveCount = 1;
+    static bool gOnce;
+    if (!gOnce) {
+        gOnce = true;
+        gMatrices[0].reset();
+        gMatrices[1].setTranslate(SkIntToScalar(-100), SkIntToScalar(100));
+        gMatrices[2].setRotate(SkIntToScalar(17));
+        gMatrices[3].setRotate(SkIntToScalar(185));
+        gMatrices[3].postTranslate(SkIntToScalar(66), SkIntToScalar(-33));
+        gMatrices[3].postScale(SkIntToScalar(2), SK_ScalarHalf);
+
+        // Perspective matrices
+        gMatrices[4].setRotate(SkIntToScalar(215));
+        gMatrices[4].set(SkMatrix::kMPersp0, 0.00013f);
+        gMatrices[4].set(SkMatrix::kMPersp1, -0.000039f);
+    }
+
+    uint32_t count = static_cast<uint32_t>(SK_ARRAY_COUNT(gMatrices));
+    if (includePerspective) {
+        return gMatrices[random->nextULessThan(count)];
+    } else {
+        return gMatrices[random->nextULessThan(count - kPerspectiveCount)];
+    }
+}
+
+namespace GrTest {
+const SkMatrix& TestMatrix(SkRandom* random) { return test_matrix(random, true); }
+
+const SkMatrix& TestMatrixPreservesRightAngles(SkRandom* random) {
+    static SkMatrix gMatrices[5];
+    static bool gOnce;
+    if (!gOnce) {
+        gOnce = true;
+        // identity
+        gMatrices[0].reset();
+        // translation
+        gMatrices[1].setTranslate(SkIntToScalar(-100), SkIntToScalar(100));
+        // scale
+        gMatrices[2].setScale(SkIntToScalar(17), SkIntToScalar(17));
+        // scale + translation
+        gMatrices[3].setScale(SkIntToScalar(-17), SkIntToScalar(-17));
+        gMatrices[3].postTranslate(SkIntToScalar(66), SkIntToScalar(-33));
+        // orthogonal basis vectors
+        gMatrices[4].reset();
+        gMatrices[4].setScale(SkIntToScalar(-1), SkIntToScalar(-1));
+        gMatrices[4].setRotate(47);
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gMatrices); i++) {
+            SkASSERT(gMatrices[i].preservesRightAngles());
+        }
+    }
+    return gMatrices[random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gMatrices)))];
+}
+
+const SkMatrix& TestMatrixRectStaysRect(SkRandom* random) {
+    static SkMatrix gMatrices[6];
+    static bool gOnce;
+    if (!gOnce) {
+        gOnce = true;
+        // identity
+        gMatrices[0].reset();
+        // translation
+        gMatrices[1].setTranslate(SkIntToScalar(-100), SkIntToScalar(100));
+        // scale
+        gMatrices[2].setScale(SkIntToScalar(17), SkIntToScalar(17));
+        // scale + translation
+        gMatrices[3].setScale(SkIntToScalar(-17), SkIntToScalar(-17));
+        gMatrices[3].postTranslate(SkIntToScalar(66), SkIntToScalar(-33));
+        // reflection
+        gMatrices[4].setScale(SkIntToScalar(-1), SkIntToScalar(-1));
+        // 90 degress rotation
+        gMatrices[5].setRotate(90);
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gMatrices); i++) {
+            SkASSERT(gMatrices[i].rectStaysRect());
+        }
+    }
+    return gMatrices[random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gMatrices)))];
+}
+
+const SkMatrix& TestMatrixInvertible(SkRandom* random) { return test_matrix(random, false); }
+
+const SkRect& TestRect(SkRandom* random) {
+    static SkRect gRects[7];
+    static bool gOnce;
+    if (!gOnce) {
+        gOnce = true;
+        gRects[0] = SkRect::MakeWH(1.f, 1.f);
+        gRects[1] = SkRect::MakeWH(1.0f, 256.0f);
+        gRects[2] = SkRect::MakeWH(256.0f, 1.0f);
+        gRects[4] = SkRect::MakeLargest();
+        gRects[5] = SkRect::MakeLTRB(-65535.0f, -65535.0f, 65535.0f, 65535.0f);
+        gRects[6] = SkRect::MakeLTRB(-10.0f, -10.0f, 10.0f, 10.0f);
+    }
+    return gRects[random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gRects)))];
+}
+
+// Just some simple rects for code which expects its input very sanitized
+const SkRect& TestSquare(SkRandom* random) {
+    static SkRect gRects[2];
+    static bool gOnce;
+    if (!gOnce) {
+        gOnce = true;
+        gRects[0] = SkRect::MakeWH(128.f, 128.f);
+        gRects[1] = SkRect::MakeWH(256.0f, 256.0f);
+    }
+    return gRects[random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gRects)))];
+}
+
+const SkRRect& TestRRectSimple(SkRandom* random) {
+    static SkRRect gRRect[2];
+    static bool gOnce;
+    if (!gOnce) {
+        gOnce = true;
+        SkRect rectangle = SkRect::MakeWH(10.f, 20.f);
+        // true round rect with circular corners
+        gRRect[0].setRectXY(rectangle, 1.f, 1.f);
+        // true round rect with elliptical corners
+        gRRect[1].setRectXY(rectangle, 2.0f, 1.0f);
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gRRect); i++) {
+            SkASSERT(gRRect[i].isSimple());
+        }
+    }
+    return gRRect[random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gRRect)))];
+}
+
+const SkPath& TestPath(SkRandom* random) {
+    static SkPath gPath[7];
+    static bool gOnce;
+    if (!gOnce) {
+        gOnce = true;
+        // line
+        gPath[0].moveTo(0.f, 0.f);
+        gPath[0].lineTo(10.f, 10.f);
+        // quad
+        gPath[1].moveTo(0.f, 0.f);
+        gPath[1].quadTo(10.f, 10.f, 20.f, 20.f);
+        // conic
+        gPath[2].moveTo(0.f, 0.f);
+        gPath[2].conicTo(10.f, 10.f, 20.f, 20.f, 1.f);
+        // cubic
+        gPath[3].moveTo(0.f, 0.f);
+        gPath[3].cubicTo(10.f, 10.f, 20.f, 20.f, 30.f, 30.f);
+        // all three
+        gPath[4].moveTo(0.f, 0.f);
+        gPath[4].lineTo(10.f, 10.f);
+        gPath[4].quadTo(10.f, 10.f, 20.f, 20.f);
+        gPath[4].conicTo(10.f, 10.f, 20.f, 20.f, 1.f);
+        gPath[4].cubicTo(10.f, 10.f, 20.f, 20.f, 30.f, 30.f);
+        // convex
+        gPath[5].moveTo(0.0f, 0.0f);
+        gPath[5].lineTo(10.0f, 0.0f);
+        gPath[5].lineTo(10.0f, 10.0f);
+        gPath[5].lineTo(0.0f, 10.0f);
+        gPath[5].close();
+        // concave
+        gPath[6].moveTo(0.0f, 0.0f);
+        gPath[6].lineTo(5.0f, 5.0f);
+        gPath[6].lineTo(10.0f, 0.0f);
+        gPath[6].lineTo(10.0f, 10.0f);
+        gPath[6].lineTo(0.0f, 10.0f);
+        gPath[6].close();
+    }
+
+    return gPath[random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gPath)))];
+}
+
+const SkPath& TestPathConvex(SkRandom* random) {
+    static SkPath gPath[3];
+    static bool gOnce;
+    if (!gOnce) {
+        gOnce = true;
+        // narrow rect
+        gPath[0].moveTo(-1.5f, -50.0f);
+        gPath[0].lineTo(-1.5f, -50.0f);
+        gPath[0].lineTo( 1.5f, -50.0f);
+        gPath[0].lineTo( 1.5f,  50.0f);
+        gPath[0].lineTo(-1.5f,  50.0f);
+        // degenerate
+        gPath[1].moveTo(-0.025f, -0.025f);
+        gPath[1].lineTo(-0.025f, -0.025f);
+        gPath[1].lineTo( 0.025f, -0.025f);
+        gPath[1].lineTo( 0.025f,  0.025f);
+        gPath[1].lineTo(-0.025f,  0.025f);
+        // clipped triangle
+        gPath[2].moveTo(-10.0f, -50.0f);
+        gPath[2].lineTo(-10.0f, -50.0f);
+        gPath[2].lineTo( 10.0f, -50.0f);
+        gPath[2].lineTo( 50.0f,  31.0f);
+        gPath[2].lineTo( 40.0f,  50.0f);
+        gPath[2].lineTo(-40.0f,  50.0f);
+        gPath[2].lineTo(-50.0f,  31.0f);
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gPath); i++) {
+            SkASSERT(SkPath::kConvex_Convexity == gPath[i].getConvexity());
+        }
+    }
+
+    return gPath[random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gPath)))];
+}
+
+SkStrokeRec TestStrokeRec(SkRandom* random) {
+    SkStrokeRec::InitStyle style =
+            SkStrokeRec::InitStyle(random->nextULessThan(SkStrokeRec::kFill_InitStyle + 1));
+    SkStrokeRec rec(style);
+    bool strokeAndFill = random->nextBool();
+    SkScalar strokeWidth = random->nextBool() ? 0.f : 1.f;
+    rec.setStrokeStyle(strokeWidth, strokeAndFill);
+
+    SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::kCapCount));
+    SkPaint::Join join = SkPaint::Join(random->nextULessThan(SkPaint::kJoinCount));
+    SkScalar miterLimit = random->nextRangeScalar(1.f, 5.f);
+    rec.setStrokeParams(cap, join, miterLimit);
+    return rec;
+}
+
+};
+
+#endif
diff --git a/src/gpu/GrTextBlobCache.cpp b/src/gpu/GrTextBlobCache.cpp
new file mode 100644
index 0000000..d66f432
--- /dev/null
+++ b/src/gpu/GrTextBlobCache.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrTextBlobCache.h"
+
+static const int kVerticesPerGlyph = 4;
+
+GrTextBlobCache::~GrTextBlobCache() {
+    this->freeAll();
+}
+
+GrAtlasTextContext::BitmapTextBlob* GrTextBlobCache::createBlob(int glyphCount, int runCount,
+                                                                size_t maxVASize) {
+    // We allocate size for the BitmapTextBlob itself, plus size for the vertices array,
+    // and size for the glyphIds array.
+    size_t verticesCount = glyphCount * kVerticesPerGlyph * maxVASize;
+    size_t size = sizeof(BitmapTextBlob) +
+                  verticesCount +
+                  glyphCount * sizeof(GrGlyph**) +
+                  sizeof(BitmapTextBlob::Run) * runCount;
+
+    BitmapTextBlob* cacheBlob = SkNEW_PLACEMENT(fPool.allocate(size), BitmapTextBlob);
+
+    // setup offsets for vertices / glyphs
+    cacheBlob->fVertices = sizeof(BitmapTextBlob) + reinterpret_cast<unsigned char*>(cacheBlob);
+    cacheBlob->fGlyphs = reinterpret_cast<GrGlyph**>(cacheBlob->fVertices + verticesCount);
+    cacheBlob->fRuns = reinterpret_cast<BitmapTextBlob::Run*>(cacheBlob->fGlyphs + glyphCount);
+
+    // Initialize runs
+    for (int i = 0; i < runCount; i++) {
+        SkNEW_PLACEMENT(&cacheBlob->fRuns[i], BitmapTextBlob::Run);
+    }
+    cacheBlob->fRunCount = runCount;
+    cacheBlob->fPool = &fPool;
+    return cacheBlob;
+}
+
+void GrTextBlobCache::freeAll() {
+    SkTDynamicHash<BitmapTextBlob, BitmapTextBlob::Key>::Iter iter(&fCache);
+    while (!iter.done()) {
+        (&(*iter))->unref();
+        ++iter;
+    }
+    fCache.rewind();
+}
diff --git a/src/gpu/GrTextBlobCache.h b/src/gpu/GrTextBlobCache.h
new file mode 100644
index 0000000..1776c69
--- /dev/null
+++ b/src/gpu/GrTextBlobCache.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrTextBlobCache_DEFINED
+#define GrTextBlobCache_DEFINED
+
+#include "GrAtlasTextContext.h"
+#include "SkTDynamicHash.h"
+#include "SkTextBlob.h"
+
+class GrTextBlobCache {
+public:
+    typedef GrAtlasTextContext::BitmapTextBlob BitmapTextBlob;
+
+    /**
+     * The callback function used by the cache when it is still over budget after a purge. The
+     * passed in 'data' is the same 'data' handed to setOverbudgetCallback.
+     */
+    typedef void (*PFOverBudgetCB)(void* data);
+
+    GrTextBlobCache(PFOverBudgetCB cb, void* data)
+        : fPool(kPreAllocSize, kMinGrowthSize)
+        , fCallback(cb)
+        , fData(data) {
+        SkASSERT(cb && data);
+    }
+    ~GrTextBlobCache();
+
+    // creates an uncached blob
+    BitmapTextBlob* createBlob(int glyphCount, int runCount, size_t maxVASize);
+    BitmapTextBlob* createBlob(const SkTextBlob* blob, size_t maxVAStride) {
+        int glyphCount = 0;
+        int runCount = 0;
+        BlobGlyphCount(&glyphCount, &runCount, blob);
+        BitmapTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride);
+        return cacheBlob;
+    }
+
+    BitmapTextBlob* createCachedBlob(const SkTextBlob* blob,
+                                     const BitmapTextBlob::Key& key,
+                                     const SkMaskFilter::BlurRec& blurRec,
+                                     const SkPaint& paint,
+                                     size_t maxVAStride) {
+        int glyphCount = 0;
+        int runCount = 0;
+        BlobGlyphCount(&glyphCount, &runCount, blob);
+        BitmapTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride);
+        cacheBlob->fKey = key;
+        if (key.fHasBlur) {
+            cacheBlob->fBlurRec = blurRec;
+        }
+        if (key.fStyle != SkPaint::kFill_Style) {
+            cacheBlob->fStrokeInfo.fFrameWidth = paint.getStrokeWidth();
+            cacheBlob->fStrokeInfo.fMiterLimit = paint.getStrokeMiter();
+            cacheBlob->fStrokeInfo.fJoin = paint.getStrokeJoin();
+        }
+        this->add(cacheBlob);
+        return cacheBlob;
+    }
+
+    BitmapTextBlob* find(const BitmapTextBlob::Key& key) {
+        return fCache.find(key);
+    }
+
+    void remove(BitmapTextBlob* blob) {
+        fCache.remove(blob->fKey);
+        fBlobList.remove(blob);
+        blob->unref();
+    }
+
+    void add(BitmapTextBlob* blob) {
+        fCache.add(blob);
+        fBlobList.addToHead(blob);
+
+        // If we are overbudget, then unref until we are below budget again
+        if (fPool.size() > kBudget) {
+            BitmapBlobList::Iter iter;
+            iter.init(fBlobList, BitmapBlobList::Iter::kTail_IterStart);
+            BitmapTextBlob* lruBlob = iter.get();
+            SkASSERT(lruBlob);
+            while (fPool.size() > kBudget && (lruBlob = iter.get()) && lruBlob != blob) {
+                fCache.remove(lruBlob->fKey);
+
+                // Backup the iterator before removing and unrefing the blob
+                iter.prev();
+                fBlobList.remove(lruBlob);
+                lruBlob->unref();
+            }
+
+            // If we break out of the loop with lruBlob == blob, then we haven't purged enough
+            // use the call back and try to free some more.  If we are still overbudget after this,
+            // then this single textblob is over our budget
+            if (lruBlob == blob) {
+                (*fCallback)(fData);
+            }
+
+#ifdef SK_DEBUG
+            if (fPool.size() > kBudget) {
+                SkDebugf("Single textblob is larger than our whole budget");
+            }
+#endif
+        }
+    }
+
+    void makeMRU(BitmapTextBlob* blob) {
+        if (fBlobList.head() == blob) {
+            return;
+        }
+
+        fBlobList.remove(blob);
+        fBlobList.addToHead(blob);
+    }
+
+    void freeAll();
+
+private:
+    // TODO move to SkTextBlob
+    void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
+        SkTextBlob::RunIterator itCounter(blob);
+        for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
+            *glyphCount += itCounter.glyphCount();
+        }
+    }
+
+    typedef SkTInternalLList<BitmapTextBlob> BitmapBlobList;
+
+    // Budget was chosen to be ~4 megabytes.  The min alloc and pre alloc sizes in the pool are
+    // based off of the largest cached textblob I have seen in the skps(a couple of kilobytes).
+    static const int kPreAllocSize = 1 << 17;
+    static const int kMinGrowthSize = 1 << 17;
+    static const int kBudget = 1 << 22;
+    BitmapBlobList fBlobList;
+    SkTDynamicHash<BitmapTextBlob, BitmapTextBlob::Key> fCache;
+    GrMemoryPool fPool;
+    PFOverBudgetCB fCallback;
+    void* fData;
+};
+
+#endif
diff --git a/src/gpu/GrTextContext.cpp b/src/gpu/GrTextContext.cpp
index ad5e7c0..4c6dabc 100644
--- a/src/gpu/GrTextContext.cpp
+++ b/src/gpu/GrTextContext.cpp
@@ -11,11 +11,21 @@
 #include "GrFontScaler.h"
 
 #include "SkAutoKern.h"
+#include "SkDrawFilter.h"
+#include "SkDrawProcs.h"
 #include "SkGlyphCache.h"
+#include "SkGpuDevice.h"
+#include "SkTextBlob.h"
+#include "SkTextMapStateProc.h"
+#include "SkTextToPathIter.h"
 
-GrTextContext::GrTextContext(GrContext* context, const SkDeviceProperties& properties) :
-                            fFallbackTextContext(NULL),
-                            fContext(context), fDeviceProperties(properties), fDrawTarget(NULL) {
+GrTextContext::GrTextContext(GrContext* context, SkGpuDevice* gpuDevice,
+                             const SkDeviceProperties& properties)
+    : fFallbackTextContext(NULL)
+    , fContext(context)
+    , fGpuDevice(gpuDevice)
+    , fDeviceProperties(properties)
+    , fDrawTarget(NULL) {
 }
 
 GrTextContext::~GrTextContext() {
@@ -23,11 +33,12 @@
 }
 
 void GrTextContext::init(GrRenderTarget* rt, const GrClip& clip, const GrPaint& grPaint,
-                         const SkPaint& skPaint) {
+                         const SkPaint& skPaint, const SkIRect& regionClipBounds) {
     fClip = clip;
 
     fRenderTarget.reset(SkRef(rt));
 
+    fRegionClipBounds = regionClipBounds;
     fClip.getConservativeBounds(fRenderTarget->width(), fRenderTarget->height(), &fClipRect);
 
     fDrawTarget = fContext->getTextTarget();
@@ -36,48 +47,172 @@
     fSkPaint = skPaint;
 }
 
-bool GrTextContext::drawText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
+void GrTextContext::drawText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
                              const SkPaint& skPaint, const SkMatrix& viewMatrix,
                              const char text[], size_t byteLength,
-                             SkScalar x, SkScalar y) {
+                             SkScalar x, SkScalar y, const SkIRect& clipBounds) {
     if (!fContext->getTextTarget()) {
-        return false;
+        return;
     }
 
     GrTextContext* textContext = this;
     do {
-        if (textContext->canDraw(skPaint, viewMatrix)) {
-            textContext->onDrawText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, x, y);
-            return true;
+        if (textContext->canDraw(rt, clip, paint, skPaint, viewMatrix)) {
+            textContext->onDrawText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, x, y,
+                                    clipBounds);
+            return;
         }
         textContext = textContext->fFallbackTextContext;
     } while (textContext);
 
-    return false;
+    // fall back to drawing as a path
+    SkASSERT(fGpuDevice);
+    this->drawTextAsPath(skPaint, viewMatrix, text, byteLength, x, y, clipBounds);
 }
 
-bool GrTextContext::drawPosText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
+void GrTextContext::drawPosText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
                                 const SkPaint& skPaint, const SkMatrix& viewMatrix,
                                 const char text[], size_t byteLength,
                                 const SkScalar pos[], int scalarsPerPosition,
-                                const SkPoint& offset) {
+                                const SkPoint& offset, const SkIRect& clipBounds) {
     if (!fContext->getTextTarget()) {
-        return false;
+        return;
     }
 
     GrTextContext* textContext = this;
     do {
-        if (textContext->canDraw(skPaint, viewMatrix)) {
+        if (textContext->canDraw(rt, clip, paint, skPaint, viewMatrix)) {
             textContext->onDrawPosText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, pos,
-                                       scalarsPerPosition, offset);
-            return true;
+                                       scalarsPerPosition, offset, clipBounds);
+            return;
         }
         textContext = textContext->fFallbackTextContext;
     } while (textContext);
 
-    return false;
+    // fall back to drawing as a path
+    SkASSERT(fGpuDevice);
+    this->drawPosTextAsPath(skPaint, viewMatrix, text, byteLength, pos, scalarsPerPosition, offset,
+                            clipBounds);
 }
 
+void GrTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip, const SkPaint& skPaint,
+                                 const SkMatrix& viewMatrix, const SkTextBlob* blob,
+                                 SkScalar x, SkScalar y,
+                                 SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
+    SkPaint runPaint = skPaint;
+
+    SkTextBlob::RunIterator it(blob);
+    for (;!it.done(); it.next()) {
+        size_t textLen = it.glyphCount() * sizeof(uint16_t);
+        const SkPoint& offset = it.offset();
+        // applyFontToPaint() always overwrites the exact same attributes,
+        // so it is safe to not re-seed the paint for this reason.
+        it.applyFontToPaint(&runPaint);
+
+        if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
+            // A false return from filter() means we should abort the current draw.
+            runPaint = skPaint;
+            continue;
+        }
+
+        runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
+
+        GrPaint grPaint;
+        if (!SkPaint2GrPaint(fContext, fRenderTarget, runPaint, viewMatrix, true, &grPaint)) {
+            return;
+        }
+
+        switch (it.positioning()) {
+        case SkTextBlob::kDefault_Positioning:
+            this->drawText(rt, clip, grPaint, runPaint, viewMatrix, (const char *)it.glyphs(),
+                           textLen, x + offset.x(), y + offset.y(), clipBounds);
+            break;
+        case SkTextBlob::kHorizontal_Positioning:
+            this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
+                              textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), clipBounds);
+            break;
+        case SkTextBlob::kFull_Positioning:
+            this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
+                              textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
+            break;
+        default:
+            SkFAIL("unhandled positioning mode");
+        }
+
+        if (drawFilter) {
+            // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
+            runPaint = skPaint;
+        }
+    }
+}
+
+void GrTextContext::drawTextAsPath(const SkPaint& skPaint, const SkMatrix& viewMatrix,
+                                   const char text[], size_t byteLength, SkScalar x, SkScalar y,
+                                   const SkIRect& clipBounds) {
+    SkTextToPathIter iter(text, byteLength, skPaint, true);
+
+    SkMatrix    matrix;
+    matrix.setScale(iter.getPathScale(), iter.getPathScale());
+    matrix.postTranslate(x, y);
+
+    const SkPath* iterPath;
+    SkScalar xpos, prevXPos = 0;
+
+    while (iter.next(&iterPath, &xpos)) {
+        matrix.postTranslate(xpos - prevXPos, 0);
+        if (iterPath) {
+            const SkPaint& pnt = iter.getPaint();
+            fGpuDevice->internalDrawPath(*iterPath, pnt, viewMatrix, &matrix, clipBounds, false);
+        }
+        prevXPos = xpos;
+    }
+}
+
+void GrTextContext::drawPosTextAsPath(const SkPaint& origPaint, const SkMatrix& viewMatrix,
+                                      const char text[], size_t byteLength,
+                                      const SkScalar pos[], int scalarsPerPosition,
+                                      const SkPoint& offset, const SkIRect& clipBounds) {
+    // setup our std paint, in hopes of getting hits in the cache
+    SkPaint paint(origPaint);
+    SkScalar matrixScale = paint.setupForAsPaths();
+
+    SkMatrix matrix;
+    matrix.setScale(matrixScale, matrixScale);
+
+    // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
+    paint.setStyle(SkPaint::kFill_Style);
+    paint.setPathEffect(NULL);
+
+    SkDrawCacheProc     glyphCacheProc = paint.getDrawCacheProc();
+    SkAutoGlyphCache    autoCache(paint, NULL, NULL);
+    SkGlyphCache*       cache = autoCache.getCache();
+
+    const char*        stop = text + byteLength;
+    SkTextAlignProc    alignProc(paint.getTextAlign());
+    SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
+
+    // Now restore the original settings, so we "draw" with whatever style/stroking.
+    paint.setStyle(origPaint.getStyle());
+    paint.setPathEffect(origPaint.getPathEffect());
+
+    while (text < stop) {
+        const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+        if (glyph.fWidth) {
+            const SkPath* path = cache->findPath(glyph);
+            if (path) {
+                SkPoint tmsLoc;
+                tmsProc(pos, &tmsLoc);
+                SkPoint loc;
+                alignProc(tmsLoc, glyph, &loc);
+
+                matrix[SkMatrix::kMTransX] = loc.fX;
+                matrix[SkMatrix::kMTransY] = loc.fY;
+                fGpuDevice->internalDrawPath(*path, paint, viewMatrix, &matrix, clipBounds, false);
+            }
+        }
+        pos += scalarsPerPosition;
+    }
+}
 
 //*** change to output positions?
 int GrTextContext::MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
diff --git a/src/gpu/GrTextContext.h b/src/gpu/GrTextContext.h
index d333c63..8529528 100644
--- a/src/gpu/GrTextContext.h
+++ b/src/gpu/GrTextContext.h
@@ -12,13 +12,15 @@
 #include "GrGlyph.h"
 #include "GrPaint.h"
 #include "SkDeviceProperties.h"
-
 #include "SkPostConfig.h"
 
 class GrClip;
 class GrContext;
 class GrDrawTarget;
 class GrFontScaler;
+class SkDrawFilter;
+class SkGpuDevice;
+class SkTextBlob;
 
 /*
  * This class wraps the state for a single text render
@@ -27,47 +29,68 @@
 public:
     virtual ~GrTextContext();
 
-    bool drawText(GrRenderTarget* rt, const GrClip&,  const GrPaint&, const SkPaint&,
+    void drawText(GrRenderTarget* rt, const GrClip&,  const GrPaint&, const SkPaint&,
                   const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x,
-                  SkScalar y);
-    bool drawPosText(GrRenderTarget* rt, const GrClip&, const GrPaint&, const SkPaint&,
+                  SkScalar y, const SkIRect& clipBounds);
+    void drawPosText(GrRenderTarget* rt, const GrClip&, const GrPaint&, const SkPaint&,
                      const SkMatrix& viewMatrix,
                      const char text[], size_t byteLength,
                      const SkScalar pos[], int scalarsPerPosition,
-                     const SkPoint& offset);
+                     const SkPoint& offset, const SkIRect& clipBounds);
+    virtual void drawTextBlob(GrRenderTarget*, const GrClip&, const SkPaint&,
+                              const SkMatrix& viewMatrix, const SkTextBlob*, SkScalar x, SkScalar y,
+                              SkDrawFilter*, const SkIRect& clipBounds);
 
 protected:
     GrTextContext*                 fFallbackTextContext;
     GrContext*                     fContext;
+    // TODO we probably don't really need to store a back pointer to the owning SkGpuDevice, except
+    // we need to be able to call drawPath on it in the event no other text context can draw the
+    // text.  We might be able to move this logic to context though.  This is unreffed because
+    // GrTextContext is completely owned by SkGpuDevice
+    SkGpuDevice*                   fGpuDevice;
     SkDeviceProperties             fDeviceProperties;
 
     SkAutoTUnref<GrRenderTarget>   fRenderTarget;
     GrClip                         fClip;
     GrDrawTarget*                  fDrawTarget;
     SkIRect                        fClipRect;
+    SkIRect                        fRegionClipBounds;
     GrPaint                        fPaint;
     SkPaint                        fSkPaint;
 
-    GrTextContext(GrContext*, const SkDeviceProperties&);
+    GrTextContext(GrContext*, SkGpuDevice*, const SkDeviceProperties&);
 
-    virtual bool canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) = 0;
+    virtual bool canDraw(const GrRenderTarget*, const GrClip&, const GrPaint&,
+                         const SkPaint&, const SkMatrix& viewMatrix) = 0;
 
     virtual void onDrawText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
                             const SkMatrix& viewMatrix, const char text[], size_t byteLength,
-                            SkScalar x, SkScalar y) = 0;
+                            SkScalar x, SkScalar y, const SkIRect& clipBounds) = 0;
     virtual void onDrawPosText(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
                                const SkMatrix& viewMatrix,
                                const char text[], size_t byteLength,
                                const SkScalar pos[], int scalarsPerPosition,
-                               const SkPoint& offset) = 0;
+                               const SkPoint& offset, const SkIRect& clipBounds) = 0;
 
-    void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&);
+    void drawTextAsPath(const SkPaint& origPaint, const SkMatrix& viewMatrix,
+                        const char text[], size_t byteLength, SkScalar x, SkScalar y,
+                        const SkIRect& clipBounds);
+    void drawPosTextAsPath(const SkPaint& origPaint, const SkMatrix& viewMatrix,
+                           const char text[], size_t byteLength,
+                           const SkScalar pos[], int scalarsPerPosition,
+                           const SkPoint& offset, const SkIRect& clipBounds);
+
+    void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
+              const SkIRect& regionClipBounds);
     void finish() { fDrawTarget = NULL; }
 
     static GrFontScaler* GetGrFontScaler(SkGlyphCache* cache);
     // sets extent in stopVector and returns glyph count
     static int MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
                            const char text[], size_t byteLength, SkVector* stopVector);
+
+    friend class BitmapTextBatch;
 };
 
 #endif
diff --git a/src/gpu/GrTextureProvider.cpp b/src/gpu/GrTextureProvider.cpp
new file mode 100644
index 0000000..c195398
--- /dev/null
+++ b/src/gpu/GrTextureProvider.cpp
@@ -0,0 +1,134 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrTextureProvider.h"
+#include "GrTexturePriv.h"
+#include "GrResourceCache.h"
+#include "GrGpu.h"
+
+enum ScratchTextureFlags {
+    kExact_ScratchTextureFlag           = 0x1,
+    kNoPendingIO_ScratchTextureFlag     = 0x2,
+    kNoCreate_ScratchTextureFlag        = 0x4,
+};
+
+GrTexture* GrTextureProvider::createTexture(const GrSurfaceDesc& desc, bool budgeted,
+                                            const void* srcData, size_t rowBytes) {
+    if (this->isAbandoned()) {
+        return NULL;
+    }
+    if ((desc.fFlags & kRenderTarget_GrSurfaceFlag) &&
+        !fGpu->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
+        return NULL;
+    }
+    if (!GrPixelConfigIsCompressed(desc.fConfig)) {
+        static const uint32_t kFlags = kExact_ScratchTextureFlag |
+                                       kNoCreate_ScratchTextureFlag;
+        if (GrTexture* texture = this->internalRefScratchTexture(desc, kFlags)) {
+            if (!srcData || texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+                                                 srcData, rowBytes)) {
+                if (!budgeted) {
+                    texture->resourcePriv().makeUnbudgeted();
+                }
+                return texture;
+            }
+            texture->unref();
+        }
+    }
+    return fGpu->createTexture(desc, budgeted, srcData, rowBytes);
+}
+
+GrTexture* GrTextureProvider::refScratchTexture(const GrSurfaceDesc& desc, ScratchTexMatch match,
+                                                bool calledDuringFlush) {
+    if (this->isAbandoned()) {
+        return NULL;
+    }
+    // Currently we don't recycle compressed textures as scratch.
+    if (GrPixelConfigIsCompressed(desc.fConfig)) {
+        return NULL;
+    } else {
+        uint32_t flags = 0;
+        if (kExact_ScratchTexMatch == match) {
+            flags |= kExact_ScratchTextureFlag;
+        }
+        if (calledDuringFlush) {
+            flags |= kNoPendingIO_ScratchTextureFlag;
+        }
+        return this->internalRefScratchTexture(desc, flags);
+    }
+}
+
+GrTexture* GrTextureProvider::internalRefScratchTexture(const GrSurfaceDesc& inDesc,
+                                                        uint32_t flags) {
+    SkASSERT(!this->isAbandoned());
+    SkASSERT(!GrPixelConfigIsCompressed(inDesc.fConfig));
+
+    SkTCopyOnFirstWrite<GrSurfaceDesc> desc(inDesc);
+
+    if (fGpu->caps()->reuseScratchTextures() || (desc->fFlags & kRenderTarget_GrSurfaceFlag)) {
+        if (!(kExact_ScratchTextureFlag & flags)) {
+            // bin by pow2 with a reasonable min
+            static const int MIN_SIZE = 16;
+            GrSurfaceDesc* wdesc = desc.writable();
+            wdesc->fWidth  = SkTMax(MIN_SIZE, GrNextPow2(desc->fWidth));
+            wdesc->fHeight = SkTMax(MIN_SIZE, GrNextPow2(desc->fHeight));
+        }
+
+        GrScratchKey key;
+        GrTexturePriv::ComputeScratchKey(*desc, &key);
+        uint32_t scratchFlags = 0;
+        if (kNoPendingIO_ScratchTextureFlag & flags) {
+            scratchFlags = GrResourceCache::kRequireNoPendingIO_ScratchFlag;
+        } else  if (!(desc->fFlags & kRenderTarget_GrSurfaceFlag)) {
+            // If it is not a render target then it will most likely be populated by
+            // writePixels() which will trigger a flush if the texture has pending IO.
+            scratchFlags = GrResourceCache::kPreferNoPendingIO_ScratchFlag;
+        }
+        GrGpuResource* resource = fCache->findAndRefScratchResource(key, scratchFlags);
+        if (resource) {
+            GrSurface* surface = static_cast<GrSurface*>(resource);
+            GrRenderTarget* rt = surface->asRenderTarget();
+            if (rt && fGpu->caps()->discardRenderTargetSupport()) {
+                rt->discard();
+            }
+            return surface->asTexture();
+        }
+    }
+
+    if (!(kNoCreate_ScratchTextureFlag & flags)) {
+        return fGpu->createTexture(*desc, true, NULL, 0);
+    }
+
+    return NULL;
+}
+
+GrTexture* GrTextureProvider::wrapBackendTexture(const GrBackendTextureDesc& desc) {
+    if (this->isAbandoned()) {
+        return NULL;
+    }
+    return fGpu->wrapBackendTexture(desc);
+}
+
+GrRenderTarget* GrTextureProvider::wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc) {
+    return this->isAbandoned() ? NULL : fGpu->wrapBackendRenderTarget(desc);
+}
+
+void GrTextureProvider::assignUniqueKeyToResource(const GrUniqueKey& key, GrGpuResource* resource) {
+    if (this->isAbandoned() || !resource) {
+        return;
+    }
+    resource->resourcePriv().setUniqueKey(key);
+}
+
+bool GrTextureProvider::existsResourceWithUniqueKey(const GrUniqueKey& key) const {
+    return this->isAbandoned() ? false : fCache->hasUniqueKey(key);
+}
+
+GrGpuResource* GrTextureProvider::findAndRefResourceByUniqueKey(const GrUniqueKey& key) {
+    return this->isAbandoned() ? NULL : fCache->findAndRefUniqueResource(key);
+}
diff --git a/src/gpu/GrVertices.h b/src/gpu/GrVertices.h
new file mode 100644
index 0000000..10b2679
--- /dev/null
+++ b/src/gpu/GrVertices.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrVertices_DEFINED
+#define GrVertices_DEFINED
+
+#include "GrIndexBuffer.h"
+#include "GrVertexBuffer.h"
+
+class GrNonInstancedVertices {
+public:
+    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; }
+    bool isIndexed() const { return fIndexCount > 0; }
+
+    const GrVertexBuffer* vertexBuffer() const { return fVertexBuffer.get(); }
+    const GrIndexBuffer* indexBuffer() const { return fIndexBuffer.get(); }
+
+protected:
+    GrPrimitiveType         fPrimitiveType;
+    int                     fStartVertex;
+    int                     fStartIndex;
+    int                     fVertexCount;
+    int                     fIndexCount;
+    GrPendingIOResource<const GrVertexBuffer, kRead_GrIOType> fVertexBuffer;
+    GrPendingIOResource<const GrIndexBuffer, kRead_GrIOType>  fIndexBuffer;
+    friend class GrVertices;
+};
+
+/**
+ * Used to communicate index and vertex buffers, counts, and offsets for a draw from GrBatch to
+ * GrGpu. It also holds the primitive type for the draw. TODO: Consider moving ownership of this
+ * and draw-issuing responsibility to GrPrimitiveProcessor. The rest of the vertex info lives there
+ * already (stride, attribute mappings).
+ */
+class GrVertices : public GrNonInstancedVertices {
+public:
+    GrVertices() {}
+    GrVertices(const GrVertices& di) { (*this) = di; }
+    GrVertices& operator =(const GrVertices& di);
+
+    void init(GrPrimitiveType primType, const GrVertexBuffer* vertexBuffer, int startVertex,
+                int vertexCount) {
+        SkASSERT(vertexBuffer);
+        SkASSERT(vertexCount);
+        SkASSERT(startVertex >= 0);
+        fPrimitiveType = primType;
+        fVertexBuffer.reset(vertexBuffer);
+        fIndexBuffer.reset(NULL);
+        fStartVertex = startVertex;
+        fStartIndex = 0;
+        fVertexCount = vertexCount;
+        fIndexCount = 0;
+        fInstanceCount = 0;
+        fVerticesPerInstance = 0;
+        fIndicesPerInstance = 0;
+        fMaxInstancesPerDraw = 0;
+    }
+
+    void initIndexed(GrPrimitiveType primType,
+                        const GrVertexBuffer* vertexBuffer,
+                        const GrIndexBuffer* indexBuffer,
+                        int startVertex,
+                        int startIndex,
+                        int vertexCount,
+                        int indexCount) {
+        SkASSERT(indexBuffer);
+        SkASSERT(vertexBuffer);
+        SkASSERT(indexCount);
+        SkASSERT(vertexCount);
+        SkASSERT(startIndex >= 0);
+        SkASSERT(startVertex >= 0);
+        fPrimitiveType = primType;
+        fVertexBuffer.reset(vertexBuffer);
+        fIndexBuffer.reset(indexBuffer);
+        fStartVertex = startVertex;
+        fStartIndex = startIndex;
+        fVertexCount = vertexCount;
+        fIndexCount = indexCount;
+        fInstanceCount = 0;
+        fVerticesPerInstance = 0;
+        fIndicesPerInstance = 0;
+        fMaxInstancesPerDraw = 0;
+    }
+
+
+    /** Variation of the above that may be used when the total number of instances may exceed
+        the number of instances supported by the index buffer. To be used with
+        nextInstances() to draw in max-sized batches.*/
+    void initInstanced(GrPrimitiveType primType,
+                        const GrVertexBuffer* vertexBuffer,
+                        const GrIndexBuffer* indexBuffer,
+                        int startVertex,
+                        int verticesPerInstance,
+                        int indicesPerInstance,
+                        int instanceCount,
+                        int maxInstancesPerDraw) {
+        SkASSERT(vertexBuffer);
+        SkASSERT(indexBuffer);
+        SkASSERT(instanceCount);
+        SkASSERT(verticesPerInstance);
+        SkASSERT(indicesPerInstance);
+        SkASSERT(startVertex >= 0);
+        fPrimitiveType = primType;
+        fVertexBuffer.reset(vertexBuffer);
+        fIndexBuffer.reset(indexBuffer);
+        fStartVertex = startVertex;
+        fStartIndex = 0;
+        fVerticesPerInstance = verticesPerInstance;
+        fIndicesPerInstance = indicesPerInstance;
+        fInstanceCount = instanceCount;
+        fVertexCount = instanceCount * fVerticesPerInstance;
+        fIndexCount = instanceCount * fIndicesPerInstance;
+        fMaxInstancesPerDraw = maxInstancesPerDraw;
+    }
+
+
+    /** These return 0 if initInstanced was not used to initialize the GrVertices. */
+    int verticesPerInstance() const { return fVerticesPerInstance; }
+    int indicesPerInstance() const { return fIndicesPerInstance; }
+    int instanceCount() const { return fInstanceCount; }
+
+    bool isInstanced() const { return fInstanceCount > 0; }
+
+    class Iterator {
+    public:
+        const GrNonInstancedVertices* init(const GrVertices& vertices) {
+            fVertices = &vertices;
+            if (vertices.fInstanceCount <= vertices.fMaxInstancesPerDraw) {
+                fInstancesRemaining = 0;
+                // Note, this also covers the non-instanced case!
+                return &vertices;
+            }
+            SkASSERT(vertices.isInstanced());
+            fInstanceBatch.fIndexBuffer.reset(vertices.fIndexBuffer.get());
+            fInstanceBatch.fVertexBuffer.reset(vertices.fVertexBuffer.get());
+            fInstanceBatch.fIndexCount = vertices.fMaxInstancesPerDraw *
+                                         vertices.fIndicesPerInstance;
+            fInstanceBatch.fVertexCount = vertices.fMaxInstancesPerDraw *
+                                          vertices.fVerticesPerInstance;
+            fInstanceBatch.fPrimitiveType = vertices.fPrimitiveType;
+            fInstanceBatch.fStartIndex = vertices.fStartIndex;
+            fInstanceBatch.fStartVertex = vertices.fStartVertex;
+            fInstancesRemaining = vertices.fInstanceCount - vertices.fMaxInstancesPerDraw;
+            return &fInstanceBatch;
+        }
+
+        const GrNonInstancedVertices* next() {
+            if (!fInstancesRemaining) {
+                return NULL;
+            }
+            fInstanceBatch.fStartVertex += fInstanceBatch.fVertexCount;
+            int instances = SkTMin(fInstancesRemaining, fVertices->fMaxInstancesPerDraw);
+            fInstanceBatch.fIndexCount = instances * fVertices->fIndicesPerInstance;
+            fInstanceBatch.fVertexCount = instances * fVertices->fVerticesPerInstance;
+            fInstancesRemaining -= instances;
+            return &fInstanceBatch;
+        }
+    private:
+        GrNonInstancedVertices fInstanceBatch;
+        const GrVertices* fVertices;
+        int fInstancesRemaining;
+    };
+
+private:
+    int                     fInstanceCount;
+    int                     fVerticesPerInstance;
+    int                     fIndicesPerInstance;
+    int                     fMaxInstancesPerDraw;
+};
+
+#endif
diff --git a/src/gpu/GrXferProcessor.cpp b/src/gpu/GrXferProcessor.cpp
index 358a249..8eb6ff7 100644
--- a/src/gpu/GrXferProcessor.cpp
+++ b/src/gpu/GrXferProcessor.cpp
@@ -8,20 +8,40 @@
 #include "GrXferProcessor.h"
 #include "gl/GrGLCaps.h"
 
-GrXferProcessor::GrXferProcessor() : fWillReadDstColor(false), fDstCopyTextureOffset() {
+GrXferProcessor::GrXferProcessor()
+    : fWillReadDstColor(false), fReadsCoverage(true), fDstCopyTextureOffset() {
 }
 
 GrXferProcessor::GrXferProcessor(const GrDeviceCoordTexture* dstCopy, bool willReadDstColor)
     : fWillReadDstColor(willReadDstColor)
+    , fReadsCoverage(true)
     , fDstCopyTextureOffset() {
     if (dstCopy && dstCopy->texture()) {
         fDstCopy.reset(dstCopy->texture());
         fDstCopyTextureOffset = dstCopy->offset();
         this->addTextureAccess(&fDstCopy);
+        this->setWillReadFragmentPosition();
     }
 }
 
-void GrXferProcessor::getGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const {
+GrXferProcessor::OptFlags GrXferProcessor::getOptimizations(const GrProcOptInfo& colorPOI,
+                                                            const GrProcOptInfo& coveragePOI,
+                                                            bool doesStencilWrite,
+                                                            GrColor* overrideColor,
+                                                            const GrDrawTargetCaps& caps) {
+    GrXferProcessor::OptFlags flags = this->onGetOptimizations(colorPOI,
+                                                               coveragePOI,
+                                                               doesStencilWrite,
+                                                               overrideColor,
+                                                               caps);
+
+    if (flags & GrXferProcessor::kIgnoreCoverage_OptFlag) {
+        fReadsCoverage = false;
+    }
+    return flags;
+}
+
+void GrXferProcessor::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
     uint32_t key = this->willReadDstColor() ? 0x1 : 0x0;
     if (this->getDstCopyTexture() &&
         kTopLeft_GrSurfaceOrigin == this->getDstCopyTexture()->origin()) {
@@ -31,6 +51,113 @@
     this->onGetGLProcessorKey(caps, b);
 }
 
+bool GrXferProcessor::willNeedXferBarrier(const GrRenderTarget* rt,
+                                          const GrDrawTargetCaps& caps,
+                                          GrXferBarrierType* outBarrierType) const {
+    if (static_cast<const GrSurface*>(rt) == this->getDstCopyTexture()) {
+        // Texture barriers are required when a shader reads and renders to the same texture.
+        SkASSERT(rt);
+        SkASSERT(caps.textureBarrierSupport());
+        *outBarrierType = kTexture_GrXferBarrierType;
+        return true;
+    }
+    return this->onWillNeedXferBarrier(rt, caps, outBarrierType);
+}
+
+#ifdef SK_DEBUG
+static const char* equation_string(GrBlendEquation eq) {
+    switch (eq) {
+        case kAdd_GrBlendEquation:
+            return "add";
+        case kSubtract_GrBlendEquation:
+            return "subtract";
+        case kReverseSubtract_GrBlendEquation:
+            return "reverse_subtract";
+        case kScreen_GrBlendEquation:
+            return "screen";
+        case kOverlay_GrBlendEquation:
+            return "overlay";
+        case kDarken_GrBlendEquation:
+            return "darken";
+        case kLighten_GrBlendEquation:
+            return "lighten";
+        case kColorDodge_GrBlendEquation:
+            return "color_dodge";
+        case kColorBurn_GrBlendEquation:
+            return "color_burn";
+        case kHardLight_GrBlendEquation:
+            return "hard_light";
+        case kSoftLight_GrBlendEquation:
+            return "soft_light";
+        case kDifference_GrBlendEquation:
+            return "difference";
+        case kExclusion_GrBlendEquation:
+            return "exclusion";
+        case kMultiply_GrBlendEquation:
+            return "multiply";
+        case kHSLHue_GrBlendEquation:
+            return "hsl_hue";
+        case kHSLSaturation_GrBlendEquation:
+            return "hsl_saturation";
+        case kHSLColor_GrBlendEquation:
+            return "hsl_color";
+        case kHSLLuminosity_GrBlendEquation:
+            return "hsl_luminosity";
+    };
+    return "";
+}
+
+static const char* coeff_string(GrBlendCoeff coeff) {
+    switch (coeff) {
+        case kZero_GrBlendCoeff:
+            return "zero";
+        case kOne_GrBlendCoeff:
+            return "one";
+        case kSC_GrBlendCoeff:
+            return "src_color";
+        case kISC_GrBlendCoeff:
+            return "inv_src_color";
+        case kDC_GrBlendCoeff:
+            return "dst_color";
+        case kIDC_GrBlendCoeff:
+            return "inv_dst_color";
+        case kSA_GrBlendCoeff:
+            return "src_alpha";
+        case kISA_GrBlendCoeff:
+            return "inv_src_alpha";
+        case kDA_GrBlendCoeff:
+            return "dst_alpha";
+        case kIDA_GrBlendCoeff:
+            return "inv_dst_alpha";
+        case kConstC_GrBlendCoeff:
+            return "const_color";
+        case kIConstC_GrBlendCoeff:
+            return "inv_const_color";
+        case kConstA_GrBlendCoeff:
+            return "const_alpha";
+        case kIConstA_GrBlendCoeff:
+            return "inv_const_alpha";
+        case kS2C_GrBlendCoeff:
+            return "src2_color";
+        case kIS2C_GrBlendCoeff:
+            return "inv_src2_color";
+        case kS2A_GrBlendCoeff:
+            return "src2_alpha";
+        case kIS2A_GrBlendCoeff:
+            return "inv_src2_alpha";
+    }
+    return "";
+}
+
+SkString GrXferProcessor::BlendInfo::dump() const {
+    SkString out;
+    out.printf("write_color(%d) equation(%s) src_coeff(%s) dst_coeff:(%s) const(0x%08x)",
+               fWriteColor, equation_string(fEquation), coeff_string(fSrcBlend),
+               coeff_string(fDstBlend), fBlendConstant);
+    return out;
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 GrXferProcessor* GrXPFactory::createXferProcessor(const GrProcOptInfo& colorPOI,
@@ -39,7 +166,7 @@
                                                   const GrDrawTargetCaps& caps) const {
 #ifdef SK_DEBUG
     if (this->willReadDstColor(caps, colorPOI, coveragePOI)) {
-        if (!caps.dstReadInShaderSupport()) {
+        if (!caps.shaderCaps()->dstReadInShaderSupport()) {
             SkASSERT(dstCopy && dstCopy->texture());
         } else {
             SkASSERT(!dstCopy || !dstCopy->texture()); 
@@ -53,6 +180,7 @@
 
 bool GrXPFactory::willNeedDstCopy(const GrDrawTargetCaps& caps, const GrProcOptInfo& colorPOI,
                                   const GrProcOptInfo& coveragePOI) const {
-    return (this->willReadDstColor(caps, colorPOI, coveragePOI) && !caps.dstReadInShaderSupport());
+    return (this->willReadDstColor(caps, colorPOI, coveragePOI) 
+            && !caps.shaderCaps()->dstReadInShaderSupport());
 }
 
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index fb5fa7c..7b36363 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -7,14 +7,13 @@
 
 #include "SkGpuDevice.h"
 
-#include "GrBitmapTextContext.h"
 #include "GrContext.h"
-#include "GrDistanceFieldTextContext.h"
 #include "GrGpu.h"
 #include "GrGpuResourcePriv.h"
 #include "GrLayerHoister.h"
 #include "GrRecordReplaceDraw.h"
 #include "GrStrokeInfo.h"
+#include "GrTextContext.h"
 #include "GrTracing.h"
 #include "SkCanvasPriv.h"
 #include "SkDeviceImageFilterProxy.h"
@@ -22,6 +21,7 @@
 #include "SkErrorInternals.h"
 #include "SkGlyphCache.h"
 #include "SkGrTexturePixelRef.h"
+#include "SkImage_Base.h"
 #include "SkImageFilter.h"
 #include "SkLayerInfo.h"
 #include "SkMaskFilter.h"
@@ -121,10 +121,15 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 SkGpuDevice* SkGpuDevice::Create(GrRenderTarget* rt, const SkSurfaceProps* props, unsigned flags) {
+    return SkGpuDevice::Create(rt, rt->width(), rt->height(), props, flags);
+}
+
+SkGpuDevice* SkGpuDevice::Create(GrRenderTarget* rt, int width, int height,
+                                 const SkSurfaceProps* props, unsigned flags) {
     if (!rt || rt->wasDestroyed()) {
         return NULL;
     }
-    return SkNEW_ARGS(SkGpuDevice, (rt, props, flags));
+    return SkNEW_ARGS(SkGpuDevice, (rt, width, height, props, flags));
 }
 
 static SkDeviceProperties surfaceprops_to_deviceprops(const SkSurfaceProps* props) {
@@ -143,7 +148,8 @@
     }
 }
 
-SkGpuDevice::SkGpuDevice(GrRenderTarget* rt, const SkSurfaceProps* props, unsigned flags)
+SkGpuDevice::SkGpuDevice(GrRenderTarget* rt, int width, int height,
+                         const SkSurfaceProps* props, unsigned flags)
     : INHERITED(surfaceprops_to_deviceprops(props))
     , fSurfaceProps(copy_or_default_props(props))
 {
@@ -154,13 +160,14 @@
 
     fRenderTarget = SkRef(rt);
 
-    SkImageInfo info = rt->surfacePriv().info();
+    SkImageInfo info = rt->surfacePriv().info().makeWH(width, height);
     SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, rt));
     fLegacyBitmap.setInfo(info);
     fLegacyBitmap.setPixelRef(pr)->unref();
 
     bool useDFT = fSurfaceProps.isUseDistanceFieldFonts();
-    fTextContext = fContext->createTextContext(fRenderTarget, this->getLeakyProperties(), useDFT);
+    fTextContext = fContext->createTextContext(fRenderTarget, this, this->getLeakyProperties(),
+                                               useDFT);
 }
 
 GrRenderTarget* SkGpuDevice::CreateRenderTarget(GrContext* context, SkSurface::Budgeted budgeted,
@@ -193,7 +200,8 @@
     desc.fHeight = info.height();
     desc.fConfig = SkImageInfo2GrPixelConfig(info);
     desc.fSampleCnt = sampleCount;
-    GrTexture* texture = context->createTexture(desc, SkToBool(budgeted), NULL, 0);
+    GrTexture* texture = context->textureProvider()->createTexture(
+        desc, SkToBool(budgeted), NULL, 0);
     if (NULL == texture) {
         return NULL;
     }
@@ -210,7 +218,7 @@
         return NULL;
     }
 
-    return SkNEW_ARGS(SkGpuDevice, (rt, props, flags));
+    return SkNEW_ARGS(SkGpuDevice, (rt, info.width(), info.height(), props, flags));
 }
 
 SkGpuDevice::~SkGpuDevice() {
@@ -340,25 +348,14 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,
-                  shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5,
-                  shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch);
-
-///////////////////////////////////////////////////////////////////////////////
-
 void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
     CHECK_SHOULD_DRAW(draw);
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawPaint", fContext);
 
     GrPaint grPaint;
-    SkPaint2GrPaintShader(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint);
+    if (!SkPaint2GrPaint(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint)) {
+        return;
+    }
 
     fContext->drawPaint(fRenderTarget, fClip, grPaint, *draw.fMatrix);
 }
@@ -383,7 +380,10 @@
     if (paint.getPathEffect() && 2 == count && SkCanvas::kLines_PointMode == mode) {
         GrStrokeInfo strokeInfo(paint, SkPaint::kStroke_Style);
         GrPaint grPaint;
-        SkPaint2GrPaintShader(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint);
+        if (!SkPaint2GrPaint(this->context(), fRenderTarget, paint, *draw.fMatrix, true,
+                             &grPaint)) {
+            return;
+        }
         SkPath path;
         path.setIsVolatile(true);
         path.moveTo(pts[0]);
@@ -400,7 +400,9 @@
     }
 
     GrPaint grPaint;
-    SkPaint2GrPaintShader(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint);
+    if (!SkPaint2GrPaint(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint)) {
+        return;
+    }
 
     fContext->drawVertices(fRenderTarget,
                            fClip,
@@ -472,7 +474,9 @@
     }
 
     GrPaint grPaint;
-    SkPaint2GrPaintShader(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint);
+    if (!SkPaint2GrPaint(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint)) {
+        return;
+    }
 
     fContext->drawRect(fRenderTarget, fClip, grPaint, *draw.fMatrix, rect, &strokeInfo);
 }
@@ -486,7 +490,9 @@
     CHECK_SHOULD_DRAW(draw);
 
     GrPaint grPaint;
-    SkPaint2GrPaintShader(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint);
+    if (!SkPaint2GrPaint(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint)) {
+        return;
+    }
 
     GrStrokeInfo strokeInfo(paint);
     if (paint.getMaskFilter()) {
@@ -554,7 +560,10 @@
         CHECK_SHOULD_DRAW(draw);
 
         GrPaint grPaint;
-        SkPaint2GrPaintShader(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint);
+        if (!SkPaint2GrPaint(this->context(), fRenderTarget, paint, *draw.fMatrix, true,
+                             &grPaint)) {
+            return;
+        }
 
         if (NULL == paint.getMaskFilter() && NULL == paint.getPathEffect()) {
             fContext->drawDRRect(fRenderTarget, fClip, grPaint, *draw.fMatrix, outer, inner);
@@ -602,7 +611,9 @@
     }
 
     GrPaint grPaint;
-    SkPaint2GrPaintShader(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint);
+    if (!SkPaint2GrPaint(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint)) {
+        return;
+    }
 
     fContext->drawOval(fRenderTarget, fClip, grPaint, *draw.fMatrix, oval, strokeInfo);
 }
@@ -639,18 +650,22 @@
     return true;
 }
 
+static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
+    return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
+}
+
 bool draw_with_mask_filter(GrContext* context,
                            GrRenderTarget* rt,
                            const GrClip& clipData,
                            const SkMatrix& viewMatrix,
                            const SkPath& devPath,
                            SkMaskFilter* filter,
-                           const SkRegion& clip,
+                           const SkIRect& clipBounds,
                            GrPaint* grp,
                            SkPaint::Style style) {
     SkMask  srcM, dstM;
 
-    if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &viewMatrix, &srcM,
+    if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
                             SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
         return false;
     }
@@ -662,7 +677,7 @@
     // this will free-up dstM when we're done (allocated in filterMask())
     SkAutoMaskFreeImage autoDst(dstM.fImage);
 
-    if (clip.quickReject(dstM.fBounds)) {
+    if (clip_bounds_quick_reject(clipBounds, dstM.fBounds)) {
         return false;
     }
 
@@ -673,8 +688,8 @@
     desc.fHeight = dstM.fBounds.height();
     desc.fConfig = kAlpha_8_GrPixelConfig;
 
-    SkAutoTUnref<GrTexture> texture(
-        context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch));
+    SkAutoTUnref<GrTexture> texture(context->textureProvider()->refScratchTexture(
+        desc, GrTextureProvider::kApprox_ScratchTexMatch));
     if (!texture) {
         return false;
     }
@@ -708,7 +723,8 @@
         desc.fConfig = kAlpha_8_GrPixelConfig;
     }
 
-    GrTexture* mask = context->refScratchTexture(desc,GrContext::kApprox_ScratchTexMatch);
+    GrTexture* mask = context->textureProvider()->refScratchTexture(
+        desc, GrTextureProvider::kApprox_ScratchTexMatch);
     if (NULL == mask) {
         return NULL;
     }
@@ -731,9 +747,9 @@
     return mask;
 }
 
-SkBitmap wrap_texture(GrTexture* texture) {
+SkBitmap wrap_texture(GrTexture* texture, int width, int height) {
     SkBitmap result;
-    result.setInfo(texture->surfacePriv().info());
+    result.setInfo(SkImageInfo::MakeN32Premul(width, height));
     result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (result.info(), texture)))->unref();
     return result;
 }
@@ -747,6 +763,13 @@
     CHECK_SHOULD_DRAW(draw);
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawPath", fContext);
 
+    return this->internalDrawPath(origSrcPath, paint, *draw.fMatrix, prePathMatrix,
+                                  draw.fClip->getBounds(), pathIsMutable);
+}
+
+void SkGpuDevice::internalDrawPath(const SkPath& origSrcPath, const SkPaint& paint,
+                                   const SkMatrix& origViewMatrix, const SkMatrix* prePathMatrix,
+                                   const SkIRect& clipBounds, bool pathIsMutable) {
     SkASSERT(!pathIsMutable || origSrcPath.isVolatile());
 
     GrStrokeInfo strokeInfo(paint);
@@ -759,12 +782,12 @@
     SkTLazy<SkPath> effectPath;
     SkPathEffect* pathEffect = paint.getPathEffect();
 
-    SkMatrix viewMatrix = *draw.fMatrix;
+    SkMatrix viewMatrix = origViewMatrix;
 
     if (prePathMatrix) {
-        // stroking and path effects are supposed to be applied *after* the prePathMatrix.
-        // The pre-path-matrix also should not affect shadeing.
-        if (NULL == pathEffect && NULL == paint.getShader() &&
+        // stroking, path effects, and blurs are supposed to be applied *after* the prePathMatrix.
+        // The pre-path-matrix also should not affect shading.
+        if (NULL == paint.getMaskFilter() && NULL == pathEffect && NULL == paint.getShader() &&
             (strokeInfo.getStrokeRec().isFillStyle() ||
              strokeInfo.getStrokeRec().isHairlineStyle())) {
             viewMatrix.preConcat(*prePathMatrix);
@@ -786,21 +809,29 @@
     SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
 
     GrPaint grPaint;
-    SkPaint2GrPaintShader(this->context(), fRenderTarget, paint, viewMatrix, true, &grPaint);
+    if (!SkPaint2GrPaint(this->context(), fRenderTarget, paint, viewMatrix, true, &grPaint)) {
+        return;
+    }
 
     const SkRect* cullRect = NULL;  // TODO: what is our bounds?
     SkStrokeRec* strokePtr = strokeInfo.getStrokeRecPtr();
-    if (pathEffect && pathEffect->filterPath(effectPath.init(), *pathPtr, strokePtr,
-                                             cullRect)) {
+    if (!strokeInfo.isDashed() && pathEffect && pathEffect->filterPath(effectPath.init(), *pathPtr,
+                                                                       strokePtr, cullRect)) {
         pathPtr = effectPath.get();
         pathIsMutable = true;
-        strokeInfo.removeDash();
     }
 
     const SkStrokeRec& stroke = strokeInfo.getStrokeRec();
     if (paint.getMaskFilter()) {
         if (!stroke.isHairlineStyle()) {
             SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init();
+            if (strokeInfo.isDashed()) {
+                if (pathEffect->filterPath(strokedPath, *pathPtr, strokePtr, cullRect)) {
+                    pathPtr = strokedPath;
+                    pathIsMutable = true;
+                }
+                strokeInfo.removeDash();
+            }
             if (stroke.applyToPath(strokedPath, *pathPtr)) {
                 pathPtr = strokedPath;
                 pathIsMutable = true;
@@ -819,12 +850,12 @@
 
         SkRect maskRect;
         if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),
-                                                    draw.fClip->getBounds(),
+                                                    clipBounds,
                                                     viewMatrix,
                                                     &maskRect)) {
             SkIRect finalIRect;
             maskRect.roundOut(&finalIRect);
-            if (draw.fClip->quickReject(finalIRect)) {
+            if (clip_bounds_quick_reject(clipBounds, finalIRect)) {
                 // clipped out
                 return;
             }
@@ -874,7 +905,7 @@
         SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :
                                                           SkPaint::kFill_Style;
         draw_with_mask_filter(fContext, fRenderTarget, fClip, viewMatrix, *devPathPtr,
-                              paint.getMaskFilter(), *draw.fClip, &grPaint, style);
+                              paint.getMaskFilter(), clipBounds, &grPaint, style);
         return;
     }
 
@@ -1085,11 +1116,14 @@
                                  const SkMatrix& contextMatrix,
                                  bool bicubic) {
     bool needsTextureDomain = false;
+    GrTexture* tex = bitmap.getTexture();
+    int width = tex ? tex->width() : bitmap.width();
+    int height = tex ? tex->height() : bitmap.height();
 
     if (bicubic || params.filterMode() != GrTextureParams::kNone_FilterMode) {
         // Need texture domain if drawing a sub rect
-        needsTextureDomain = srcRect.width() < bitmap.width() ||
-                             srcRect.height() < bitmap.height();
+        needsTextureDomain = srcRect.width() < width ||
+                             srcRect.height() < height;
         if (!bicubic && needsTextureDomain && contextMatrix.rectStaysRect()) {
             // sampling is axis-aligned
             SkRect transformedRect;
@@ -1124,15 +1158,17 @@
         dstSize.fWidth = w;
         dstSize.fHeight = h;
         srcRect.set(0, 0, w, h);
-        flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag);
     } else {
         SkASSERT(dstSizePtr);
         srcRect = *srcRectPtr;
         dstSize = *dstSizePtr;
-        if (srcRect.fLeft <= 0 && srcRect.fTop <= 0 &&
-            srcRect.fRight >= bitmap.width() && srcRect.fBottom >= bitmap.height()) {
-            flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag);
-        }
+    }
+    GrTexture* tex = bitmap.getTexture();
+    int width = tex ? tex->width() : bitmap.width();
+    int height = tex ? tex->height() : bitmap.height();
+    if (srcRect.fLeft <= 0 && srcRect.fTop <= 0 &&
+        srcRect.fRight >= width && srcRect.fBottom >= height) {
+        flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag);
     }
 
     // If the render target is not msaa and draw is antialiased, we call
@@ -1220,19 +1256,19 @@
     viewM.preConcat(m);
 
     GrTextureParams params;
-    SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel();
+    SkFilterQuality paintFilterQuality = paint.getFilterQuality();
     GrTextureParams::FilterMode textureFilterMode;
 
     bool doBicubic = false;
 
-    switch(paintFilterLevel) {
-        case SkPaint::kNone_FilterLevel:
+    switch(paintFilterQuality) {
+        case kNone_SkFilterQuality:
             textureFilterMode = GrTextureParams::kNone_FilterMode;
             break;
-        case SkPaint::kLow_FilterLevel:
+        case kLow_SkFilterQuality:
             textureFilterMode = GrTextureParams::kBilerp_FilterMode;
             break;
-        case SkPaint::kMedium_FilterLevel:
+        case kMedium_SkFilterQuality:
             if (viewM.getMinScale() < SK_Scalar1) {
                 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
             } else {
@@ -1240,7 +1276,7 @@
                 textureFilterMode = GrTextureParams::kBilerp_FilterMode;
             }
             break;
-        case SkPaint::kHigh_FilterLevel:
+        case kHigh_SkFilterQuality:
             // Minification can look bad with the bicubic effect.
             doBicubic =
                 GrBicubicEffect::ShouldUseBicubic(viewM, &textureFilterMode);
@@ -1455,13 +1491,17 @@
     bool alphaOnly = !(kAlpha_8_SkColorType == bitmap.colorType());
     GrColor paintColor = (alphaOnly) ? SkColor2GrColorJustAlpha(paint.getColor()) :
                                        SkColor2GrColor(paint.getColor());
-    SkPaint2GrPaintNoShader(this->context(), fRenderTarget, paint, paintColor, false, &grPaint);
+    if (!SkPaint2GrPaintNoShader(this->context(), fRenderTarget, paint, paintColor, false,
+                                 &grPaint)) {
+        return;
+    }
 
     fContext->drawNonAARectToRect(fRenderTarget, fClip, grPaint, viewMatrix, dstRect,
                                   paintRect);
 }
 
 bool SkGpuDevice::filterTexture(GrContext* context, GrTexture* texture,
+                                int width, int height,
                                 const SkImageFilter* filter,
                                 const SkImageFilter::Context& ctx,
                                 SkBitmap* result, SkIPoint* offset) {
@@ -1472,7 +1512,8 @@
     SkDeviceImageFilterProxy proxy(this, SkSurfaceProps(0, getLeakyProperties().pixelGeometry()));
 
     if (filter->canFilterImageGPU()) {
-        return filter->filterImageGPU(&proxy, wrap_texture(texture), ctx, result, offset);
+        return filter->filterImageGPU(&proxy, wrap_texture(texture, width, height),
+                                      ctx, result, offset);
     } else {
         return false;
     }
@@ -1511,7 +1552,7 @@
         // This cache is transient, and is freed (along with all its contained
         // textures) when it goes out of scope.
         SkImageFilter::Context ctx(matrix, clipBounds, cache);
-        if (this->filterTexture(fContext, texture, filter, ctx, &filteredBitmap,
+        if (this->filterTexture(fContext, texture, w, h, filter, ctx, &filteredBitmap,
                                 &offset)) {
             texture = (GrTexture*) filteredBitmap.getTexture();
             w = filteredBitmap.width();
@@ -1526,8 +1567,10 @@
     GrPaint grPaint;
     grPaint.addColorTextureProcessor(texture, SkMatrix::I());
 
-    SkPaint2GrPaintNoShader(this->context(), fRenderTarget, paint,
-                            SkColor2GrColorJustAlpha(paint.getColor()), false, &grPaint);
+    if (!SkPaint2GrPaintNoShader(this->context(), fRenderTarget, paint,
+                                 SkColor2GrColorJustAlpha(paint.getColor()), false, &grPaint)) {
+        return;
+    }
 
     fContext->drawNonAARectToRect(fRenderTarget,
                                   fClip,
@@ -1625,8 +1668,8 @@
         // textures) when it goes out of scope.
         SkAutoTUnref<SkImageFilter::Cache> cache(getImageFilterCache());
         SkImageFilter::Context ctx(matrix, clipBounds, cache);
-        if (this->filterTexture(fContext, devTex, filter, ctx, &filteredBitmap,
-                                &offset)) {
+        if (this->filterTexture(fContext, devTex, device->width(), device->height(),
+                                filter, ctx, &filteredBitmap, &offset)) {
             devTex = filteredBitmap.getTexture();
             w = filteredBitmap.width();
             h = filteredBitmap.height();
@@ -1640,8 +1683,10 @@
     GrPaint grPaint;
     grPaint.addColorTextureProcessor(devTex, SkMatrix::I());
 
-    SkPaint2GrPaintNoShader(this->context(), fRenderTarget, paint,
-                            SkColor2GrColorJustAlpha(paint.getColor()), false, &grPaint);
+    if (!SkPaint2GrPaintNoShader(this->context(), fRenderTarget, paint,
+                                 SkColor2GrColorJustAlpha(paint.getColor()), false, &grPaint)) {
+        return;
+    }
 
     SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x),
                                       SkIntToScalar(y),
@@ -1678,8 +1723,38 @@
     // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
     // must be pushed upstack.
     AutoBitmapTexture abt(fContext, src, NULL, &texture);
+    if (!texture) {
+        return false;
+    }
 
-    return this->filterTexture(fContext, texture, filter, ctx, result, offset);
+    return this->filterTexture(fContext, texture, src.width(), src.height(),
+                               filter, ctx, result, offset);
+}
+
+static bool wrap_as_bm(const SkImage* image, SkBitmap* bm) {
+    GrTexture* tex = image->getTexture();
+    if (tex) {
+        GrWrapTextureInBitmap(tex, image->width(), image->height(), image->isOpaque(), bm);
+        return true;
+    } else {
+        return as_IB(image)->getROPixels(bm);
+    }
+}
+
+void SkGpuDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y,
+                            const SkPaint& paint) {
+    SkBitmap bm;
+    if (wrap_as_bm(image, &bm)) {
+        this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
+    }
+}
+
+void SkGpuDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const SkRect* src,
+                                const SkRect& dst, const SkPaint& paint) {
+    SkBitmap bm;
+    if (wrap_as_bm(image, &bm)) {
+        this->drawBitmapRect(draw, bm, src, dst, paint, SkCanvas::kNone_DrawBitmapRectFlag);
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1715,8 +1790,10 @@
         copy.setStrokeWidth(0);
 
         // we ignore the shader if texs is null.
-        SkPaint2GrPaintNoShader(this->context(), fRenderTarget, copy,
-                                SkColor2GrColor(copy.getColor()), NULL == colors, &grPaint);
+        if (!SkPaint2GrPaintNoShader(this->context(), fRenderTarget, copy,
+                                     SkColor2GrColor(copy.getColor()), NULL == colors, &grPaint)) {
+            return;
+        }
 
         primType = kLines_GrPrimitiveType;
         int triangleCount = 0;
@@ -1755,12 +1832,16 @@
         primType = gVertexMode2PrimitiveType[vmode];
 
         if (NULL == texs || NULL == paint.getShader()) {
-            SkPaint2GrPaintNoShader(this->context(), fRenderTarget, paint,
-                                    SkColor2GrColor(paint.getColor()),
-                                    NULL == colors, &grPaint);
+            if (!SkPaint2GrPaintNoShader(this->context(), fRenderTarget, paint,
+                                         SkColor2GrColor(paint.getColor()),
+                                         NULL == colors, &grPaint)) {
+                return;
+            }
         } else {
-            SkPaint2GrPaintShader(this->context(), fRenderTarget, paint, *draw.fMatrix,
-                                  NULL == colors, &grPaint);
+            if (!SkPaint2GrPaint(this->context(), fRenderTarget, paint, *draw.fMatrix,
+                                 NULL == colors, &grPaint)) {
+                return;
+            }
         }
     }
 
@@ -1809,15 +1890,14 @@
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawText", fContext);
 
     GrPaint grPaint;
-    SkPaint2GrPaintShader(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint);
+    if (!SkPaint2GrPaint(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint)) {
+        return;
+    }
 
     SkDEBUGCODE(this->validate();)
 
-    if (!fTextContext->drawText(fRenderTarget, fClip, grPaint, paint, *draw.fMatrix,
-                                (const char *)text, byteLength, x, y)) {
-        // this will just call our drawPath()
-        draw.drawText_asPaths((const char*)text, byteLength, x, y, paint);
-    }
+    fTextContext->drawText(fRenderTarget, fClip, grPaint, paint, *draw.fMatrix,
+                           (const char *)text, byteLength, x, y, draw.fClip->getBounds());
 }
 
 void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, size_t byteLength,
@@ -1827,15 +1907,26 @@
     CHECK_SHOULD_DRAW(draw);
 
     GrPaint grPaint;
-    SkPaint2GrPaintShader(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint);
+    if (!SkPaint2GrPaint(this->context(), fRenderTarget, paint, *draw.fMatrix, true, &grPaint)) {
+        return;
+    }
 
     SkDEBUGCODE(this->validate();)
 
-    if (!fTextContext->drawPosText(fRenderTarget, fClip, grPaint, paint, *draw.fMatrix,
-                                   (const char *)text, byteLength, pos, scalarsPerPos, offset)) {
-        // this will just call our drawPath()
-        draw.drawPosText_asPaths((const char*)text, byteLength, pos, scalarsPerPos, offset, paint);
-    }
+    fTextContext->drawPosText(fRenderTarget, fClip, grPaint, paint, *draw.fMatrix,
+                              (const char *)text, byteLength, pos, scalarsPerPos, offset,
+                              draw.fClip->getBounds());
+}
+
+void SkGpuDevice::drawTextBlob(const SkDraw& draw, const SkTextBlob* blob, SkScalar x, SkScalar y,
+                               const SkPaint& paint, SkDrawFilter* drawFilter) {
+    GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawTextBlob", fContext);
+    CHECK_SHOULD_DRAW(draw);
+
+    SkDEBUGCODE(this->validate();)
+
+    fTextContext->drawTextBlob(fRenderTarget, fClip, paint, *draw.fMatrix, blob, x, y, drawFilter,
+                               draw.fClip->getBounds());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1862,7 +1953,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkBaseDevice* SkGpuDevice::onCreateCompatibleDevice(const CreateInfo& cinfo) {
+SkBaseDevice* SkGpuDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) {
     GrSurfaceDesc desc;
     desc.fConfig = fRenderTarget->config();
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
@@ -1876,17 +1967,18 @@
 
     // layers are never draw in repeat modes, so we can request an approx
     // match and ignore any padding.
-    const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == cinfo.fUsage) ?
-                                                GrContext::kApprox_ScratchTexMatch :
-                                                GrContext::kExact_ScratchTexMatch;
-    texture.reset(fContext->refScratchTexture(desc, match));
+    const GrTextureProvider::ScratchTexMatch match = (kNever_TileUsage == cinfo.fTileUsage) ?
+                                                  GrTextureProvider::kApprox_ScratchTexMatch :
+                                                  GrTextureProvider::kExact_ScratchTexMatch;
+    texture.reset(fContext->textureProvider()->refScratchTexture(desc, match));
 
     if (texture) {
         SkSurfaceProps props(fSurfaceProps.flags(), cinfo.fPixelGeometry);
-        return SkGpuDevice::Create(texture->asRenderTarget(), &props, flags);
+        return SkGpuDevice::Create(
+            texture->asRenderTarget(), cinfo.fInfo.width(), cinfo.fInfo.height(), &props, flags);
     } else {
         SkErrorInternals::SetError( kInternalError_SkError,
-                                    "---- failed to create compatible device texture [%d %d]\n",
+                                    "---- failed to create gpu device texture [%d %d]\n",
                                     cinfo.fInfo.width(), cinfo.fInfo.height());
         return NULL;
     }
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 6b520e7..89959e1 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -41,6 +41,13 @@
     static SkGpuDevice* Create(GrRenderTarget* target, const SkSurfaceProps*, unsigned flags = 0);
 
     /**
+     * Creates an SkGpuDevice from a GrRenderTarget whose texture width/height is
+     * different than its actual width/height (e.g., approx-match scratch texture).
+     */
+    static SkGpuDevice* Create(GrRenderTarget* target, int width, int height,
+                               const SkSurfaceProps*, unsigned flags = 0);
+
+    /**
      * New device that will create an offscreen renderTarget based on the ImageInfo and
      * sampleCount. The Budgeted param controls whether the device's backing store counts against
      * the resource cache budget. On failure, returns NULL.
@@ -51,9 +58,9 @@
     virtual ~SkGpuDevice();
 
     SkGpuDevice* cloneDevice(const SkSurfaceProps& props) {
-        SkBaseDevice* dev = this->onCreateCompatibleDevice(CreateInfo(this->imageInfo(),
-                                                                      kGeneral_Usage,
-                                                                      props.pixelGeometry()));
+        SkBaseDevice* dev = this->onCreateDevice(CreateInfo(this->imageInfo(), kPossible_TileUsage,
+                                                            props.pixelGeometry()),
+                                                 NULL);
         return static_cast<SkGpuDevice*>(dev);
     }
 
@@ -64,73 +71,78 @@
 
     void replaceRenderTarget(bool shouldRetainContent);
 
-    GrRenderTarget* accessRenderTarget() SK_OVERRIDE;
+    GrRenderTarget* accessRenderTarget() override;
 
-    SkImageInfo imageInfo() const SK_OVERRIDE {
-        return fRenderTarget ? fRenderTarget->surfacePriv().info() : SkImageInfo::MakeUnknown();
+    SkImageInfo imageInfo() const override {
+        return fLegacyBitmap.info();
     }
 
     const SkSurfaceProps& surfaceProps() const { return fSurfaceProps; }
 
-    void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE;
+    void drawPaint(const SkDraw&, const SkPaint& paint) override;
     virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
-                            const SkPoint[], const SkPaint& paint) SK_OVERRIDE;
+                            const SkPoint[], const SkPaint& paint) override;
     virtual void drawRect(const SkDraw&, const SkRect& r,
-                          const SkPaint& paint) SK_OVERRIDE;
+                          const SkPaint& paint) override;
     virtual void drawRRect(const SkDraw&, const SkRRect& r,
-                           const SkPaint& paint) SK_OVERRIDE;
+                           const SkPaint& paint) override;
     virtual void drawDRRect(const SkDraw& draw, const SkRRect& outer,
-                            const SkRRect& inner, const SkPaint& paint) SK_OVERRIDE;
+                            const SkRRect& inner, const SkPaint& paint) override;
     virtual void drawOval(const SkDraw&, const SkRect& oval,
-                          const SkPaint& paint) SK_OVERRIDE;
+                          const SkPaint& paint) override;
     virtual void drawPath(const SkDraw&, const SkPath& path,
                           const SkPaint& paint, const SkMatrix* prePathMatrix,
-                          bool pathIsMutable) SK_OVERRIDE;
+                          bool pathIsMutable) override;
     virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
-                            const SkMatrix&, const SkPaint&) SK_OVERRIDE;
+                            const SkMatrix&, const SkPaint&) override;
     virtual void drawBitmapRect(const SkDraw&, const SkBitmap&,
                                 const SkRect* srcOrNull, const SkRect& dst,
                                 const SkPaint& paint,
-                                SkCanvas::DrawBitmapRectFlags flags) SK_OVERRIDE;
+                                SkCanvas::DrawBitmapRectFlags flags) override;
     virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
-                            int x, int y, const SkPaint& paint) SK_OVERRIDE;
+                            int x, int y, const SkPaint& paint) override;
     virtual void drawText(const SkDraw&, const void* text, size_t len,
-                          SkScalar x, SkScalar y, const SkPaint&) SK_OVERRIDE;
+                          SkScalar x, SkScalar y, const SkPaint&) override;
     virtual void drawPosText(const SkDraw&, const void* text, size_t len,
                              const SkScalar pos[], int scalarsPerPos,
-                             const SkPoint& offset, const SkPaint&) SK_OVERRIDE;
+                             const SkPoint& offset, const SkPaint&) override;
+    virtual void drawTextBlob(const SkDraw&, const SkTextBlob*, SkScalar x, SkScalar y,
+                              const SkPaint& paint, SkDrawFilter* drawFilter) override;
     virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount,
                               const SkPoint verts[], const SkPoint texs[],
                               const SkColor colors[], SkXfermode* xmode,
                               const uint16_t indices[], int indexCount,
-                              const SkPaint&) SK_OVERRIDE;
+                              const SkPaint&) override;
     virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
-                            const SkPaint&) SK_OVERRIDE;
+                            const SkPaint&) override;
+    void drawImage(const SkDraw&, const SkImage*, SkScalar x, SkScalar y, const SkPaint&) override;
+    void drawImageRect(const SkDraw&, const SkImage*, const SkRect* src, const SkRect& dst,
+                       const SkPaint&) override;
 
-    void flush() SK_OVERRIDE;
+    void flush() override;
 
-    void onAttachToCanvas(SkCanvas* canvas) SK_OVERRIDE;
-    void onDetachFromCanvas() SK_OVERRIDE;
+    void onAttachToCanvas(SkCanvas* canvas) override;
+    void onDetachFromCanvas() override;
 
-    const SkBitmap& onAccessBitmap() SK_OVERRIDE;
+    const SkBitmap& onAccessBitmap() override;
 
-    bool canHandleImageFilter(const SkImageFilter*) SK_OVERRIDE;
+    bool canHandleImageFilter(const SkImageFilter*) override;
     virtual bool filterImage(const SkImageFilter*, const SkBitmap&,
                              const SkImageFilter::Context&,
-                             SkBitmap*, SkIPoint*) SK_OVERRIDE;
+                             SkBitmap*, SkIPoint*) override;
 
-    bool filterTexture(GrContext*, GrTexture*, const SkImageFilter*,
+    bool filterTexture(GrContext*, GrTexture*, int width, int height, const SkImageFilter*,
                        const SkImageFilter::Context&,
                        SkBitmap* result, SkIPoint* offset);
 
 protected:
-    bool onReadPixels(const SkImageInfo&, void*, size_t, int, int) SK_OVERRIDE;
-    bool onWritePixels(const SkImageInfo&, const void*, size_t, int, int) SK_OVERRIDE;
-    bool onShouldDisableLCD(const SkPaint&) const SK_OVERRIDE;
+    bool onReadPixels(const SkImageInfo&, void*, size_t, int, int) override;
+    bool onWritePixels(const SkImageInfo&, const void*, size_t, int, int) override;
+    bool onShouldDisableLCD(const SkPaint&) const override;
 
     /**  PRIVATE / EXPERIMENTAL -- do not call */
     virtual bool EXPERIMENTAL_drawPicture(SkCanvas* canvas, const SkPicture* picture,
-                                          const SkMatrix*, const SkPaint*) SK_OVERRIDE;
+                                          const SkMatrix*, const SkPaint*) override;
 
 private:
     GrContext*                      fContext;
@@ -145,15 +157,15 @@
     SkBitmap                        fLegacyBitmap;
     bool                            fNeedClear;
 
-    SkGpuDevice(GrRenderTarget*, const SkSurfaceProps*, unsigned flags);
+    SkGpuDevice(GrRenderTarget*, int width, int height, const SkSurfaceProps*, unsigned flags);
 
-    SkBaseDevice* onCreateCompatibleDevice(const CreateInfo&) SK_OVERRIDE;
+    SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
 
-    SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) SK_OVERRIDE;
+    SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) override;
 
-    SkImageFilter::Cache* getImageFilterCache() SK_OVERRIDE;
+    SkImageFilter::Cache* getImageFilterCache() override;
 
-    bool forceConservativeRasterClip() const SK_OVERRIDE { return true; }
+    bool forceConservativeRasterClip() const override { return true; }
 
     // sets the render target and clip on context
     void prepareDraw(const SkDraw&);
@@ -199,6 +211,10 @@
                          int tileSize,
                          bool bicubic);
 
+    void internalDrawPath(const SkPath& origSrcPath, const SkPaint& paint,
+                          const SkMatrix& origViewMatrix, const SkMatrix* prePathMatrix,
+                          const SkIRect& clipBounds, bool pathIsMutable);
+
     bool drawDashLine(const SkPoint pts[2], const SkPaint& paint);
 
     static SkPicture::AccelData::Key ComputeAccelDataKey();
@@ -206,6 +222,8 @@
     static GrRenderTarget* CreateRenderTarget(GrContext*, SkSurface::Budgeted, const SkImageInfo&,
                                               int sampleCount);
 
+    friend class GrAtlasTextContext;
+    friend class GrTextContext;
     typedef SkBaseDevice INHERITED;
 };
 
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index d105418..2fc0996 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -12,6 +12,7 @@
 #include "SkConfig8888.h"
 #include "SkData.h"
 #include "SkErrorInternals.h"
+#include "SkGrPixelRef.h"
 #include "SkMessageBus.h"
 #include "SkPixelRef.h"
 #include "SkResourceCache.h"
@@ -163,7 +164,7 @@
 private:
     GrUniqueKeyInvalidatedMessage fMsg;
 
-    void onChange() SK_OVERRIDE {
+    void onChange() override {
         SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
     }
 };
@@ -177,11 +178,11 @@
                                          SkPixelRef* pixelRefForInvalidationNotification,
                                          const void* pixels,
                                          size_t rowBytes) {
-    GrTexture* result = ctx->createTexture(desc, true, pixels, rowBytes);
+    GrTexture* result = ctx->textureProvider()->createTexture(desc, true, pixels, rowBytes);
     if (result && optionalKey.isValid()) {
         BitmapInvalidator* listener = SkNEW_ARGS(BitmapInvalidator, (optionalKey));
         pixelRefForInvalidationNotification->addGenIDChangeListener(listener);
-        ctx->addResourceToCache(optionalKey, result);
+        ctx->textureProvider()->assignUniqueKeyToTexture(optionalKey, result);
     }
     return result;
 }
@@ -368,9 +369,9 @@
         bool needsExactTexture =
             (yuvDesc.fWidth  != yuvInfo.fSize[0].fWidth) ||
             (yuvDesc.fHeight != yuvInfo.fSize[0].fHeight);
-        yuvTextures[i].reset(ctx->refScratchTexture(yuvDesc,
-            needsExactTexture ? GrContext::kExact_ScratchTexMatch :
-                                GrContext::kApprox_ScratchTexMatch));
+        yuvTextures[i].reset(ctx->textureProvider()->refScratchTexture(yuvDesc,
+            needsExactTexture ? GrTextureProvider::kExact_ScratchTexMatch :
+                                GrTextureProvider::kApprox_ScratchTexMatch));
         if (!yuvTextures[i] ||
             !yuvTextures[i]->writePixels(0, 0, yuvDesc.fWidth, yuvDesc.fHeight,
                                          yuvDesc.fConfig, planes[i], yuvInfo.fRowBytes[i])) {
@@ -471,7 +472,7 @@
         SkAutoTUnref<GrTexture> unstretched;
         // Check if we have the unstretched version in the cache, if not create it.
         if (unstretchedKey.isValid()) {
-            unstretched.reset(ctx->findAndRefCachedTexture(unstretchedKey));
+            unstretched.reset(ctx->textureProvider()->findAndRefTextureByUniqueKey(unstretchedKey));
         }
         if (!unstretched) {
             unstretched.reset(create_unstretched_bitmap_texture(ctx, bmp, unstretchedKey));
@@ -509,7 +510,7 @@
         }
         GrUniqueKey stretchedKey;
         make_stretched_key(key, stretch, &stretchedKey);
-        return ctx->isResourceInCache(stretchedKey);
+        return ctx->textureProvider()->existsTextureWithUniqueKey(stretchedKey);
     }
 
     // We don't cache volatile bitmaps
@@ -519,7 +520,8 @@
 
     GrUniqueKey key, stretchedKey;
     make_bitmap_keys(bitmap, stretch, &key, &stretchedKey);
-    return ctx->isResourceInCache((kNo_Stretch == stretch) ? key : stretchedKey);
+    return ctx->textureProvider()->existsTextureWithUniqueKey(
+        (kNo_Stretch == stretch) ? key : stretchedKey);
 }
 
 GrTexture* GrRefCachedBitmapTexture(GrContext* ctx,
@@ -539,7 +541,8 @@
             const GrUniqueKey& key = result->getUniqueKey();
             if (key.isValid()) {
                 make_stretched_key(key, stretch, &stretchedKey);
-                GrTexture* stretched = ctx->findAndRefCachedTexture(stretchedKey);
+                GrTexture* stretched =
+                    ctx->textureProvider()->findAndRefTextureByUniqueKey(stretchedKey);
                 if (stretched) {
                     return stretched;
                 }
@@ -554,7 +557,8 @@
         // If the bitmap isn't changing try to find a cached copy first.
         make_bitmap_keys(bitmap, stretch, &key, &resizedKey);
 
-        result = ctx->findAndRefCachedTexture(resizedKey.isValid() ? resizedKey : key);
+        result = ctx->textureProvider()->findAndRefTextureByUniqueKey(
+            resizedKey.isValid() ? resizedKey : key);
         if (result) {
             return result;
         }
@@ -594,6 +598,8 @@
             return kBGRA_8888_GrPixelConfig;
         case kIndex_8_SkColorType:
             return kIndex_8_GrPixelConfig;
+        case kGray_8_SkColorType:
+            return kAlpha_8_GrPixelConfig; // TODO: gray8 support on gpu
     }
     SkASSERT(0);    // shouldn't get here
     return kUnknown_GrPixelConfig;
@@ -640,7 +646,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkPaint2GrPaintNoShader(GrContext* context, GrRenderTarget* rt, const SkPaint& skPaint,
+bool SkPaint2GrPaintNoShader(GrContext* context, GrRenderTarget* rt, const SkPaint& skPaint,
                              GrColor paintColor, bool constantColor, GrPaint* grPaint) {
 
     grPaint->setDither(skPaint.isDither());
@@ -650,6 +656,7 @@
     GrXPFactory* xpFactory = NULL;
     if (!SkXfermode::AsXPFactory(mode, &xpFactory)) {
         // Fall back to src-over
+        // return false here?
         xpFactory = GrPorterDuffXPFactory::Create(SkXfermode::kSrcOver_Mode);
     }
     SkASSERT(xpFactory);
@@ -667,6 +674,7 @@
             grPaint->setColor(SkColor2GrColor(filtered));
         } else {
             SkTDArray<GrFragmentProcessor*> array;
+            // return false if failed?
             if (colorFilter->asFragmentProcessors(context, &array)) {
                 for (int i = 0; i < array.count(); ++i) {
                     grPaint->addColorProcessor(array[i]);
@@ -697,15 +705,15 @@
         }
     }
 #endif
+    return true;
 }
 
-void SkPaint2GrPaintShader(GrContext* context, GrRenderTarget* rt, const SkPaint& skPaint,
-                           const SkMatrix& viewM, bool constantColor, GrPaint* grPaint) {
+bool SkPaint2GrPaint(GrContext* context, GrRenderTarget* rt, const SkPaint& skPaint,
+                     const SkMatrix& viewM, bool constantColor, GrPaint* grPaint) {
     SkShader* shader = skPaint.getShader();
     if (NULL == shader) {
-        SkPaint2GrPaintNoShader(context, rt, skPaint, SkColor2GrColor(skPaint.getColor()),
-                                constantColor, grPaint);
-        return;
+        return SkPaint2GrPaintNoShader(context, rt, skPaint, SkColor2GrColor(skPaint.getColor()),
+                                       constantColor, grPaint);
     }
 
     GrColor paintColor = SkColor2GrColor(skPaint.getColor());
@@ -717,7 +725,10 @@
         // Allow the shader to modify paintColor and also create an effect to be installed as
         // the first color effect on the GrPaint.
         GrFragmentProcessor* fp = NULL;
-        if (shader->asFragmentProcessor(context, skPaint, viewM, NULL, &paintColor, &fp) && fp) {
+        if (!shader->asFragmentProcessor(context, skPaint, viewM, NULL, &paintColor, &fp)) {
+            return false;
+        }
+        if (fp) {
             grPaint->addColorProcessor(fp)->unref();
             constantColor = false;
         }
@@ -725,5 +736,27 @@
 
     // The grcolor is automatically set when calling asFragmentProcessor.
     // If the shader can be seen as an effect it returns true and adds its effect to the grpaint.
-    SkPaint2GrPaintNoShader(context, rt, skPaint, paintColor, constantColor, grPaint);
+    return SkPaint2GrPaintNoShader(context, rt, skPaint, paintColor, constantColor, grPaint);
+}
+
+SkImageInfo GrMakeInfoFromTexture(GrTexture* tex, int w, int h, bool isOpaque) {
+#ifdef SK_DEBUG
+    const GrSurfaceDesc& desc = tex->desc();
+    SkASSERT(w <= desc.fWidth);
+    SkASSERT(h <= desc.fHeight);
+#endif
+    const GrPixelConfig config = tex->config();
+    SkColorType ct;
+    SkAlphaType at = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
+    if (!GrPixelConfig2ColorAndProfileType(config, &ct, NULL)) {
+        ct = kUnknown_SkColorType;
+    }
+    return SkImageInfo::Make(w, h, ct, at);
+}
+
+
+void GrWrapTextureInBitmap(GrTexture* src, int w, int h, bool isOpaque, SkBitmap* dst) {
+    const SkImageInfo info = GrMakeInfoFromTexture(src, w, h, isOpaque);
+    dst->setInfo(info);
+    dst->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, src)))->unref();
 }
diff --git a/src/gpu/SkGrPixelRef.cpp b/src/gpu/SkGrPixelRef.cpp
index 01444af..3fa0491 100644
--- a/src/gpu/SkGrPixelRef.cpp
+++ b/src/gpu/SkGrPixelRef.cpp
@@ -80,7 +80,7 @@
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
     desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, kPremul_SkAlphaType, dstPT);
 
-    GrTexture* dst = context->createTexture(desc, false, NULL, 0);
+    GrTexture* dst = context->textureProvider()->createTexture(desc, false, NULL, 0);
     if (NULL == dst) {
         return NULL;
     }
diff --git a/src/gpu/effects/GrBezierEffect.cpp b/src/gpu/effects/GrBezierEffect.cpp
index 158fd5d..a09d6ce 100644
--- a/src/gpu/effects/GrBezierEffect.cpp
+++ b/src/gpu/effects/GrBezierEffect.cpp
@@ -24,17 +24,18 @@
     GrGLConicEffect(const GrGeometryProcessor&,
                     const GrBatchTracker&);
 
-    void onEmitCode(EmitArgs&, GrGPArgs*) SK_OVERRIDE;
+    void onEmitCode(EmitArgs&, GrGPArgs*) override;
 
     static inline void GenKey(const GrGeometryProcessor&,
                               const GrBatchTracker&,
-                              const GrGLCaps&,
+                              const GrGLSLCaps&,
                               GrProcessorKeyBuilder*);
 
     virtual void setData(const GrGLProgramDataManager& pdman,
                          const GrPrimitiveProcessor& primProc,
-                         const GrBatchTracker& bt) SK_OVERRIDE {
-        this->setUniformViewMatrix(pdman, primProc.viewMatrix());
+                         const GrBatchTracker& bt) override {
+        const GrConicEffect& ce = primProc.cast<GrConicEffect>();
+        this->setUniformViewMatrix(pdman, ce.viewMatrix());
 
         const ConicBatchTracker& local = bt.cast<ConicBatchTracker>();
         if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
@@ -49,6 +50,13 @@
         }
     }
 
+    void setTransformData(const GrPrimitiveProcessor& primProc,
+                          const GrGLProgramDataManager& pdman,
+                          int index,
+                          const SkTArray<const GrCoordTransform*, true>& transforms) override {
+        this->setTransformDataHelper<GrConicEffect>(primProc, pdman, index, transforms);
+    }
+
 private:
     GrColor fColor;
     uint8_t fCoverageScale;
@@ -90,7 +98,7 @@
     this->emitTransforms(pb, gpArgs->fPositionVar, gp.inPosition()->fName, gp.localMatrix(),
                          args.fTransformsIn, args.fTransformsOut);
 
-    GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
     fsBuilder->codeAppend("float edgeAlpha;");
 
     switch (fEdgeType) {
@@ -162,15 +170,15 @@
 
 void GrGLConicEffect::GenKey(const GrGeometryProcessor& gp,
                              const GrBatchTracker& bt,
-                             const GrGLCaps&,
+                             const GrGLSLCaps&,
                              GrProcessorKeyBuilder* b) {
     const GrConicEffect& ce = gp.cast<GrConicEffect>();
     const ConicBatchTracker& local = bt.cast<ConicBatchTracker>();
     uint32_t key = ce.isAntiAliased() ? (ce.isFilled() ? 0x0 : 0x1) : 0x2;
     key |= kUniform_GrGPInput == local.fInputColorType ? 0x4 : 0x0;
     key |= 0xff != local.fCoverageScale ? 0x8 : 0x0;
-    key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x10 : 0x0;
-    key |= ComputePosKey(gp.viewMatrix()) << 5;
+    key |= local.fUsesLocalCoords && ce.localMatrix().hasPerspective() ? 0x10 : 0x0;
+    key |= ComputePosKey(ce.viewMatrix()) << 5;
     b->add32(key);
 }
 
@@ -179,19 +187,21 @@
 GrConicEffect::~GrConicEffect() {}
 
 void GrConicEffect::getGLProcessorKey(const GrBatchTracker& bt,
-                                      const GrGLCaps& caps,
+                                      const GrGLSLCaps& caps,
                                       GrProcessorKeyBuilder* b) const {
     GrGLConicEffect::GenKey(*this, bt, caps, b);
 }
 
 GrGLPrimitiveProcessor* GrConicEffect::createGLInstance(const GrBatchTracker& bt,
-                                                        const GrGLCaps&) const {
+                                                        const GrGLSLCaps&) const {
     return SkNEW_ARGS(GrGLConicEffect, (*this, bt));
 }
 
 GrConicEffect::GrConicEffect(GrColor color, const SkMatrix& viewMatrix, uint8_t coverage,
                              GrPrimitiveEdgeType edgeType, const SkMatrix& localMatrix)
-    : INHERITED(color, viewMatrix, localMatrix)
+    : fColor(color)
+    , fViewMatrix(viewMatrix)
+    , fLocalMatrix(viewMatrix)
     , fCoverageScale(coverage)
     , fEdgeType(edgeType) {
     this->initClassID<GrConicEffect>();
@@ -200,11 +210,6 @@
                                                         kVec4f_GrVertexAttribType));
 }
 
-bool GrConicEffect::onIsEqual(const GrGeometryProcessor& other) const {
-    const GrConicEffect& ce = other.cast<GrConicEffect>();
-    return (ce.fEdgeType == fEdgeType);
-}
-
 void GrConicEffect::initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const {
     ConicBatchTracker* local = bt->cast<ConicBatchTracker>();
     local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
@@ -212,18 +217,6 @@
     local->fUsesLocalCoords = init.fUsesLocalCoords;
 }
 
-bool GrConicEffect::onCanMakeEqual(const GrBatchTracker& m,
-                                   const GrGeometryProcessor& that,
-                                   const GrBatchTracker& t) const {
-    const ConicBatchTracker& mine = m.cast<ConicBatchTracker>();
-    const ConicBatchTracker& theirs = t.cast<ConicBatchTracker>();
-    return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                   that, theirs.fUsesLocalCoords) &&
-           CanCombineOutput(mine.fInputColorType, mine.fColor,
-                            theirs.fInputColorType, theirs.fColor) &&
-           mine.fCoverageScale == theirs.fCoverageScale;
-}
-
 //////////////////////////////////////////////////////////////////////////////
 
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrConicEffect);
@@ -236,9 +229,9 @@
     do {
         GrPrimitiveEdgeType edgeType = static_cast<GrPrimitiveEdgeType>(
                                                     random->nextULessThan(kGrProcessorEdgeTypeCnt));
-        gp = GrConicEffect::Create(GrRandomColor(random), GrProcessorUnitTest::TestMatrix(random),
+        gp = GrConicEffect::Create(GrRandomColor(random), GrTest::TestMatrix(random),
                                    edgeType, caps,
-                                   GrProcessorUnitTest::TestMatrix(random));
+                                   GrTest::TestMatrix(random));
     } while (NULL == gp);
     return gp;
 }
@@ -259,17 +252,18 @@
     GrGLQuadEffect(const GrGeometryProcessor&,
                    const GrBatchTracker&);
 
-    void onEmitCode(EmitArgs&, GrGPArgs*) SK_OVERRIDE;
+    void onEmitCode(EmitArgs&, GrGPArgs*) override;
 
     static inline void GenKey(const GrGeometryProcessor&,
                               const GrBatchTracker&,
-                              const GrGLCaps&,
+                              const GrGLSLCaps&,
                               GrProcessorKeyBuilder*);
 
     virtual void setData(const GrGLProgramDataManager& pdman,
                          const GrPrimitiveProcessor& primProc,
-                         const GrBatchTracker& bt) SK_OVERRIDE {
-        this->setUniformViewMatrix(pdman, primProc.viewMatrix());
+                         const GrBatchTracker& bt) override {
+        const GrQuadEffect& qe = primProc.cast<GrQuadEffect>();
+        this->setUniformViewMatrix(pdman, qe.viewMatrix());
 
         const QuadBatchTracker& local = bt.cast<QuadBatchTracker>();
         if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
@@ -284,6 +278,13 @@
         }
     }
 
+    void setTransformData(const GrPrimitiveProcessor& primProc,
+                          const GrGLProgramDataManager& pdman,
+                          int index,
+                          const SkTArray<const GrCoordTransform*, true>& transforms) override {
+        this->setTransformDataHelper<GrQuadEffect>(primProc, pdman, index, transforms);
+    }
+
 private:
     GrColor fColor;
     uint8_t fCoverageScale;
@@ -325,7 +326,7 @@
     this->emitTransforms(pb, gpArgs->fPositionVar, gp.inPosition()->fName, gp.localMatrix(),
                          args.fTransformsIn, args.fTransformsOut);
 
-    GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
     fsBuilder->codeAppendf("float edgeAlpha;");
 
     switch (fEdgeType) {
@@ -383,15 +384,15 @@
 
 void GrGLQuadEffect::GenKey(const GrGeometryProcessor& gp,
                             const GrBatchTracker& bt,
-                            const GrGLCaps&,
+                            const GrGLSLCaps&,
                             GrProcessorKeyBuilder* b) {
     const GrQuadEffect& ce = gp.cast<GrQuadEffect>();
     const QuadBatchTracker& local = bt.cast<QuadBatchTracker>();
     uint32_t key = ce.isAntiAliased() ? (ce.isFilled() ? 0x0 : 0x1) : 0x2;
     key |= kUniform_GrGPInput == local.fInputColorType ? 0x4 : 0x0;
     key |= 0xff != local.fCoverageScale ? 0x8 : 0x0;
-    key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x10 : 0x0;
-    key |= ComputePosKey(gp.viewMatrix()) << 5;
+    key |= local.fUsesLocalCoords && ce.localMatrix().hasPerspective() ? 0x10 : 0x0;
+    key |= ComputePosKey(ce.viewMatrix()) << 5;
     b->add32(key);
 }
 
@@ -400,19 +401,21 @@
 GrQuadEffect::~GrQuadEffect() {}
 
 void GrQuadEffect::getGLProcessorKey(const GrBatchTracker& bt,
-                                     const GrGLCaps& caps,
+                                     const GrGLSLCaps& caps,
                                      GrProcessorKeyBuilder* b) const {
     GrGLQuadEffect::GenKey(*this, bt, caps, b);
 }
 
 GrGLPrimitiveProcessor* GrQuadEffect::createGLInstance(const GrBatchTracker& bt,
-                                                       const GrGLCaps&) const {
+                                                       const GrGLSLCaps&) const {
     return SkNEW_ARGS(GrGLQuadEffect, (*this, bt));
 }
 
 GrQuadEffect::GrQuadEffect(GrColor color, const SkMatrix& viewMatrix, uint8_t coverage,
                            GrPrimitiveEdgeType edgeType, const SkMatrix& localMatrix)
-    : INHERITED(color, viewMatrix, localMatrix)
+    : fColor(color)
+    , fViewMatrix(viewMatrix)
+    , fLocalMatrix(localMatrix)
     , fCoverageScale(coverage)
     , fEdgeType(edgeType) {
     this->initClassID<GrQuadEffect>();
@@ -421,11 +424,6 @@
                                                         kVec4f_GrVertexAttribType));
 }
 
-bool GrQuadEffect::onIsEqual(const GrGeometryProcessor& other) const {
-    const GrQuadEffect& ce = other.cast<GrQuadEffect>();
-    return (ce.fEdgeType == fEdgeType);
-}
-
 void GrQuadEffect::initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const {
     QuadBatchTracker* local = bt->cast<QuadBatchTracker>();
     local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
@@ -433,18 +431,6 @@
     local->fUsesLocalCoords = init.fUsesLocalCoords;
 }
 
-bool GrQuadEffect::onCanMakeEqual(const GrBatchTracker& m,
-                                  const GrGeometryProcessor& that,
-                                  const GrBatchTracker& t) const {
-    const QuadBatchTracker& mine = m.cast<QuadBatchTracker>();
-    const QuadBatchTracker& theirs = t.cast<QuadBatchTracker>();
-    return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                   that, theirs.fUsesLocalCoords) &&
-           CanCombineOutput(mine.fInputColorType, mine.fColor,
-                            theirs.fInputColorType, theirs.fColor) &&
-           mine.fCoverageScale == theirs.fCoverageScale;
-}
-
 //////////////////////////////////////////////////////////////////////////////
 
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrQuadEffect);
@@ -458,9 +444,9 @@
         GrPrimitiveEdgeType edgeType = static_cast<GrPrimitiveEdgeType>(
                 random->nextULessThan(kGrProcessorEdgeTypeCnt));
         gp = GrQuadEffect::Create(GrRandomColor(random),
-                                  GrProcessorUnitTest::TestMatrix(random),
+                                  GrTest::TestMatrix(random),
                                   edgeType, caps,
-                                  GrProcessorUnitTest::TestMatrix(random));
+                                  GrTest::TestMatrix(random));
     } while (NULL == gp);
     return gp;
 }
@@ -480,17 +466,18 @@
     GrGLCubicEffect(const GrGeometryProcessor&,
                     const GrBatchTracker&);
 
-    void onEmitCode(EmitArgs&, GrGPArgs*) SK_OVERRIDE;
+    void onEmitCode(EmitArgs&, GrGPArgs*) override;
 
     static inline void GenKey(const GrGeometryProcessor&,
                               const GrBatchTracker&,
-                              const GrGLCaps&,
+                              const GrGLSLCaps&,
                               GrProcessorKeyBuilder*);
 
     virtual void setData(const GrGLProgramDataManager& pdman,
                          const GrPrimitiveProcessor& primProc,
-                         const GrBatchTracker& bt) SK_OVERRIDE {
-        this->setUniformViewMatrix(pdman, primProc.viewMatrix());
+                         const GrBatchTracker& bt) override {
+        const GrCubicEffect& ce = primProc.cast<GrCubicEffect>();
+        this->setUniformViewMatrix(pdman, ce.viewMatrix());
 
         const CubicBatchTracker& local = bt.cast<CubicBatchTracker>();
         if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
@@ -536,10 +523,10 @@
     this->setupPosition(args.fPB, gpArgs, gp.inPosition()->fName, gp.viewMatrix());
 
     // emit transforms with position
-    this->emitTransforms(args.fPB, gpArgs->fPositionVar, gp.inPosition()->fName, gp.localMatrix(),
-                         args.fTransformsIn, args.fTransformsOut);
+    this->emitTransforms(args.fPB, gpArgs->fPositionVar, gp.inPosition()->fName, args.fTransformsIn,
+                         args.fTransformsOut);
 
-    GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
 
     GrGLShaderVar edgeAlpha("edgeAlpha", kFloat_GrSLType, 0, kHigh_GrSLPrecision);
     GrGLShaderVar dklmdx("dklmdx", kVec3f_GrSLType, 0, kHigh_GrSLPrecision);
@@ -628,14 +615,13 @@
 
 void GrGLCubicEffect::GenKey(const GrGeometryProcessor& gp,
                              const GrBatchTracker& bt,
-                             const GrGLCaps&,
+                             const GrGLSLCaps&,
                              GrProcessorKeyBuilder* b) {
     const GrCubicEffect& ce = gp.cast<GrCubicEffect>();
     const CubicBatchTracker& local = bt.cast<CubicBatchTracker>();
     uint32_t key = ce.isAntiAliased() ? (ce.isFilled() ? 0x0 : 0x1) : 0x2;
     key |= kUniform_GrGPInput == local.fInputColorType ? 0x4 : 0x8;
-    key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x10 : 0x0;
-    key |= ComputePosKey(gp.viewMatrix()) << 5;
+    key |= ComputePosKey(ce.viewMatrix()) << 5;
     b->add32(key);
 }
 
@@ -644,47 +630,33 @@
 GrCubicEffect::~GrCubicEffect() {}
 
 void GrCubicEffect::getGLProcessorKey(const GrBatchTracker& bt,
-                                      const GrGLCaps& caps,
+                                      const GrGLSLCaps& caps,
                                       GrProcessorKeyBuilder* b) const {
     GrGLCubicEffect::GenKey(*this, bt, caps, b);
 }
 
 GrGLPrimitiveProcessor* GrCubicEffect::createGLInstance(const GrBatchTracker& bt,
-                                                        const GrGLCaps&) const {
+                                                        const GrGLSLCaps&) const {
     return SkNEW_ARGS(GrGLCubicEffect, (*this, bt));
 }
 
 GrCubicEffect::GrCubicEffect(GrColor color, const SkMatrix& viewMatrix,
                              GrPrimitiveEdgeType edgeType)
-    : INHERITED(color, viewMatrix), fEdgeType(edgeType) {
+    : fColor(color)
+    , fViewMatrix(viewMatrix)
+    , fEdgeType(edgeType) {
     this->initClassID<GrCubicEffect>();
     fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
     fInCubicCoeffs = &this->addVertexAttrib(Attribute("inCubicCoeffs",
                                                         kVec4f_GrVertexAttribType));
 }
 
-bool GrCubicEffect::onIsEqual(const GrGeometryProcessor& other) const {
-    const GrCubicEffect& ce = other.cast<GrCubicEffect>();
-    return (ce.fEdgeType == fEdgeType);
-}
-
 void GrCubicEffect::initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const {
     CubicBatchTracker* local = bt->cast<CubicBatchTracker>();
     local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
     local->fUsesLocalCoords = init.fUsesLocalCoords;
 }
 
-bool GrCubicEffect::onCanMakeEqual(const GrBatchTracker& m,
-                                   const GrGeometryProcessor& that,
-                                   const GrBatchTracker& t) const {
-    const CubicBatchTracker& mine = m.cast<CubicBatchTracker>();
-    const CubicBatchTracker& theirs = t.cast<CubicBatchTracker>();
-    return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                   that, theirs.fUsesLocalCoords) &&
-           CanCombineOutput(mine.fInputColorType, mine.fColor,
-                            theirs.fInputColorType, theirs.fColor);
-}
-
 //////////////////////////////////////////////////////////////////////////////
 
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrCubicEffect);
@@ -698,7 +670,7 @@
         GrPrimitiveEdgeType edgeType = static_cast<GrPrimitiveEdgeType>(
                                                     random->nextULessThan(kGrProcessorEdgeTypeCnt));
         gp = GrCubicEffect::Create(GrRandomColor(random),
-                                   GrProcessorUnitTest::TestMatrix(random), edgeType, caps);
+                                   GrTest::TestMatrix(random), edgeType, caps);
     } while (NULL == gp);
     return gp;
 }
diff --git a/src/gpu/effects/GrBezierEffect.h b/src/gpu/effects/GrBezierEffect.h
index b0039e9..f1b22fa 100644
--- a/src/gpu/effects/GrBezierEffect.h
+++ b/src/gpu/effects/GrBezierEffect.h
@@ -66,14 +66,14 @@
                                        uint8_t coverage = 0xff) {
         switch (edgeType) {
             case kFillAA_GrProcessorEdgeType:
-                if (!caps.shaderDerivativeSupport()) {
+                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
                     return NULL;
                 }
                 return SkNEW_ARGS(GrConicEffect, (color, viewMatrix, coverage,
                                                   kFillAA_GrProcessorEdgeType,
                                                   localMatrix));
             case kHairlineAA_GrProcessorEdgeType:
-                if (!caps.shaderDerivativeSupport()) {
+                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
                     return NULL;
                 }
                 return SkNEW_ARGS(GrConicEffect, (color, viewMatrix, coverage,
@@ -90,38 +90,35 @@
 
     virtual ~GrConicEffect();
 
-    const char* name() const SK_OVERRIDE { return "Conic"; }
+    const char* name() const override { return "Conic"; }
 
     inline const Attribute* inPosition() const { return fInPosition; }
     inline const Attribute* inConicCoeffs() const { return fInConicCoeffs; }
     inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); }
     inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); }
     inline GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& viewMatrix() const { return fViewMatrix; }
+    const SkMatrix& localMatrix() const { return fLocalMatrix; }
 
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE;
+                                                     const GrGLSLCaps&) const override;
 
-    void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const SK_OVERRIDE;
-    bool onCanMakeEqual(const GrBatchTracker&,
-                        const GrGeometryProcessor&,
-                        const GrBatchTracker&) const SK_OVERRIDE;
+    void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const override;
 
 private:
     GrConicEffect(GrColor, const SkMatrix& viewMatrix, uint8_t coverage, GrPrimitiveEdgeType,
                   const SkMatrix& localMatrix);
 
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
-        out->setUnknownSingleComponent();
-    }
-
-    uint8_t               fCoverageScale;
-    GrPrimitiveEdgeType   fEdgeType;
+    GrColor             fColor;
+    SkMatrix            fViewMatrix;
+    SkMatrix            fLocalMatrix;
+    uint8_t             fCoverageScale;
+    GrPrimitiveEdgeType fEdgeType;
     const Attribute*    fInPosition;
     const Attribute*    fInConicCoeffs;
 
@@ -151,14 +148,14 @@
                                        uint8_t coverage = 0xff) {
         switch (edgeType) {
             case kFillAA_GrProcessorEdgeType:
-                if (!caps.shaderDerivativeSupport()) {
+                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
                     return NULL;
                 }
                 return SkNEW_ARGS(GrQuadEffect, (color, viewMatrix, coverage,
                                                  kFillAA_GrProcessorEdgeType,
                                                  localMatrix));
             case kHairlineAA_GrProcessorEdgeType:
-                if (!caps.shaderDerivativeSupport()) {
+                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
                     return NULL;
                 }
                 return SkNEW_ARGS(GrQuadEffect, (color, viewMatrix, coverage,
@@ -175,38 +172,35 @@
 
     virtual ~GrQuadEffect();
 
-    const char* name() const SK_OVERRIDE { return "Quad"; }
+    const char* name() const override { return "Quad"; }
 
     inline const Attribute* inPosition() const { return fInPosition; }
     inline const Attribute* inHairQuadEdge() const { return fInHairQuadEdge; }
     inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); }
     inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); }
     inline GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& viewMatrix() const { return fViewMatrix; }
+    const SkMatrix& localMatrix() const { return fLocalMatrix; }
 
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE;
+                                                     const GrGLSLCaps&) const override;
 
-    void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const SK_OVERRIDE;
-    bool onCanMakeEqual(const GrBatchTracker&,
-                        const GrGeometryProcessor&,
-                        const GrBatchTracker&) const SK_OVERRIDE;
+    void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const override;
 
 private:
     GrQuadEffect(GrColor, const SkMatrix& viewMatrix, uint8_t coverage, GrPrimitiveEdgeType,
                  const SkMatrix& localMatrix);
 
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
-        out->setUnknownSingleComponent();
-    }
-
-    uint8_t               fCoverageScale;
-    GrPrimitiveEdgeType   fEdgeType;
+    GrColor             fColor;
+    SkMatrix            fViewMatrix;
+    SkMatrix            fLocalMatrix;
+    uint8_t             fCoverageScale;
+    GrPrimitiveEdgeType fEdgeType;
     const Attribute*    fInPosition;
     const Attribute*    fInHairQuadEdge;
 
@@ -236,12 +230,12 @@
                                        const GrDrawTargetCaps& caps) {
         switch (edgeType) {
             case kFillAA_GrProcessorEdgeType:
-                if (!caps.shaderDerivativeSupport()) {
+                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
                     return NULL;
                 }
                 return SkNEW_ARGS(GrCubicEffect, (color, viewMatrix, kFillAA_GrProcessorEdgeType));
             case kHairlineAA_GrProcessorEdgeType:
-                if (!caps.shaderDerivativeSupport()) {
+                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
                     return NULL;
                 }
                 return SkNEW_ARGS(GrCubicEffect, (color, viewMatrix,
@@ -256,36 +250,31 @@
 
     virtual ~GrCubicEffect();
 
-    const char* name() const SK_OVERRIDE { return "Cubic"; }
+    const char* name() const override { return "Cubic"; }
 
     inline const Attribute* inPosition() const { return fInPosition; }
     inline const Attribute* inCubicCoeffs() const { return fInCubicCoeffs; }
     inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); }
     inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); }
     inline GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& viewMatrix() const { return fViewMatrix; }
 
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE;
+                                                     const GrGLSLCaps&) const override;
 
-    void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const SK_OVERRIDE;
-    bool onCanMakeEqual(const GrBatchTracker&,
-                        const GrGeometryProcessor&,
-                        const GrBatchTracker&) const SK_OVERRIDE;
+    void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const override;
 
 private:
     GrCubicEffect(GrColor, const SkMatrix& viewMatrix, GrPrimitiveEdgeType);
 
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
-        out->setUnknownSingleComponent();
-    }
-
-    GrPrimitiveEdgeType   fEdgeType;
+    GrColor             fColor;
+    SkMatrix            fViewMatrix;
+    GrPrimitiveEdgeType fEdgeType;
     const Attribute*    fInPosition;
     const Attribute*    fInCubicCoeffs;
 
diff --git a/src/gpu/effects/GrBicubicEffect.cpp b/src/gpu/effects/GrBicubicEffect.cpp
index 7d2dbf4..9176972 100644
--- a/src/gpu/effects/GrBicubicEffect.cpp
+++ b/src/gpu/effects/GrBicubicEffect.cpp
@@ -28,11 +28,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
-    static inline void GenKey(const GrProcessor& effect, const GrGLCaps&,
+    static inline void GenKey(const GrProcessor& effect, const GrGLSLCaps&,
                               GrProcessorKeyBuilder* b) {
         const GrTextureDomain& domain = effect.cast<GrBicubicEffect>().domain();
         b->add32(GrTextureDomain::GLDomain::DomainKey(domain));
@@ -79,7 +79,7 @@
         GrGLShaderVar("c2",            kVec4f_GrSLType),
         GrGLShaderVar("c3",            kVec4f_GrSLType),
     };
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
     fsBuilder->emitFunction(kVec4f_GrSLType,
                             "cubicBlend",
@@ -158,7 +158,7 @@
 GrBicubicEffect::~GrBicubicEffect() {
 }
 
-void GrBicubicEffect::getGLProcessorKey(const GrGLCaps& caps,
+void GrBicubicEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                         GrProcessorKeyBuilder* b) const {
     GrGLBicubicEffect::GenKey(*this, caps, b);
 }
diff --git a/src/gpu/effects/GrBicubicEffect.h b/src/gpu/effects/GrBicubicEffect.h
index 89e43c1..b81c93e 100644
--- a/src/gpu/effects/GrBicubicEffect.h
+++ b/src/gpu/effects/GrBicubicEffect.h
@@ -25,11 +25,11 @@
 
     const float* coefficients() const { return fCoefficients; }
 
-    const char* name() const SK_OVERRIDE { return "Bicubic"; }
+    const char* name() const override { return "Bicubic"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     const GrTextureDomain& domain() const { return fDomain; }
 
@@ -91,9 +91,9 @@
                     const SkMatrix &matrix, const SkShader::TileMode tileModes[2]);
     GrBicubicEffect(GrTexture*, const SkScalar coefficients[16],
                     const SkMatrix &matrix, const SkRect& domain);
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     float           fCoefficients[16];
     GrTextureDomain fDomain;
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.cpp b/src/gpu/effects/GrBitmapTextGeoProc.cpp
index 2c187a9..a7261c0 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.cpp
+++ b/src/gpu/effects/GrBitmapTextGeoProc.cpp
@@ -26,7 +26,7 @@
     GrGLBitmapTextGeoProc(const GrGeometryProcessor&, const GrBatchTracker&)
         : fColor(GrColor_ILLEGAL) {}
 
-    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
+    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
         const GrBitmapTextGeoProc& cte = args.fGP.cast<GrBitmapTextGeoProc>();
         const BitmapTextBatchTracker& local = args.fBT.cast<BitmapTextBatchTracker>();
 
@@ -54,13 +54,13 @@
                                     &fColorUniform);
 
         // Setup position
-        this->setupPosition(pb, gpArgs, cte.inPosition()->fName, cte.viewMatrix());
+        this->setupPosition(pb, gpArgs, cte.inPosition()->fName);
 
         // emit transforms
         this->emitTransforms(args.fPB, gpArgs->fPositionVar, cte.inPosition()->fName,
                              cte.localMatrix(), args.fTransformsIn, args.fTransformsOut);
 
-        GrGLGPFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder();
+        GrGLFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder();
         if (cte.maskFormat() == kARGB_GrMaskFormat) {
             fsBuilder->codeAppendf("%s = ", args.fOutputColor);
             fsBuilder->appendTextureLookupAndModulate(args.fOutputColor,
@@ -78,9 +78,7 @@
 
     virtual void setData(const GrGLProgramDataManager& pdman,
                          const GrPrimitiveProcessor& gp,
-                         const GrBatchTracker& bt) SK_OVERRIDE {
-        this->setUniformViewMatrix(pdman, gp.viewMatrix());
-
+                         const GrBatchTracker& bt) override {
         const BitmapTextBatchTracker& local = bt.cast<BitmapTextBatchTracker>();
         if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
             GrGLfloat c[4];
@@ -90,9 +88,16 @@
         }
     }
 
+    void setTransformData(const GrPrimitiveProcessor& primProc,
+                          const GrGLProgramDataManager& pdman,
+                          int index,
+                          const SkTArray<const GrCoordTransform*, true>& transforms) override {
+        this->setTransformDataHelper<GrBitmapTextGeoProc>(primProc, pdman, index, transforms);
+    }
+
     static inline void GenKey(const GrGeometryProcessor& proc,
                               const GrBatchTracker& bt,
-                              const GrGLCaps&,
+                              const GrGLSLCaps&,
                               GrProcessorKeyBuilder* b) {
         const BitmapTextBatchTracker& local = bt.cast<BitmapTextBatchTracker>();
         // We have to put the optional vertex attribute as part of the key.  See the comment
@@ -101,9 +106,8 @@
         const GrBitmapTextGeoProc& gp = proc.cast<GrBitmapTextGeoProc>();
         uint32_t key = 0;
         key |= SkToBool(gp.inColor()) ? 0x1 : 0x0;
-        key |= local.fUsesLocalCoords && proc.localMatrix().hasPerspective() ? 0x2 : 0x0;
+        key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
         key |= gp.maskFormat() == kARGB_GrMaskFormat ? 0x4 : 0x0;
-        key |= ComputePosKey(gp.viewMatrix()) << 3;
         b->add32(local.fInputColorType << 16 | key);
     }
 
@@ -118,8 +122,9 @@
 
 GrBitmapTextGeoProc::GrBitmapTextGeoProc(GrColor color, GrTexture* texture,
                                          const GrTextureParams& params, GrMaskFormat format,
-                                         bool opaqueVertexColors, const SkMatrix& localMatrix)
-    : INHERITED(color, SkMatrix::I(), localMatrix, opaqueVertexColors)
+                                         const SkMatrix& localMatrix)
+    : fColor(color)
+    , fLocalMatrix(localMatrix)
     , fTextureAccess(texture, params)
     , fInColor(NULL)
     , fMaskFormat(format) {
@@ -129,49 +134,21 @@
     bool hasVertexColor = kA8_GrMaskFormat == fMaskFormat;
     if (hasVertexColor) {
         fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
-        this->setHasVertexColor();
     }
     fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
                                                         kVec2s_GrVertexAttribType));
     this->addTextureAccess(&fTextureAccess);
 }
 
-bool GrBitmapTextGeoProc::onIsEqual(const GrGeometryProcessor& other) const {
-    const GrBitmapTextGeoProc& gp = other.cast<GrBitmapTextGeoProc>();
-    return SkToBool(this->inColor()) == SkToBool(gp.inColor());
-}
-
-void GrBitmapTextGeoProc::onGetInvariantOutputColor(GrInitInvariantOutput* out) const {
-    if (kARGB_GrMaskFormat == fMaskFormat) {
-        out->setUnknownFourComponents();
-    }
-}
-
-void GrBitmapTextGeoProc::onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const {
-    if (kARGB_GrMaskFormat != fMaskFormat) {
-        if (GrPixelConfigIsAlphaOnly(this->texture(0)->config())) {
-            out->setUnknownSingleComponent();
-        } else if (GrPixelConfigIsOpaque(this->texture(0)->config())) {
-            out->setUnknownOpaqueFourComponents();
-            out->setUsingLCDCoverage();
-        } else {
-            out->setUnknownFourComponents();
-            out->setUsingLCDCoverage();
-        }
-    } else {
-        out->setKnownSingleComponent(0xff);
-    }
-}
-
 void GrBitmapTextGeoProc::getGLProcessorKey(const GrBatchTracker& bt,
-                                            const GrGLCaps& caps,
+                                            const GrGLSLCaps& caps,
                                             GrProcessorKeyBuilder* b) const {
     GrGLBitmapTextGeoProc::GenKey(*this, bt, caps, b);
 }
 
 GrGLPrimitiveProcessor*
 GrBitmapTextGeoProc::createGLInstance(const GrBatchTracker& bt,
-                                      const GrGLCaps& caps) const {
+                                      const GrGLSLCaps& caps) const {
     return SkNEW_ARGS(GrGLBitmapTextGeoProc, (*this, bt));
 }
 
@@ -182,17 +159,6 @@
     local->fUsesLocalCoords = init.fUsesLocalCoords;
 }
 
-bool GrBitmapTextGeoProc::onCanMakeEqual(const GrBatchTracker& m,
-                                         const GrGeometryProcessor& that,
-                                         const GrBatchTracker& t) const {
-    const BitmapTextBatchTracker& mine = m.cast<BitmapTextBatchTracker>();
-    const BitmapTextBatchTracker& theirs = t.cast<BitmapTextBatchTracker>();
-    return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                   that, theirs.fUsesLocalCoords) &&
-           CanCombineOutput(mine.fInputColorType, mine.fColor,
-                            theirs.fInputColorType, theirs.fColor);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrBitmapTextGeoProc);
@@ -231,6 +197,5 @@
     }
 
     return GrBitmapTextGeoProc::Create(GrRandomColor(random), textures[texIdx], params,
-                                       format, random->nextBool(),
-                                       GrProcessorUnitTest::TestMatrix(random));
+                                       format, GrTest::TestMatrix(random));
 }
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.h b/src/gpu/effects/GrBitmapTextGeoProc.h
index b5a4145..837dcb6 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.h
+++ b/src/gpu/effects/GrBitmapTextGeoProc.h
@@ -22,43 +22,37 @@
 class GrBitmapTextGeoProc : public GrGeometryProcessor {
 public:
     static GrGeometryProcessor* Create(GrColor color, GrTexture* tex, const GrTextureParams& p,
-                                       GrMaskFormat format, bool opaqueVertexColors,
+                                       GrMaskFormat format,
                                        const SkMatrix& localMatrix) {
-        return SkNEW_ARGS(GrBitmapTextGeoProc, (color, tex, p, format, opaqueVertexColors,
-                                                localMatrix));
+        return SkNEW_ARGS(GrBitmapTextGeoProc, (color, tex, p, format, localMatrix));
     }
 
     virtual ~GrBitmapTextGeoProc() {}
 
-    const char* name() const SK_OVERRIDE { return "Texture"; }
+    const char* name() const override { return "Texture"; }
 
     const Attribute* inPosition() const { return fInPosition; }
     const Attribute* inColor() const { return fInColor; }
     const Attribute* inTextureCoords() const { return fInTextureCoords; }
     GrMaskFormat maskFormat() const { return fMaskFormat; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& localMatrix() const { return fLocalMatrix; }
 
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps& caps) const SK_OVERRIDE;
+                                                     const GrGLSLCaps& caps) const override;
 
-    void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const SK_OVERRIDE;
-    bool onCanMakeEqual(const GrBatchTracker&,
-                        const GrGeometryProcessor&,
-                        const GrBatchTracker&) const SK_OVERRIDE;
+    void initBatchTracker(GrBatchTracker*, const GrPipelineInfo&) const override;
 
 private:
     GrBitmapTextGeoProc(GrColor, GrTexture* texture, const GrTextureParams& params,
-                        GrMaskFormat format, bool opaqueVertexColors, const SkMatrix& localMatrix);
+                        GrMaskFormat format, const SkMatrix& localMatrix);
 
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
-
-    void onGetInvariantOutputColor(GrInitInvariantOutput*) const SK_OVERRIDE;
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput*) const SK_OVERRIDE;
-
+    GrColor          fColor;
+    SkMatrix         fLocalMatrix;
     GrTextureAccess  fTextureAccess;
     const Attribute* fInPosition;
     const Attribute* fInColor;
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index fedc470..8b75a88 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -27,13 +27,13 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray& coords,
-                          const TextureSamplerArray& samplers) SK_OVERRIDE {
+                          const TextureSamplerArray& samplers) override {
         // Using highp for GLES here in order to avoid some precision issues on specific GPUs.
         GrGLShaderVar tmpVar("tmpColor", kVec4f_GrSLType, 0, kHigh_GrSLPrecision);
         SkString tmpDecl;
         tmpVar.appendDecl(builder->ctxInfo(), &tmpDecl);
 
-        GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+        GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
 
         fsBuilder->codeAppendf("%s;", tmpDecl.c_str());
 
@@ -82,7 +82,7 @@
         fsBuilder->codeAppend(modulate.c_str());
     }
 
-    static inline void GenKey(const GrProcessor& processor, const GrGLCaps&,
+    static inline void GenKey(const GrProcessor& processor, const GrGLSLCaps&,
                               GrProcessorKeyBuilder* b) {
         const GrConfigConversionEffect& conv = processor.cast<GrConfigConversionEffect>();
         uint32_t key = (conv.swapsRedAndBlue() ? 0 : 1) | (conv.pmConversion() << 1);
@@ -142,12 +142,12 @@
                                       (textures[GrProcessorUnitTest::kSkiaPMTextureIdx],
                                        swapRB,
                                        pmConv,
-                                       GrProcessorUnitTest::TestMatrix(random)));
+                                       GrTest::TestMatrix(random)));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void GrConfigConversionEffect::getGLProcessorKey(const GrGLCaps& caps,
+void GrConfigConversionEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                                  GrProcessorKeyBuilder* b) const {
     GrGLConfigConversionEffect::GenKey(*this, caps, b);
 }
@@ -186,16 +186,16 @@
     desc.fHeight = 256;
     desc.fConfig = kRGBA_8888_GrPixelConfig;
 
-    SkAutoTUnref<GrTexture> readTex(context->createTexture(desc, true, NULL, 0));
+    SkAutoTUnref<GrTexture> readTex(context->textureProvider()->createTexture(desc, true, NULL, 0));
     if (!readTex.get()) {
         return;
     }
-    SkAutoTUnref<GrTexture> tempTex(context->createTexture(desc, true, NULL, 0));
+    SkAutoTUnref<GrTexture> tempTex(context->textureProvider()->createTexture(desc, true, NULL, 0));
     if (!tempTex.get()) {
         return;
     }
     desc.fFlags = kNone_GrSurfaceFlags;
-    SkAutoTUnref<GrTexture> dataTex(context->createTexture(desc, true, data, 0));
+    SkAutoTUnref<GrTexture> dataTex(context->textureProvider()->createTexture(desc, true, data, 0));
     if (!dataTex.get()) {
         return;
     }
diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h
index 5760bb4..3b47166 100644
--- a/src/gpu/effects/GrConfigConversionEffect.h
+++ b/src/gpu/effects/GrConfigConversionEffect.h
@@ -37,11 +37,11 @@
     static const GrFragmentProcessor* Create(GrTexture*, bool swapRedAndBlue, PMConversion,
                                              const SkMatrix&);
 
-    const char* name() const SK_OVERRIDE { return "Config Conversion"; }
+    const char* name() const override { return "Config Conversion"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     bool swapsRedAndBlue() const { return fSwapRedAndBlue; }
     PMConversion  pmConversion() const { return fPMConversion; }
@@ -61,9 +61,9 @@
                             PMConversion pmConversion,
                             const SkMatrix& matrix);
 
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     bool            fSwapRedAndBlue;
     PMConversion    fPMConversion;
diff --git a/src/gpu/effects/GrConstColorProcessor.cpp b/src/gpu/effects/GrConstColorProcessor.cpp
new file mode 100644
index 0000000..ab703fd
--- /dev/null
+++ b/src/gpu/effects/GrConstColorProcessor.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "effects/GrConstColorProcessor.h"
+#include "gl/GrGLProcessor.h"
+#include "gl/GrGLSL.h"
+#include "gl/builders/GrGLProgramBuilder.h"
+
+class GLConstColorProcessor : public GrGLFragmentProcessor {
+public:
+    GLConstColorProcessor() : fPrevColor(GrColor_ILLEGAL) {}
+
+    void emitCode(GrGLFPBuilder* builder,
+                  const GrFragmentProcessor& fp,
+                  const char* outputColor,
+                  const char* inputColor,
+                  const TransformedCoordsArray& coords,
+                  const TextureSamplerArray& samplers) override {
+        GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+        const char* colorUni;
+        fColorUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                            kVec4f_GrSLType, kMedium_GrSLPrecision, "constantColor",
+                                            &colorUni);
+        switch (fp.cast<GrConstColorProcessor>().inputMode()) {
+            case GrConstColorProcessor::kIgnore_InputMode:
+                fsBuilder->codeAppendf("%s = %s;", outputColor, colorUni);
+                break;
+            case GrConstColorProcessor::kModulateRGBA_InputMode:
+                fsBuilder->codeAppendf("%s = %s * %s;", outputColor, inputColor, colorUni);
+                break;
+            case GrConstColorProcessor::kModulateA_InputMode:
+                fsBuilder->codeAppendf("%s = %s.a * %s;", outputColor, inputColor, colorUni);
+                break;
+        }
+    }
+
+    void setData(const GrGLProgramDataManager& pdm, const GrProcessor& processor) override {
+        GrColor color = processor.cast<GrConstColorProcessor>().color();
+        // We use the "illegal" color value as an uninit sentinel. However, ut isn't inherently
+        // illegal to use this processor with unpremul colors. So we correctly handle the case
+        // when the "illegal" color is used but we will always upload it.
+        if (GrColor_ILLEGAL == color || fPrevColor != color) {
+            static const GrGLfloat scale = 1.f / 255.f;
+            GrGLfloat floatColor[4] = {
+                GrColorUnpackR(color) * scale,
+                GrColorUnpackG(color) * scale,
+                GrColorUnpackB(color) * scale,
+                GrColorUnpackA(color) * scale,
+            };
+            pdm.set4fv(fColorUniform, 1, floatColor);
+            fPrevColor = color;
+        }
+    }
+
+private:
+    GrGLProgramDataManager::UniformHandle fColorUniform;
+    GrColor                               fPrevColor;
+
+    typedef GrGLFragmentProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrConstColorProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const {
+    if (kIgnore_InputMode == fMode) {
+        inout->setToOther(kRGBA_GrColorComponentFlags, fColor, GrInvariantOutput::kWill_ReadInput);
+    } else {
+        GrColor r = GrColorUnpackR(fColor);
+        bool colorIsSingleChannel = r == GrColorUnpackG(fColor) && r == GrColorUnpackB(fColor) &&
+                                    r == GrColorUnpackA(fColor);
+        if (kModulateRGBA_InputMode == fMode) {
+            if (colorIsSingleChannel) {
+                inout->mulByKnownSingleComponent(r);
+            } else {
+                inout->mulByKnownFourComponents(fColor);
+            }
+        } else {
+            if (colorIsSingleChannel) {
+                inout->mulAlphaByKnownSingleComponent(r);
+            } else {
+                inout->mulAlphaByKnownFourComponents(fColor);
+            }
+        }
+    }
+}
+
+void GrConstColorProcessor::getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder* b) const {
+    b->add32(fMode);
+}
+
+GrGLFragmentProcessor* GrConstColorProcessor::createGLInstance() const  {
+    return SkNEW(GLConstColorProcessor);
+}
+
+bool GrConstColorProcessor::onIsEqual(const GrFragmentProcessor& other) const {
+    const GrConstColorProcessor& that = other.cast<GrConstColorProcessor>();
+    return fMode == that.fMode && fColor == that.fColor;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConstColorProcessor);
+
+GrFragmentProcessor* GrConstColorProcessor::TestCreate(SkRandom* random,
+                                                       GrContext*,
+                                                       const GrDrawTargetCaps&,
+                                                       GrTexture*[]) {
+    GrColor color;
+    int colorPicker = random->nextULessThan(3);
+    switch (colorPicker) {
+        case 0: {
+            uint32_t a = random->nextULessThan(0x100);
+            uint32_t r = random->nextULessThan(a+1);
+            uint32_t g = random->nextULessThan(a+1);
+            uint32_t b = random->nextULessThan(a+1);
+            color = GrColorPackRGBA(r, g, b, a);
+            break;
+        }
+        case 1:
+            color = 0;
+            break;
+        case 2:
+            color = random->nextULessThan(0x100);
+            color = color | (color << 8) | (color << 16) | (color << 24);
+            break;
+    }
+    InputMode mode = static_cast<InputMode>(random->nextULessThan(kInputModeCnt));
+    return GrConstColorProcessor::Create(color, mode);
+}
diff --git a/src/gpu/effects/GrConvexPolyEffect.cpp b/src/gpu/effects/GrConvexPolyEffect.cpp
index 7035094..2ad4ce0 100644
--- a/src/gpu/effects/GrConvexPolyEffect.cpp
+++ b/src/gpu/effects/GrConvexPolyEffect.cpp
@@ -23,11 +23,11 @@
 
     GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
 
-    const char* name() const SK_OVERRIDE { return "AARect"; }
+    const char* name() const override { return "AARect"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
 private:
     AARectEffect(GrPrimitiveEdgeType edgeType, const SkRect& rect)
@@ -36,12 +36,12 @@
         this->setWillReadFragmentPosition();
     }
 
-    bool onIsEqual(const GrFragmentProcessor& other) const SK_OVERRIDE {
+    bool onIsEqual(const GrFragmentProcessor& other) const override {
         const AARectEffect& aare = other.cast<AARectEffect>();
         return fRect == aare.fRect;
     }
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE {
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
         if (fRect.isEmpty()) {
             // An empty rect will have no coverage anywhere.
             inout->mulByKnownSingleComponent(0);
@@ -90,11 +90,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     GrGLProgramDataManager::UniformHandle fRectUniform;
@@ -122,7 +122,7 @@
                                        "rect",
                                        &rectName);
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     const char* fragmentPos = fsBuilder->fragmentPosition();
     if (GrProcessorEdgeTypeIsAA(aare.getEdgeType())) {
         // The amount of coverage removed in x and y by the edges is computed as a pair of negative
@@ -160,13 +160,13 @@
     }
 }
 
-void GLAARectEffect::GenKey(const GrProcessor& processor, const GrGLCaps&,
+void GLAARectEffect::GenKey(const GrProcessor& processor, const GrGLSLCaps&,
                             GrProcessorKeyBuilder* b) {
     const AARectEffect& aare = processor.cast<AARectEffect>();
     b->add32(aare.getEdgeType());
 }
 
-void AARectEffect::getGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const {
+void AARectEffect::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
     GLAARectEffect::GenKey(*this, caps, b);
 }
 
@@ -185,11 +185,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     GrGLProgramDataManager::UniformHandle fEdgeUniform;
@@ -216,7 +216,7 @@
                                              "edges",
                                             cpe.getEdgeCount(),
                                             &edgeArrayName);
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     fsBuilder->codeAppend("\t\tfloat alpha = 1.0;\n");
     fsBuilder->codeAppend("\t\tfloat edge;\n");
     const char* fragmentPos = fsBuilder->fragmentPosition();
@@ -252,7 +252,7 @@
     }
 }
 
-void GrGLConvexPolyEffect::GenKey(const GrProcessor& processor, const GrGLCaps&,
+void GrGLConvexPolyEffect::GenKey(const GrProcessor& processor, const GrGLSLCaps&,
                                   GrProcessorKeyBuilder* b) {
     const GrConvexPolyEffect& cpe = processor.cast<GrConvexPolyEffect>();
     GR_STATIC_ASSERT(kGrProcessorEdgeTypeCnt <= 8);
@@ -326,7 +326,7 @@
     inout->mulByUnknownSingleComponent();
 }
 
-void GrConvexPolyEffect::getGLProcessorKey(const GrGLCaps& caps,
+void GrConvexPolyEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                            GrProcessorKeyBuilder* b) const {
     GrGLConvexPolyEffect::GenKey(*this, caps, b);
 }
diff --git a/src/gpu/effects/GrConvexPolyEffect.h b/src/gpu/effects/GrConvexPolyEffect.h
index e0eee5d..ba9456d 100644
--- a/src/gpu/effects/GrConvexPolyEffect.h
+++ b/src/gpu/effects/GrConvexPolyEffect.h
@@ -61,7 +61,7 @@
 
     virtual ~GrConvexPolyEffect();
 
-    const char* name() const SK_OVERRIDE { return "ConvexPoly"; }
+    const char* name() const override { return "ConvexPoly"; }
 
     GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
 
@@ -69,16 +69,16 @@
 
     const SkScalar* getEdges() const { return fEdges; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
 private:
     GrConvexPolyEffect(GrPrimitiveEdgeType edgeType, int n, const SkScalar edges[]);
 
-    bool onIsEqual(const GrFragmentProcessor& other) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor& other) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     GrPrimitiveEdgeType    fEdgeType;
     int                    fEdgeCount;
diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp
index ef892dc..3d96a00 100644
--- a/src/gpu/effects/GrConvolutionEffect.cpp
+++ b/src/gpu/effects/GrConvolutionEffect.cpp
@@ -23,11 +23,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    void setData(const GrGLProgramDataManager& pdman, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager& pdman, const GrProcessor&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
 
 private:
     int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); }
@@ -69,7 +69,7 @@
                                           kFloat_GrSLType, kDefault_GrSLPrecision,
                                           "Kernel", this->width());
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
 
     fsBuilder->codeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
@@ -134,7 +134,7 @@
     pdman.set1fv(fKernelUni, this->width(), conv.kernel());
 }
 
-void GrGLConvolutionEffect::GenKey(const GrProcessor& processor, const GrGLCaps&,
+void GrGLConvolutionEffect::GenKey(const GrProcessor& processor, const GrGLSLCaps&,
                                    GrProcessorKeyBuilder* b) {
     const GrConvolutionEffect& conv = processor.cast<GrConvolutionEffect>();
     uint32_t key = conv.radius();
@@ -196,7 +196,7 @@
 GrConvolutionEffect::~GrConvolutionEffect() {
 }
 
-void GrConvolutionEffect::getGLProcessorKey(const GrGLCaps& caps,
+void GrConvolutionEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                         GrProcessorKeyBuilder* b) const {
     GrGLConvolutionEffect::GenKey(*this, caps, b);
 }
diff --git a/src/gpu/effects/GrConvolutionEffect.h b/src/gpu/effects/GrConvolutionEffect.h
index 4ea436c..b885f19 100644
--- a/src/gpu/effects/GrConvolutionEffect.h
+++ b/src/gpu/effects/GrConvolutionEffect.h
@@ -57,11 +57,11 @@
     const float* bounds() const { return fBounds; }
     bool useBounds() const { return fUseBounds; }
 
-    const char* name() const SK_OVERRIDE { return "Convolution"; }
+    const char* name() const override { return "Convolution"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     enum {
         // This was decided based on the min allowed value for the max texture
@@ -94,9 +94,9 @@
                         bool useBounds,
                         float bounds[2]);
 
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE {
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
         // If the texture was opaque we could know that the output color if we knew the sum of the
         // kernel values.
         inout->mulByUnknownFourComponents();
diff --git a/src/gpu/effects/GrCoverageSetOpXP.cpp b/src/gpu/effects/GrCoverageSetOpXP.cpp
index 6f49a1d..02552b3 100644
--- a/src/gpu/effects/GrCoverageSetOpXP.cpp
+++ b/src/gpu/effects/GrCoverageSetOpXP.cpp
@@ -21,30 +21,30 @@
         return SkNEW_ARGS(CoverageSetOpXP, (regionOp, invertCoverage));
     }
 
-    ~CoverageSetOpXP() SK_OVERRIDE;
+    ~CoverageSetOpXP() override;
 
-    const char* name() const SK_OVERRIDE { return "Coverage Set Op"; }
+    const char* name() const override { return "Coverage Set Op"; }
 
-    GrGLXferProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLXferProcessor* createGLInstance() const override;
 
-    bool hasSecondaryOutput() const SK_OVERRIDE { return false; }
-
-    GrXferProcessor::OptFlags getOptimizations(const GrProcOptInfo& colorPOI,
-                                               const GrProcOptInfo& coveragePOI,
-                                               bool doesStencilWrite,
-                                               GrColor* color,
-                                               const GrDrawTargetCaps& caps) SK_OVERRIDE;
-
-    void getBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const SK_OVERRIDE;
+    bool hasSecondaryOutput() const override { return false; }
 
     bool invertCoverage() const { return fInvertCoverage; }
 
 private:
     CoverageSetOpXP(SkRegion::Op regionOp, bool fInvertCoverage);
 
-    void onGetGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+    GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
+                                                 const GrProcOptInfo& coveragePOI,
+                                                 bool doesStencilWrite,
+                                                 GrColor* color,
+                                                 const GrDrawTargetCaps& caps) override;
 
-    bool onIsEqual(const GrXferProcessor& xpBase) const SK_OVERRIDE {
+    void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
+
+    void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override;
+
+    bool onIsEqual(const GrXferProcessor& xpBase) const override {
         const CoverageSetOpXP& xp = xpBase.cast<CoverageSetOpXP>();
         return (fRegionOp == xp.fRegionOp &&
                 fInvertCoverage == xp.fInvertCoverage);
@@ -62,9 +62,9 @@
 public:
     GLCoverageSetOpXP(const GrProcessor&) {}
 
-    ~GLCoverageSetOpXP() SK_OVERRIDE {}
+    ~GLCoverageSetOpXP() override {}
 
-    static void GenKey(const GrProcessor& processor, const GrGLCaps& caps,
+    static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps,
                        GrProcessorKeyBuilder* b) {
         const CoverageSetOpXP& xp = processor.cast<CoverageSetOpXP>();
         uint32_t key = xp.invertCoverage() ?  0x0 : 0x1;
@@ -72,9 +72,9 @@
     };
 
 private:
-    void onEmitCode(const EmitArgs& args) SK_OVERRIDE {
+    void onEmitCode(const EmitArgs& args) override {
         const CoverageSetOpXP& xp = args.fXP.cast<CoverageSetOpXP>();
-        GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+        GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
 
         if (xp.invertCoverage()) {
             fsBuilder->codeAppendf("%s = 1.0 - %s;", args.fOutputPrimary, args.fInputCoverage);
@@ -83,7 +83,7 @@
         }
     }
 
-    void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) SK_OVERRIDE {};
+    void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {};
 
     typedef GrGLXferProcessor INHERITED;
 };
@@ -99,7 +99,7 @@
 CoverageSetOpXP::~CoverageSetOpXP() {
 }
 
-void CoverageSetOpXP::onGetGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const {
+void CoverageSetOpXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
     GLCoverageSetOpXP::GenKey(*this, caps, b);
 }
 
@@ -108,16 +108,16 @@
 }
 
 GrXferProcessor::OptFlags
-CoverageSetOpXP::getOptimizations(const GrProcOptInfo& colorPOI,
-                                  const GrProcOptInfo& coveragePOI,
-                                  bool doesStencilWrite,
-                                  GrColor* color,
-                                  const GrDrawTargetCaps& caps) {
+CoverageSetOpXP::onGetOptimizations(const GrProcOptInfo& colorPOI,
+                                    const GrProcOptInfo& coveragePOI,
+                                    bool doesStencilWrite,
+                                    GrColor* color,
+                                    const GrDrawTargetCaps& caps) {
     // We never look at the color input
     return GrXferProcessor::kIgnoreColor_OptFlag; 
 }
 
-void CoverageSetOpXP::getBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const {
+void CoverageSetOpXP::onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const {
     switch (fRegionOp) {
         case SkRegion::kReplace_Op:
             blendInfo->fSrcBlend = kOne_GrBlendCoeff;
diff --git a/src/gpu/effects/GrCustomXfermode.cpp b/src/gpu/effects/GrCustomXfermode.cpp
index e4d65e4..1fdd3b7 100644
--- a/src/gpu/effects/GrCustomXfermode.cpp
+++ b/src/gpu/effects/GrCustomXfermode.cpp
@@ -17,6 +17,7 @@
 #include "GrTextureAccess.h"
 #include "SkXfermode.h"
 #include "gl/GrGLCaps.h"
+#include "gl/GrGLGpu.h"
 #include "gl/GrGLProcessor.h"
 #include "gl/GrGLProgramDataManager.h"
 #include "gl/builders/GrGLProgramBuilder.h"
@@ -29,7 +30,28 @@
 // Static helpers
 ///////////////////////////////////////////////////////////////////////////////
 
-static void hard_light(GrGLFPFragmentBuilder* fsBuilder,
+static GrBlendEquation hw_blend_equation(SkXfermode::Mode mode) {
+    enum { kOffset = kOverlay_GrBlendEquation - SkXfermode::kOverlay_Mode };
+    return static_cast<GrBlendEquation>(mode + kOffset);
+
+    GR_STATIC_ASSERT(kOverlay_GrBlendEquation == SkXfermode::kOverlay_Mode + kOffset);
+    GR_STATIC_ASSERT(kDarken_GrBlendEquation == SkXfermode::kDarken_Mode + kOffset);
+    GR_STATIC_ASSERT(kLighten_GrBlendEquation == SkXfermode::kLighten_Mode + kOffset);
+    GR_STATIC_ASSERT(kColorDodge_GrBlendEquation == SkXfermode::kColorDodge_Mode + kOffset);
+    GR_STATIC_ASSERT(kColorBurn_GrBlendEquation == SkXfermode::kColorBurn_Mode + kOffset);
+    GR_STATIC_ASSERT(kHardLight_GrBlendEquation == SkXfermode::kHardLight_Mode + kOffset);
+    GR_STATIC_ASSERT(kSoftLight_GrBlendEquation == SkXfermode::kSoftLight_Mode + kOffset);
+    GR_STATIC_ASSERT(kDifference_GrBlendEquation == SkXfermode::kDifference_Mode + kOffset);
+    GR_STATIC_ASSERT(kExclusion_GrBlendEquation == SkXfermode::kExclusion_Mode + kOffset);
+    GR_STATIC_ASSERT(kMultiply_GrBlendEquation == SkXfermode::kMultiply_Mode + kOffset);
+    GR_STATIC_ASSERT(kHSLHue_GrBlendEquation == SkXfermode::kHue_Mode + kOffset);
+    GR_STATIC_ASSERT(kHSLSaturation_GrBlendEquation == SkXfermode::kSaturation_Mode + kOffset);
+    GR_STATIC_ASSERT(kHSLColor_GrBlendEquation == SkXfermode::kColor_Mode + kOffset);
+    GR_STATIC_ASSERT(kHSLLuminosity_GrBlendEquation == SkXfermode::kLuminosity_Mode + kOffset);
+    GR_STATIC_ASSERT(kGrBlendEquationCnt == SkXfermode::kLastMode + 1 + kOffset);
+}
+
+static void hard_light(GrGLFragmentBuilder* fsBuilder,
                        const char* final,
                        const char* src,
                        const char* dst) {
@@ -50,7 +72,7 @@
 }
 
 // Does one component of color-dodge
-static void color_dodge_component(GrGLFPFragmentBuilder* fsBuilder,
+static void color_dodge_component(GrGLFragmentBuilder* fsBuilder,
                                   const char* final,
                                   const char* src,
                                   const char* dst,
@@ -74,7 +96,7 @@
 }
 
 // Does one component of color-burn
-static void color_burn_component(GrGLFPFragmentBuilder* fsBuilder,
+static void color_burn_component(GrGLFragmentBuilder* fsBuilder,
                                  const char* final,
                                  const char* src,
                                  const char* dst,
@@ -95,7 +117,7 @@
 }
 
 // Does one component of soft-light. Caller should have already checked that dst alpha > 0.
-static void soft_light_component_pos_dst_alpha(GrGLFPFragmentBuilder* fsBuilder,
+static void soft_light_component_pos_dst_alpha(GrGLFragmentBuilder* fsBuilder,
                                                const char* final,
                                                const char* src,
                                                const char* dst,
@@ -138,7 +160,7 @@
 // hue and saturation of the first color, the luminosity of the second color, and the input
 // alpha. It has this signature:
 //      vec3 set_luminance(vec3 hueSatColor, float alpha, vec3 lumColor).
-static void add_lum_function(GrGLFPFragmentBuilder* fsBuilder, SkString* setLumFunction) {
+static void add_lum_function(GrGLFragmentBuilder* fsBuilder, SkString* setLumFunction) {
     // Emit a helper that gets the luminance of a color.
     SkString getFunction;
     GrGLShaderVar getLumArgs[] = {
@@ -183,7 +205,7 @@
 // Adds a function that creates a color with the hue and luminosity of one input color and
 // the saturation of another color. It will have this signature:
 //      float set_saturation(vec3 hueLumColor, vec3 satColor)
-static void add_sat_function(GrGLFPFragmentBuilder* fsBuilder, SkString* setSatFunction) {
+static void add_sat_function(GrGLFragmentBuilder* fsBuilder, SkString* setSatFunction) {
     // Emit a helper that gets the saturation of a color
     SkString getFunction;
     GrGLShaderVar getSatArgs[] = { GrGLShaderVar("color", kVec3f_GrSLType) };
@@ -256,7 +278,7 @@
 }
 
 static void emit_custom_xfermode_code(SkXfermode::Mode mode,
-                                      GrGLFPFragmentBuilder* fsBuilder,
+                                      GrGLFragmentBuilder* fsBuilder,
                                       const char* outputColor,
                                       const char* inputColor,
                                       const char* dstColor) {
@@ -407,16 +429,16 @@
 class GLCustomXferFP : public GrGLFragmentProcessor {
 public:
     GLCustomXferFP(const GrFragmentProcessor&) {}
-    ~GLCustomXferFP() SK_OVERRIDE {};
+    ~GLCustomXferFP() override {};
 
     void emitCode(GrGLFPBuilder* builder,
                   const GrFragmentProcessor& fp,
                   const char* outputColor,
                   const char* inputColor,
                   const TransformedCoordsArray& coords,
-                  const TextureSamplerArray& samplers) SK_OVERRIDE {
+                  const TextureSamplerArray& samplers) override {
         SkXfermode::Mode mode = fp.cast<GrCustomXferFP>().mode();
-        GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+        GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
         const char* dstColor = "bgColor";
         fsBuilder->codeAppendf("vec4 %s = ", dstColor);
         fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0].getType());
@@ -425,9 +447,9 @@
         emit_custom_xfermode_code(mode, fsBuilder, outputColor, inputColor, dstColor); 
     }
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE {}
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override {}
 
-    static void GenKey(const GrFragmentProcessor& proc, const GrGLCaps&, GrProcessorKeyBuilder* b) {
+    static void GenKey(const GrFragmentProcessor& proc, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
         // The background may come from the dst or from a texture.
         uint32_t key = proc.numTextures();
         SkASSERT(key <= 1);
@@ -453,7 +475,7 @@
     this->addTextureAccess(&fBackgroundAccess);
 }
 
-void GrCustomXferFP::getGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const {
+void GrCustomXferFP::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
     GLCustomXferFP::GenKey(*this, caps, b);
 }
 
@@ -495,36 +517,43 @@
         }
     }
 
-    ~CustomXP() SK_OVERRIDE {};
+    ~CustomXP() override {};
 
-    const char* name() const SK_OVERRIDE { return "Custom Xfermode"; }
+    const char* name() const override { return "Custom Xfermode"; }
 
-    GrGLXferProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLXferProcessor* createGLInstance() const override;
 
-    bool hasSecondaryOutput() const SK_OVERRIDE { return false; }
-
-    GrXferProcessor::OptFlags getOptimizations(const GrProcOptInfo& colorPOI,
-                                               const GrProcOptInfo& coveragePOI,
-                                               bool doesStencilWrite,
-                                               GrColor* overrideColor,
-                                               const GrDrawTargetCaps& caps) SK_OVERRIDE;
-
-    void getBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const SK_OVERRIDE {
-        blendInfo->fSrcBlend = kOne_GrBlendCoeff;
-        blendInfo->fDstBlend = kZero_GrBlendCoeff;
-        blendInfo->fBlendConstant = 0;
-    }
+    bool hasSecondaryOutput() const override { return false; }
 
     SkXfermode::Mode mode() const { return fMode; }
+    bool hasHWBlendEquation() const { return -1 != static_cast<int>(fHWBlendEquation); }
+
+    GrBlendEquation hwBlendEquation() const {
+        SkASSERT(this->hasHWBlendEquation());
+        return fHWBlendEquation;
+    }
 
 private:
     CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
 
-    void onGetGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+    GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
+                                                 const GrProcOptInfo& coveragePOI,
+                                                 bool doesStencilWrite,
+                                                 GrColor* overrideColor,
+                                                 const GrDrawTargetCaps& caps) override;
 
-    bool onIsEqual(const GrXferProcessor& xpBase) const SK_OVERRIDE;
+    void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
+
+    bool onWillNeedXferBarrier(const GrRenderTarget* rt,
+                               const GrDrawTargetCaps& caps,
+                               GrXferBarrierType* outBarrierType) const override;
+
+    void onGetBlendInfo(BlendInfo*) const override;
+
+    bool onIsEqual(const GrXferProcessor& xpBase) const override;
 
     SkXfermode::Mode fMode;
+    GrBlendEquation  fHWBlendEquation;
 
     typedef GrXferProcessor INHERITED;
 };
@@ -544,29 +573,53 @@
 class GLCustomXP : public GrGLXferProcessor {
 public:
     GLCustomXP(const GrXferProcessor&) {}
-    ~GLCustomXP() SK_OVERRIDE {}
+    ~GLCustomXP() override {}
 
-    static void GenKey(const GrXferProcessor& proc, const GrGLCaps&, GrProcessorKeyBuilder* b) {
-        uint32_t key = proc.numTextures();
+    static void GenKey(const GrXferProcessor& p, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {
+        const CustomXP& xp = p.cast<CustomXP>();
+        uint32_t key = xp.numTextures();
         SkASSERT(key <= 1);
-        key |= proc.cast<CustomXP>().mode() << 1;
+        key |= xp.readsCoverage() << 1;
+        if (xp.hasHWBlendEquation()) {
+            SkASSERT(caps.advBlendEqInteraction() > 0);  // 0 will mean !xp.hasHWBlendEquation().
+            key |= caps.advBlendEqInteraction() << 2;
+        }
+        if (!xp.hasHWBlendEquation() || caps.mustEnableSpecificAdvBlendEqs()) {
+            GR_STATIC_ASSERT(GrGLSLCaps::kLast_AdvBlendEqInteraction < 4);
+            key |= xp.mode() << 4;
+        }
         b->add32(key);
     }
 
 private:
-    void onEmitCode(const EmitArgs& args) SK_OVERRIDE {
-        SkXfermode::Mode mode = args.fXP.cast<CustomXP>().mode();
-        GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
-        const char* dstColor = fsBuilder->dstColor();
+    void onEmitCode(const EmitArgs& args) override {
+        const CustomXP& xp = args.fXP.cast<CustomXP>();
+        GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
 
-        emit_custom_xfermode_code(mode, fsBuilder, args.fOutputPrimary, args.fInputColor, dstColor);
-
-        fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
-                               args.fOutputPrimary, args.fOutputPrimary, args.fInputCoverage,
-                               args.fInputCoverage, dstColor);
+        if (xp.hasHWBlendEquation()) {
+            // The blend mode will be implemented in hardware; only output the src color.
+            fsBuilder->enableAdvancedBlendEquationIfNeeded(xp.hwBlendEquation());
+            if (xp.readsCoverage()) {
+                // Do coverage modulation by multiplying it into the src color before blending.
+                // (See getOptimizations())
+                fsBuilder->codeAppendf("%s = %s * %s;",
+                                       args.fOutputPrimary, args.fInputCoverage, args.fInputColor);
+            } else {
+                fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor);
+            }
+        } else {
+            const char* dstColor = fsBuilder->dstColor();
+            emit_custom_xfermode_code(xp.mode(), fsBuilder, args.fOutputPrimary, args.fInputColor,
+                                      dstColor);
+            if (xp.readsCoverage()) {
+                fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
+                                       args.fOutputPrimary, args.fOutputPrimary,
+                                       args.fInputCoverage, args.fInputCoverage, dstColor);
+            }
+        }
     }
 
-    void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) SK_OVERRIDE {}
+    void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
 
     typedef GrGLFragmentProcessor INHERITED;
 };
@@ -575,29 +628,155 @@
 
 CustomXP::CustomXP(SkXfermode::Mode mode, const GrDeviceCoordTexture* dstCopy,
                    bool willReadDstColor)
-    : INHERITED(dstCopy, willReadDstColor), fMode(mode) {
+    : INHERITED(dstCopy, willReadDstColor),
+      fMode(mode),
+      fHWBlendEquation(static_cast<GrBlendEquation>(-1)) {
     this->initClassID<CustomXP>();
 }
 
-void CustomXP::onGetGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const {
+void CustomXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
     GLCustomXP::GenKey(*this, caps, b);
 }
 
 GrGLXferProcessor* CustomXP::createGLInstance() const {
+    SkASSERT(this->willReadDstColor() != this->hasHWBlendEquation());
     return SkNEW_ARGS(GLCustomXP, (*this));
 }
 
 bool CustomXP::onIsEqual(const GrXferProcessor& other) const {
     const CustomXP& s = other.cast<CustomXP>();
-    return fMode == s.fMode;
+    return fMode == s.fMode && fHWBlendEquation == s.fHWBlendEquation;
 }
 
-GrXferProcessor::OptFlags CustomXP::getOptimizations(const GrProcOptInfo& colorPOI,
+GrXferProcessor::OptFlags CustomXP::onGetOptimizations(const GrProcOptInfo& colorPOI,
                                                        const GrProcOptInfo& coveragePOI,
                                                        bool doesStencilWrite,
                                                        GrColor* overrideColor,
                                                        const GrDrawTargetCaps& caps) {
-   return GrXferProcessor::kNone_Opt;
+  /*
+    Most the optimizations we do here are based on tweaking alpha for coverage.
+
+    The general SVG blend equation is defined in the spec as follows:
+
+      Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa)
+      Da'  = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa)
+
+    (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha,
+     and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied
+     RGB colors.)
+
+    For every blend mode supported by this class, i.e. the "advanced" blend
+    modes, X=Y=Z=1 and this equation reduces to the PDF blend equation.
+
+    It can be shown that when X=Y=Z=1, these equations can modulate alpha for
+    coverage.
+
+
+    == Color ==
+
+    We substitute Y=Z=1 and define a blend() function that calculates Dca' in
+    terms of premultiplied alpha only:
+
+      blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0,
+                                 Sca : if Da == 0,
+                                 B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if Sa,Da != 0}
+
+    And for coverage modulation, we use a post blend src-over model:
+
+      Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
+
+    (Where f is the fractional coverage.)
+
+    Next we show that canTweakAlphaForCoverage() is true by proving the
+    following relationship:
+
+      blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
+
+    General case (f,Sa,Da != 0):
+
+      f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
+        = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca  [Sa,Da != 0, definition of blend()]
+        = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca
+        = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca
+        = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca
+        = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca
+        = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)
+        = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa)  [f!=0]
+        = blend(f*Sca, Dca, f*Sa, Da)  [definition of blend()]
+
+    Corner cases (Sa=0, Da=0, and f=0):
+
+      Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
+              = f * Dca + (1-f) * Dca  [Sa=0, definition of blend()]
+              = Dca
+              = blend(0, Dca, 0, Da)  [definition of blend()]
+              = blend(f*Sca, Dca, f*Sa, Da)  [Sa=0]
+
+      Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
+              = f * Sca + (1-f) * Dca  [Da=0, definition of blend()]
+              = f * Sca  [Da=0]
+              = blend(f*Sca, 0, f*Sa, 0)  [definition of blend()]
+              = blend(f*Sca, Dca, f*Sa, Da)  [Da=0]
+
+      f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca
+             = Dca  [f=0]
+             = blend(0, Dca, 0, Da)  [definition of blend()]
+             = blend(f*Sca, Dca, f*Sa, Da)  [f=0]
+
+    == Alpha ==
+
+    We substitute X=Y=Z=1 and define a blend() function that calculates Da':
+
+      blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa)
+                    = Sa * Da + Sa - Sa * Da + Da - Da * Sa
+                    = Sa + Da - Sa * Da
+
+    We use the same model for coverage modulation as we did with color:
+
+      Da'' = f * blend(Sa, Da) + (1-f) * Da
+
+    And show that canTweakAlphaForCoverage() is true by proving the following
+    relationship:
+
+      blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da
+
+
+      f * blend(Sa, Da) + (1-f) * Da
+        = f * (Sa + Da - Sa * Da) + (1-f) * Da
+        = f*Sa + f*Da - f*Sa * Da + Da - f*Da
+        = f*Sa - f*Sa * Da + Da
+        = f*Sa + Da - f*Sa * Da
+        = blend(f*Sa, Da)
+   */
+
+    OptFlags flags = kNone_Opt;
+    if (colorPOI.allStagesMultiplyInput()) {
+        flags |= kCanTweakAlphaForCoverage_OptFlag;
+    }
+    if (coveragePOI.isSolidWhite()) {
+        flags |= kIgnoreCoverage_OptFlag;
+    }
+    if (caps.advancedBlendEquationSupport() && !coveragePOI.isFourChannelOutput()) {
+        // This blend mode can be implemented in hardware.
+        fHWBlendEquation = hw_blend_equation(fMode);
+    }
+    return flags;
+}
+
+bool CustomXP::onWillNeedXferBarrier(const GrRenderTarget* rt,
+                                     const GrDrawTargetCaps& caps,
+                                     GrXferBarrierType* outBarrierType) const {
+    if (this->hasHWBlendEquation() && !caps.advancedCoherentBlendEquationSupport()) {
+        *outBarrierType = kBlend_GrXferBarrierType;
+        return true;
+    }
+    return false;
+}
+
+void CustomXP::onGetBlendInfo(BlendInfo* blendInfo) const {
+    if (this->hasHWBlendEquation()) {
+        blendInfo->fEquation = this->hwBlendEquation();
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -615,6 +794,19 @@
     return CustomXP::Create(fMode, dstCopy, this->willReadDstColor(caps, colorPOI, coveragePOI));
 }
 
+bool GrCustomXPFactory::willReadDstColor(const GrDrawTargetCaps& caps,
+                                         const GrProcOptInfo& colorPOI,
+                                         const GrProcOptInfo& coveragePOI) const {
+    if (!caps.advancedBlendEquationSupport()) {
+        // No hardware support for advanced blend equations; we will need to do it in the shader.
+        return true;
+    }
+    if (coveragePOI.isFourChannelOutput()) {
+        // Advanced blend equations can't tweak alpha for RGB coverage.
+        return true;
+    }
+    return false;
+}
 
 void GrCustomXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI,
                                                const GrProcOptInfo& coveragePOI,
diff --git a/src/gpu/effects/GrCustomXfermodePriv.h b/src/gpu/effects/GrCustomXfermodePriv.h
index 85092a9..43aaeab 100644
--- a/src/gpu/effects/GrCustomXfermodePriv.h
+++ b/src/gpu/effects/GrCustomXfermodePriv.h
@@ -29,19 +29,19 @@
 public:
     GrCustomXferFP(SkXfermode::Mode mode, GrTexture* background);
 
-    void getGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const SK_OVERRIDE; 
+    void getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override; 
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
-    const char* name() const SK_OVERRIDE { return "Custom Xfermode"; }
+    const char* name() const override { return "Custom Xfermode"; }
 
     SkXfermode::Mode mode() const { return fMode; }
     const GrTextureAccess&  backgroundAccess() const { return fBackgroundAccess; }
 
 private:
-    bool onIsEqual(const GrFragmentProcessor& other) const SK_OVERRIDE; 
+    bool onIsEqual(const GrFragmentProcessor& other) const override; 
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
@@ -60,30 +60,24 @@
 public:
     GrCustomXPFactory(SkXfermode::Mode mode); 
 
-    bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const SK_OVERRIDE {
+    bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const override {
         return true;
     }
 
-    bool canTweakAlphaForCoverage() const SK_OVERRIDE {
-        return false;
-    }
-
     void getInvariantOutput(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI,
-                            GrXPFactory::InvariantOutput*) const SK_OVERRIDE;
+                            GrXPFactory::InvariantOutput*) const override;
 
 private:
     GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
                                            const GrProcOptInfo& colorPOI,
                                            const GrProcOptInfo& coveragePOI,
-                                           const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE; 
+                                           const GrDeviceCoordTexture* dstCopy) const override; 
 
     bool willReadDstColor(const GrDrawTargetCaps& caps,
                           const GrProcOptInfo& colorPOI,
-                          const GrProcOptInfo& coveragePOI) const SK_OVERRIDE {
-        return true;
-    }
+                          const GrProcOptInfo& coveragePOI) const override;
 
-    bool onIsEqual(const GrXPFactory& xpfBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrXPFactory& xpfBase) const override {
         const GrCustomXPFactory& xpf = xpfBase.cast<GrCustomXPFactory>();
         return fMode == xpf.fMode;
     }
diff --git a/src/gpu/effects/GrDashingEffect.cpp b/src/gpu/effects/GrDashingEffect.cpp
index eb0467c..05aa277 100644
--- a/src/gpu/effects/GrDashingEffect.cpp
+++ b/src/gpu/effects/GrDashingEffect.cpp
@@ -9,7 +9,7 @@
 
 #include "GrBatch.h"
 #include "GrBatchTarget.h"
-#include "GrBufferAllocPool.h"
+#include "GrBatchTest.h"
 #include "GrGeometryProcessor.h"
 #include "GrContext.h"
 #include "GrCoordTransform.h"
@@ -19,6 +19,7 @@
 #include "GrInvariantOutput.h"
 #include "GrProcessor.h"
 #include "GrStrokeInfo.h"
+#include "GrVertexBuffer.h"
 #include "SkGr.h"
 #include "gl/GrGLGeometryProcessor.h"
 #include "gl/GrGLProcessor.h"
@@ -28,13 +29,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 // Returns whether or not the gpu can fast path the dash line effect.
-static bool can_fast_path_dash(const SkPoint pts[2], const GrStrokeInfo& strokeInfo,
-                               const GrDrawTarget& target, const GrPipelineBuilder& pipelineBuilder,
-                               const SkMatrix& viewMatrix) {
-    if (pipelineBuilder.getRenderTarget()->isMultisampled()) {
-        return false;
-    }
-
+bool GrDashingEffect::CanDrawDashLine(const SkPoint pts[2], const GrStrokeInfo& strokeInfo,
+                                      const SkMatrix& viewMatrix) {
     // Pts must be either horizontal or vertical in src space
     if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) {
         return false;
@@ -46,18 +42,18 @@
         return false;
     }
 
-    if (!strokeInfo.isDashed() || 2 != strokeInfo.dashCount()) {
+    if (!strokeInfo.isDashed() || 2 != strokeInfo.getDashCount()) {
         return false;
     }
 
-    const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
-    if (0 == info.fIntervals[0] && 0 == info.fIntervals[1]) {
+    const SkScalar* intervals = strokeInfo.getDashIntervals();
+    if (0 == intervals[0] && 0 == intervals[1]) {
         return false;
     }
 
     SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
     // Current we do don't handle Round or Square cap dashes
-    if (SkPaint::kRound_Cap == cap && info.fIntervals[0] != 0.f) {
+    if (SkPaint::kRound_Cap == cap && intervals[0] != 0.f) {
         return false;
     }
 
@@ -78,6 +74,14 @@
     SkScalar fRadius;
     SkScalar fCenterX;
 };
+
+enum DashAAMode {
+    kBW_DashAAMode,
+    kEdgeAA_DashAAMode,
+    kMSAA_DashAAMode,
+
+    kDashAAModeCount,
+};
 };
 
 static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale,
@@ -131,10 +135,10 @@
     }
     SkScalar srcIntervalLen = intervals[0] + intervals[1];
     SkScalar totalLen = pts[1].fX - pts[0].fX;
-    SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen);
+    SkScalar temp = totalLen / srcIntervalLen;
     SkScalar numFullIntervals = SkScalarFloorToScalar(temp);
     *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase;
-    temp = SkScalarDiv(*endingInt, srcIntervalLen);
+    temp = *endingInt / srcIntervalLen;
     *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen;
     if (0 == *endingInt) {
         *endingInt = srcIntervalLen;
@@ -157,11 +161,12 @@
 
 template <typename T>
 void setup_dashed_rect_common(const SkRect& rect, const SkMatrix& matrix, T* vertices, int idx,
-                              SkScalar offset, SkScalar bloat, SkScalar len, SkScalar stroke) {
-    SkScalar startDashX = offset - bloat;
-    SkScalar endDashX = offset + len + bloat;
-    SkScalar startDashY = -stroke - bloat;
-    SkScalar endDashY = stroke + bloat;
+                              SkScalar offset, SkScalar bloatX, SkScalar bloatY, SkScalar len,
+                              SkScalar stroke) {
+    SkScalar startDashX = offset - bloatX;
+    SkScalar endDashX = offset + len + bloatX;
+    SkScalar startDashY = -stroke - bloatY;
+    SkScalar endDashY = stroke + bloatY;
     vertices[idx].fDashPos = SkPoint::Make(startDashX , startDashY);
     vertices[idx + 1].fDashPos = SkPoint::Make(startDashX, endDashY);
     vertices[idx + 2].fDashPos = SkPoint::Make(endDashX, endDashY);
@@ -176,18 +181,18 @@
 }
 
 static void setup_dashed_rect(const SkRect& rect, void* vertices, int idx,
-                              const SkMatrix& matrix, SkScalar offset, SkScalar bloat,
-                              SkScalar len, SkScalar stroke, SkScalar startInterval,
-                              SkScalar endInterval, SkScalar strokeWidth, DashCap cap,
-                              const size_t vertexStride) {
+                              const SkMatrix& matrix, SkScalar offset, SkScalar bloatX,
+                              SkScalar bloatY, SkScalar len, SkScalar stroke,
+                              SkScalar startInterval, SkScalar endInterval, SkScalar strokeWidth,
+                              DashCap cap, const size_t vertexStride) {
     SkScalar intervalLength = startInterval + endInterval;
 
     if (kRound_DashCap == cap) {
         SkASSERT(vertexStride == sizeof(DashCircleVertex));
         DashCircleVertex* verts = reinterpret_cast<DashCircleVertex*>(vertices);
 
-        setup_dashed_rect_common<DashCircleVertex>(rect, matrix, verts, idx, offset, bloat, len,
-                                                   stroke);
+        setup_dashed_rect_common<DashCircleVertex>(rect, matrix, verts, idx, offset, bloatX,
+                                                   bloatY, len, stroke);
 
         SkScalar radius = SkScalarHalf(strokeWidth) - 0.5f;
         SkScalar centerX = SkScalarHalf(endInterval);
@@ -202,8 +207,8 @@
         SkASSERT(kNonRound_DashCap == cap && vertexStride == sizeof(DashLineVertex));
         DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(vertices);
 
-        setup_dashed_rect_common<DashLineVertex>(rect, matrix, verts, idx, offset, bloat, len,
-                                                 stroke);
+        setup_dashed_rect_common<DashLineVertex>(rect, matrix, verts, idx, offset, bloatX,
+                                                 bloatY, len, stroke);
 
         SkScalar halfOffLen = SkScalarHalf(endInterval);
         SkScalar halfStroke = SkScalarHalf(strokeWidth);
@@ -234,7 +239,7 @@
  * position relative to the dashed line.
  */
 static GrGeometryProcessor* create_dash_gp(GrColor,
-                                           GrPrimitiveEdgeType edgeType,
+                                           DashAAMode aaMode,
                                            DashCap cap,
                                            const SkMatrix& localMatrix);
 
@@ -250,24 +255,24 @@
         SkScalar fIntervals[2];
         SkScalar fParallelScale;
         SkScalar fPerpendicularScale;
-        SkDEBUGCODE(SkRect fDevBounds;)
     };
 
-    static GrBatch* Create(const Geometry& geometry, SkPaint::Cap cap, bool useAA, bool fullDash) {
-        return SkNEW_ARGS(DashBatch, (geometry, cap, useAA, fullDash));
+    static GrBatch* Create(const Geometry& geometry, SkPaint::Cap cap, DashAAMode aaMode,
+                           bool fullDash) {
+        return SkNEW_ARGS(DashBatch, (geometry, cap, aaMode, fullDash));
     }
 
-    const char* name() const SK_OVERRIDE { return "DashBatch"; }
+    const char* name() const override { return "DashBatch"; }
 
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
         // When this is called on a batch, there is only one geometry bundle
         out->setKnownFourComponents(fGeoData[0].fColor);
     }
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
         out->setUnknownSingleComponent();
     }
 
-    void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE {
+    void initBatchTracker(const GrPipelineInfo& init) override {
         // Handle any color overrides
         if (init.fColorIgnored) {
             fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -287,13 +292,14 @@
         SkScalar fStrokeWidth;
         SkScalar fLineLength;
         SkScalar fHalfDevStroke;
-        SkScalar fDevBloat;
+        SkScalar fDevBloatX;
+        SkScalar fDevBloatY;
         bool fLineDone;
         bool fHasStartRect;
         bool fHasEndRect;
     };
 
-    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
+    void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
         int instanceCount = fGeoData.count();
 
         SkMatrix invert;
@@ -309,9 +315,7 @@
         bool isRoundCap = SkPaint::kRound_Cap == cap;
         DashCap capType = isRoundCap ? kRound_DashCap : kNonRound_DashCap;
         if (this->fullDash()) {
-            GrPrimitiveEdgeType edgeType = this->useAA() ? kFillAA_GrProcessorEdgeType :
-                                                           kFillBW_GrProcessorEdgeType;
-            gp.reset(create_dash_gp(this->color(), edgeType, capType, invert));
+            gp.reset(create_dash_gp(this->color(), this->aaMode(), capType, invert));
         } else {
             // Set up the vertex data for the line and start/end dashes
             gp.reset(GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
@@ -330,7 +334,8 @@
         init.fUsesLocalCoords = this->usesLocalCoords();
         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
 
-        bool useAA = this->useAA();
+        // useAA here means Edge AA or MSAA
+        bool useAA = this->aaMode() != kBW_DashAAMode;
         bool fullDash = this->fullDash();
 
         // We do two passes over all of the dashes.  First we setup the start, end, and bounds,
@@ -341,6 +346,7 @@
 
         int totalRectCount = 0;
         int rectOffset = 0;
+        rects.push_back_n(3 * instanceCount);
         for (int i = 0; i < instanceCount; i++) {
             Geometry& args = fGeoData[i];
 
@@ -360,16 +366,10 @@
 
             SkScalar startAdj = 0;
 
-            SkMatrix& combinedMatrix = args.fSrcRotInv;
-            combinedMatrix.postConcat(args.fViewMatrix);
-
             bool lineDone = false;
 
             // Too simplify the algorithm, we always push back rects for start and end rect.
             // Otherwise we'd have to track start / end rects for each individual geometry
-            rects.push_back();
-            rects.push_back();
-            rects.push_back();
             SkRect& bounds = rects[rectOffset++];
             SkRect& startRect = rects[rectOffset++];
             SkRect& endRect = rects[rectOffset++];
@@ -444,7 +444,7 @@
             SkScalar devPhase = args.fPhase * args.fParallelScale;
             SkScalar strokeWidth = args.fSrcStrokeWidth * args.fPerpendicularScale;
 
-            if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) {
+            if ((strokeWidth < 1.f && useAA) || 0.f == strokeWidth) {
                 strokeWidth = 1.f;
             }
 
@@ -457,10 +457,14 @@
             }
             SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
 
-            SkScalar bloatX = useAA ? 0.5f / args.fParallelScale : 0.f;
-            SkScalar bloatY = useAA ? 0.5f / args.fPerpendicularScale : 0.f;
+            // For EdgeAA, we bloat in X & Y for both square and round caps.
+            // For MSAA, we don't bloat at all for square caps, and bloat in Y only for round caps.
+            SkScalar devBloatX = this->aaMode() == kEdgeAA_DashAAMode ? 0.5f : 0.0f;
+            SkScalar devBloatY = (SkPaint::kRound_Cap == cap && this->aaMode() == kMSAA_DashAAMode)
+                                 ? 0.5f : devBloatX;
 
-            SkScalar devBloat = useAA ? 0.5f : 0.f;
+            SkScalar bloatX = devBloatX / args.fParallelScale;
+            SkScalar bloatY = devBloatY / args.fPerpendicularScale;
 
             if (devIntervals[1] <= 0.f && useAA) {
                 // Case when we end up drawing a solid AA rect
@@ -519,7 +523,8 @@
             }
 
             draw.fStartOffset = startOffset;
-            draw.fDevBloat = devBloat;
+            draw.fDevBloatX = devBloatX;
+            draw.fDevBloatY = devBloatY;
             draw.fHalfDevStroke = halfDevStroke;
             draw.fStrokeWidth = strokeWidth;
             draw.fHasStartRect = hasStartRect;
@@ -527,36 +532,33 @@
             draw.fHasEndRect = hasEndRect;
         }
 
-        const GrVertexBuffer* vertexBuffer;
-        int firstVertex;
+        if (!totalRectCount) {
+            return;
+        }
 
-        size_t vertexStride = gp->getVertexStride();
-        void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
-                                                              totalRectCount * kVertsPerDash,
-                                                              &vertexBuffer,
-                                                              &firstVertex);
-
-        if (!vertices || !batchTarget->quadIndexBuffer()) {
-            SkDebugf("Could not allocate buffers\n");
+        QuadHelper helper;
+        void* vertices = helper.init(batchTarget, gp->getVertexStride(), totalRectCount);
+        if (!vertices) {
             return;
         }
 
         int curVIdx = 0;
         int rectIndex = 0;
         for (int i = 0; i < instanceCount; i++) {
-            Geometry& args = fGeoData[i];
+            Geometry& geom = fGeoData[i];
 
             if (!draws[i].fLineDone) {
                 if (fullDash) {
-                    setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args.fSrcRotInv,
-                                      draws[i].fStartOffset, draws[i].fDevBloat,
-                                      draws[i].fLineLength, draws[i].fHalfDevStroke,
-                                      args.fIntervals[0], args.fIntervals[1], draws[i].fStrokeWidth,
+                    setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
+                                      draws[i].fStartOffset, draws[i].fDevBloatX,
+                                      draws[i].fDevBloatY, draws[i].fLineLength,
+                                      draws[i].fHalfDevStroke, geom.fIntervals[0],
+                                      geom.fIntervals[1], draws[i].fStrokeWidth,
                                       capType, gp->getVertexStride());
                 } else {
                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
                     SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
-                    setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRotInv, verts);
+                    setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
                 }
                 curVIdx += 4;
             }
@@ -564,79 +566,69 @@
 
             if (draws[i].fHasStartRect) {
                 if (fullDash) {
-                    setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args.fSrcRotInv,
-                                      draws[i].fStartOffset, draws[i].fDevBloat, args.fIntervals[0],
-                                      draws[i].fHalfDevStroke, args.fIntervals[0],
-                                      args.fIntervals[1], draws[i].fStrokeWidth, capType,
+                    setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
+                                      draws[i].fStartOffset, draws[i].fDevBloatX,
+                                      draws[i].fDevBloatY, geom.fIntervals[0],
+                                      draws[i].fHalfDevStroke, geom.fIntervals[0],
+                                      geom.fIntervals[1], draws[i].fStrokeWidth, capType,
                                       gp->getVertexStride());
                 } else {
                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
                     SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
-                    setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRotInv, verts);
+                    setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
                 }
-
                 curVIdx += 4;
             }
             rectIndex++;
 
             if (draws[i].fHasEndRect) {
                 if (fullDash) {
-                    setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args.fSrcRotInv,
-                                      draws[i].fStartOffset, draws[i].fDevBloat, args.fIntervals[0],
-                                      draws[i].fHalfDevStroke, args.fIntervals[0],
-                                      args.fIntervals[1], draws[i].fStrokeWidth, capType,
+                    setup_dashed_rect(rects[rectIndex], vertices, curVIdx, geom.fSrcRotInv,
+                                      draws[i].fStartOffset, draws[i].fDevBloatX,
+                                      draws[i].fDevBloatY, geom.fIntervals[0],
+                                      draws[i].fHalfDevStroke, geom.fIntervals[0],
+                                      geom.fIntervals[1], draws[i].fStrokeWidth, capType,
                                       gp->getVertexStride());
                 } else {
                     SkPoint* verts = reinterpret_cast<SkPoint*>(vertices);
                     SkASSERT(gp->getVertexStride() == sizeof(SkPoint));
-                    setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRotInv, verts);
+                    setup_dashed_rect_pos(rects[rectIndex], curVIdx, geom.fSrcRotInv, verts);
                 }
                 curVIdx += 4;
             }
             rectIndex++;
         }
-
-        const GrIndexBuffer* dashIndexBuffer = batchTarget->quadIndexBuffer();
-
-        GrDrawTarget::DrawInfo drawInfo;
-        drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
-        drawInfo.setStartVertex(0);
-        drawInfo.setStartIndex(0);
-        drawInfo.setVerticesPerInstance(kVertsPerDash);
-        drawInfo.setIndicesPerInstance(kIndicesPerDash);
-        drawInfo.adjustStartVertex(firstVertex);
-        drawInfo.setVertexBuffer(vertexBuffer);
-        drawInfo.setIndexBuffer(dashIndexBuffer);
-
-        int maxInstancesPerDraw = dashIndexBuffer->maxQuads();
-        while (totalRectCount) {
-            drawInfo.setInstanceCount(SkTMin(totalRectCount, maxInstancesPerDraw));
-            drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
-            drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
-
-            batchTarget->draw(drawInfo);
-
-            drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
-            totalRectCount -= drawInfo.instanceCount();
-       }
+        SkASSERT(0 == (curVIdx % 4) && (curVIdx / 4) == totalRectCount);
+        helper.issueDraw(batchTarget);
     }
 
     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
 
 private:
-    DashBatch(const Geometry& geometry, SkPaint::Cap cap, bool useAA, bool fullDash) {
+    DashBatch(const Geometry& geometry, SkPaint::Cap cap, DashAAMode aaMode, bool fullDash) {
         this->initClassID<DashBatch>();
         fGeoData.push_back(geometry);
 
-        fBatch.fUseAA = useAA;
+        fBatch.fAAMode = aaMode;
         fBatch.fCap = cap;
         fBatch.fFullDash = fullDash;
+
+        // compute bounds
+        SkScalar halfStrokeWidth = 0.5f * geometry.fSrcStrokeWidth;
+        SkScalar xBloat = SkPaint::kButt_Cap == cap ? 0 : halfStrokeWidth;
+        fBounds.set(geometry.fPtsRot[0], geometry.fPtsRot[1]);
+        fBounds.outset(xBloat, halfStrokeWidth);
+
+        // Note, we actually create the combined matrix here, and save the work
+        SkMatrix& combinedMatrix = fGeoData[0].fSrcRotInv;
+        combinedMatrix.postConcat(geometry.fViewMatrix);
+        combinedMatrix.mapRect(&fBounds);
     }
 
-    bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE {
+    bool onCombineIfPossible(GrBatch* t) override {
         DashBatch* that = t->cast<DashBatch>();
 
-        if (this->useAA() != that->useAA()) {
+        if (this->aaMode() != that->aaMode()) {
             return false;
         }
 
@@ -659,13 +651,14 @@
         }
 
         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
         return true;
     }
 
     GrColor color() const { return fBatch.fColor; }
     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
-    bool useAA() const { return fBatch.fUseAA; }
+    DashAAMode aaMode() const { return fBatch.fAAMode; }
     bool fullDash() const { return fBatch.fFullDash; }
     SkPaint::Cap cap() const { return fBatch.fCap; }
 
@@ -675,7 +668,7 @@
         bool fColorIgnored;
         bool fCoverageIgnored;
         SkPaint::Cap fCap;
-        bool fUseAA;
+        DashAAMode fAAMode;
         bool fFullDash;
     };
 
@@ -686,16 +679,10 @@
     SkSTArray<1, Geometry, true> fGeoData;
 };
 
-
-bool GrDashingEffect::DrawDashLine(GrGpu* gpu, GrDrawTarget* target,
-                                   GrPipelineBuilder* pipelineBuilder, GrColor color,
-                                   const SkMatrix& viewMatrix, const SkPoint pts[2],
-                                   const GrPaint& paint, const GrStrokeInfo& strokeInfo) {
-    if (!can_fast_path_dash(pts, strokeInfo, *target, *pipelineBuilder, viewMatrix)) {
-        return false;
-    }
-
-    const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
+static GrBatch* create_batch(GrColor color, const SkMatrix& viewMatrix, const SkPoint pts[2],
+                             bool useAA, const GrStrokeInfo& strokeInfo, bool msaaRT) {
+    const SkScalar* intervals = strokeInfo.getDashIntervals();
+    SkScalar phase = strokeInfo.getDashPhase();
 
     SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
 
@@ -703,7 +690,7 @@
     geometry.fSrcStrokeWidth = strokeInfo.getStrokeRec().getWidth();
 
     // the phase should be normalized to be [0, sum of all intervals)
-    SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
+    SkASSERT(phase >= 0 && phase < intervals[0] + intervals[1]);
 
     // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX
     if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
@@ -711,7 +698,7 @@
         align_to_x_axis(pts, &rotMatrix, geometry.fPtsRot);
         if(!rotMatrix.invert(&geometry.fSrcRotInv)) {
             SkDebugf("Failed to create invertible rotation matrix!\n");
-            return false;
+            return NULL;
         }
     } else {
         geometry.fSrcRotInv.reset();
@@ -722,7 +709,7 @@
     calc_dash_scaling(&geometry.fParallelScale, &geometry.fPerpendicularScale, viewMatrix,
                       geometry.fPtsRot);
 
-    SkScalar offInterval = info.fIntervals[1] * geometry.fParallelScale;
+    SkScalar offInterval = intervals[1] * geometry.fParallelScale;
     SkScalar strokeWidth = geometry.fSrcStrokeWidth * geometry.fPerpendicularScale;
 
     if (SkPaint::kSquare_Cap == cap && 0 != geometry.fSrcStrokeWidth) {
@@ -730,19 +717,32 @@
         offInterval -= strokeWidth;
     }
 
-    bool useAA = paint.isAntiAlias();
+    DashAAMode aaMode = msaaRT ? kMSAA_DashAAMode :
+                                 useAA ? kEdgeAA_DashAAMode : kBW_DashAAMode;
+
     // TODO we can do a real rect call if not using fulldash(ie no off interval, not using AA)
-    bool fullDash = offInterval > 0.f || useAA;
+    bool fullDash = offInterval > 0.f || aaMode != kBW_DashAAMode;
 
     geometry.fColor = color;
     geometry.fViewMatrix = viewMatrix;
-    geometry.fPhase = info.fPhase;
-    geometry.fIntervals[0] = info.fIntervals[0];
-    geometry.fIntervals[1] = info.fIntervals[1];
+    geometry.fPhase = phase;
+    geometry.fIntervals[0] = intervals[0];
+    geometry.fIntervals[1] = intervals[1];
 
-    SkAutoTUnref<GrBatch> batch(DashBatch::Create(geometry, cap, useAA, fullDash));
+    return DashBatch::Create(geometry, cap, aaMode, fullDash);
+}
+
+bool GrDashingEffect::DrawDashLine(GrDrawTarget* target,
+                                   GrPipelineBuilder* pipelineBuilder, GrColor color,
+                                   const SkMatrix& viewMatrix, const SkPoint pts[2],
+                                   bool useAA, const GrStrokeInfo& strokeInfo) {
+    SkAutoTUnref<GrBatch> batch(create_batch(color, viewMatrix, pts, useAA, strokeInfo,
+                                             pipelineBuilder->getRenderTarget()->isMultisampled()));
+    if (!batch) {
+        return false;
+    }
+
     target->drawBatch(pipelineBuilder, batch);
-
     return true;
 }
 
@@ -770,12 +770,10 @@
     typedef SkPathEffect::DashInfo DashInfo;
 
     static GrGeometryProcessor* Create(GrColor,
-                                       GrPrimitiveEdgeType edgeType,
+                                       DashAAMode aaMode,
                                        const SkMatrix& localMatrix);
 
-    virtual ~DashingCircleEffect();
-
-    const char* name() const SK_OVERRIDE { return "DashingCircleEffect"; }
+    const char* name() const override { return "DashingCircleEffect"; }
 
     const Attribute* inPosition() const { return fInPosition; }
 
@@ -783,29 +781,27 @@
 
     const Attribute* inCircleParams() const { return fInCircleParams; }
 
-    GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
+    DashAAMode aaMode() const { return fAAMode; }
+
+    GrColor color() const { return fColor; }
+
+    const SkMatrix& localMatrix() const { return fLocalMatrix; }
 
     virtual void getGLProcessorKey(const GrBatchTracker&,
-                                   const GrGLCaps&,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+                                   const GrGLSLCaps&,
+                                   GrProcessorKeyBuilder* b) const override;
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker&,
-                                                     const GrGLCaps&) const SK_OVERRIDE;
+                                                     const GrGLSLCaps&) const override;
 
-    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE;
-
-    bool onCanMakeEqual(const GrBatchTracker&,
-                        const GrGeometryProcessor&,
-                        const GrBatchTracker&) const SK_OVERRIDE;
+    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override;
 
 private:
-    DashingCircleEffect(GrColor, GrPrimitiveEdgeType edgeType, const SkMatrix& localMatrix);
+    DashingCircleEffect(GrColor, DashAAMode aaMode, const SkMatrix& localMatrix);
 
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput*) const SK_OVERRIDE;
-
-    GrPrimitiveEdgeType fEdgeType;
+    GrColor             fColor;
+    SkMatrix            fLocalMatrix;
+    DashAAMode          fAAMode;
     const Attribute*    fInPosition;
     const Attribute*    fInDashParams;
     const Attribute*    fInCircleParams;
@@ -821,16 +817,23 @@
 public:
     GLDashingCircleEffect(const GrGeometryProcessor&, const GrBatchTracker&);
 
-    void onEmitCode(EmitArgs&, GrGPArgs*) SK_OVERRIDE;
+    void onEmitCode(EmitArgs&, GrGPArgs*) override;
 
     static inline void GenKey(const GrGeometryProcessor&,
                               const GrBatchTracker&,
-                              const GrGLCaps&,
+                              const GrGLSLCaps&,
                               GrProcessorKeyBuilder*);
 
     virtual void setData(const GrGLProgramDataManager&,
                          const GrPrimitiveProcessor&,
-                         const GrBatchTracker&) SK_OVERRIDE;
+                         const GrBatchTracker&) override;
+
+    void setTransformData(const GrPrimitiveProcessor& primProc,
+                          const GrGLProgramDataManager& pdman,
+                          int index,
+                          const SkTArray<const GrCoordTransform*, true>& transforms) override {
+        this->setTransformDataHelper<DashingCircleEffect>(primProc, pdman, index, transforms);
+    }
 
 private:
     UniformHandle fParamUniform;
@@ -864,7 +867,7 @@
     args.fPB->addVarying("DashParam", &dashParams);
     vsBuilder->codeAppendf("%s = %s;", dashParams.vsOut(), dce.inDashParams()->fName);
 
-    // xy, refer to circle radius - 0.5, z refers to cicles center x coord
+    // x refers to circle radius - 0.5, y refers to cicle's center x coord
     GrGLVertToFrag circleParams(kVec2f_GrSLType);
     args.fPB->addVarying("CircleParams", &circleParams);
     vsBuilder->codeAppendf("%s = %s;", circleParams.vsOut(), dce.inCircleParams()->fName);
@@ -873,21 +876,21 @@
     this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL, &fColorUniform);
 
     // Setup position
-    this->setupPosition(pb, gpArgs, dce.inPosition()->fName, dce.viewMatrix());
+    this->setupPosition(pb, gpArgs, dce.inPosition()->fName);
 
     // emit transforms
     this->emitTransforms(args.fPB, gpArgs->fPositionVar, dce.inPosition()->fName, dce.localMatrix(),
                          args.fTransformsIn, args.fTransformsOut);
 
     // transforms all points so that we can compare them to our test circle
-    GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
     fsBuilder->codeAppendf("float xShifted = %s.x - floor(%s.x / %s.z) * %s.z;",
                            dashParams.fsIn(), dashParams.fsIn(), dashParams.fsIn(),
                            dashParams.fsIn());
     fsBuilder->codeAppendf("vec2 fragPosShifted = vec2(xShifted, %s.y);", dashParams.fsIn());
     fsBuilder->codeAppendf("vec2 center = vec2(%s.y, 0.0);", circleParams.fsIn());
     fsBuilder->codeAppend("float dist = length(center - fragPosShifted);");
-    if (GrProcessorEdgeTypeIsAA(dce.getEdgeType())) {
+    if (dce.aaMode() != kBW_DashAAMode) {
         fsBuilder->codeAppendf("float diff = dist - %s.x;", circleParams.fsIn());
         fsBuilder->codeAppend("diff = 1.0 - diff;");
         fsBuilder->codeAppend("float alpha = clamp(diff, 0.0, 1.0);");
@@ -901,8 +904,6 @@
 void GLDashingCircleEffect::setData(const GrGLProgramDataManager& pdman,
                                     const GrPrimitiveProcessor& processor,
                                     const GrBatchTracker& bt) {
-    this->setUniformViewMatrix(pdman, processor.viewMatrix());
-
     const DashingCircleBatchTracker& local = bt.cast<DashingCircleBatchTracker>();
     if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
         GrGLfloat c[4];
@@ -914,46 +915,41 @@
 
 void GLDashingCircleEffect::GenKey(const GrGeometryProcessor& gp,
                                    const GrBatchTracker& bt,
-                                   const GrGLCaps&,
+                                   const GrGLSLCaps&,
                                    GrProcessorKeyBuilder* b) {
     const DashingCircleBatchTracker& local = bt.cast<DashingCircleBatchTracker>();
     const DashingCircleEffect& dce = gp.cast<DashingCircleEffect>();
     uint32_t key = 0;
-    key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 : 0x0;
-    key |= ComputePosKey(gp.viewMatrix()) << 1;
-    key |= dce.getEdgeType() << 8;
+    key |= local.fUsesLocalCoords && dce.localMatrix().hasPerspective() ? 0x1 : 0x0;
+    key |= dce.aaMode() << 8;
     b->add32(key << 16 | local.fInputColorType);
 }
 
 //////////////////////////////////////////////////////////////////////////////
 
 GrGeometryProcessor* DashingCircleEffect::Create(GrColor color,
-                                                 GrPrimitiveEdgeType edgeType,
+                                                 DashAAMode aaMode,
                                                  const SkMatrix& localMatrix) {
-    return SkNEW_ARGS(DashingCircleEffect, (color, edgeType, localMatrix));
-}
-
-DashingCircleEffect::~DashingCircleEffect() {}
-
-void DashingCircleEffect::onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const {
-    out->setUnknownSingleComponent();
+    return SkNEW_ARGS(DashingCircleEffect, (color, aaMode, localMatrix));
 }
 
 void DashingCircleEffect::getGLProcessorKey(const GrBatchTracker& bt,
-                                            const GrGLCaps& caps,
+                                            const GrGLSLCaps& caps,
                                             GrProcessorKeyBuilder* b) const {
     GLDashingCircleEffect::GenKey(*this, bt, caps, b);
 }
 
 GrGLPrimitiveProcessor* DashingCircleEffect::createGLInstance(const GrBatchTracker& bt,
-                                                              const GrGLCaps&) const {
+                                                              const GrGLSLCaps&) const {
     return SkNEW_ARGS(GLDashingCircleEffect, (*this, bt));
 }
 
 DashingCircleEffect::DashingCircleEffect(GrColor color,
-                                         GrPrimitiveEdgeType edgeType,
+                                         DashAAMode aaMode,
                                          const SkMatrix& localMatrix)
-    : INHERITED(color, SkMatrix::I(), localMatrix), fEdgeType(edgeType) {
+    : fColor(color)
+    , fLocalMatrix(localMatrix)
+    , fAAMode(aaMode) {
     this->initClassID<DashingCircleEffect>();
     fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
     fInDashParams = &this->addVertexAttrib(Attribute("inDashParams", kVec3f_GrVertexAttribType));
@@ -961,38 +957,21 @@
                                                        kVec2f_GrVertexAttribType));
 }
 
-bool DashingCircleEffect::onIsEqual(const GrGeometryProcessor& other) const {
-    const DashingCircleEffect& dce = other.cast<DashingCircleEffect>();
-    return fEdgeType == dce.fEdgeType;
-}
-
 void DashingCircleEffect::initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const {
     DashingCircleBatchTracker* local = bt->cast<DashingCircleBatchTracker>();
     local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
     local->fUsesLocalCoords = init.fUsesLocalCoords;
 }
 
-bool DashingCircleEffect::onCanMakeEqual(const GrBatchTracker& m,
-                                         const GrGeometryProcessor& that,
-                                         const GrBatchTracker& t) const {
-    const DashingCircleBatchTracker& mine = m.cast<DashingCircleBatchTracker>();
-    const DashingCircleBatchTracker& theirs = t.cast<DashingCircleBatchTracker>();
-    return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                   that, theirs.fUsesLocalCoords) &&
-           CanCombineOutput(mine.fInputColorType, mine.fColor,
-                            theirs.fInputColorType, theirs.fColor);
-}
-
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingCircleEffect);
 
 GrGeometryProcessor* DashingCircleEffect::TestCreate(SkRandom* random,
                                                      GrContext*,
                                                      const GrDrawTargetCaps& caps,
                                                      GrTexture*[]) {
-    GrPrimitiveEdgeType edgeType = static_cast<GrPrimitiveEdgeType>(random->nextULessThan(
-            kGrProcessorEdgeTypeCnt));
+    DashAAMode aaMode = static_cast<DashAAMode>(random->nextULessThan(kDashAAModeCount));
     return DashingCircleEffect::Create(GrRandomColor(random),
-                                       edgeType, GrProcessorUnitTest::TestMatrix(random));
+                                      aaMode, GrTest::TestMatrix(random));
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -1019,12 +998,10 @@
     typedef SkPathEffect::DashInfo DashInfo;
 
     static GrGeometryProcessor* Create(GrColor,
-                                       GrPrimitiveEdgeType edgeType,
+                                       DashAAMode aaMode,
                                        const SkMatrix& localMatrix);
 
-    virtual ~DashingLineEffect();
-
-    const char* name() const SK_OVERRIDE { return "DashingEffect"; }
+    const char* name() const override { return "DashingEffect"; }
 
     const Attribute* inPosition() const { return fInPosition; }
 
@@ -1032,29 +1009,27 @@
 
     const Attribute* inRectParams() const { return fInRectParams; }
 
-    GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
+    DashAAMode aaMode() const { return fAAMode; }
+
+    GrColor color() const { return fColor; }
+
+    const SkMatrix& localMatrix() const { return fLocalMatrix; }
 
     virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
 
     virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE;
+                                                     const GrGLSLCaps&) const override;
 
-    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE;
-
-    bool onCanMakeEqual(const GrBatchTracker&,
-                        const GrGeometryProcessor&,
-                        const GrBatchTracker&) const SK_OVERRIDE;
+    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override;
 
 private:
-    DashingLineEffect(GrColor, GrPrimitiveEdgeType edgeType, const SkMatrix& localMatrix);
+    DashingLineEffect(GrColor, DashAAMode aaMode, const SkMatrix& localMatrix);
 
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput*) const SK_OVERRIDE;
-
-    GrPrimitiveEdgeType fEdgeType;
+    GrColor             fColor;
+    SkMatrix            fLocalMatrix;
+    DashAAMode          fAAMode;
     const Attribute*    fInPosition;
     const Attribute*    fInDashParams;
     const Attribute*    fInRectParams;
@@ -1070,16 +1045,23 @@
 public:
     GLDashingLineEffect(const GrGeometryProcessor&, const GrBatchTracker&);
 
-    void onEmitCode(EmitArgs&, GrGPArgs*) SK_OVERRIDE;
+    void onEmitCode(EmitArgs&, GrGPArgs*) override;
 
     static inline void GenKey(const GrGeometryProcessor&,
                               const GrBatchTracker&,
-                              const GrGLCaps&,
+                              const GrGLSLCaps&,
                               GrProcessorKeyBuilder*);
 
     virtual void setData(const GrGLProgramDataManager&,
                          const GrPrimitiveProcessor&,
-                         const GrBatchTracker&) SK_OVERRIDE;
+                         const GrBatchTracker&) override;
+
+    void setTransformData(const GrPrimitiveProcessor& primProc,
+                          const GrGLProgramDataManager& pdman,
+                          int index,
+                          const SkTArray<const GrCoordTransform*, true>& transforms) override {
+        this->setTransformDataHelper<DashingLineEffect>(primProc, pdman, index, transforms);
+    }
 
 private:
     GrColor       fColor;
@@ -1117,19 +1099,19 @@
     this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL, &fColorUniform);
 
     // Setup position
-    this->setupPosition(pb, gpArgs, de.inPosition()->fName, de.viewMatrix());
+    this->setupPosition(pb, gpArgs, de.inPosition()->fName);
 
     // emit transforms
     this->emitTransforms(args.fPB, gpArgs->fPositionVar, de.inPosition()->fName, de.localMatrix(),
                          args.fTransformsIn, args.fTransformsOut);
 
     // transforms all points so that we can compare them to our test rect
-    GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
     fsBuilder->codeAppendf("float xShifted = %s.x - floor(%s.x / %s.z) * %s.z;",
                            inDashParams.fsIn(), inDashParams.fsIn(), inDashParams.fsIn(),
                            inDashParams.fsIn());
     fsBuilder->codeAppendf("vec2 fragPosShifted = vec2(xShifted, %s.y);", inDashParams.fsIn());
-    if (GrProcessorEdgeTypeIsAA(de.getEdgeType())) {
+    if (de.aaMode() == kEdgeAA_DashAAMode) {
         // The amount of coverage removed in x and y by the edges is computed as a pair of negative
         // numbers, xSub and ySub.
         fsBuilder->codeAppend("float xSub, ySub;");
@@ -1140,6 +1122,14 @@
         // Now compute coverage in x and y and multiply them to get the fraction of the pixel
         // covered.
         fsBuilder->codeAppendf("float alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));");
+    } else if (de.aaMode() == kMSAA_DashAAMode) {
+        // For MSAA, we don't modulate the alpha by the Y distance, since MSAA coverage will handle
+        // AA on the the top and bottom edges. The shader is only responsible for intra-dash alpha.
+        fsBuilder->codeAppend("float xSub;");
+        fsBuilder->codeAppendf("xSub = min(fragPosShifted.x - %s.x, 0.0);", inRectParams.fsIn());
+        fsBuilder->codeAppendf("xSub += min(%s.z - fragPosShifted.x, 0.0);", inRectParams.fsIn());
+        // Now compute coverage in x to get the fraction of the pixel covered.
+        fsBuilder->codeAppendf("float alpha = (1.0 + max(xSub, -1.0));");
     } else {
         // Assuming the bounding geometry is tight so no need to check y values
         fsBuilder->codeAppendf("float alpha = 1.0;");
@@ -1154,8 +1144,6 @@
 void GLDashingLineEffect::setData(const GrGLProgramDataManager& pdman,
                                   const GrPrimitiveProcessor& processor,
                                   const GrBatchTracker& bt) {
-    this->setUniformViewMatrix(pdman, processor.viewMatrix());
-
     const DashingLineBatchTracker& local = bt.cast<DashingLineBatchTracker>();
     if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
         GrGLfloat c[4];
@@ -1167,100 +1155,158 @@
 
 void GLDashingLineEffect::GenKey(const GrGeometryProcessor& gp,
                                  const GrBatchTracker& bt,
-                                 const GrGLCaps&,
+                                 const GrGLSLCaps&,
                                  GrProcessorKeyBuilder* b) {
     const DashingLineBatchTracker& local = bt.cast<DashingLineBatchTracker>();
     const DashingLineEffect& de = gp.cast<DashingLineEffect>();
     uint32_t key = 0;
-    key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 : 0x0;
-    key |= ComputePosKey(gp.viewMatrix()) << 1;
-    key |= de.getEdgeType() << 8;
+    key |= local.fUsesLocalCoords && de.localMatrix().hasPerspective() ? 0x1 : 0x0;
+    key |= de.aaMode() << 8;
     b->add32(key << 16 | local.fInputColorType);
 }
 
 //////////////////////////////////////////////////////////////////////////////
 
 GrGeometryProcessor* DashingLineEffect::Create(GrColor color,
-                                               GrPrimitiveEdgeType edgeType,
+                                               DashAAMode aaMode,
                                                const SkMatrix& localMatrix) {
-    return SkNEW_ARGS(DashingLineEffect, (color, edgeType, localMatrix));
-}
-
-DashingLineEffect::~DashingLineEffect() {}
-
-void DashingLineEffect::onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const {
-    out->setUnknownSingleComponent();
+    return SkNEW_ARGS(DashingLineEffect, (color, aaMode, localMatrix));
 }
 
 void DashingLineEffect::getGLProcessorKey(const GrBatchTracker& bt,
-                                          const GrGLCaps& caps,
+                                          const GrGLSLCaps& caps,
                                           GrProcessorKeyBuilder* b) const {
     GLDashingLineEffect::GenKey(*this, bt, caps, b);
 }
 
 GrGLPrimitiveProcessor* DashingLineEffect::createGLInstance(const GrBatchTracker& bt,
-                                                            const GrGLCaps&) const {
+                                                            const GrGLSLCaps&) const {
     return SkNEW_ARGS(GLDashingLineEffect, (*this, bt));
 }
 
 DashingLineEffect::DashingLineEffect(GrColor color,
-                                     GrPrimitiveEdgeType edgeType,
+                                     DashAAMode aaMode,
                                      const SkMatrix& localMatrix)
-    : INHERITED(color, SkMatrix::I(), localMatrix), fEdgeType(edgeType) {
+    : fColor(color)
+    , fLocalMatrix(localMatrix)
+    , fAAMode(aaMode) {
     this->initClassID<DashingLineEffect>();
     fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
     fInDashParams = &this->addVertexAttrib(Attribute("inDashParams", kVec3f_GrVertexAttribType));
     fInRectParams = &this->addVertexAttrib(Attribute("inRect", kVec4f_GrVertexAttribType));
 }
 
-bool DashingLineEffect::onIsEqual(const GrGeometryProcessor& other) const {
-    const DashingLineEffect& de = other.cast<DashingLineEffect>();
-    return fEdgeType == de.fEdgeType;
-}
-
 void DashingLineEffect::initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const {
     DashingLineBatchTracker* local = bt->cast<DashingLineBatchTracker>();
     local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
     local->fUsesLocalCoords = init.fUsesLocalCoords;
 }
 
-bool DashingLineEffect::onCanMakeEqual(const GrBatchTracker& m,
-                                       const GrGeometryProcessor& that,
-                                       const GrBatchTracker& t) const {
-    const DashingLineBatchTracker& mine = m.cast<DashingLineBatchTracker>();
-    const DashingLineBatchTracker& theirs = t.cast<DashingLineBatchTracker>();
-    return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                  that, theirs.fUsesLocalCoords) &&
-           CanCombineOutput(mine.fInputColorType, mine.fColor,
-                            theirs.fInputColorType, theirs.fColor);
-}
-
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingLineEffect);
 
 GrGeometryProcessor* DashingLineEffect::TestCreate(SkRandom* random,
                                                    GrContext*,
                                                    const GrDrawTargetCaps& caps,
                                                    GrTexture*[]) {
-    GrPrimitiveEdgeType edgeType = static_cast<GrPrimitiveEdgeType>(random->nextULessThan(
-            kGrProcessorEdgeTypeCnt));
-
+    DashAAMode aaMode = static_cast<DashAAMode>(random->nextULessThan(kDashAAModeCount));
     return DashingLineEffect::Create(GrRandomColor(random),
-                                     edgeType, GrProcessorUnitTest::TestMatrix(random));
+                                     aaMode, GrTest::TestMatrix(random));
 }
 
 //////////////////////////////////////////////////////////////////////////////
 
 static GrGeometryProcessor* create_dash_gp(GrColor color,
-                                           GrPrimitiveEdgeType edgeType,
+                                           DashAAMode dashAAMode,
                                            DashCap cap,
                                            const SkMatrix& localMatrix) {
     switch (cap) {
         case kRound_DashCap:
-            return DashingCircleEffect::Create(color, edgeType, localMatrix);
+            return DashingCircleEffect::Create(color, dashAAMode, localMatrix);
         case kNonRound_DashCap:
-            return DashingLineEffect::Create(color, edgeType, localMatrix);
+            return DashingLineEffect::Create(color, dashAAMode, localMatrix);
         default:
             SkFAIL("Unexpected dashed cap.");
     }
     return NULL;
 }
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef GR_TEST_UTILS
+
+BATCH_TEST_DEFINE(DashBatch) {
+    GrColor color = GrRandomColor(random);
+    SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
+    bool useAA = random->nextBool();
+    bool msaaRT = random->nextBool();
+
+    // We can only dash either horizontal or vertical lines
+    SkPoint pts[2];
+    if (random->nextBool()) {
+        // vertical
+        pts[0].fX = 1.f;
+        pts[0].fY = random->nextF() * 10.f;
+        pts[1].fX = 1.f;
+        pts[1].fY = random->nextF() * 10.f;
+    } else {
+        // horizontal
+        pts[0].fX = random->nextF() * 10.f;
+        pts[0].fY = 1.f;
+        pts[1].fX = random->nextF() * 10.f;
+        pts[1].fY = 1.f;
+    }
+
+    // pick random cap
+    SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::Cap::kCapCount));
+
+    SkScalar intervals[2];
+
+    // We can only dash with the following intervals
+    enum Intervals {
+        kOpenOpen_Intervals ,
+        kOpenClose_Intervals,
+        kCloseOpen_Intervals,
+    };
+
+    Intervals intervalType = SkPaint::kRound_Cap ?
+                             kOpenClose_Intervals :
+                             Intervals(random->nextULessThan(kCloseOpen_Intervals + 1));
+    static const SkScalar kIntervalMin = 0.1f;
+    static const SkScalar kIntervalMax = 10.f;
+    switch (intervalType) {
+        case kOpenOpen_Intervals:
+            intervals[0] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
+            intervals[1] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
+            break;
+        case kOpenClose_Intervals:
+            intervals[0] = 0.f;
+            intervals[1] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
+            break;
+        case kCloseOpen_Intervals:
+            intervals[0] = random->nextRangeScalar(kIntervalMin, kIntervalMax);
+            intervals[1] = 0.f;
+            break;
+
+    }
+
+    // phase is 0 < sum (i0, i1)
+    SkScalar phase = random->nextRangeScalar(0, intervals[0] + intervals[1]);
+
+    SkPaint p;
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SkIntToScalar(1));
+    p.setStrokeCap(cap);
+
+    GrStrokeInfo strokeInfo(p);
+
+    SkPathEffect::DashInfo info;
+    info.fIntervals = intervals;
+    info.fCount = 2;
+    info.fPhase = phase;
+    SkDEBUGCODE(bool success = ) strokeInfo.setDashInfo(info);
+    SkASSERT(success);
+
+    return create_batch(color, viewMatrix, pts, useAA, strokeInfo, msaaRT);
+}
+
+#endif
diff --git a/src/gpu/effects/GrDashingEffect.h b/src/gpu/effects/GrDashingEffect.h
index 26b016b..05b1c90 100644
--- a/src/gpu/effects/GrDashingEffect.h
+++ b/src/gpu/effects/GrDashingEffect.h
@@ -15,15 +15,16 @@
 
 class GrClip;
 class GrDrawTarget;
-class GrGpu;
 class GrPaint;
 class GrPipelineBuilder;
 class GrStrokeInfo;
 
 namespace GrDashingEffect {
-    bool DrawDashLine(GrGpu*, GrDrawTarget*, GrPipelineBuilder*, GrColor,
-                      const SkMatrix& viewMatrix, const SkPoint pts[2], const GrPaint& paint,
+    bool DrawDashLine(GrDrawTarget*, GrPipelineBuilder*, GrColor,
+                      const SkMatrix& viewMatrix, const SkPoint pts[2], bool useAA,
                       const GrStrokeInfo& strokeInfo);
+    bool CanDrawDashLine(const SkPoint pts[2], const GrStrokeInfo& strokeInfo,
+                         const SkMatrix& viewMatrix);
 }
 
 #endif
diff --git a/src/gpu/effects/GrDisableColorXP.cpp b/src/gpu/effects/GrDisableColorXP.cpp
index d97589d..9b513f6 100644
--- a/src/gpu/effects/GrDisableColorXP.cpp
+++ b/src/gpu/effects/GrDisableColorXP.cpp
@@ -21,30 +21,30 @@
         return SkNEW(DisableColorXP);
     }
 
-    ~DisableColorXP() SK_OVERRIDE {};
+    ~DisableColorXP() override {};
 
-    const char* name() const SK_OVERRIDE { return "Disable Color"; }
+    const char* name() const override { return "Disable Color"; }
 
-    GrGLXferProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLXferProcessor* createGLInstance() const override;
 
-    bool hasSecondaryOutput() const SK_OVERRIDE { return false; }
-
-    GrXferProcessor::OptFlags getOptimizations(const GrProcOptInfo& colorPOI,
-                                               const GrProcOptInfo& coveragePOI,
-                                               bool doesStencilWrite,
-                                               GrColor* color,
-                                               const GrDrawTargetCaps& caps) SK_OVERRIDE {
-        return GrXferProcessor::kIgnoreColor_OptFlag | GrXferProcessor::kIgnoreCoverage_OptFlag;
-    }
-
-    void getBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const SK_OVERRIDE;
+    bool hasSecondaryOutput() const override { return false; }
 
 private:
     DisableColorXP();
 
-    void onGetGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const SK_OVERRIDE;
+    GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
+                                                 const GrProcOptInfo& coveragePOI,
+                                                 bool doesStencilWrite,
+                                                 GrColor* color,
+                                                 const GrDrawTargetCaps& caps) override {
+        return GrXferProcessor::kIgnoreColor_OptFlag | GrXferProcessor::kIgnoreCoverage_OptFlag;
+    }
 
-    bool onIsEqual(const GrXferProcessor& xpBase) const SK_OVERRIDE {
+    void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
+
+    void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override;
+
+    bool onIsEqual(const GrXferProcessor& xpBase) const override {
         return true;
     }
 
@@ -57,20 +57,20 @@
 public:
     GLDisableColorXP(const GrProcessor&) {}
 
-    ~GLDisableColorXP() SK_OVERRIDE {}
+    ~GLDisableColorXP() override {}
 
-    static void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*) {}
+    static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) {}
 
 private:
-    void onEmitCode(const EmitArgs& args) SK_OVERRIDE {
+    void onEmitCode(const EmitArgs& args) override {
         // This emit code should be empty. However, on the nexus 6 there is a driver bug where if
         // you do not give gl_FragColor a value, the gl context is lost and we end up drawing
         // nothing. So this fix just sets the gl_FragColor arbitrarily to 0.
-        GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+        GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
         fsBuilder->codeAppendf("%s = vec4(0);", args.fOutputPrimary);
     }
 
-    void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) SK_OVERRIDE {}
+    void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
 
     typedef GrGLXferProcessor INHERITED;
 };
@@ -81,7 +81,7 @@
     this->initClassID<DisableColorXP>();
 }
 
-void DisableColorXP::onGetGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const {
+void DisableColorXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
     GLDisableColorXP::GenKey(*this, caps, b);
 }
 
@@ -89,7 +89,7 @@
     return SkNEW_ARGS(GLDisableColorXP, (*this));
 }
 
-void DisableColorXP::getBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const {
+void DisableColorXP::onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const {
     blendInfo->fWriteColor = false;
 }
 
diff --git a/src/gpu/effects/GrDisableColorXP.h b/src/gpu/effects/GrDisableColorXP.h
index fb855ee..f5fe4fd 100644
--- a/src/gpu/effects/GrDisableColorXP.h
+++ b/src/gpu/effects/GrDisableColorXP.h
@@ -19,14 +19,12 @@
         return SkNEW(GrDisableColorXPFactory);
     }
 
-    bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const SK_OVERRIDE {
+    bool supportsRGBCoverage(GrColor knownColor, uint32_t knownColorFlags) const override {
         return true;
     }
 
-    bool canTweakAlphaForCoverage() const SK_OVERRIDE { return true; }
-
     void getInvariantOutput(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI,
-                            GrXPFactory::InvariantOutput* output) const SK_OVERRIDE {
+                            GrXPFactory::InvariantOutput* output) const override {
         output->fBlendedColorFlags = 0;
         output->fWillBlendWithDst = 0;
     }
@@ -37,15 +35,15 @@
     GrXferProcessor* onCreateXferProcessor(const GrDrawTargetCaps& caps,
                                            const GrProcOptInfo& colorPOI,
                                            const GrProcOptInfo& coveragePOI,
-                                           const GrDeviceCoordTexture* dstCopy) const SK_OVERRIDE;
+                                           const GrDeviceCoordTexture* dstCopy) const override;
 
     bool willReadDstColor(const GrDrawTargetCaps& caps,
                           const GrProcOptInfo& colorPOI,
-                          const GrProcOptInfo& coveragePOI) const SK_OVERRIDE {
+                          const GrProcOptInfo& coveragePOI) const override {
         return false;
     }
 
-    bool onIsEqual(const GrXPFactory& xpfBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrXPFactory& xpfBase) const override {
         return true;
     }
 
diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.cpp b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
new file mode 100755
index 0000000..ba42bb4
--- /dev/null
+++ b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
@@ -0,0 +1,755 @@
+/*
+ * 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 "GrDistanceFieldGeoProc.h"
+#include "GrFontAtlasSizes.h"
+#include "GrInvariantOutput.h"
+#include "GrTexture.h"
+
+#include "SkDistanceFieldGen.h"
+
+#include "gl/GrGLProcessor.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "gl/GrGLGeometryProcessor.h"
+#include "gl/builders/GrGLProgramBuilder.h"
+
+// Assuming a radius of a little less than the diagonal of the fragment
+#define SK_DistanceFieldAAFactor     "0.65"
+
+struct DistanceFieldBatchTracker {
+    GrGPInput fInputColorType;
+    GrColor fColor;
+    bool fUsesLocalCoords;
+};
+
+class GrGLDistanceFieldA8TextGeoProc : public GrGLGeometryProcessor {
+public:
+    GrGLDistanceFieldA8TextGeoProc(const GrGeometryProcessor&,
+                                   const GrBatchTracker&)
+        : fColor(GrColor_ILLEGAL)
+#ifdef SK_GAMMA_APPLY_TO_A8
+        , fDistanceAdjust(-1.0f)
+#endif
+        {}
+
+    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
+        const GrDistanceFieldA8TextGeoProc& dfTexEffect =
+                args.fGP.cast<GrDistanceFieldA8TextGeoProc>();
+        const DistanceFieldBatchTracker& local = args.fBT.cast<DistanceFieldBatchTracker>();
+        GrGLGPBuilder* pb = args.fPB;
+        GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+        SkAssertResult(fsBuilder->enableFeature(
+                GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
+
+        GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
+
+        // emit attributes
+        vsBuilder->emitAttributes(dfTexEffect);
+
+#ifdef SK_GAMMA_APPLY_TO_A8
+        // adjust based on gamma
+        const char* distanceAdjustUniName = NULL;
+        // width, height, 1/(3*width)
+        fDistanceAdjustUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+            kFloat_GrSLType, kDefault_GrSLPrecision,
+            "DistanceAdjust", &distanceAdjustUniName);
+#endif
+
+        // Setup pass through color
+        this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor,
+                                    dfTexEffect.inColor(), &fColorUniform);
+
+        // Setup position
+        this->setupPosition(pb, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix());
+
+        // emit transforms
+        this->emitTransforms(args.fPB, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName,
+                             args.fTransformsIn, args.fTransformsOut);
+
+        // add varyings
+        GrGLVertToFrag recipScale(kFloat_GrSLType);
+        GrGLVertToFrag st(kVec2f_GrSLType);
+        bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag);
+        args.fPB->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision);
+        vsBuilder->codeAppendf("%s = %s;", st.vsOut(), dfTexEffect.inTextureCoords()->fName);
+
+        GrGLVertToFrag uv(kVec2f_GrSLType);
+        args.fPB->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision);
+        // this is only used with text, so our texture bounds always match the glyph atlas
+        vsBuilder->codeAppendf("%s = vec2(" GR_FONT_ATLAS_A8_RECIP_WIDTH ", "
+                               GR_FONT_ATLAS_RECIP_HEIGHT ")*%s;", uv.vsOut(),
+                               dfTexEffect.inTextureCoords()->fName);
+        
+        // Use highp to work around aliasing issues
+        fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision,
+                                                             pb->ctxInfo().standard()));
+        fsBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn());
+
+        fsBuilder->codeAppend("\tfloat texColor = ");
+        fsBuilder->appendTextureLookup(args.fSamplers[0],
+                                       "uv",
+                                       kVec2f_GrSLType);
+        fsBuilder->codeAppend(".r;\n");
+        fsBuilder->codeAppend("\tfloat distance = "
+                       SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");");
+#ifdef SK_GAMMA_APPLY_TO_A8
+        // adjust width based on gamma
+        fsBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
+#endif
+
+        fsBuilder->codeAppend("float afwidth;");
+        if (isSimilarity) {
+            // For uniform scale, we adjust for the effect of the transformation on the distance
+            // by using the length of the gradient of the texture coordinates. We use st coordinates
+            // to ensure we're mapping 1:1 from texel space to pixel space.
+
+            // this gives us a smooth step across approximately one fragment
+            // we use y to work around a Mali400 bug in the x direction
+            fsBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(%s.y));",
+                                       st.fsIn());
+        } else {
+            // For general transforms, to determine the amount of correction we multiply a unit
+            // vector pointing along the SDF gradient direction by the Jacobian of the st coords
+            // (which is the inverse transform for this fragment) and take the length of the result.
+            fsBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));");
+            // the length of the gradient may be 0, so we need to check for this
+            // this also compensates for the Adreno, which likes to drop tiles on division by 0
+            fsBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);");
+            fsBuilder->codeAppend("if (dg_len2 < 0.0001) {");
+            fsBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);");
+            fsBuilder->codeAppend("} else {");
+            fsBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
+            fsBuilder->codeAppend("}");
+
+            fsBuilder->codeAppendf("vec2 Jdx = dFdx(%s);", st.fsIn());
+            fsBuilder->codeAppendf("vec2 Jdy = dFdy(%s);", st.fsIn());
+            fsBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
+            fsBuilder->codeAppend("                 dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
+
+            // this gives us a smooth step across approximately one fragment
+            fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
+        }
+        fsBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);");
+
+        fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
+    }
+
+    virtual void setData(const GrGLProgramDataManager& pdman,
+                         const GrPrimitiveProcessor& proc,
+                         const GrBatchTracker& bt) override {
+#ifdef SK_GAMMA_APPLY_TO_A8
+        const GrDistanceFieldA8TextGeoProc& dfTexEffect = proc.cast<GrDistanceFieldA8TextGeoProc>();
+        float distanceAdjust = dfTexEffect.getDistanceAdjust();
+        if (distanceAdjust != fDistanceAdjust) {
+            pdman.set1f(fDistanceAdjustUni, distanceAdjust);
+            fDistanceAdjust = distanceAdjust;
+        }
+#endif
+
+        const GrDistanceFieldA8TextGeoProc& dfa8gp = proc.cast<GrDistanceFieldA8TextGeoProc>();
+        this->setUniformViewMatrix(pdman, dfa8gp.viewMatrix());
+
+        const DistanceFieldBatchTracker& local = bt.cast<DistanceFieldBatchTracker>();
+        if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
+            GrGLfloat c[4];
+            GrColorToRGBAFloat(local.fColor, c);
+            pdman.set4fv(fColorUniform, 1, c);
+            fColor = local.fColor;
+        }
+    }
+
+    static inline void GenKey(const GrGeometryProcessor& gp,
+                              const GrBatchTracker& bt,
+                              const GrGLSLCaps&,
+                              GrProcessorKeyBuilder* b) {
+        const GrDistanceFieldA8TextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldA8TextGeoProc>();
+        const DistanceFieldBatchTracker& local = bt.cast<DistanceFieldBatchTracker>();
+        uint32_t key = dfTexEffect.getFlags();
+        key |= local.fInputColorType << 16;
+        key |= ComputePosKey(dfTexEffect.viewMatrix()) << 25;
+        b->add32(key);
+    }
+
+private:
+    GrColor       fColor;
+    UniformHandle fColorUniform;
+#ifdef SK_GAMMA_APPLY_TO_A8
+    float         fDistanceAdjust;
+    UniformHandle fDistanceAdjustUni;
+#endif
+
+    typedef GrGLGeometryProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(GrColor color,
+                                                           const SkMatrix& viewMatrix,
+                                                           GrTexture* texture,
+                                                           const GrTextureParams& params,
+#ifdef SK_GAMMA_APPLY_TO_A8
+                                                           float distanceAdjust,
+#endif
+                                                           uint32_t flags)
+    : fColor(color)
+    , fViewMatrix(viewMatrix)
+    , fTextureAccess(texture, params)
+#ifdef SK_GAMMA_APPLY_TO_A8
+    , fDistanceAdjust(distanceAdjust)
+#endif
+    , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
+    , fInColor(NULL) {
+    SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
+    this->initClassID<GrDistanceFieldA8TextGeoProc>();
+    fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
+    if (flags & kColorAttr_DistanceFieldEffectFlag) {
+        fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
+    }
+    fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
+                                                          kVec2s_GrVertexAttribType));
+    this->addTextureAccess(&fTextureAccess);
+}
+
+void GrDistanceFieldA8TextGeoProc::getGLProcessorKey(const GrBatchTracker& bt,
+                                                     const GrGLSLCaps& caps,
+                                                     GrProcessorKeyBuilder* b) const {
+    GrGLDistanceFieldA8TextGeoProc::GenKey(*this, bt, caps, b);
+}
+
+GrGLPrimitiveProcessor*
+GrDistanceFieldA8TextGeoProc::createGLInstance(const GrBatchTracker& bt,
+                                               const GrGLSLCaps&) const {
+    return SkNEW_ARGS(GrGLDistanceFieldA8TextGeoProc, (*this, bt));
+}
+
+void GrDistanceFieldA8TextGeoProc::initBatchTracker(GrBatchTracker* bt,
+                                                    const GrPipelineInfo& init) const {
+    DistanceFieldBatchTracker* local = bt->cast<DistanceFieldBatchTracker>();
+    local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init,
+                                               SkToBool(fInColor));
+    local->fUsesLocalCoords = init.fUsesLocalCoords;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldA8TextGeoProc);
+
+GrGeometryProcessor* GrDistanceFieldA8TextGeoProc::TestCreate(SkRandom* random,
+                                                              GrContext*,
+                                                              const GrDrawTargetCaps&,
+                                                              GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
+                                      GrProcessorUnitTest::kAlphaTextureIdx;
+    static const SkShader::TileMode kTileModes[] = {
+        SkShader::kClamp_TileMode,
+        SkShader::kRepeat_TileMode,
+        SkShader::kMirror_TileMode,
+    };
+    SkShader::TileMode tileModes[] = {
+        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+    };
+    GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
+                                                           GrTextureParams::kNone_FilterMode);
+
+    return GrDistanceFieldA8TextGeoProc::Create(GrRandomColor(random),
+                                                GrTest::TestMatrix(random),
+                                                textures[texIdx], params,
+#ifdef SK_GAMMA_APPLY_TO_A8
+                                                random->nextF(),
+#endif
+                                                random->nextBool() ?
+                                                    kSimilarity_DistanceFieldEffectFlag : 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct DistanceFieldPathBatchTracker {
+    GrGPInput fInputColorType;
+    GrColor fColor;
+    bool fUsesLocalCoords;
+};
+
+class GrGLDistanceFieldPathGeoProc : public GrGLGeometryProcessor {
+public:
+    GrGLDistanceFieldPathGeoProc(const GrGeometryProcessor&,
+                                          const GrBatchTracker&)
+        : fColor(GrColor_ILLEGAL), fTextureSize(SkISize::Make(-1, -1)) {}
+
+    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
+        const GrDistanceFieldPathGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldPathGeoProc>();
+
+        const DistanceFieldPathBatchTracker& local = args.fBT.cast<DistanceFieldPathBatchTracker>();
+        GrGLGPBuilder* pb = args.fPB;
+        GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+        SkAssertResult(fsBuilder->enableFeature(
+                                     GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
+
+        GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
+
+        // emit attributes
+        vsBuilder->emitAttributes(dfTexEffect);
+
+        GrGLVertToFrag v(kVec2f_GrSLType);
+        args.fPB->addVarying("TextureCoords", &v, kHigh_GrSLPrecision);
+
+        // setup pass through color
+        this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor,
+                                    dfTexEffect.inColor(), &fColorUniform);
+
+        vsBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName);
+
+        // Setup position
+        this->setupPosition(pb, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix());
+
+        // emit transforms
+        this->emitTransforms(args.fPB, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName,
+                             args.fTransformsIn, args.fTransformsOut);
+
+        const char* textureSizeUniName = NULL;
+        fTextureSizeUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                              kVec2f_GrSLType, kDefault_GrSLPrecision,
+                                              "TextureSize", &textureSizeUniName);
+
+        // Use highp to work around aliasing issues
+        fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision,
+                                                             pb->ctxInfo().standard()));
+        fsBuilder->codeAppendf("vec2 uv = %s;", v.fsIn());
+
+        fsBuilder->codeAppend("float texColor = ");
+        fsBuilder->appendTextureLookup(args.fSamplers[0],
+                                       "uv",
+                                       kVec2f_GrSLType);
+        fsBuilder->codeAppend(".r;");
+        fsBuilder->codeAppend("float distance = "
+            SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");");
+
+        fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision,
+                                                             pb->ctxInfo().standard()));
+        fsBuilder->codeAppendf("vec2 st = uv*%s;", textureSizeUniName);
+        fsBuilder->codeAppend("float afwidth;");
+        if (dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag) {
+            // For uniform scale, we adjust for the effect of the transformation on the distance
+            // by using the length of the gradient of the texture coordinates. We use st coordinates
+            // to ensure we're mapping 1:1 from texel space to pixel space.
+
+            // this gives us a smooth step across approximately one fragment
+            fsBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(st.y));");
+        } else {
+            // For general transforms, to determine the amount of correction we multiply a unit
+            // vector pointing along the SDF gradient direction by the Jacobian of the st coords
+            // (which is the inverse transform for this fragment) and take the length of the result.
+            fsBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));");
+            // the length of the gradient may be 0, so we need to check for this
+            // this also compensates for the Adreno, which likes to drop tiles on division by 0
+            fsBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);");
+            fsBuilder->codeAppend("if (dg_len2 < 0.0001) {");
+            fsBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);");
+            fsBuilder->codeAppend("} else {");
+            fsBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
+            fsBuilder->codeAppend("}");
+
+            fsBuilder->codeAppend("vec2 Jdx = dFdx(st);");
+            fsBuilder->codeAppend("vec2 Jdy = dFdy(st);");
+            fsBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
+            fsBuilder->codeAppend("                 dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
+
+            // this gives us a smooth step across approximately one fragment
+            fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
+        }
+        fsBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);");
+
+        fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
+    }
+
+    virtual void setData(const GrGLProgramDataManager& pdman,
+                         const GrPrimitiveProcessor& proc,
+                         const GrBatchTracker& bt) override {
+        SkASSERT(fTextureSizeUni.isValid());
+
+        GrTexture* texture = proc.texture(0);
+        if (texture->width() != fTextureSize.width() || 
+            texture->height() != fTextureSize.height()) {
+            fTextureSize = SkISize::Make(texture->width(), texture->height());
+            pdman.set2f(fTextureSizeUni,
+                        SkIntToScalar(fTextureSize.width()),
+                        SkIntToScalar(fTextureSize.height()));
+        }
+
+        const GrDistanceFieldPathGeoProc& dfpgp = proc.cast<GrDistanceFieldPathGeoProc>();
+        this->setUniformViewMatrix(pdman, dfpgp.viewMatrix());
+
+        const DistanceFieldPathBatchTracker& local = bt.cast<DistanceFieldPathBatchTracker>();
+        if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
+            GrGLfloat c[4];
+            GrColorToRGBAFloat(local.fColor, c);
+            pdman.set4fv(fColorUniform, 1, c);
+            fColor = local.fColor;
+        }
+    }
+
+    static inline void GenKey(const GrGeometryProcessor& gp,
+                              const GrBatchTracker& bt,
+                              const GrGLSLCaps&,
+                              GrProcessorKeyBuilder* b) {
+        const GrDistanceFieldPathGeoProc& dfTexEffect = gp.cast<GrDistanceFieldPathGeoProc>();
+
+        const DistanceFieldPathBatchTracker& local = bt.cast<DistanceFieldPathBatchTracker>();
+        uint32_t key = dfTexEffect.getFlags();
+        key |= local.fInputColorType << 16;
+        key |= ComputePosKey(dfTexEffect.viewMatrix()) << 25;
+        b->add32(key);
+    }
+
+private:
+    UniformHandle fColorUniform;
+    UniformHandle fTextureSizeUni;
+    GrColor       fColor;
+    SkISize       fTextureSize;
+
+    typedef GrGLGeometryProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc(
+        GrColor color,
+        const SkMatrix& viewMatrix,
+        GrTexture* texture,
+        const GrTextureParams& params,
+        uint32_t flags)
+    : fColor(color)
+    , fViewMatrix(viewMatrix)
+    , fTextureAccess(texture, params)
+    , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
+    , fInColor(NULL) {
+    SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
+    this->initClassID<GrDistanceFieldPathGeoProc>();
+    fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
+    if (flags & kColorAttr_DistanceFieldEffectFlag) {
+        fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
+    }
+    fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
+                                                          kVec2f_GrVertexAttribType));
+    this->addTextureAccess(&fTextureAccess);
+}
+
+void GrDistanceFieldPathGeoProc::getGLProcessorKey(const GrBatchTracker& bt,
+                                                   const GrGLSLCaps& caps,
+                                                   GrProcessorKeyBuilder* b) const {
+    GrGLDistanceFieldPathGeoProc::GenKey(*this, bt, caps, b);
+}
+
+GrGLPrimitiveProcessor*
+GrDistanceFieldPathGeoProc::createGLInstance(const GrBatchTracker& bt, const GrGLSLCaps&) const {
+    return SkNEW_ARGS(GrGLDistanceFieldPathGeoProc, (*this, bt));
+}
+
+void GrDistanceFieldPathGeoProc::initBatchTracker(GrBatchTracker* bt,
+                                                  const GrPipelineInfo& init) const {
+    DistanceFieldPathBatchTracker* local = bt->cast<DistanceFieldPathBatchTracker>();
+    local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init,
+                                               SkToBool(fInColor));
+    local->fUsesLocalCoords = init.fUsesLocalCoords;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldPathGeoProc);
+
+GrGeometryProcessor* GrDistanceFieldPathGeoProc::TestCreate(SkRandom* random,
+                                                            GrContext*,
+                                                            const GrDrawTargetCaps&,
+                                                            GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx 
+                                    : GrProcessorUnitTest::kAlphaTextureIdx;
+    static const SkShader::TileMode kTileModes[] = {
+        SkShader::kClamp_TileMode,
+        SkShader::kRepeat_TileMode,
+        SkShader::kMirror_TileMode,
+    };
+    SkShader::TileMode tileModes[] = {
+        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+    };
+    GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode 
+                                                         : GrTextureParams::kNone_FilterMode);
+
+    return GrDistanceFieldPathGeoProc::Create(GrRandomColor(random),
+                                              GrTest::TestMatrix(random),
+                                              textures[texIdx],
+                                              params,
+        random->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct DistanceFieldLCDBatchTracker {
+    GrGPInput fInputColorType;
+    GrColor fColor;
+    bool fUsesLocalCoords;
+};
+
+class GrGLDistanceFieldLCDTextGeoProc : public GrGLGeometryProcessor {
+public:
+    GrGLDistanceFieldLCDTextGeoProc(const GrGeometryProcessor&, const GrBatchTracker&)
+        : fColor(GrColor_ILLEGAL) {
+        fDistanceAdjust = GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(1.0f, 1.0f, 1.0f);
+    }
+
+    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
+        const GrDistanceFieldLCDTextGeoProc& dfTexEffect =
+                args.fGP.cast<GrDistanceFieldLCDTextGeoProc>();
+        const DistanceFieldLCDBatchTracker& local = args.fBT.cast<DistanceFieldLCDBatchTracker>();
+        GrGLGPBuilder* pb = args.fPB;
+
+        GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
+
+        // emit attributes
+        vsBuilder->emitAttributes(dfTexEffect);
+
+        // setup pass through color
+        this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
+                                    &fColorUniform);
+
+        // Setup position
+        this->setupPosition(pb, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix());
+
+        // emit transforms
+        this->emitTransforms(args.fPB, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName,
+                             args.fTransformsIn, args.fTransformsOut);
+
+        // set up varyings
+        bool isUniformScale = SkToBool(dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask);
+        GrGLVertToFrag recipScale(kFloat_GrSLType);
+        GrGLVertToFrag st(kVec2f_GrSLType);
+        args.fPB->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision);
+        vsBuilder->codeAppendf("%s = %s;", st.vsOut(), dfTexEffect.inTextureCoords()->fName);
+
+        GrGLVertToFrag uv(kVec2f_GrSLType);
+        args.fPB->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision);
+        // this is only used with text, so our texture bounds always match the glyph atlas
+        vsBuilder->codeAppendf("%s = vec2(" GR_FONT_ATLAS_A8_RECIP_WIDTH ", "
+                               GR_FONT_ATLAS_RECIP_HEIGHT ")*%s;", uv.vsOut(),
+                               dfTexEffect.inTextureCoords()->fName);
+
+        // add frag shader code
+        GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+
+        SkAssertResult(fsBuilder->enableFeature(
+                GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
+
+        // create LCD offset adjusted by inverse of transform
+        // Use highp to work around aliasing issues
+        fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision,
+                                                             pb->ctxInfo().standard()));
+        fsBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn());
+        fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision,
+                                                             pb->ctxInfo().standard()));
+        if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) {
+            fsBuilder->codeAppend("float delta = -" GR_FONT_ATLAS_LCD_DELTA ";\n");
+        } else {
+            fsBuilder->codeAppend("float delta = " GR_FONT_ATLAS_LCD_DELTA ";\n");
+        }
+        if (isUniformScale) {
+            fsBuilder->codeAppendf("float dy = abs(dFdy(%s.y));", st.fsIn());
+            fsBuilder->codeAppend("vec2 offset = vec2(dy*delta, 0.0);");
+        } else {
+            fsBuilder->codeAppendf("vec2 st = %s;\n", st.fsIn());
+
+            fsBuilder->codeAppend("vec2 Jdx = dFdx(st);");
+            fsBuilder->codeAppend("vec2 Jdy = dFdy(st);");
+            fsBuilder->codeAppend("vec2 offset = delta*Jdx;");
+        }
+
+        // green is distance to uv center
+        fsBuilder->codeAppend("\tvec4 texColor = ");
+        fsBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType);
+        fsBuilder->codeAppend(";\n");
+        fsBuilder->codeAppend("\tvec3 distance;\n");
+        fsBuilder->codeAppend("\tdistance.y = texColor.r;\n");
+        // red is distance to left offset
+        fsBuilder->codeAppend("\tvec2 uv_adjusted = uv - offset;\n");
+        fsBuilder->codeAppend("\ttexColor = ");
+        fsBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType);
+        fsBuilder->codeAppend(";\n");
+        fsBuilder->codeAppend("\tdistance.x = texColor.r;\n");
+        // blue is distance to right offset
+        fsBuilder->codeAppend("\tuv_adjusted = uv + offset;\n");
+        fsBuilder->codeAppend("\ttexColor = ");
+        fsBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType);
+        fsBuilder->codeAppend(";\n");
+        fsBuilder->codeAppend("\tdistance.z = texColor.r;\n");
+
+        fsBuilder->codeAppend("\tdistance = "
+           "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"));");
+
+        // adjust width based on gamma
+        const char* distanceAdjustUniName = NULL;
+        fDistanceAdjustUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+            kVec3f_GrSLType, kDefault_GrSLPrecision,
+            "DistanceAdjust", &distanceAdjustUniName);
+        fsBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
+
+        // To be strictly correct, we should compute the anti-aliasing factor separately
+        // for each color component. However, this is only important when using perspective
+        // transformations, and even then using a single factor seems like a reasonable
+        // trade-off between quality and speed.
+        fsBuilder->codeAppend("float afwidth;");
+        if (isUniformScale) {
+            // For uniform scale, we adjust for the effect of the transformation on the distance
+            // by using the length of the gradient of the texture coordinates. We use st coordinates
+            // to ensure we're mapping 1:1 from texel space to pixel space.
+
+            // this gives us a smooth step across approximately one fragment
+            fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*dy;");
+        } else {
+            // For general transforms, to determine the amount of correction we multiply a unit
+            // vector pointing along the SDF gradient direction by the Jacobian of the st coords
+            // (which is the inverse transform for this fragment) and take the length of the result.
+            fsBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance.r), dFdy(distance.r));");
+            // the length of the gradient may be 0, so we need to check for this
+            // this also compensates for the Adreno, which likes to drop tiles on division by 0
+            fsBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);");
+            fsBuilder->codeAppend("if (dg_len2 < 0.0001) {");
+            fsBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);");
+            fsBuilder->codeAppend("} else {");
+            fsBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);");
+            fsBuilder->codeAppend("}");
+            fsBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,");
+            fsBuilder->codeAppend("                 dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);");
+
+            // this gives us a smooth step across approximately one fragment
+            fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
+        }
+
+        fsBuilder->codeAppend(
+                      "vec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);");
+
+        fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
+    }
+
+    virtual void setData(const GrGLProgramDataManager& pdman,
+                         const GrPrimitiveProcessor& processor,
+                         const GrBatchTracker& bt) override {
+        SkASSERT(fDistanceAdjustUni.isValid());
+
+        const GrDistanceFieldLCDTextGeoProc& dfTexEffect =
+                processor.cast<GrDistanceFieldLCDTextGeoProc>();
+        GrDistanceFieldLCDTextGeoProc::DistanceAdjust wa = dfTexEffect.getDistanceAdjust();
+        if (wa != fDistanceAdjust) {
+            pdman.set3f(fDistanceAdjustUni,
+                        wa.fR,
+                        wa.fG,
+                        wa.fB);
+            fDistanceAdjust = wa;
+        }
+
+        this->setUniformViewMatrix(pdman, dfTexEffect.viewMatrix());
+
+        const DistanceFieldLCDBatchTracker& local = bt.cast<DistanceFieldLCDBatchTracker>();
+        if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
+            GrGLfloat c[4];
+            GrColorToRGBAFloat(local.fColor, c);
+            pdman.set4fv(fColorUniform, 1, c);
+            fColor = local.fColor;
+        }
+    }
+
+    static inline void GenKey(const GrGeometryProcessor& gp,
+                              const GrBatchTracker& bt,
+                              const GrGLSLCaps&,
+                              GrProcessorKeyBuilder* b) {
+        const GrDistanceFieldLCDTextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldLCDTextGeoProc>();
+
+        const DistanceFieldLCDBatchTracker& local = bt.cast<DistanceFieldLCDBatchTracker>();
+        uint32_t key = dfTexEffect.getFlags();
+        key |= local.fInputColorType << 16;
+        key |= ComputePosKey(dfTexEffect.viewMatrix()) << 25;
+        b->add32(key);
+    }
+
+private:
+    GrColor                                      fColor;
+    UniformHandle                                fColorUniform;
+    GrDistanceFieldLCDTextGeoProc::DistanceAdjust fDistanceAdjust;
+    UniformHandle                                fDistanceAdjustUni;
+
+    typedef GrGLGeometryProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(
+                                                  GrColor color, const SkMatrix& viewMatrix,
+                                                  GrTexture* texture, const GrTextureParams& params,
+                                                  DistanceAdjust distanceAdjust,
+                                                  uint32_t flags)
+    : fColor(color)
+    , fViewMatrix(viewMatrix)
+    , fTextureAccess(texture, params)
+    , fDistanceAdjust(distanceAdjust)
+    , fFlags(flags & kLCD_DistanceFieldEffectMask){
+    SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag));
+    this->initClassID<GrDistanceFieldLCDTextGeoProc>();
+    fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
+    fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
+                                                          kVec2s_GrVertexAttribType));
+    this->addTextureAccess(&fTextureAccess);
+}
+
+void GrDistanceFieldLCDTextGeoProc::getGLProcessorKey(const GrBatchTracker& bt,
+                                                      const GrGLSLCaps& caps,
+                                                      GrProcessorKeyBuilder* b) const {
+    GrGLDistanceFieldLCDTextGeoProc::GenKey(*this, bt, caps, b);
+}
+
+GrGLPrimitiveProcessor*
+GrDistanceFieldLCDTextGeoProc::createGLInstance(const GrBatchTracker& bt,
+                                                const GrGLSLCaps&) const {
+    return SkNEW_ARGS(GrGLDistanceFieldLCDTextGeoProc, (*this, bt));
+}
+
+void GrDistanceFieldLCDTextGeoProc::initBatchTracker(GrBatchTracker* bt,
+                                                     const GrPipelineInfo& init) const {
+    DistanceFieldLCDBatchTracker* local = bt->cast<DistanceFieldLCDBatchTracker>();
+    local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
+    local->fUsesLocalCoords = init.fUsesLocalCoords;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldLCDTextGeoProc);
+
+GrGeometryProcessor* GrDistanceFieldLCDTextGeoProc::TestCreate(SkRandom* random,
+                                                                 GrContext*,
+                                                                 const GrDrawTargetCaps&,
+                                                                 GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
+                                      GrProcessorUnitTest::kAlphaTextureIdx;
+    static const SkShader::TileMode kTileModes[] = {
+        SkShader::kClamp_TileMode,
+        SkShader::kRepeat_TileMode,
+        SkShader::kMirror_TileMode,
+    };
+    SkShader::TileMode tileModes[] = {
+        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
+    };
+    GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
+                           GrTextureParams::kNone_FilterMode);
+    DistanceAdjust wa = { 0.0f, 0.1f, -0.1f };
+    uint32_t flags = kUseLCD_DistanceFieldEffectFlag;
+    flags |= random->nextBool() ? kUniformScale_DistanceFieldEffectMask : 0;
+    flags |= random->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
+    return GrDistanceFieldLCDTextGeoProc::Create(GrRandomColor(random),
+                                                 GrTest::TestMatrix(random),
+                                                 textures[texIdx], params,
+                                                 wa,
+                                                 flags);
+}
diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.h b/src/gpu/effects/GrDistanceFieldGeoProc.h
new file mode 100644
index 0000000..ec661ce
--- /dev/null
+++ b/src/gpu/effects/GrDistanceFieldGeoProc.h
@@ -0,0 +1,231 @@
+/*
+ * 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 GrDistanceFieldGeoProc_DEFINED
+#define GrDistanceFieldGeoProc_DEFINED
+
+#include "GrProcessor.h"
+#include "GrGeometryProcessor.h"
+
+class GrGLDistanceFieldA8TextGeoProc;
+class GrGLDistanceFieldPathGeoProc;
+class GrGLDistanceFieldLCDTextGeoProc;
+class GrInvariantOutput;
+
+enum GrDistanceFieldEffectFlags {
+    kSimilarity_DistanceFieldEffectFlag = 0x01,   // ctm is similarity matrix
+    kRectToRect_DistanceFieldEffectFlag = 0x02,   // ctm maps rects to rects
+    kUseLCD_DistanceFieldEffectFlag     = 0x04,   // use lcd text
+    kBGR_DistanceFieldEffectFlag        = 0x08,   // lcd display has bgr order
+    kPortrait_DistanceFieldEffectFlag   = 0x10,   // lcd display is in portrait mode (not used yet)
+    kColorAttr_DistanceFieldEffectFlag  = 0x20,   // color vertex attribute
+
+    kInvalid_DistanceFieldEffectFlag    = 0x80,   // invalid state (for initialization)
+    
+    kUniformScale_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag |
+                                            kRectToRect_DistanceFieldEffectFlag,
+    // The subset of the flags relevant to GrDistanceFieldA8TextGeoProc
+    kNonLCD_DistanceFieldEffectMask       = kSimilarity_DistanceFieldEffectFlag |
+                                            kColorAttr_DistanceFieldEffectFlag,
+    // The subset of the flags relevant to GrDistanceFieldLCDTextGeoProc
+    kLCD_DistanceFieldEffectMask          = kSimilarity_DistanceFieldEffectFlag |
+                                            kRectToRect_DistanceFieldEffectFlag |
+                                            kUseLCD_DistanceFieldEffectFlag |
+                                            kBGR_DistanceFieldEffectFlag,
+};
+
+/**
+ * The output color of this effect is a modulation of the input color and a sample from a
+ * distance field texture (using a smoothed step function near 0.5).
+ * It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input
+ * coords are a custom attribute. Gamma correction is handled via a texture LUT.
+ */
+class GrDistanceFieldA8TextGeoProc : public GrGeometryProcessor {
+public:
+#ifdef SK_GAMMA_APPLY_TO_A8
+    static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix,
+                                       GrTexture* tex, const GrTextureParams& params,
+                                       float lum, uint32_t flags) {
+        return SkNEW_ARGS(GrDistanceFieldA8TextGeoProc, (color, viewMatrix, tex, params, lum,
+                                                         flags));
+    }
+#else
+    static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix,
+                                       GrTexture* tex, const GrTextureParams& params,
+                                       uint32_t flags) {
+        return SkNEW_ARGS(GrDistanceFieldA8TextGeoProc, (color, viewMatrix, tex,  params, flags));
+    }
+#endif
+
+    virtual ~GrDistanceFieldA8TextGeoProc() {}
+
+    const char* name() const override { return "DistanceFieldTexture"; }
+
+    const Attribute* inPosition() const { return fInPosition; }
+    const Attribute* inColor() const { return fInColor; }
+    const Attribute* inTextureCoords() const { return fInTextureCoords; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& viewMatrix() const { return fViewMatrix; }
+#ifdef SK_GAMMA_APPLY_TO_A8
+    float getDistanceAdjust() const { return fDistanceAdjust; }
+#endif
+    uint32_t getFlags() const { return fFlags; }
+
+    virtual void getGLProcessorKey(const GrBatchTracker& bt,
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
+
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLSLCaps&) const override;
+
+    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override;
+
+private:
+    GrDistanceFieldA8TextGeoProc(GrColor, const SkMatrix& viewMatrix,
+                                 GrTexture* texture, const GrTextureParams& params,
+#ifdef SK_GAMMA_APPLY_TO_A8
+                                 float distanceAdjust,
+#endif
+                                 uint32_t flags);
+
+    GrColor          fColor;
+    SkMatrix         fViewMatrix;
+    GrTextureAccess  fTextureAccess;
+#ifdef SK_GAMMA_APPLY_TO_A8
+    float            fDistanceAdjust;
+#endif
+    uint32_t         fFlags;
+    const Attribute* fInPosition;
+    const Attribute* fInColor;
+    const Attribute* fInTextureCoords;
+
+    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
+
+    typedef GrGeometryProcessor INHERITED;
+};
+
+
+/**
+* The output color of this effect is a modulation of the input color and a sample from a
+* distance field texture (using a smoothed step function near 0.5).
+* It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input
+* coords are a custom attribute. No gamma correct blending is applied. Used for paths only.
+*/
+class GrDistanceFieldPathGeoProc : public GrGeometryProcessor {
+public:
+    static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, GrTexture* tex,
+                                       const GrTextureParams& params,
+                                       uint32_t flags) {
+        return SkNEW_ARGS(GrDistanceFieldPathGeoProc, (color, viewMatrix, tex, params, flags));
+    }
+
+    virtual ~GrDistanceFieldPathGeoProc() {}
+
+    const char* name() const override { return "DistanceFieldTexture"; }
+
+    const Attribute* inPosition() const { return fInPosition; }
+    const Attribute* inColor() const { return fInColor; }
+    const Attribute* inTextureCoords() const { return fInTextureCoords; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& viewMatrix() const { return fViewMatrix; }
+    uint32_t getFlags() const { return fFlags; }
+
+    virtual void getGLProcessorKey(const GrBatchTracker& bt,
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
+
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLSLCaps&) const override;
+
+    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override;
+
+private:
+    GrDistanceFieldPathGeoProc(GrColor, const SkMatrix& viewMatrix, GrTexture* texture,
+                               const GrTextureParams& params, uint32_t flags);
+
+    GrColor          fColor;
+    SkMatrix         fViewMatrix;
+    GrTextureAccess  fTextureAccess;
+    uint32_t         fFlags;
+    const Attribute* fInPosition;
+    const Attribute* fInColor;
+    const Attribute* fInTextureCoords;
+
+    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
+
+    typedef GrGeometryProcessor INHERITED;
+};
+
+/**
+ * The output color of this effect is a modulation of the input color and samples from a
+ * distance field texture (using a smoothed step function near 0.5), adjusted for LCD displays.
+ * It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input
+ * coords are a custom attribute. Gamma correction is handled via a texture LUT.
+ */
+class GrDistanceFieldLCDTextGeoProc : public GrGeometryProcessor {
+public:
+    struct DistanceAdjust {
+        SkScalar fR, fG, fB;
+        static DistanceAdjust Make(SkScalar r, SkScalar g, SkScalar b) {
+            DistanceAdjust result;
+            result.fR = r; result.fG = g; result.fB = b;
+            return result;
+        }
+        bool operator==(const DistanceAdjust& wa) const {
+            return (fR == wa.fR && fG == wa.fG && fB == wa.fB);
+        }
+        bool operator!=(const DistanceAdjust& wa) const {
+            return !(*this == wa);
+        }
+    };
+
+    static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix,
+                                       GrTexture* tex, const GrTextureParams& params,
+                                       DistanceAdjust distanceAdjust, uint32_t flags) {
+        return SkNEW_ARGS(GrDistanceFieldLCDTextGeoProc,
+                          (color, viewMatrix, tex, params, distanceAdjust, flags));
+    }
+
+    virtual ~GrDistanceFieldLCDTextGeoProc() {}
+
+    const char* name() const override { return "DistanceFieldLCDTexture"; }
+
+    const Attribute* inPosition() const { return fInPosition; }
+    const Attribute* inTextureCoords() const { return fInTextureCoords; }
+    DistanceAdjust getDistanceAdjust() const { return fDistanceAdjust; }
+    GrColor color() const { return fColor; }
+    const SkMatrix& viewMatrix() const { return fViewMatrix; }
+    uint32_t getFlags() const { return fFlags; }
+
+    virtual void getGLProcessorKey(const GrBatchTracker& bt,
+                                   const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override;
+
+    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
+                                                     const GrGLSLCaps&) const override;
+
+    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override;
+
+private:
+    GrDistanceFieldLCDTextGeoProc(GrColor, const SkMatrix& viewMatrix,
+                                  GrTexture* texture, const GrTextureParams& params,
+                                  DistanceAdjust wa, uint32_t flags);
+
+    GrColor          fColor;
+    SkMatrix         fViewMatrix;
+    GrTextureAccess  fTextureAccess;
+    DistanceAdjust   fDistanceAdjust;
+    uint32_t         fFlags;
+    const Attribute* fInPosition;
+    const Attribute* fInTextureCoords;
+
+    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
+
+    typedef GrGeometryProcessor INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
deleted file mode 100755
index 89a634c..0000000
--- a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
+++ /dev/null
@@ -1,865 +0,0 @@
-/*
- * 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 "GrDistanceFieldTextureEffect.h"
-#include "GrFontAtlasSizes.h"
-#include "GrInvariantOutput.h"
-#include "GrTexture.h"
-#include "SkDistanceFieldGen.h"
-#include "gl/GrGLProcessor.h"
-#include "gl/GrGLSL.h"
-#include "gl/GrGLTexture.h"
-#include "gl/GrGLGeometryProcessor.h"
-#include "gl/builders/GrGLProgramBuilder.h"
-
-// Assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2
-#define SK_DistanceFieldAAFactor     "0.7071"
-
-struct DistanceFieldBatchTracker {
-    GrGPInput fInputColorType;
-    GrColor fColor;
-    bool fUsesLocalCoords;
-};
-
-class GrGLDistanceFieldTextureEffect : public GrGLGeometryProcessor {
-public:
-    GrGLDistanceFieldTextureEffect(const GrGeometryProcessor&,
-                                   const GrBatchTracker&)
-        : fColor(GrColor_ILLEGAL)
-#ifdef SK_GAMMA_APPLY_TO_A8
-        , fLuminance(-1.0f)
-#endif
-        {}
-
-    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
-        const GrDistanceFieldTextureEffect& dfTexEffect =
-                args.fGP.cast<GrDistanceFieldTextureEffect>();
-        const DistanceFieldBatchTracker& local = args.fBT.cast<DistanceFieldBatchTracker>();
-        GrGLGPBuilder* pb = args.fPB;
-        GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
-        SkAssertResult(fsBuilder->enableFeature(
-                GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
-
-        GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
-
-        // emit attributes
-        vsBuilder->emitAttributes(dfTexEffect);
-
-        GrGLVertToFrag st(kVec2f_GrSLType);
-        args.fPB->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision);
-        vsBuilder->codeAppendf("%s = %s;", st.vsOut(), dfTexEffect.inTextureCoords()->fName);
-        
-        GrGLVertToFrag uv(kVec2f_GrSLType);
-        args.fPB->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision);
-        // this is only used with text, so our texture bounds always match the glyph atlas
-        vsBuilder->codeAppendf("%s = vec2(" GR_FONT_ATLAS_A8_RECIP_WIDTH ", "
-                               GR_FONT_ATLAS_RECIP_HEIGHT ")*%s;", uv.vsOut(),
-                               dfTexEffect.inTextureCoords()->fName);
-
-        // Setup pass through color
-        this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor,
-                                    dfTexEffect.inColor(), &fColorUniform);
-
-        // Setup position
-        this->setupPosition(pb, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix());
-
-        // emit transforms
-        this->emitTransforms(args.fPB, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName,
-                             dfTexEffect.localMatrix(), args.fTransformsIn, args.fTransformsOut);
-
-        // Use highp to work around aliasing issues
-        fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision,
-                                                             pb->ctxInfo().standard()));
-        fsBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn());
-
-        fsBuilder->codeAppend("\tfloat texColor = ");
-        fsBuilder->appendTextureLookup(args.fSamplers[0],
-                                       "uv",
-                                       kVec2f_GrSLType);
-        fsBuilder->codeAppend(".r;\n");
-        fsBuilder->codeAppend("\tfloat distance = "
-                       SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");");
-
-        // we adjust for the effect of the transformation on the distance by using
-        // the length of the gradient of the texture coordinates. We use st coordinates
-        // to ensure we're mapping 1:1 from texel space to pixel space.
-        fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision,
-                                                             pb->ctxInfo().standard()));
-        fsBuilder->codeAppendf("vec2 st = %s;\n", st.fsIn());
-        fsBuilder->codeAppend("\tfloat afwidth;\n");
-        if (dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag) {
-            // this gives us a smooth step across approximately one fragment
-            fsBuilder->codeAppend("\tafwidth = abs(" SK_DistanceFieldAAFactor "*dFdx(st.x));\n");
-        } else {
-            fsBuilder->codeAppend("\tvec2 Jdx = dFdx(st);\n");
-            fsBuilder->codeAppend("\tvec2 Jdy = dFdy(st);\n");
-
-            fsBuilder->codeAppend("\tvec2 uv_grad;\n");
-            if (args.fPB->ctxInfo().caps()->dropsTileOnZeroDivide()) {
-                // this is to compensate for the Adreno, which likes to drop tiles on division by 0
-                fsBuilder->codeAppend("\tfloat uv_len2 = dot(uv, uv);\n");
-                fsBuilder->codeAppend("\tif (uv_len2 < 0.0001) {\n");
-                fsBuilder->codeAppend("\t\tuv_grad = vec2(0.7071, 0.7071);\n");
-                fsBuilder->codeAppend("\t} else {\n");
-                fsBuilder->codeAppend("\t\tuv_grad = uv*inversesqrt(uv_len2);\n");
-                fsBuilder->codeAppend("\t}\n");
-            } else {
-                fsBuilder->codeAppend("\tuv_grad = normalize(uv);\n");
-            }
-            fsBuilder->codeAppend("\tvec2 grad = vec2(uv_grad.x*Jdx.x + uv_grad.y*Jdy.x,\n");
-            fsBuilder->codeAppend("\t                 uv_grad.x*Jdx.y + uv_grad.y*Jdy.y);\n");
-
-            // this gives us a smooth step across approximately one fragment
-            fsBuilder->codeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*length(grad);\n");
-        }
-        fsBuilder->codeAppend("\tfloat val = smoothstep(-afwidth, afwidth, distance);\n");
-
-#ifdef SK_GAMMA_APPLY_TO_A8
-        // adjust based on gamma
-        const char* luminanceUniName = NULL;
-        // width, height, 1/(3*width)
-        fLuminanceUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
-                                             kFloat_GrSLType, kDefault_GrSLPrecision,
-                                             "Luminance", &luminanceUniName);
-
-        fsBuilder->codeAppendf("\tuv = vec2(val, %s);\n", luminanceUniName);
-        fsBuilder->codeAppend("\tvec4 gammaColor = ");
-        fsBuilder->appendTextureLookup(args.fSamplers[1], "uv", kVec2f_GrSLType);
-        fsBuilder->codeAppend(";\n");
-        fsBuilder->codeAppend("\tval = gammaColor.r;\n");
-#endif
-
-        fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
-    }
-
-    virtual void setData(const GrGLProgramDataManager& pdman,
-                         const GrPrimitiveProcessor& proc,
-                         const GrBatchTracker& bt) SK_OVERRIDE {
-#ifdef SK_GAMMA_APPLY_TO_A8
-        const GrDistanceFieldTextureEffect& dfTexEffect =
-                proc.cast<GrDistanceFieldTextureEffect>();
-        float luminance = dfTexEffect.getLuminance();
-        if (luminance != fLuminance) {
-            pdman.set1f(fLuminanceUni, luminance);
-            fLuminance = luminance;
-        }
-#endif
-
-        this->setUniformViewMatrix(pdman, proc.viewMatrix());
-
-        const DistanceFieldBatchTracker& local = bt.cast<DistanceFieldBatchTracker>();
-        if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
-            GrGLfloat c[4];
-            GrColorToRGBAFloat(local.fColor, c);
-            pdman.set4fv(fColorUniform, 1, c);
-            fColor = local.fColor;
-        }
-    }
-
-    static inline void GenKey(const GrGeometryProcessor& gp,
-                              const GrBatchTracker& bt,
-                              const GrGLCaps&,
-                              GrProcessorKeyBuilder* b) {
-        const GrDistanceFieldTextureEffect& dfTexEffect = gp.cast<GrDistanceFieldTextureEffect>();
-        const DistanceFieldBatchTracker& local = bt.cast<DistanceFieldBatchTracker>();
-        uint32_t key = dfTexEffect.getFlags();
-        key |= local.fInputColorType << 16;
-        key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 24: 0x0;
-        key |= ComputePosKey(gp.viewMatrix()) << 25;
-        b->add32(key);
-    }
-
-private:
-    GrColor       fColor;
-    UniformHandle fColorUniform;
-#ifdef SK_GAMMA_APPLY_TO_A8
-    UniformHandle fLuminanceUni;
-    float         fLuminance;
-#endif
-
-    typedef GrGLGeometryProcessor INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-GrDistanceFieldTextureEffect::GrDistanceFieldTextureEffect(GrColor color,
-                                                           const SkMatrix& viewMatrix,
-                                                           GrTexture* texture,
-                                                           const GrTextureParams& params,
-#ifdef SK_GAMMA_APPLY_TO_A8
-                                                           GrTexture* gamma,
-                                                           const GrTextureParams& gammaParams,
-                                                           float luminance,
-#endif
-                                                           uint32_t flags, bool opaqueVertexColors)
-    : INHERITED(color, viewMatrix, SkMatrix::I(), opaqueVertexColors)
-    , fTextureAccess(texture, params)
-#ifdef SK_GAMMA_APPLY_TO_A8
-    , fGammaTextureAccess(gamma, gammaParams)
-    , fLuminance(luminance)
-#endif
-    , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
-    , fInColor(NULL) {
-    SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
-    this->initClassID<GrDistanceFieldTextureEffect>();
-    fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
-    if (flags & kColorAttr_DistanceFieldEffectFlag) {
-        fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
-        this->setHasVertexColor();
-    }
-    fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
-                                                          kVec2s_GrVertexAttribType));
-    this->addTextureAccess(&fTextureAccess);
-#ifdef SK_GAMMA_APPLY_TO_A8
-    this->addTextureAccess(&fGammaTextureAccess);
-#endif
-}
-
-bool GrDistanceFieldTextureEffect::onIsEqual(const GrGeometryProcessor& other) const {
-    const GrDistanceFieldTextureEffect& cte = other.cast<GrDistanceFieldTextureEffect>();
-    return
-#ifdef SK_GAMMA_APPLY_TO_A8
-           fLuminance == cte.fLuminance &&
-#endif
-           fFlags == cte.fFlags;
-}
-
-void GrDistanceFieldTextureEffect::onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const {
-    out->setUnknownSingleComponent();
-}
-
-void GrDistanceFieldTextureEffect::getGLProcessorKey(const GrBatchTracker& bt,
-                                                     const GrGLCaps& caps,
-                                                     GrProcessorKeyBuilder* b) const {
-    GrGLDistanceFieldTextureEffect::GenKey(*this, bt, caps, b);
-}
-
-GrGLPrimitiveProcessor*
-GrDistanceFieldTextureEffect::createGLInstance(const GrBatchTracker& bt,
-                                               const GrGLCaps&) const {
-    return SkNEW_ARGS(GrGLDistanceFieldTextureEffect, (*this, bt));
-}
-
-void GrDistanceFieldTextureEffect::initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const {
-    DistanceFieldBatchTracker* local = bt->cast<DistanceFieldBatchTracker>();
-    local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init,
-                                               SkToBool(fInColor));
-    local->fUsesLocalCoords = init.fUsesLocalCoords;
-}
-
-bool GrDistanceFieldTextureEffect::onCanMakeEqual(const GrBatchTracker& m,
-                                                  const GrGeometryProcessor& that,
-                                                  const GrBatchTracker& t) const {
-    const DistanceFieldBatchTracker& mine = m.cast<DistanceFieldBatchTracker>();
-    const DistanceFieldBatchTracker& theirs = t.cast<DistanceFieldBatchTracker>();
-    return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                   that, theirs.fUsesLocalCoords) &&
-           CanCombineOutput(mine.fInputColorType, mine.fColor,
-                            theirs.fInputColorType, theirs.fColor);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldTextureEffect);
-
-GrGeometryProcessor* GrDistanceFieldTextureEffect::TestCreate(SkRandom* random,
-                                                              GrContext*,
-                                                              const GrDrawTargetCaps&,
-                                                              GrTexture* textures[]) {
-    int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
-                                      GrProcessorUnitTest::kAlphaTextureIdx;
-#ifdef SK_GAMMA_APPLY_TO_A8
-    int texIdx2 = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
-                                       GrProcessorUnitTest::kAlphaTextureIdx;
-#endif
-    static const SkShader::TileMode kTileModes[] = {
-        SkShader::kClamp_TileMode,
-        SkShader::kRepeat_TileMode,
-        SkShader::kMirror_TileMode,
-    };
-    SkShader::TileMode tileModes[] = {
-        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
-        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
-    };
-    GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
-                                                           GrTextureParams::kNone_FilterMode);
-#ifdef SK_GAMMA_APPLY_TO_A8
-    GrTextureParams params2(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
-                                                            GrTextureParams::kNone_FilterMode);
-#endif
-
-    return GrDistanceFieldTextureEffect::Create(GrRandomColor(random),
-                                                GrProcessorUnitTest::TestMatrix(random),
-                                                textures[texIdx], params,
-#ifdef SK_GAMMA_APPLY_TO_A8
-                                                textures[texIdx2], params2,
-                                                random->nextF(),
-#endif
-                                                random->nextBool() ?
-                                                    kSimilarity_DistanceFieldEffectFlag : 0,
-                                                random->nextBool());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct DistanceFieldNoGammaBatchTracker {
-    GrGPInput fInputColorType;
-    GrColor fColor;
-    bool fUsesLocalCoords;
-};
-
-class GrGLDistanceFieldNoGammaTextureEffect : public GrGLGeometryProcessor {
-public:
-    GrGLDistanceFieldNoGammaTextureEffect(const GrGeometryProcessor&,
-                                          const GrBatchTracker&)
-        : fColor(GrColor_ILLEGAL), fTextureSize(SkISize::Make(-1, -1)) {}
-
-    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
-        const GrDistanceFieldNoGammaTextureEffect& dfTexEffect =
-                args.fGP.cast<GrDistanceFieldNoGammaTextureEffect>();
-
-        const DistanceFieldNoGammaBatchTracker& local =
-                args.fBT.cast<DistanceFieldNoGammaBatchTracker>();
-        GrGLGPBuilder* pb = args.fPB;
-        GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
-        SkAssertResult(fsBuilder->enableFeature(
-                                     GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
-
-        GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
-
-        // emit attributes
-        vsBuilder->emitAttributes(dfTexEffect);
-
-        GrGLVertToFrag v(kVec2f_GrSLType);
-        args.fPB->addVarying("TextureCoords", &v, kHigh_GrSLPrecision);
-
-        // setup pass through color
-        this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor,
-                                    dfTexEffect.inColor(), &fColorUniform);
-
-        vsBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName);
-
-        // Setup position
-        this->setupPosition(pb, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix());
-
-        // emit transforms
-        this->emitTransforms(args.fPB, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName,
-                             dfTexEffect.localMatrix(), args.fTransformsIn, args.fTransformsOut);
-
-        const char* textureSizeUniName = NULL;
-        fTextureSizeUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
-                                              kVec2f_GrSLType, kDefault_GrSLPrecision,
-                                              "TextureSize", &textureSizeUniName);
-
-        // Use highp to work around aliasing issues
-        fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision,
-                                                             pb->ctxInfo().standard()));
-        fsBuilder->codeAppendf("vec2 uv = %s;", v.fsIn());
-
-        fsBuilder->codeAppend("float texColor = ");
-        fsBuilder->appendTextureLookup(args.fSamplers[0],
-                                       "uv",
-                                       kVec2f_GrSLType);
-        fsBuilder->codeAppend(".r;");
-        fsBuilder->codeAppend("float distance = "
-            SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");");
-
-        // we adjust for the effect of the transformation on the distance by using
-        // the length of the gradient of the texture coordinates. We use st coordinates
-        // to ensure we're mapping 1:1 from texel space to pixel space.
-        fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision,
-                                                             pb->ctxInfo().standard()));
-        fsBuilder->codeAppendf("vec2 st = uv*%s;", textureSizeUniName);
-        fsBuilder->codeAppend("float afwidth;");
-        if (dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag) {
-            // this gives us a smooth step across approximately one fragment
-            fsBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdx(st.x));");
-        } else {
-            fsBuilder->codeAppend("vec2 Jdx = dFdx(st);");
-            fsBuilder->codeAppend("vec2 Jdy = dFdy(st);");
-
-            fsBuilder->codeAppend("vec2 uv_grad;");
-            if (args.fPB->ctxInfo().caps()->dropsTileOnZeroDivide()) {
-                // this is to compensate for the Adreno, which likes to drop tiles on division by 0
-                fsBuilder->codeAppend("float uv_len2 = dot(uv, uv);");
-                fsBuilder->codeAppend("if (uv_len2 < 0.0001) {");
-                fsBuilder->codeAppend("uv_grad = vec2(0.7071, 0.7071);");
-                fsBuilder->codeAppend("} else {");
-                fsBuilder->codeAppend("uv_grad = uv*inversesqrt(uv_len2);");
-                fsBuilder->codeAppend("}");
-            } else {
-                fsBuilder->codeAppend("uv_grad = normalize(uv);");
-            }
-            fsBuilder->codeAppend("vec2 grad = vec2(uv_grad.x*Jdx.x + uv_grad.y*Jdy.x,");
-            fsBuilder->codeAppend("                 uv_grad.x*Jdx.y + uv_grad.y*Jdy.y);");
-
-            // this gives us a smooth step across approximately one fragment
-            fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
-        }
-        fsBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);");
-
-        fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
-    }
-
-    virtual void setData(const GrGLProgramDataManager& pdman,
-                         const GrPrimitiveProcessor& proc,
-                         const GrBatchTracker& bt) SK_OVERRIDE {
-        SkASSERT(fTextureSizeUni.isValid());
-
-        GrTexture* texture = proc.texture(0);
-        if (texture->width() != fTextureSize.width() || 
-            texture->height() != fTextureSize.height()) {
-            fTextureSize = SkISize::Make(texture->width(), texture->height());
-            pdman.set2f(fTextureSizeUni,
-                        SkIntToScalar(fTextureSize.width()),
-                        SkIntToScalar(fTextureSize.height()));
-        }
-
-        this->setUniformViewMatrix(pdman, proc.viewMatrix());
-
-        const DistanceFieldNoGammaBatchTracker& local = bt.cast<DistanceFieldNoGammaBatchTracker>();
-        if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
-            GrGLfloat c[4];
-            GrColorToRGBAFloat(local.fColor, c);
-            pdman.set4fv(fColorUniform, 1, c);
-            fColor = local.fColor;
-        }
-    }
-
-    static inline void GenKey(const GrGeometryProcessor& gp,
-                              const GrBatchTracker& bt,
-                              const GrGLCaps&,
-                              GrProcessorKeyBuilder* b) {
-        const GrDistanceFieldNoGammaTextureEffect& dfTexEffect =
-            gp.cast<GrDistanceFieldNoGammaTextureEffect>();
-
-        const DistanceFieldNoGammaBatchTracker& local = bt.cast<DistanceFieldNoGammaBatchTracker>();
-        uint32_t key = dfTexEffect.getFlags();
-        key |= local.fInputColorType << 16;
-        key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 24: 0x0;
-        key |= ComputePosKey(gp.viewMatrix()) << 25;
-        b->add32(key);
-    }
-
-private:
-    UniformHandle fColorUniform;
-    UniformHandle fTextureSizeUni;
-    GrColor       fColor;
-    SkISize       fTextureSize;
-
-    typedef GrGLGeometryProcessor INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-GrDistanceFieldNoGammaTextureEffect::GrDistanceFieldNoGammaTextureEffect(
-        GrColor color,
-        const SkMatrix& viewMatrix,
-        GrTexture* texture,
-        const GrTextureParams& params,
-        uint32_t flags,
-        bool opaqueVertexColors)
-    : INHERITED(color, viewMatrix, SkMatrix::I(), opaqueVertexColors)
-    , fTextureAccess(texture, params)
-    , fFlags(flags & kNonLCD_DistanceFieldEffectMask)
-    , fInColor(NULL) {
-    SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
-    this->initClassID<GrDistanceFieldNoGammaTextureEffect>();
-    fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
-    if (flags & kColorAttr_DistanceFieldEffectFlag) {
-        fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
-        this->setHasVertexColor();
-    }
-    fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
-                                                          kVec2f_GrVertexAttribType));
-    this->addTextureAccess(&fTextureAccess);
-}
-
-bool GrDistanceFieldNoGammaTextureEffect::onIsEqual(const GrGeometryProcessor& other) const {
-    const GrDistanceFieldNoGammaTextureEffect& cte = 
-                                                 other.cast<GrDistanceFieldNoGammaTextureEffect>();
-    return fFlags == cte.fFlags;
-}
-
-void GrDistanceFieldNoGammaTextureEffect::onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const{
-    out->setUnknownSingleComponent();
-}
-
-void GrDistanceFieldNoGammaTextureEffect::getGLProcessorKey(const GrBatchTracker& bt,
-                                                            const GrGLCaps& caps,
-                                                            GrProcessorKeyBuilder* b) const {
-    GrGLDistanceFieldNoGammaTextureEffect::GenKey(*this, bt, caps, b);
-}
-
-GrGLPrimitiveProcessor*
-GrDistanceFieldNoGammaTextureEffect::createGLInstance(const GrBatchTracker& bt,
-                                                      const GrGLCaps&) const {
-    return SkNEW_ARGS(GrGLDistanceFieldNoGammaTextureEffect, (*this, bt));
-}
-
-void GrDistanceFieldNoGammaTextureEffect::initBatchTracker(GrBatchTracker* bt,
-                                                           const GrPipelineInfo& init) const {
-    DistanceFieldNoGammaBatchTracker* local = bt->cast<DistanceFieldNoGammaBatchTracker>();
-    local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init,
-                                               SkToBool(fInColor));
-    local->fUsesLocalCoords = init.fUsesLocalCoords;
-}
-
-bool GrDistanceFieldNoGammaTextureEffect::onCanMakeEqual(const GrBatchTracker& m,
-                                                         const GrGeometryProcessor& that,
-                                                         const GrBatchTracker& t) const {
-    const DistanceFieldNoGammaBatchTracker& mine = m.cast<DistanceFieldNoGammaBatchTracker>();
-    const DistanceFieldNoGammaBatchTracker& theirs = t.cast<DistanceFieldNoGammaBatchTracker>();
-    return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                   that, theirs.fUsesLocalCoords) &&
-           CanCombineOutput(mine.fInputColorType, mine.fColor,
-                            theirs.fInputColorType, theirs.fColor);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldNoGammaTextureEffect);
-
-GrGeometryProcessor* GrDistanceFieldNoGammaTextureEffect::TestCreate(SkRandom* random,
-                                                                     GrContext*,
-                                                                     const GrDrawTargetCaps&,
-                                                                     GrTexture* textures[]) {
-    int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx 
-                                    : GrProcessorUnitTest::kAlphaTextureIdx;
-    static const SkShader::TileMode kTileModes[] = {
-        SkShader::kClamp_TileMode,
-        SkShader::kRepeat_TileMode,
-        SkShader::kMirror_TileMode,
-    };
-    SkShader::TileMode tileModes[] = {
-        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
-        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
-    };
-    GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode 
-                                                         : GrTextureParams::kNone_FilterMode);
-
-    return GrDistanceFieldNoGammaTextureEffect::Create(GrRandomColor(random),
-                                                       GrProcessorUnitTest::TestMatrix(random),
-                                                       textures[texIdx],
-                                                       params,
-        random->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0, random->nextBool());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct DistanceFieldLCDBatchTracker {
-    GrGPInput fInputColorType;
-    GrColor fColor;
-    bool fUsesLocalCoords;
-};
-
-class GrGLDistanceFieldLCDTextureEffect : public GrGLGeometryProcessor {
-public:
-    GrGLDistanceFieldLCDTextureEffect(const GrGeometryProcessor&,
-                                      const GrBatchTracker&)
-    : fColor(GrColor_ILLEGAL)
-    , fTextColor(GrColor_ILLEGAL) {}
-
-    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) SK_OVERRIDE{
-        const GrDistanceFieldLCDTextureEffect& dfTexEffect =
-                args.fGP.cast<GrDistanceFieldLCDTextureEffect>();
-        const DistanceFieldLCDBatchTracker& local = args.fBT.cast<DistanceFieldLCDBatchTracker>();
-        GrGLGPBuilder* pb = args.fPB;
-
-        GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
-
-        // emit attributes
-        vsBuilder->emitAttributes(dfTexEffect);
-
-        GrGLVertToFrag st(kVec2f_GrSLType);
-        args.fPB->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision);
-        vsBuilder->codeAppendf("%s = %s;", st.vsOut(), dfTexEffect.inTextureCoords()->fName);
-        
-        GrGLVertToFrag uv(kVec2f_GrSLType);
-        args.fPB->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision);
-        // this is only used with text, so our texture bounds always match the glyph atlas
-        vsBuilder->codeAppendf("%s = vec2(" GR_FONT_ATLAS_A8_RECIP_WIDTH ", "
-                               GR_FONT_ATLAS_RECIP_HEIGHT ")*%s;", uv.vsOut(),
-                               dfTexEffect.inTextureCoords()->fName);
-        
-        // setup pass through color
-        this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
-                                    &fColorUniform);
-
-        // Setup position
-        this->setupPosition(pb, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix());
-
-        // emit transforms
-        this->emitTransforms(args.fPB, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName,
-                             dfTexEffect.localMatrix(), args.fTransformsIn, args.fTransformsOut);
-
-        GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
-
-        SkAssertResult(fsBuilder->enableFeature(
-                GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
-
-        // create LCD offset adjusted by inverse of transform
-        // Use highp to work around aliasing issues
-        fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision,
-                                                             pb->ctxInfo().standard()));
-        fsBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn());
-        fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision,
-                                                             pb->ctxInfo().standard()));
-        fsBuilder->codeAppendf("vec2 st = %s;\n", st.fsIn());
-        bool isUniformScale = !!(dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask);
-        
-        if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) {
-            fsBuilder->codeAppend("float delta = -" GR_FONT_ATLAS_LCD_DELTA ";\n");
-        } else {
-            fsBuilder->codeAppend("float delta = " GR_FONT_ATLAS_LCD_DELTA ";\n");
-        }
-        if (isUniformScale) {
-            fsBuilder->codeAppend("\tfloat dx = dFdx(st.x);\n");
-            fsBuilder->codeAppend("\tvec2 offset = vec2(dx*delta, 0.0);\n");
-        } else {
-            fsBuilder->codeAppend("\tvec2 Jdx = dFdx(st);\n");
-            fsBuilder->codeAppend("\tvec2 Jdy = dFdy(st);\n");
-            fsBuilder->codeAppend("\tvec2 offset = delta*Jdx;\n");
-        }
-
-        // green is distance to uv center
-        fsBuilder->codeAppend("\tvec4 texColor = ");
-        fsBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType);
-        fsBuilder->codeAppend(";\n");
-        fsBuilder->codeAppend("\tvec3 distance;\n");
-        fsBuilder->codeAppend("\tdistance.y = texColor.r;\n");
-        // red is distance to left offset
-        fsBuilder->codeAppend("\tvec2 uv_adjusted = uv - offset;\n");
-        fsBuilder->codeAppend("\ttexColor = ");
-        fsBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType);
-        fsBuilder->codeAppend(";\n");
-        fsBuilder->codeAppend("\tdistance.x = texColor.r;\n");
-        // blue is distance to right offset
-        fsBuilder->codeAppend("\tuv_adjusted = uv + offset;\n");
-        fsBuilder->codeAppend("\ttexColor = ");
-        fsBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType);
-        fsBuilder->codeAppend(";\n");
-        fsBuilder->codeAppend("\tdistance.z = texColor.r;\n");
-
-        fsBuilder->codeAppend("\tdistance = "
-           "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"));");
-
-        // we adjust for the effect of the transformation on the distance by using
-        // the length of the gradient of the texture coordinates. We use st coordinates
-        // to ensure we're mapping 1:1 from texel space to pixel space.
-
-        // To be strictly correct, we should compute the anti-aliasing factor separately
-        // for each color component. However, this is only important when using perspective
-        // transformations, and even then using a single factor seems like a reasonable
-        // trade-off between quality and speed.
-        fsBuilder->codeAppend("\tfloat afwidth;\n");
-        if (isUniformScale) {
-            // this gives us a smooth step across approximately one fragment
-            fsBuilder->codeAppend("\tafwidth = abs(" SK_DistanceFieldAAFactor "*dx);\n");
-        } else {
-            fsBuilder->codeAppend("\tvec2 uv_grad;\n");
-            if (args.fPB->ctxInfo().caps()->dropsTileOnZeroDivide()) {
-                // this is to compensate for the Adreno, which likes to drop tiles on division by 0
-                fsBuilder->codeAppend("\tfloat uv_len2 = dot(uv, uv);\n");
-                fsBuilder->codeAppend("\tif (uv_len2 < 0.0001) {\n");
-                fsBuilder->codeAppend("\t\tuv_grad = vec2(0.7071, 0.7071);\n");
-                fsBuilder->codeAppend("\t} else {\n");
-                fsBuilder->codeAppend("\t\tuv_grad = uv*inversesqrt(uv_len2);\n");
-                fsBuilder->codeAppend("\t}\n");
-            } else {
-                fsBuilder->codeAppend("\tuv_grad = normalize(uv);\n");
-            }
-            fsBuilder->codeAppend("\tvec2 grad = vec2(uv_grad.x*Jdx.x + uv_grad.y*Jdy.x,\n");
-            fsBuilder->codeAppend("\t                 uv_grad.x*Jdx.y + uv_grad.y*Jdy.y);\n");
-
-            // this gives us a smooth step across approximately one fragment
-            fsBuilder->codeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*length(grad);\n");
-        }
-
-        fsBuilder->codeAppend("\tvec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);\n");
-
-        // adjust based on gamma
-        const char* textColorUniName = NULL;
-        fTextColorUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
-                                             kVec3f_GrSLType, kDefault_GrSLPrecision,
-                                             "TextColor", &textColorUniName);
-
-        fsBuilder->codeAppendf("\tuv = vec2(val.x, %s.x);\n", textColorUniName);
-        fsBuilder->codeAppend("float gammaColor = ");
-        fsBuilder->appendTextureLookup(args.fSamplers[1], "uv", kVec2f_GrSLType);
-        fsBuilder->codeAppend(".r;\n");
-        fsBuilder->codeAppend("\tval.x = gammaColor;\n");
-
-        fsBuilder->codeAppendf("\tuv = vec2(val.y, %s.y);\n", textColorUniName);
-        fsBuilder->codeAppend("\tgammaColor = ");
-        fsBuilder->appendTextureLookup(args.fSamplers[1], "uv", kVec2f_GrSLType);
-        fsBuilder->codeAppend(".r;\n");
-        fsBuilder->codeAppend("\tval.y = gammaColor;\n");
-
-        fsBuilder->codeAppendf("\tuv = vec2(val.z, %s.z);\n", textColorUniName);
-        fsBuilder->codeAppend("\tgammaColor = ");
-        fsBuilder->appendTextureLookup(args.fSamplers[1], "uv", kVec2f_GrSLType);
-        fsBuilder->codeAppend(".r;\n");
-        fsBuilder->codeAppend("\tval.z = gammaColor;\n");
-
-        fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage);
-    }
-
-    virtual void setData(const GrGLProgramDataManager& pdman,
-                         const GrPrimitiveProcessor& processor,
-                         const GrBatchTracker& bt) SK_OVERRIDE {
-        SkASSERT(fTextColorUni.isValid());
-
-        const GrDistanceFieldLCDTextureEffect& dfTexEffect =
-                processor.cast<GrDistanceFieldLCDTextureEffect>();
-        GrColor textColor = dfTexEffect.getTextColor();
-        if (textColor != fTextColor) {
-            static const float ONE_OVER_255 = 1.f / 255.f;
-            pdman.set3f(fTextColorUni,
-                        GrColorUnpackR(textColor) * ONE_OVER_255,
-                        GrColorUnpackG(textColor) * ONE_OVER_255,
-                        GrColorUnpackB(textColor) * ONE_OVER_255);
-            fTextColor = textColor;
-        }
-
-        this->setUniformViewMatrix(pdman, processor.viewMatrix());
-
-        const DistanceFieldLCDBatchTracker& local = bt.cast<DistanceFieldLCDBatchTracker>();
-        if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
-            GrGLfloat c[4];
-            GrColorToRGBAFloat(local.fColor, c);
-            pdman.set4fv(fColorUniform, 1, c);
-            fColor = local.fColor;
-        }
-    }
-
-    static inline void GenKey(const GrGeometryProcessor& gp,
-                              const GrBatchTracker& bt,
-                              const GrGLCaps&,
-                              GrProcessorKeyBuilder* b) {
-        const GrDistanceFieldLCDTextureEffect& dfTexEffect =
-                gp.cast<GrDistanceFieldLCDTextureEffect>();
-
-        const DistanceFieldLCDBatchTracker& local = bt.cast<DistanceFieldLCDBatchTracker>();
-        uint32_t key = dfTexEffect.getFlags();
-        key |= local.fInputColorType << 16;
-        key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 24: 0x0;
-        key |= ComputePosKey(gp.viewMatrix()) << 25;
-        b->add32(key);
-    }
-
-private:
-    GrColor       fColor;
-    UniformHandle fColorUniform;
-    UniformHandle fTextColorUni;
-    SkColor       fTextColor;
-
-    typedef GrGLGeometryProcessor INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-GrDistanceFieldLCDTextureEffect::GrDistanceFieldLCDTextureEffect(
-                                                  GrColor color, const SkMatrix& viewMatrix,
-                                                  GrTexture* texture, const GrTextureParams& params,
-                                                  GrTexture* gamma, const GrTextureParams& gParams,
-                                                  SkColor textColor,
-                                                  uint32_t flags)
-    : INHERITED(color, viewMatrix, SkMatrix::I())
-    , fTextureAccess(texture, params)
-    , fGammaTextureAccess(gamma, gParams)
-    , fTextColor(textColor)
-    , fFlags(flags & kLCD_DistanceFieldEffectMask){
-    SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag));
-    this->initClassID<GrDistanceFieldLCDTextureEffect>();
-    fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
-    fInTextureCoords = &this->addVertexAttrib(Attribute("inTextureCoords",
-                                                          kVec2s_GrVertexAttribType));
-    this->addTextureAccess(&fTextureAccess);
-    this->addTextureAccess(&fGammaTextureAccess);
-}
-
-bool GrDistanceFieldLCDTextureEffect::onIsEqual(const GrGeometryProcessor& other) const {
-    const GrDistanceFieldLCDTextureEffect& cte = other.cast<GrDistanceFieldLCDTextureEffect>();
-    return (fTextColor == cte.fTextColor &&
-            fFlags == cte.fFlags);
-}
-
-void GrDistanceFieldLCDTextureEffect::onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const {
-    out->setUnknownFourComponents();
-    out->setUsingLCDCoverage();
-}
-
-void GrDistanceFieldLCDTextureEffect::getGLProcessorKey(const GrBatchTracker& bt,
-                                                        const GrGLCaps& caps,
-                                                        GrProcessorKeyBuilder* b) const {
-    GrGLDistanceFieldLCDTextureEffect::GenKey(*this, bt, caps, b);
-}
-
-GrGLPrimitiveProcessor*
-GrDistanceFieldLCDTextureEffect::createGLInstance(const GrBatchTracker& bt,
-                                                  const GrGLCaps&) const {
-    return SkNEW_ARGS(GrGLDistanceFieldLCDTextureEffect, (*this, bt));
-}
-
-void GrDistanceFieldLCDTextureEffect::initBatchTracker(GrBatchTracker* bt,
-                                                       const GrPipelineInfo& init) const {
-    DistanceFieldLCDBatchTracker* local = bt->cast<DistanceFieldLCDBatchTracker>();
-    local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
-    local->fUsesLocalCoords = init.fUsesLocalCoords;
-}
-
-bool GrDistanceFieldLCDTextureEffect::onCanMakeEqual(const GrBatchTracker& m,
-                                                     const GrGeometryProcessor& that,
-                                                     const GrBatchTracker& t) const {
-    const DistanceFieldLCDBatchTracker& mine = m.cast<DistanceFieldLCDBatchTracker>();
-    const DistanceFieldLCDBatchTracker& theirs = t.cast<DistanceFieldLCDBatchTracker>();
-    return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
-                                   that, theirs.fUsesLocalCoords) &&
-           CanCombineOutput(mine.fInputColorType, mine.fColor,
-                            theirs.fInputColorType, theirs.fColor);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldLCDTextureEffect);
-
-GrGeometryProcessor* GrDistanceFieldLCDTextureEffect::TestCreate(SkRandom* random,
-                                                                 GrContext*,
-                                                                 const GrDrawTargetCaps&,
-                                                                 GrTexture* textures[]) {
-    int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
-                                      GrProcessorUnitTest::kAlphaTextureIdx;
-    int texIdx2 = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
-                                       GrProcessorUnitTest::kAlphaTextureIdx;
-    static const SkShader::TileMode kTileModes[] = {
-        SkShader::kClamp_TileMode,
-        SkShader::kRepeat_TileMode,
-        SkShader::kMirror_TileMode,
-    };
-    SkShader::TileMode tileModes[] = {
-        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
-        kTileModes[random->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
-    };
-    GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
-                           GrTextureParams::kNone_FilterMode);
-    GrTextureParams params2(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
-                           GrTextureParams::kNone_FilterMode);
-    GrColor textColor = GrColorPackRGBA(random->nextULessThan(256),
-                                        random->nextULessThan(256),
-                                        random->nextULessThan(256),
-                                        random->nextULessThan(256));
-    uint32_t flags = kUseLCD_DistanceFieldEffectFlag;
-    flags |= random->nextBool() ? kUniformScale_DistanceFieldEffectMask : 0;
-    flags |= random->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
-    return GrDistanceFieldLCDTextureEffect::Create(GrRandomColor(random),
-                                                   GrProcessorUnitTest::TestMatrix(random),
-                                                   textures[texIdx], params,
-                                                   textures[texIdx2], params2,
-                                                   textColor,
-                                                   flags);
-}
diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.h b/src/gpu/effects/GrDistanceFieldTextureEffect.h
deleted file mode 100644
index 33209a5..0000000
--- a/src/gpu/effects/GrDistanceFieldTextureEffect.h
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * 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 GrDistanceFieldTextureEffect_DEFINED
-#define GrDistanceFieldTextureEffect_DEFINED
-
-#include "GrProcessor.h"
-#include "GrGeometryProcessor.h"
-
-class GrGLDistanceFieldTextureEffect;
-class GrGLDistanceFieldNoGammaTextureEffect;
-class GrGLDistanceFieldLCDTextureEffect;
-class GrInvariantOutput;
-
-enum GrDistanceFieldEffectFlags {
-    kSimilarity_DistanceFieldEffectFlag = 0x01,   // ctm is similarity matrix
-    kRectToRect_DistanceFieldEffectFlag = 0x02,   // ctm maps rects to rects
-    kUseLCD_DistanceFieldEffectFlag     = 0x04,   // use lcd text
-    kBGR_DistanceFieldEffectFlag        = 0x08,   // lcd display has bgr order
-    kPortrait_DistanceFieldEffectFlag   = 0x10,   // lcd display is in portrait mode (not used yet)
-    kColorAttr_DistanceFieldEffectFlag  = 0x20,   // color vertex attribute
-
-    kInvalid_DistanceFieldEffectFlag    = 0x80,   // invalid state (for initialization)
-    
-    kUniformScale_DistanceFieldEffectMask = kSimilarity_DistanceFieldEffectFlag |
-                                            kRectToRect_DistanceFieldEffectFlag,
-    // The subset of the flags relevant to GrDistanceFieldTextureEffect
-    kNonLCD_DistanceFieldEffectMask       = kSimilarity_DistanceFieldEffectFlag |
-                                            kColorAttr_DistanceFieldEffectFlag,
-    // The subset of the flags relevant to GrDistanceFieldLCDTextureEffect
-    kLCD_DistanceFieldEffectMask          = kSimilarity_DistanceFieldEffectFlag |
-                                            kRectToRect_DistanceFieldEffectFlag |
-                                            kUseLCD_DistanceFieldEffectFlag |
-                                            kBGR_DistanceFieldEffectFlag,
-};
-
-/**
- * The output color of this effect is a modulation of the input color and a sample from a
- * distance field texture (using a smoothed step function near 0.5).
- * It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input
- * coords are a custom attribute. Gamma correction is handled via a texture LUT.
- */
-class GrDistanceFieldTextureEffect : public GrGeometryProcessor {
-public:
-#ifdef SK_GAMMA_APPLY_TO_A8
-    static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, GrTexture* tex,
-                                       const GrTextureParams& params,
-                                       GrTexture* gamma, const GrTextureParams& gammaParams,
-                                       float lum, uint32_t flags, bool opaqueVertexColors) {
-       return SkNEW_ARGS(GrDistanceFieldTextureEffect, (color, viewMatrix, tex, params, gamma,
-                                                        gammaParams, lum,
-                                                        flags, opaqueVertexColors));
-    }
-#else
-    static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, GrTexture* tex,
-                                       const GrTextureParams& params,
-                                       uint32_t flags, bool opaqueVertexColors) {
-        return  SkNEW_ARGS(GrDistanceFieldTextureEffect, (color, viewMatrix, tex, params, flags,
-                                                          opaqueVertexColors));
-    }
-#endif
-
-    virtual ~GrDistanceFieldTextureEffect() {}
-
-    const char* name() const SK_OVERRIDE { return "DistanceFieldTexture"; }
-
-    const Attribute* inPosition() const { return fInPosition; }
-    const Attribute* inColor() const { return fInColor; }
-    const Attribute* inTextureCoords() const { return fInTextureCoords; }
-#ifdef SK_GAMMA_APPLY_TO_A8
-    float getLuminance() const { return fLuminance; }
-#endif
-    uint32_t getFlags() const { return fFlags; }
-
-    virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
-
-    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE;
-
-    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE;
-
-    bool onCanMakeEqual(const GrBatchTracker&,
-                        const GrGeometryProcessor&,
-                        const GrBatchTracker&) const SK_OVERRIDE;
-
-private:
-    GrDistanceFieldTextureEffect(GrColor, const SkMatrix& viewMatrix, GrTexture* texture,
-                                 const GrTextureParams& params,
-#ifdef SK_GAMMA_APPLY_TO_A8
-                                 GrTexture* gamma, const GrTextureParams& gammaParams, float lum,
-#endif
-                                 uint32_t flags, bool opaqueVertexColors);
-
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput*) const SK_OVERRIDE;
-
-    GrTextureAccess    fTextureAccess;
-#ifdef SK_GAMMA_APPLY_TO_A8
-    GrTextureAccess    fGammaTextureAccess;
-    float              fLuminance;
-#endif
-    uint32_t           fFlags;
-    const Attribute* fInPosition;
-    const Attribute* fInColor;
-    const Attribute* fInTextureCoords;
-
-    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
-
-    typedef GrGeometryProcessor INHERITED;
-};
-
-
-/**
-* The output color of this effect is a modulation of the input color and a sample from a
-* distance field texture (using a smoothed step function near 0.5).
-* It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input
-* coords are a custom attribute. No gamma correct blending is applied.
-*/
-class GrDistanceFieldNoGammaTextureEffect : public GrGeometryProcessor {
-public:
-    static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, GrTexture* tex,
-                                       const GrTextureParams& params,
-                                       uint32_t flags, bool opaqueVertexColors) {
-        return SkNEW_ARGS(GrDistanceFieldNoGammaTextureEffect, (color, viewMatrix, tex, params,
-                                                                flags, opaqueVertexColors));
-    }
-
-    virtual ~GrDistanceFieldNoGammaTextureEffect() {}
-
-    const char* name() const SK_OVERRIDE { return "DistanceFieldTexture"; }
-
-    const Attribute* inPosition() const { return fInPosition; }
-    const Attribute* inColor() const { return fInColor; }
-    const Attribute* inTextureCoords() const { return fInTextureCoords; }
-    uint32_t getFlags() const { return fFlags; }
-
-    virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
-
-    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE;
-
-    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE;
-
-    bool onCanMakeEqual(const GrBatchTracker&,
-                        const GrGeometryProcessor&,
-                        const GrBatchTracker&) const SK_OVERRIDE;
-
-private:
-    GrDistanceFieldNoGammaTextureEffect(GrColor, const SkMatrix& viewMatrix, GrTexture* texture,
-                                        const GrTextureParams& params, uint32_t flags,
-                                        bool opaqueVertexColors);
-
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput*) const SK_OVERRIDE;
-
-    GrTextureAccess    fTextureAccess;
-    uint32_t           fFlags;
-    const Attribute* fInPosition;
-    const Attribute* fInColor;
-    const Attribute* fInTextureCoords;
-
-    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
-
-    typedef GrGeometryProcessor INHERITED;
-};
-
-/**
- * The output color of this effect is a modulation of the input color and samples from a
- * distance field texture (using a smoothed step function near 0.5), adjusted for LCD displays.
- * It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input
- * coords are a custom attribute. Gamma correction is handled via a texture LUT.
- */
-class GrDistanceFieldLCDTextureEffect : public GrGeometryProcessor {
-public:
-    static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, GrTexture* tex,
-                                       const GrTextureParams& params, GrTexture* gamma,
-                                       const GrTextureParams& gammaParams,
-                                       SkColor textColor, uint32_t flags) {
-        return SkNEW_ARGS(GrDistanceFieldLCDTextureEffect,
-                          (color, viewMatrix, tex, params, gamma, gammaParams, textColor, flags));
-    }
-
-    virtual ~GrDistanceFieldLCDTextureEffect() {}
-
-    const char* name() const SK_OVERRIDE { return "DistanceFieldLCDTexture"; }
-
-    const Attribute* inPosition() const { return fInPosition; }
-    const Attribute* inTextureCoords() const { return fInTextureCoords; }
-    GrColor getTextColor() const { return fTextColor; }
-    uint32_t getFlags() const { return fFlags; }
-
-    virtual void getGLProcessorKey(const GrBatchTracker& bt,
-                                   const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE;
-
-    virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
-                                                     const GrGLCaps&) const SK_OVERRIDE;
-
-    void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const SK_OVERRIDE;
-
-    bool onCanMakeEqual(const GrBatchTracker&,
-                        const GrGeometryProcessor&,
-                        const GrBatchTracker&) const SK_OVERRIDE;
-
-private:
-    GrDistanceFieldLCDTextureEffect(GrColor, const SkMatrix& viewMatrix, GrTexture* texture,
-                                    const GrTextureParams& params,
-                                    GrTexture* gamma, const GrTextureParams& gammaParams,
-                                    SkColor textColor, uint32_t flags);
-
-    bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE;
-
-    void onGetInvariantOutputCoverage(GrInitInvariantOutput*) const SK_OVERRIDE;
-
-    GrTextureAccess    fTextureAccess;
-    GrTextureAccess    fGammaTextureAccess;
-    GrColor            fTextColor;
-    uint32_t           fFlags;
-    const Attribute* fInPosition;
-    const Attribute* fInTextureCoords;
-
-    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
-
-    typedef GrGeometryProcessor INHERITED;
-};
-
-#endif
diff --git a/src/gpu/effects/GrDitherEffect.cpp b/src/gpu/effects/GrDitherEffect.cpp
index 5e73e3b..85116ae 100644
--- a/src/gpu/effects/GrDitherEffect.cpp
+++ b/src/gpu/effects/GrDitherEffect.cpp
@@ -24,11 +24,11 @@
 
     virtual ~DitherEffect() {};
 
-    const char* name() const SK_OVERRIDE { return "Dither"; }
+    const char* name() const override { return "Dither"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
 private:
     DitherEffect() {
@@ -37,9 +37,9 @@
     }
 
     // All dither effects are equal
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE { return true; }
+    bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
@@ -72,7 +72,7 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
 private:
     typedef GrGLFragmentProcessor INHERITED;
@@ -87,7 +87,7 @@
                               const char* inputColor,
                               const TransformedCoordsArray&,
                               const TextureSamplerArray& samplers) {
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     // Generate a random number based on the fragment position. For this
     // random number generator, we use the "GLSL rand" function
     // that seems to be floating around on the internet. It works under
@@ -106,7 +106,7 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-void DitherEffect::getGLProcessorKey(const GrGLCaps& caps,
+void DitherEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                      GrProcessorKeyBuilder* b) const {
     GLDitherEffect::GenKey(*this, caps, b);
 }
diff --git a/src/gpu/effects/GrMatrixConvolutionEffect.cpp b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
index c194973..a705f2b 100644
--- a/src/gpu/effects/GrMatrixConvolutionEffect.cpp
+++ b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
@@ -18,11 +18,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     typedef GrGLProgramDataManager::UniformHandle UniformHandle;
@@ -74,7 +74,7 @@
     int kWidth = fKernelSize.width();
     int kHeight = fKernelSize.height();
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
     fsBuilder->codeAppend("vec4 sum = vec4(0, 0, 0, 0);");
     fsBuilder->codeAppendf("vec2 coord = %s - %s * %s;", coords2D.c_str(), kernelOffset,
@@ -90,6 +90,7 @@
             fDomain.sampleTexture(fsBuilder, domain, "c", coord, samplers[0]);
             if (!fConvolveAlpha) {
                 fsBuilder->codeAppend("c.rgb /= c.a;");
+                fsBuilder->codeAppend("c.rgb = clamp(c.rgb, 0.0, 1.0);");
             }
             fsBuilder->codeAppend("sum += c * k;");
         }
@@ -111,7 +112,7 @@
 }
 
 void GrGLMatrixConvolutionEffect::GenKey(const GrProcessor& processor,
-                                         const GrGLCaps&, GrProcessorKeyBuilder* b) {
+                                         const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
     const GrMatrixConvolutionEffect& m = processor.cast<GrMatrixConvolutionEffect>();
     SkASSERT(m.kernelSize().width() <= 0x7FFF && m.kernelSize().height() <= 0xFFFF);
     uint32_t key = m.kernelSize().width() << 16 | m.kernelSize().height();
@@ -152,7 +153,7 @@
     fGain(SkScalarToFloat(gain)),
     fBias(SkScalarToFloat(bias) / 255.0f),
     fConvolveAlpha(convolveAlpha),
-    fDomain(GrTextureDomain::MakeTexelDomain(texture, bounds), tileMode) {
+    fDomain(GrTextureDomain::MakeTexelDomainForMode(texture, bounds, tileMode), tileMode) {
     this->initClassID<GrMatrixConvolutionEffect>();
     for (int i = 0; i < kernelSize.width() * kernelSize.height(); i++) {
         fKernel[i] = SkScalarToFloat(kernel[i]);
@@ -164,7 +165,7 @@
 GrMatrixConvolutionEffect::~GrMatrixConvolutionEffect() {
 }
 
-void GrMatrixConvolutionEffect::getGLProcessorKey(const GrGLCaps& caps,
+void GrMatrixConvolutionEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                                   GrProcessorKeyBuilder* b) const {
     GrGLMatrixConvolutionEffect::GenKey(*this, caps, b);
 }
diff --git a/src/gpu/effects/GrMatrixConvolutionEffect.h b/src/gpu/effects/GrMatrixConvolutionEffect.h
index 8f92175..6a340fb 100644
--- a/src/gpu/effects/GrMatrixConvolutionEffect.h
+++ b/src/gpu/effects/GrMatrixConvolutionEffect.h
@@ -60,11 +60,11 @@
     bool convolveAlpha() const { return fConvolveAlpha; }
     const GrTextureDomain& domain() const { return fDomain; }
 
-    const char* name() const SK_OVERRIDE { return "MatrixConvolution"; }
+    const char* name() const override { return "MatrixConvolution"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
 private:
     GrMatrixConvolutionEffect(GrTexture*,
@@ -77,9 +77,9 @@
                               GrTextureDomain::Mode tileMode,
                               bool convolveAlpha);
 
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE {
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
         // TODO: Try to do better?
         inout->mulByUnknownFourComponents();
     }
diff --git a/src/gpu/effects/GrOvalEffect.cpp b/src/gpu/effects/GrOvalEffect.cpp
index 314b000..d387b67 100644
--- a/src/gpu/effects/GrOvalEffect.cpp
+++ b/src/gpu/effects/GrOvalEffect.cpp
@@ -22,11 +22,11 @@
 
     virtual ~CircleEffect() {};
 
-    const char* name() const SK_OVERRIDE { return "Circle"; }
+    const char* name() const override { return "Circle"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     const SkPoint& getCenter() const { return fCenter; }
     SkScalar getRadius() const { return fRadius; }
@@ -36,9 +36,9 @@
 private:
     CircleEffect(GrPrimitiveEdgeType, const SkPoint& center, SkScalar radius);
 
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     SkPoint             fCenter;
     SkScalar            fRadius;
@@ -102,11 +102,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     GrGLProgramDataManager::UniformHandle fCircleUniform;
@@ -128,23 +128,27 @@
                               const TextureSamplerArray& samplers) {
     const CircleEffect& ce = fp.cast<CircleEffect>();
     const char *circleName;
-    // The circle uniform is (center.x, center.y, radius + 0.5) for regular fills and
-    // (... ,radius - 0.5) for inverse fills.
+    // The circle uniform is (center.x, center.y, radius + 0.5, 1 / (radius + 0.5)) for regular
+    // fills and (..., radius - 0.5, 1 / (radius - 0.5)) for inverse fills.
     fCircleUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
-                                         kVec3f_GrSLType, kDefault_GrSLPrecision,
+                                         kVec4f_GrSLType, kDefault_GrSLPrecision,
                                          "circle",
                                          &circleName);
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     const char* fragmentPos = fsBuilder->fragmentPosition();
 
     SkASSERT(kHairlineAA_GrProcessorEdgeType != ce.getEdgeType());
+    // TODO: Right now the distance to circle caclulation is performed in a space normalized to the
+    // radius and then denormalized. This is to prevent overflow on devices that have a "real"
+    // mediump. It'd be nice to only to this on mediump devices but we currently don't have the
+    // caps here.
     if (GrProcessorEdgeTypeIsInverseFill(ce.getEdgeType())) {
-        fsBuilder->codeAppendf("\t\tfloat d = length(%s.xy - %s.xy) - %s.z;\n",
-                                circleName, fragmentPos, circleName);
+        fsBuilder->codeAppendf("\t\tfloat d = (length((%s.xy - %s.xy) * %s.w) - 1.0) * %s.z;\n",
+                                circleName, fragmentPos, circleName, circleName);
     } else {
-        fsBuilder->codeAppendf("\t\tfloat d = %s.z - length(%s.xy - %s.xy);\n",
-                               circleName, fragmentPos, circleName);
+        fsBuilder->codeAppendf("\t\tfloat d = (1.0 - length((%s.xy - %s.xy) *  %s.w)) * %s.z;\n",
+                               circleName, fragmentPos, circleName, circleName);
     }
     if (GrProcessorEdgeTypeIsAA(ce.getEdgeType())) {
         fsBuilder->codeAppend("\t\td = clamp(d, 0.0, 1.0);\n");
@@ -156,7 +160,7 @@
                            (GrGLSLExpr4(inputColor) * GrGLSLExpr1("d")).c_str());
 }
 
-void GLCircleEffect::GenKey(const GrProcessor& processor, const GrGLCaps&,
+void GLCircleEffect::GenKey(const GrProcessor& processor, const GrGLSLCaps&,
                             GrProcessorKeyBuilder* b) {
     const CircleEffect& ce = processor.cast<CircleEffect>();
     b->add32(ce.getEdgeType());
@@ -171,7 +175,8 @@
         } else {
             radius += 0.5f;
         }
-        pdman.set3f(fCircleUniform, ce.getCenter().fX, ce.getCenter().fY, radius);
+        pdman.set4f(fCircleUniform, ce.getCenter().fX, ce.getCenter().fY, radius,
+                    SkScalarInvert(radius));
         fPrevCenter = ce.getCenter();
         fPrevRadius = ce.getRadius();
     }
@@ -179,7 +184,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-void CircleEffect::getGLProcessorKey(const GrGLCaps& caps,
+void CircleEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                      GrProcessorKeyBuilder* b) const {
     GLCircleEffect::GenKey(*this, caps, b);
 }
@@ -197,11 +202,11 @@
 
     virtual ~EllipseEffect() {};
 
-    const char* name() const SK_OVERRIDE { return "Ellipse"; }
+    const char* name() const override { return "Ellipse"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     const SkPoint& getCenter() const { return fCenter; }
     SkVector getRadii() const { return fRadii; }
@@ -211,9 +216,9 @@
 private:
     EllipseEffect(GrPrimitiveEdgeType, const SkPoint& center, SkScalar rx, SkScalar ry);
 
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     SkPoint             fCenter;
     SkVector            fRadii;
@@ -280,11 +285,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     GrGLProgramDataManager::UniformHandle fEllipseUniform;
@@ -312,7 +317,7 @@
                                          "ellipse",
                                          &ellipseName);
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     const char* fragmentPos = fsBuilder->fragmentPosition();
 
     // d is the offset to the ellipse center
@@ -347,7 +352,7 @@
                            (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
 }
 
-void GLEllipseEffect::GenKey(const GrProcessor& effect, const GrGLCaps&,
+void GLEllipseEffect::GenKey(const GrProcessor& effect, const GrGLSLCaps&,
                              GrProcessorKeyBuilder* b) {
     const EllipseEffect& ee = effect.cast<EllipseEffect>();
     b->add32(ee.getEdgeType());
@@ -366,7 +371,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-void EllipseEffect::getGLProcessorKey(const GrGLCaps& caps,
+void EllipseEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                      GrProcessorKeyBuilder* b) const {
     GLEllipseEffect::GenKey(*this, caps, b);
 }
diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
index 4453c89..8b92eba 100644
--- a/src/gpu/effects/GrPorterDuffXferProcessor.cpp
+++ b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
@@ -41,13 +41,13 @@
                                                     willReadDstColor));
     }
 
-    ~PorterDuffXferProcessor() SK_OVERRIDE;
+    ~PorterDuffXferProcessor() override;
 
-    const char* name() const SK_OVERRIDE { return "Porter Duff"; }
+    const char* name() const override { return "Porter Duff"; }
 
-    GrGLXferProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLXferProcessor* createGLInstance() const override;
 
-    bool hasSecondaryOutput() const SK_OVERRIDE;
+    bool hasSecondaryOutput() const override;
 
     ///////////////////////////////////////////////////////////////////////////
     /// @name Stage Output Types
@@ -84,13 +84,22 @@
     PrimaryOutputType primaryOutputType() const { return fPrimaryOutputType; }
     SecondaryOutputType secondaryOutputType() const { return fSecondaryOutputType; }
 
-    GrXferProcessor::OptFlags getOptimizations(const GrProcOptInfo& colorPOI,
-                                               const GrProcOptInfo& coveragePOI,
-                                               bool doesStencilWrite,
-                                               GrColor* overrideColor,
-                                               const GrDrawTargetCaps& caps) SK_OVERRIDE;
+    GrBlendCoeff getSrcBlend() const { return fSrcBlend; }
+    GrBlendCoeff getDstBlend() const { return fDstBlend; }
 
-    void getBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const SK_OVERRIDE {
+private:
+    PorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, GrColor constant,
+                            const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
+
+    GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
+                                                 const GrProcOptInfo& coveragePOI,
+                                                 bool doesStencilWrite,
+                                                 GrColor* overrideColor,
+                                                 const GrDrawTargetCaps& caps) override;
+
+    void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
+
+    void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override {
         if (!this->willReadDstColor()) {
             blendInfo->fSrcBlend = fSrcBlend;
             blendInfo->fDstBlend = fDstBlend;
@@ -101,16 +110,7 @@
         blendInfo->fBlendConstant = fBlendConstant;
     }
 
-    GrBlendCoeff getSrcBlend() const { return fSrcBlend; }
-    GrBlendCoeff getDstBlend() const { return fDstBlend; }
-
-private:
-    PorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, GrColor constant,
-                            const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
-
-    void onGetGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const SK_OVERRIDE;
-
-    bool onIsEqual(const GrXferProcessor& xpBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrXferProcessor& xpBase) const override {
         const PorterDuffXferProcessor& xp = xpBase.cast<PorterDuffXferProcessor>();
         if (fSrcBlend != xp.fSrcBlend ||
             fDstBlend != xp.fDstBlend ||
@@ -140,7 +140,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool append_porterduff_term(GrGLFPFragmentBuilder* fsBuilder, GrBlendCoeff coeff,
+bool append_porterduff_term(GrGLXPFragmentBuilder* fsBuilder, GrBlendCoeff coeff,
                             const char* colorName, const char* srcColorName,
                             const char* dstColorName, bool hasPrevious) {
     if (kZero_GrBlendCoeff == coeff) {
@@ -190,7 +190,7 @@
 
     virtual ~GLPorterDuffXferProcessor() {}
 
-    static void GenKey(const GrProcessor& processor, const GrGLCaps& caps,
+    static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps,
                        GrProcessorKeyBuilder* b) {
         const PorterDuffXferProcessor& xp = processor.cast<PorterDuffXferProcessor>();
         b->add32(xp.primaryOutputType());
@@ -202,9 +202,9 @@
     };
 
 private:
-    void onEmitCode(const EmitArgs& args) SK_OVERRIDE {
+    void onEmitCode(const EmitArgs& args) override {
         const PorterDuffXferProcessor& xp = args.fXP.cast<PorterDuffXferProcessor>();
-        GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+        GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
         if (PorterDuffXferProcessor::kCustom_PrimaryOutputType != xp.primaryOutputType()) {
             SkASSERT(!xp.willReadDstColor());
             switch(xp.secondaryOutputType()) {
@@ -267,7 +267,7 @@
         }
     }
 
-    void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) SK_OVERRIDE {};
+    void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {};
 
     typedef GrGLXferProcessor INHERITED;
 };
@@ -291,7 +291,7 @@
 PorterDuffXferProcessor::~PorterDuffXferProcessor() {
 }
 
-void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLCaps& caps,
+void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLSLCaps& caps,
                                                   GrProcessorKeyBuilder* b) const {
     GLPorterDuffXferProcessor::GenKey(*this, caps, b);
 }
@@ -301,26 +301,14 @@
 }
 
 GrXferProcessor::OptFlags
-PorterDuffXferProcessor::getOptimizations(const GrProcOptInfo& colorPOI,
-                                          const GrProcOptInfo& coveragePOI,
-                                          bool doesStencilWrite,
-                                          GrColor* overrideColor,
-                                          const GrDrawTargetCaps& caps) {
-    GrXferProcessor::OptFlags optFlags;
-    // Optimizations when doing RGB Coverage
-    if (coveragePOI.isFourChannelOutput()) {
-        // We want to force our primary output to be alpha * Coverage, where alpha is the alpha
-        // value of the blend the constant. We should already have valid blend coeff's if we are at
-        // a point where we have RGB coverage. We don't need any color stages since the known color
-        // output is already baked into the blendConstant.
-        uint8_t alpha = GrColorUnpackA(fBlendConstant);
-        *overrideColor = GrColorPackRGBA(alpha, alpha, alpha, alpha);
-        optFlags = GrXferProcessor::kOverrideColor_OptFlag;
-    } else {
-        optFlags = this->internalGetOptimizations(colorPOI,
-                                                  coveragePOI,
-                                                  doesStencilWrite);
-    }
+PorterDuffXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI,
+                                            const GrProcOptInfo& coveragePOI,
+                                            bool doesStencilWrite,
+                                            GrColor* overrideColor,
+                                            const GrDrawTargetCaps& caps) {
+    GrXferProcessor::OptFlags optFlags = this->internalGetOptimizations(colorPOI,
+                                                                        coveragePOI,
+                                                                        doesStencilWrite);
     this->calcOutputTypes(optFlags, caps, coveragePOI.isSolidWhite());
     return optFlags;
 }
@@ -351,7 +339,7 @@
     // blending if we have any effective coverage stages OR the geometry processor doesn't emits
     // solid coverage.
     if (!(optFlags & kSetCoverageDrawing_OptFlag) && !hasSolidCoverage) {
-        if (caps.dualSourceBlendingSupport()) {
+        if (caps.shaderCaps()->dualSourceBlendingSupport()) {
             if (kZero_GrBlendCoeff == fDstBlend) {
                 // write the coverage value to second color
                 fSecondaryOutputType = kCoverage_SecondaryOutputType;
@@ -406,7 +394,8 @@
                 // if there is no coverage and coeffs are (1,0) then we
                 // won't need to read the dst at all, it gets replaced by src
                 fDstBlend = kZero_GrBlendCoeff;
-                return GrXferProcessor::kNone_Opt;
+                return GrXferProcessor::kNone_Opt |
+                       GrXferProcessor::kIgnoreCoverage_OptFlag;
             } else if (kZero_GrBlendCoeff == fSrcBlend) {
                 // if the op is "clear" then we don't need to emit a color
                 // or blend, just write transparent black into the dst.
@@ -416,54 +405,55 @@
                        GrXferProcessor::kIgnoreCoverage_OptFlag;
             }
         }
-    }  else {
-        // check whether coverage can be safely rolled into alpha
-        // of if we can skip color computation and just emit coverage
-        if (can_tweak_alpha_for_coverage(fDstBlend)) {
-            if (colorPOI.allStagesMultiplyInput()) {
-                return GrXferProcessor::kSetCoverageDrawing_OptFlag |
-                       GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag;
-            } else {
-                return GrXferProcessor::kSetCoverageDrawing_OptFlag;
+        return GrXferProcessor::kIgnoreCoverage_OptFlag;
+    }
 
-            }
-        }
-        if (dstCoeffIsZero) {
-            if (kZero_GrBlendCoeff == fSrcBlend) {
-                // the source color is not included in the blend
-                // the dst coeff is effectively zero so blend works out to:
-                // (c)(0)D + (1-c)D = (1-c)D.
-                fDstBlend = kISA_GrBlendCoeff;
-                return GrXferProcessor::kIgnoreColor_OptFlag |
-                       GrXferProcessor::kSetCoverageDrawing_OptFlag;
-            } else if (srcAIsOne) {
-                // the dst coeff is effectively zero so blend works out to:
-                // cS + (c)(0)D + (1-c)D = cS + (1-c)D.
-                // If Sa is 1 then we can replace Sa with c
-                // and set dst coeff to 1-Sa.
-                fDstBlend = kISA_GrBlendCoeff;
-                if (colorPOI.allStagesMultiplyInput()) {
-                    return GrXferProcessor::kSetCoverageDrawing_OptFlag |
-                           GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag;
-                } else {
-                    return GrXferProcessor::kSetCoverageDrawing_OptFlag;
-
-                }
-            }
-        } else if (dstCoeffIsOne) {
-            // the dst coeff is effectively one so blend works out to:
-            // cS + (c)(1)D + (1-c)D = cS + D.
-            fDstBlend = kOne_GrBlendCoeff;
-            if (colorPOI.allStagesMultiplyInput()) {
-                return GrXferProcessor::kSetCoverageDrawing_OptFlag |
-                       GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag;
-            } else {
-                return GrXferProcessor::kSetCoverageDrawing_OptFlag;
-
-            }
+    // check whether coverage can be safely rolled into alpha
+    // of if we can skip color computation and just emit coverage
+    if (can_tweak_alpha_for_coverage(fDstBlend)) {
+        if (colorPOI.allStagesMultiplyInput()) {
+            return GrXferProcessor::kSetCoverageDrawing_OptFlag |
+                GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag;
+        } else {
             return GrXferProcessor::kSetCoverageDrawing_OptFlag;
+
         }
     }
+    if (dstCoeffIsZero) {
+        if (kZero_GrBlendCoeff == fSrcBlend) {
+            // the source color is not included in the blend
+            // the dst coeff is effectively zero so blend works out to:
+            // (c)(0)D + (1-c)D = (1-c)D.
+            fDstBlend = kISA_GrBlendCoeff;
+            return GrXferProcessor::kIgnoreColor_OptFlag |
+                GrXferProcessor::kSetCoverageDrawing_OptFlag;
+        } else if (srcAIsOne) {
+            // the dst coeff is effectively zero so blend works out to:
+            // cS + (c)(0)D + (1-c)D = cS + (1-c)D.
+            // If Sa is 1 then we can replace Sa with c
+            // and set dst coeff to 1-Sa.
+            fDstBlend = kISA_GrBlendCoeff;
+            if (colorPOI.allStagesMultiplyInput()) {
+                return GrXferProcessor::kSetCoverageDrawing_OptFlag |
+                    GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag;
+            } else {
+                return GrXferProcessor::kSetCoverageDrawing_OptFlag;
+
+            }
+        }
+    } else if (dstCoeffIsOne) {
+        // the dst coeff is effectively one so blend works out to:
+        // cS + (c)(1)D + (1-c)D = cS + D.
+        fDstBlend = kOne_GrBlendCoeff;
+        if (colorPOI.allStagesMultiplyInput()) {
+            return GrXferProcessor::kSetCoverageDrawing_OptFlag |
+                GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag;
+        } else {
+            return GrXferProcessor::kSetCoverageDrawing_OptFlag;
+
+        }
+        return GrXferProcessor::kSetCoverageDrawing_OptFlag;
+    }
 
     return GrXferProcessor::kNone_Opt;
 }
@@ -474,6 +464,127 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+class PDLCDXferProcessor : public GrXferProcessor {
+public:
+    static GrXferProcessor* Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend,
+                                   const GrProcOptInfo& colorPOI);
+
+    ~PDLCDXferProcessor() override;
+
+    const char* name() const override { return "Porter Duff LCD"; }
+
+    GrGLXferProcessor* createGLInstance() const override;
+
+    bool hasSecondaryOutput() const override { return false; }
+
+private:
+    PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha);
+
+    GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
+                                                 const GrProcOptInfo& coveragePOI,
+                                                 bool doesStencilWrite,
+                                                 GrColor* overrideColor,
+                                                 const GrDrawTargetCaps& caps) override;
+
+    void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
+
+    void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override {
+        blendInfo->fSrcBlend = kConstC_GrBlendCoeff;
+        blendInfo->fDstBlend = kISC_GrBlendCoeff;
+        blendInfo->fBlendConstant = fBlendConstant;
+    }
+
+    bool onIsEqual(const GrXferProcessor& xpBase) const override {
+        const PDLCDXferProcessor& xp = xpBase.cast<PDLCDXferProcessor>();
+        if (fBlendConstant != xp.fBlendConstant ||
+            fAlpha != xp.fAlpha) {
+            return false;
+        }
+        return true;
+    }
+
+    GrColor      fBlendConstant;
+    uint8_t      fAlpha;
+
+    typedef GrXferProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GLPDLCDXferProcessor : public GrGLXferProcessor {
+public:
+    GLPDLCDXferProcessor(const GrProcessor&) {}
+
+    virtual ~GLPDLCDXferProcessor() {}
+
+    static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps,
+                       GrProcessorKeyBuilder* b) {}
+
+private:
+    void onEmitCode(const EmitArgs& args) override {
+        GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+
+        fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor,
+                               args.fInputCoverage);
+    }
+
+    void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {};
+
+    typedef GrGLXferProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+PDLCDXferProcessor::PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha)
+    : fBlendConstant(blendConstant)
+    , fAlpha(alpha) {
+    this->initClassID<PDLCDXferProcessor>();
+}
+
+GrXferProcessor* PDLCDXferProcessor::Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend,
+                                            const GrProcOptInfo& colorPOI) {
+    if (kOne_GrBlendCoeff != srcBlend || kISA_GrBlendCoeff != dstBlend) {
+        return NULL;
+    }
+
+    if (kRGBA_GrColorComponentFlags != colorPOI.validFlags()) {
+        return NULL;
+    }
+
+    GrColor blendConstant = GrUnPreMulColor(colorPOI.color());
+    uint8_t alpha = GrColorUnpackA(blendConstant);
+    blendConstant |= (0xff << GrColor_SHIFT_A);
+
+    return SkNEW_ARGS(PDLCDXferProcessor, (blendConstant, alpha));
+}
+
+PDLCDXferProcessor::~PDLCDXferProcessor() {
+}
+
+void PDLCDXferProcessor::onGetGLProcessorKey(const GrGLSLCaps& caps,
+                                             GrProcessorKeyBuilder* b) const {
+    GLPDLCDXferProcessor::GenKey(*this, caps, b);
+}
+
+GrGLXferProcessor* PDLCDXferProcessor::createGLInstance() const {
+    return SkNEW_ARGS(GLPDLCDXferProcessor, (*this));
+}
+
+GrXferProcessor::OptFlags
+PDLCDXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI,
+                                       const GrProcOptInfo& coveragePOI,
+                                       bool doesStencilWrite,
+                                       GrColor* overrideColor,
+                                       const GrDrawTargetCaps& caps) {
+        // We want to force our primary output to be alpha * Coverage, where alpha is the alpha
+        // value of the blend the constant. We should already have valid blend coeff's if we are at
+        // a point where we have RGB coverage. We don't need any color stages since the known color
+        // output is already baked into the blendConstant.
+        *overrideColor = GrColorPackRGBA(fAlpha, fAlpha, fAlpha, fAlpha);
+        return GrXferProcessor::kOverrideColor_OptFlag;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 GrPorterDuffXPFactory::GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst)
     : fSrcCoeff(src), fDstCoeff(dst) {
     this->initClassID<GrPorterDuffXPFactory>();
@@ -566,19 +677,11 @@
                                              const GrProcOptInfo& colorPOI,
                                              const GrProcOptInfo& covPOI,
                                              const GrDeviceCoordTexture* dstCopy) const {
-    if (!covPOI.isFourChannelOutput()) {
+    if (covPOI.isFourChannelOutput()) {
+        return PDLCDXferProcessor::Create(fSrcCoeff, fDstCoeff, colorPOI);
+    } else {
         return PorterDuffXferProcessor::Create(fSrcCoeff, fDstCoeff, 0, dstCopy,
                                                this->willReadDstColor(caps, colorPOI, covPOI));
-    } else {
-        if (this->supportsRGBCoverage(colorPOI.color(), colorPOI.validFlags())) {
-            SkASSERT(kRGBA_GrColorComponentFlags == colorPOI.validFlags());
-            GrColor blendConstant = GrUnPreMulColor(colorPOI.color());
-            return PorterDuffXferProcessor::Create(kConstC_GrBlendCoeff, kISC_GrBlendCoeff,
-                                                   blendConstant, dstCopy,
-                                                   this->willReadDstColor(caps, colorPOI, covPOI));
-        } else {
-            return NULL;
-        }
     }
 }
 
@@ -591,10 +694,6 @@
     return false;
 }
 
-bool GrPorterDuffXPFactory::canTweakAlphaForCoverage() const {
-    return can_tweak_alpha_for_coverage(fDstCoeff);
-}
-
 void GrPorterDuffXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI,
                                                const GrProcOptInfo& coveragePOI,
                                                GrXPFactory::InvariantOutput* output) const {
@@ -668,11 +767,11 @@
                                              const GrProcOptInfo& colorPOI,
                                              const GrProcOptInfo& coveragePOI) const {
     // We can always blend correctly if we have dual source blending.
-    if (caps.dualSourceBlendingSupport()) {
+    if (caps.shaderCaps()->dualSourceBlendingSupport()) {
         return false;
     }
 
-    if (this->canTweakAlphaForCoverage()) {
+    if (can_tweak_alpha_for_coverage(fDstCoeff)) {
         return false;
     }
 
diff --git a/src/gpu/effects/GrRRectEffect.cpp b/src/gpu/effects/GrRRectEffect.cpp
index 2cc0651..9954b63 100644
--- a/src/gpu/effects/GrRRectEffect.cpp
+++ b/src/gpu/effects/GrRRectEffect.cpp
@@ -48,11 +48,11 @@
 
     virtual ~CircularRRectEffect() {};
 
-    const char* name() const SK_OVERRIDE { return "CircularRRect"; }
+    const char* name() const override { return "CircularRRect"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     const SkRRect& getRRect() const { return fRRect; }
 
@@ -63,9 +63,9 @@
 private:
     CircularRRectEffect(GrPrimitiveEdgeType, uint32_t circularCornerFlags, const SkRRect&);
 
-    bool onIsEqual(const GrFragmentProcessor& other) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor& other) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     SkRRect                fRRect;
     GrPrimitiveEdgeType    fEdgeType;
@@ -137,11 +137,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     GrGLProgramDataManager::UniformHandle fInnerRectUniform;
@@ -176,7 +176,7 @@
                                                  "radiusPlusHalf",
                                                  &radiusPlusHalfName);
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     const char* fragmentPos = fsBuilder->fragmentPosition();
     // At each quarter-circle corner we compute a vector that is the offset of the fragment position
     // from the circle center. The vector is pinned in x and y to be in the quarter-plane relevant
@@ -287,7 +287,7 @@
                            (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
 }
 
-void GLCircularRRectEffect::GenKey(const GrProcessor& processor, const GrGLCaps&,
+void GLCircularRRectEffect::GenKey(const GrProcessor& processor, const GrGLSLCaps&,
                                    GrProcessorKeyBuilder* b) {
     const CircularRRectEffect& crre = processor.cast<CircularRRectEffect>();
     GR_STATIC_ASSERT(kGrProcessorEdgeTypeCnt <= 8);
@@ -375,7 +375,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
-void CircularRRectEffect::getGLProcessorKey(const GrGLCaps& caps,
+void CircularRRectEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                             GrProcessorKeyBuilder* b) const {
     GLCircularRRectEffect::GenKey(*this, caps, b);
 }
@@ -392,11 +392,11 @@
 
     virtual ~EllipticalRRectEffect() {};
 
-    const char* name() const SK_OVERRIDE { return "EllipticalRRect"; }
+    const char* name() const override { return "EllipticalRRect"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     const SkRRect& getRRect() const { return fRRect; }
 
@@ -405,9 +405,9 @@
 private:
     EllipticalRRectEffect(GrPrimitiveEdgeType, const SkRRect&);
 
-    bool onIsEqual(const GrFragmentProcessor& other) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor& other) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     SkRRect             fRRect;
     GrPrimitiveEdgeType    fEdgeType;
@@ -494,11 +494,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
 private:
     GrGLProgramDataManager::UniformHandle fInnerRectUniform;
@@ -525,7 +525,7 @@
                                             "innerRect",
                                             &rectName);
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     const char* fragmentPos = fsBuilder->fragmentPosition();
     // At each quarter-ellipse corner we compute a vector that is the offset of the fragment pos
     // to the ellipse center. The vector is pinned in x and y to be in the quarter-plane relevant
@@ -587,7 +587,7 @@
                            (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
 }
 
-void GLEllipticalRRectEffect::GenKey(const GrProcessor& effect, const GrGLCaps&,
+void GLEllipticalRRectEffect::GenKey(const GrProcessor& effect, const GrGLSLCaps&,
                                      GrProcessorKeyBuilder* b) {
     const EllipticalRRectEffect& erre = effect.cast<EllipticalRRectEffect>();
     GR_STATIC_ASSERT(kLast_GrProcessorEdgeType < (1 << 3));
@@ -633,7 +633,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
-void EllipticalRRectEffect::getGLProcessorKey(const GrGLCaps& caps,
+void EllipticalRRectEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                               GrProcessorKeyBuilder* b) const {
     GLEllipticalRRectEffect::GenKey(*this, caps, b);
 }
diff --git a/src/gpu/effects/GrSimpleTextureEffect.cpp b/src/gpu/effects/GrSimpleTextureEffect.cpp
index 93f7d68..e729b1b 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.cpp
+++ b/src/gpu/effects/GrSimpleTextureEffect.cpp
@@ -23,8 +23,8 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray& coords,
-                          const TextureSamplerArray& samplers) SK_OVERRIDE {
-        GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+                          const TextureSamplerArray& samplers) override {
+        GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
         fsBuilder->codeAppendf("\t%s = ", outputColor);
         fsBuilder->appendTextureLookupAndModulate(inputColor,
                                                   samplers[0],
@@ -43,7 +43,7 @@
     this->updateInvariantOutputForModulation(inout);
 }
 
-void GrSimpleTextureEffect::getGLProcessorKey(const GrGLCaps& caps,
+void GrSimpleTextureEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                               GrProcessorKeyBuilder* b) const {
     GrGLSimpleTextureEffect::GenKey(*this, caps, b);
 }
@@ -80,6 +80,6 @@
     };
     GrCoordSet coordSet = kCoordSets[random->nextULessThan(SK_ARRAY_COUNT(kCoordSets))];
 
-    const SkMatrix& matrix = GrProcessorUnitTest::TestMatrix(random);
+    const SkMatrix& matrix = GrTest::TestMatrix(random);
     return GrSimpleTextureEffect::Create(textures[texIdx], matrix, coordSet);
 }
diff --git a/src/gpu/effects/GrSimpleTextureEffect.h b/src/gpu/effects/GrSimpleTextureEffect.h
index 8afc235..79e660f 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.h
+++ b/src/gpu/effects/GrSimpleTextureEffect.h
@@ -47,11 +47,11 @@
 
     virtual ~GrSimpleTextureEffect() {}
 
-    const char* name() const SK_OVERRIDE { return "SimpleTexture"; }
+    const char* name() const override { return "SimpleTexture"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
 private:
     GrSimpleTextureEffect(GrTexture* texture,
@@ -70,9 +70,9 @@
         this->initClassID<GrSimpleTextureEffect>();
     }
 
-    bool onIsEqual(const GrFragmentProcessor& other) const SK_OVERRIDE { return true; }
+    bool onIsEqual(const GrFragmentProcessor& other) const override { return true; }
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
diff --git a/src/gpu/effects/GrTextureDomain.cpp b/src/gpu/effects/GrTextureDomain.cpp
index 9516595..bd18986 100644
--- a/src/gpu/effects/GrTextureDomain.cpp
+++ b/src/gpu/effects/GrTextureDomain.cpp
@@ -28,10 +28,10 @@
         // handle rects that do not intersect the [0..1]x[0..1] rect.
         SkASSERT(domain.fLeft <= domain.fRight);
         SkASSERT(domain.fTop <= domain.fBottom);
-        fDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft);
-        fDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight);
-        fDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop);
-        fDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom);
+        fDomain.fLeft = SkScalarPin(domain.fLeft, kFullRect.fLeft, kFullRect.fRight);
+        fDomain.fRight = SkScalarPin(domain.fRight, kFullRect.fLeft, kFullRect.fRight);
+        fDomain.fTop = SkScalarPin(domain.fTop, kFullRect.fTop, kFullRect.fBottom);
+        fDomain.fBottom = SkScalarPin(domain.fBottom, kFullRect.fTop, kFullRect.fBottom);
         SkASSERT(fDomain.fLeft <= fDomain.fRight);
         SkASSERT(fDomain.fTop <= fDomain.fBottom);
     }
@@ -174,11 +174,11 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) SK_OVERRIDE;
+                          const TextureSamplerArray&) override;
 
-    void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
+    void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
 
-    static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
+    static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
 
 private:
     GrTextureDomain::GLDomain         fGLDomain;
@@ -197,7 +197,7 @@
     const GrTextureDomainEffect& textureDomainEffect = fp.cast<GrTextureDomainEffect>();
     const GrTextureDomain& domain = textureDomainEffect.textureDomain();
 
-    GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
     fGLDomain.sampleTexture(fsBuilder, domain, outputColor, coords2D, samplers[0], inputColor);
 }
@@ -209,7 +209,7 @@
     fGLDomain.setData(pdman, domain, processor.texture(0)->origin());
 }
 
-void GrGLTextureDomainEffect::GenKey(const GrProcessor& processor, const GrGLCaps&,
+void GrGLTextureDomainEffect::GenKey(const GrProcessor& processor, const GrGLSLCaps&,
                                      GrProcessorKeyBuilder* b) {
     const GrTextureDomain& domain = processor.cast<GrTextureDomainEffect>().textureDomain();
     b->add32(GrTextureDomain::GLDomain::DomainKey(domain));
@@ -256,7 +256,7 @@
 
 }
 
-void GrTextureDomainEffect::getGLProcessorKey(const GrGLCaps& caps,
+void GrTextureDomainEffect::getGLProcessorKey(const GrGLSLCaps& caps,
                                               GrProcessorKeyBuilder* b) const {
     GrGLTextureDomainEffect::GenKey(*this, caps, b);
 }
@@ -299,7 +299,7 @@
     domain.fBottom = random->nextRangeScalar(domain.fTop, SK_Scalar1);
     GrTextureDomain::Mode mode =
         (GrTextureDomain::Mode) random->nextULessThan(GrTextureDomain::kModeCount);
-    const SkMatrix& matrix = GrProcessorUnitTest::TestMatrix(random);
+    const SkMatrix& matrix = GrTest::TestMatrix(random);
     bool bilerp = mode != GrTextureDomain::kRepeat_Mode ? random->nextBool() : false;
     GrCoordSet coords = random->nextBool() ? kLocal_GrCoordSet : kDevice_GrCoordSet;
     return GrTextureDomainEffect::Create(textures[texIdx],
diff --git a/src/gpu/effects/GrTextureDomain.h b/src/gpu/effects/GrTextureDomain.h
index 3d8b569..1610580 100644
--- a/src/gpu/effects/GrTextureDomain.h
+++ b/src/gpu/effects/GrTextureDomain.h
@@ -69,6 +69,19 @@
         return result;
     }
 
+    static const SkRect MakeTexelDomainForMode(const GrTexture* texture, const SkIRect& texelRect, Mode mode) {
+        // For Clamp mode, inset by half a texel.
+        SkScalar wInv = SK_Scalar1 / texture->width();
+        SkScalar hInv = SK_Scalar1 / texture->height();
+        SkScalar inset = (mode == kClamp_Mode && !texelRect.isEmpty()) ? SK_ScalarHalf : 0;
+        return SkRect::MakeLTRB(
+            (texelRect.fLeft + inset) * wInv,
+            (texelRect.fTop + inset) * hInv,
+            (texelRect.fRight - inset) * wInv,
+            (texelRect.fBottom - inset) * hInv
+        );
+    }
+
     bool operator== (const GrTextureDomain& that) const {
         return fMode == that.fMode && (kIgnore_Mode == fMode || fDomain == that.fDomain);
     }
@@ -153,11 +166,11 @@
 
     virtual ~GrTextureDomainEffect();
 
-    const char* name() const SK_OVERRIDE { return "TextureDomain"; }
+    const char* name() const override { return "TextureDomain"; }
 
-    void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const SK_OVERRIDE;
+    void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE;
+    GrGLFragmentProcessor* createGLInstance() const override;
 
     const GrTextureDomain& textureDomain() const { return fTextureDomain; }
 
@@ -172,9 +185,9 @@
                           GrTextureParams::FilterMode,
                           GrCoordSet);
 
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE;
+    bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE;
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
diff --git a/src/gpu/effects/GrTextureStripAtlas.cpp b/src/gpu/effects/GrTextureStripAtlas.cpp
index 65096bf..8610691 100644
--- a/src/gpu/effects/GrTextureStripAtlas.cpp
+++ b/src/gpu/effects/GrTextureStripAtlas.cpp
@@ -204,13 +204,13 @@
     builder[0] = static_cast<uint32_t>(fCacheKey);
     builder.finish();
 
-    fTexture = fDesc.fContext->findAndRefCachedTexture(key);
+    fTexture = fDesc.fContext->textureProvider()->findAndRefTextureByUniqueKey(key);
     if (NULL == fTexture) {
-        fTexture = fDesc.fContext->createTexture(texDesc, true, NULL, 0);
+        fTexture = fDesc.fContext->textureProvider()->createTexture(texDesc, true, NULL, 0);
         if (!fTexture) {
             return;
         }
-        fDesc.fContext->addResourceToCache(key, fTexture);
+        fDesc.fContext->textureProvider()->assignUniqueKeyToTexture(key, fTexture);
         // This is a new texture, so all of our cache info is now invalid
         this->initLRU();
         fKeyTable.rewind();
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.cpp b/src/gpu/effects/GrYUVtoRGBEffect.cpp
index fb69472..60c39bf 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.cpp
+++ b/src/gpu/effects/GrYUVtoRGBEffect.cpp
@@ -44,7 +44,7 @@
                                            uvFilterMode, colorSpace));
     }
 
-    const char* name() const SK_OVERRIDE { return "YUV to RGB"; }
+    const char* name() const override { return "YUV to RGB"; }
 
     SkYUVColorSpace getColorSpace() const {
         return fColorSpace;
@@ -56,7 +56,7 @@
         static const GrGLfloat kRec601ConversionMatrix[16];
 
         // this class always generates the same code.
-        static void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*) {}
+        static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) {}
 
         GLProcessor(const GrProcessor&) {}
 
@@ -65,8 +65,8 @@
                               const char* outputColor,
                               const char* inputColor,
                               const TransformedCoordsArray& coords,
-                              const TextureSamplerArray& samplers) SK_OVERRIDE {
-            GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+                              const TextureSamplerArray& samplers) override {
+            GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
 
             const char* yuvMatrix   = NULL;
             fMatrixUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
@@ -82,7 +82,7 @@
         }
 
         virtual void setData(const GrGLProgramDataManager& pdman,
-                             const GrProcessor& processor) SK_OVERRIDE {
+                             const GrProcessor& processor) override {
             const YUVtoRGBEffect& yuvEffect = processor.cast<YUVtoRGBEffect>();
             switch (yuvEffect.getColorSpace()) {
                 case kJPEG_SkYUVColorSpace:
@@ -100,12 +100,12 @@
         typedef GrGLFragmentProcessor INHERITED;
     };
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GLProcessor::GenKey(*this, caps, b);
     }
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
+    GrGLFragmentProcessor* createGLInstance() const override {
         return SkNEW_ARGS(GLProcessor, (*this));
     }
 
@@ -129,12 +129,12 @@
         this->addTextureAccess(&fVAccess);
     }
 
-    bool onIsEqual(const GrFragmentProcessor& sBase) const SK_OVERRIDE {
+    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
         const YUVtoRGBEffect& s = sBase.cast<YUVtoRGBEffect>();
         return fColorSpace == s.getColorSpace();
     }
 
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE {
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
         // YUV is opaque
         inout->setToOther(kA_GrColorComponentFlag, 0xFF << GrColor_SHIFT_A,
                           GrInvariantOutput::kWillNot_ReadInput);
diff --git a/src/gpu/gl/GrGLAssembleInterface.cpp b/src/gpu/gl/GrGLAssembleInterface.cpp
index c91da9e..89b21fd 100644
--- a/src/gpu/gl/GrGLAssembleInterface.cpp
+++ b/src/gpu/gl/GrGLAssembleInterface.cpp
@@ -70,14 +70,28 @@
     }
     GET_PROC(BeginQuery);
     GET_PROC(BindTexture);
-    GET_PROC(BlendFunc);
 
-    if (glVer >= GR_GL_VER(1,4) ||
-        extensions.has("GL_ARB_imaging") ||
-        extensions.has("GL_EXT_blend_color")) {
-        GET_PROC(BlendColor);
+    if (extensions.has("GL_KHR_blend_equation_advanced")) {
+        GET_PROC_SUFFIX(BlendBarrier, KHR);
+    } else if (extensions.has("GL_NV_blend_equation_advanced")) {
+        GET_PROC_SUFFIX(BlendBarrier, NV);
     }
 
+    if (glVer >= GR_GL_VER(1,4) ||
+        extensions.has("GL_ARB_imaging")) {
+        GET_PROC(BlendColor);
+    } else if (extensions.has("GL_EXT_blend_color")) {
+        GET_PROC_SUFFIX(BlendColor, EXT);
+    }
+
+    if (glVer >= GR_GL_VER(1,4) ||
+        extensions.has("GL_ARB_imaging")) {
+        GET_PROC(BlendEquation);
+    } else if (extensions.has("GL_EXT_blend_subtract")) {
+        GET_PROC_SUFFIX(BlendEquation, EXT);
+    }
+
+    GET_PROC(BlendFunc);
     GET_PROC(BufferData);
     GET_PROC(BufferSubData);
     GET_PROC(Clear);
@@ -163,6 +177,11 @@
         GET_PROC_SUFFIX(TexStorage2D, EXT);
     }
     GET_PROC(TexSubImage2D);
+    if (glVer >= GR_GL_VER(4,5) || extensions.has("GL_ARB_texture_barrier")) {
+        GET_PROC(TextureBarrier);
+    } else if (extensions.has("GL_NV_texture_barrier")) {
+        GET_PROC_SUFFIX(TextureBarrier, NV);
+    }
     GET_PROC(Uniform1f);
     GET_PROC(Uniform1i);
     GET_PROC(Uniform1fv);
@@ -278,6 +297,10 @@
         GET_PROC_SUFFIX(PathMemoryGlyphIndexArray, NV);
     }
 
+    if (extensions.has("GL_NV_framebuffer_mixed_samples")) {
+        GET_PROC_SUFFIX(CoverageModulation, NV);
+    }
+
     if (extensions.has("GL_EXT_debug_marker")) {
         GET_PROC_SUFFIX(InsertEventMarker, EXT);
         GET_PROC_SUFFIX(PushGroupMarker, EXT);
@@ -332,7 +355,15 @@
     GET_PROC(BindBuffer);
     GET_PROC(BindTexture);
     GET_PROC_SUFFIX(BindVertexArray, OES);
+
+    if (extensions.has("GL_KHR_blend_equation_advanced")) {
+        GET_PROC_SUFFIX(BlendBarrier, KHR);
+    } else if (extensions.has("GL_NV_blend_equation_advanced")) {
+        GET_PROC_SUFFIX(BlendBarrier, NV);
+    }
+
     GET_PROC(BlendColor);
+    GET_PROC(BlendEquation);
     GET_PROC(BlendFunc);
     GET_PROC(BufferData);
     GET_PROC(BufferSubData);
@@ -400,6 +431,10 @@
         GET_PROC_SUFFIX(TexStorage2D, EXT);
     }
 
+    if (extensions.has("GL_NV_texture_barrier")) {
+        GET_PROC_SUFFIX(TextureBarrier, NV);
+    }
+
     GET_PROC_SUFFIX(DiscardFramebuffer, EXT);
     GET_PROC(Uniform1f);
     GET_PROC(Uniform1i);
@@ -519,6 +554,10 @@
         GET_PROC_SUFFIX(PathMemoryGlyphIndexArray, NV);
     }
 
+    if (extensions.has("GL_NV_framebuffer_mixed_samples")) {
+        GET_PROC_SUFFIX(CoverageModulation, NV);
+    }
+
     interface->fStandard = kGLES_GrGLStandard;
     interface->fExtensions.swap(&extensions);
 
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 44bd6ce..06ebe3d 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -25,12 +25,10 @@
     fMSFBOType = kNone_MSFBOType;
     fInvalidateFBType = kNone_InvalidateFBType;
     fLATCAlias = kLATC_LATCAlias;
-    fNvprSupport = kNone_NvprSupport;
     fMapBufferType = kNone_MapBufferType;
     fMaxFragmentUniformVectors = 0;
     fMaxVertexAttributes = 0;
     fMaxFragmentTextureUnits = 0;
-    fMaxFixedFunctionTextureCoords = 0;
     fRGBA8RenderbufferSupport = false;
     fBGRAIsInternalFormat = false;
     fTextureSwizzleSupport = false;
@@ -49,15 +47,12 @@
     fUseNonVBOVertexAndIndexDynamicData = false;
     fIsCoreProfile = false;
     fFullClearIsFree = false;
-    fDropsTileOnZeroDivide = false;
-    fFBFetchSupport = false;
-    fFBFetchNeedsCustomOutput = false;
-    fBindFBOToReadAndDrawForAddingAttachments = false;
-    
-    fFBFetchColorName = NULL;
-    fFBFetchExtensionString = NULL;
+    fFBMixedSamplesSupport = false;
 
     fReadPixelsSupportedCache.reset();
+
+    fShaderCaps.reset(SkNEW(GrGLSLCaps));
+
 }
 
 GrGLCaps::GrGLCaps(const GrGLCaps& caps) : GrDrawTargetCaps() {
@@ -70,11 +65,9 @@
     fStencilFormats = caps.fStencilFormats;
     fStencilVerifiedColorConfigs = caps.fStencilVerifiedColorConfigs;
     fLATCAlias = caps.fLATCAlias;
-    fNvprSupport = caps.fNvprSupport;
     fMaxFragmentUniformVectors = caps.fMaxFragmentUniformVectors;
     fMaxVertexAttributes = caps.fMaxVertexAttributes;
     fMaxFragmentTextureUnits = caps.fMaxFragmentTextureUnits;
-    fMaxFixedFunctionTextureCoords = caps.fMaxFixedFunctionTextureCoords;
     fMSFBOType = caps.fMSFBOType;
     fInvalidateFBType = caps.fInvalidateFBType;
     fMapBufferType = caps.fMapBufferType;
@@ -96,12 +89,10 @@
     fUseNonVBOVertexAndIndexDynamicData = caps.fUseNonVBOVertexAndIndexDynamicData;
     fIsCoreProfile = caps.fIsCoreProfile;
     fFullClearIsFree = caps.fFullClearIsFree;
-    fDropsTileOnZeroDivide = caps.fDropsTileOnZeroDivide;
-    fFBFetchSupport = caps.fFBFetchSupport;
-    fFBFetchNeedsCustomOutput = caps.fFBFetchNeedsCustomOutput;
-    fFBFetchColorName = caps.fFBFetchColorName;
-    fFBFetchExtensionString = caps.fFBFetchExtensionString;
-    fBindFBOToReadAndDrawForAddingAttachments = caps.fBindFBOToReadAndDrawForAddingAttachments;
+    fFBMixedSamplesSupport = caps.fFBMixedSamplesSupport;
+
+    *(reinterpret_cast<GrGLSLCaps*>(fShaderCaps.get())) = 
+                                          *(reinterpret_cast<GrGLSLCaps*>(caps.fShaderCaps.get()));
 
     return *this;
 }
@@ -133,11 +124,6 @@
             GR_GL_GetIntegerv(gli, GR_GL_CONTEXT_PROFILE_MASK, &profileMask);
             fIsCoreProfile = SkToBool(profileMask & GR_GL_CONTEXT_CORE_PROFILE_BIT);
         }
-        if (!fIsCoreProfile) {
-            GR_GL_GetIntegerv(gli, GR_GL_MAX_TEXTURE_COORDS, &fMaxFixedFunctionTextureCoords);
-            // Sanity check
-            SkASSERT(fMaxFixedFunctionTextureCoords > 0 && fMaxFixedFunctionTextureCoords < 128);
-        }
     }
     GR_GL_GetIntegerv(gli, GR_GL_MAX_VERTEX_ATTRIBS, &fMaxVertexAttributes);
     GR_GL_GetIntegerv(gli, GR_GL_MAX_TEXTURE_IMAGE_UNITS, &fMaxFragmentTextureUnits);
@@ -187,6 +173,14 @@
                              ctxInfo.hasExtension("GL_EXT_texture_storage");
     }
 
+    if (kGL_GrGLStandard == standard) {
+        fTextureBarrierSupport = version >= GR_GL_VER(4,5) ||
+                                 ctxInfo.hasExtension("GL_ARB_texture_barrier") ||
+                                 ctxInfo.hasExtension("GL_NV_texture_barrier");
+    } else {
+        fTextureBarrierSupport = ctxInfo.hasExtension("GL_NV_texture_barrier");
+    }
+
     // ARB_texture_rg is part of OpenGL 3.0, but mesa doesn't support GL_RED 
     // and GL_RG on FBO textures.
     if (!ctxInfo.isMesa()) {
@@ -255,39 +249,9 @@
         fES2CompatibilitySupport = true;
     }
 
-    if (kGLES_GrGLStandard == standard) {
-        if (ctxInfo.hasExtension("GL_EXT_shader_framebuffer_fetch")) {
-            fFBFetchNeedsCustomOutput = (version >= GR_GL_VER(3, 0));
-            fFBFetchSupport = true;
-            fFBFetchColorName = "gl_LastFragData[0]";
-            fFBFetchExtensionString = "GL_EXT_shader_framebuffer_fetch";
-        } else if (ctxInfo.hasExtension("GL_NV_shader_framebuffer_fetch")) {
-            // Actually, we haven't seen an ES3.0 device with this extension yet, so we don't know
-            fFBFetchNeedsCustomOutput = false;
-            fFBFetchSupport = true;
-            fFBFetchColorName = "gl_LastFragData[0]";
-            fFBFetchExtensionString = "GL_NV_shader_framebuffer_fetch";
-        } else if (ctxInfo.hasExtension("GL_ARM_shader_framebuffer_fetch")) {
-            // The arm extension also requires an additional flag which we will set onResetContext
-            fFBFetchNeedsCustomOutput = false;
-            fFBFetchSupport = true;
-            fFBFetchColorName = "gl_LastFragColorARM";
-            fFBFetchExtensionString = "GL_ARM_shader_framebuffer_fetch";
-        }
-    }
-
-    // Adreno GPUs have a tendency to drop tiles when there is a divide-by-zero in a shader
-    fDropsTileOnZeroDivide = kQualcomm_GrGLVendor == ctxInfo.vendor();
-
     this->initFSAASupport(ctxInfo, gli);
     this->initStencilFormats(ctxInfo);
 
-#ifdef SK_BUILD_FOR_MAC
-    // This relies on the fact that initFSAASupport() was already called.
-    fBindFBOToReadAndDrawForAddingAttachments = ctxInfo.isChromium() &&
-                                                this->usesMSAARenderBuffers();
-#endif
-
     /**************************************************************************
      * GrDrawTargetCaps fields
      **************************************************************************/
@@ -305,6 +269,23 @@
         fStencilWrapOpsSupport = true;
     }
 
+// Disabling advanced blend until we can resolve various bugs
+#if 0
+    if (kIntel_GrGLVendor != ctxInfo.vendor()) {
+        if (ctxInfo.hasExtension("GL_KHR_blend_equation_advanced_coherent") ||
+            ctxInfo.hasExtension("GL_NV_blend_equation_advanced_coherent")) {
+            fBlendEquationSupport = kAdvancedCoherent_BlendEquationSupport;
+        } else if (ctxInfo.hasExtension("GL_KHR_blend_equation_advanced") ||
+                   ctxInfo.hasExtension("GL_NV_blend_equation_advanced")) {
+            fBlendEquationSupport = kAdvanced_BlendEquationSupport;
+        } else {
+            fBlendEquationSupport = kBasic_BlendEquationSupport;
+        }
+    } else {
+        // On Intel platforms, KHR_blend_equation_advanced is not conformant.
+        fBlendEquationSupport = kBasic_BlendEquationSupport;
+    }
+#endif
     if (kGL_GrGLStandard == standard) {
         fMapBufferFlags = kCanMap_MapFlag; // we require VBO support and the desktop VBO
                                             // extension includes glMapBuffer.
@@ -346,64 +327,20 @@
         fMipMapSupport = fNPOTTextureTileSupport || ctxInfo.hasExtension("GL_IMG_texture_npot");
     }
 
-    fHWAALineSupport = (kGL_GrGLStandard == standard);
-
     GR_GL_GetIntegerv(gli, GR_GL_MAX_TEXTURE_SIZE, &fMaxTextureSize);
     GR_GL_GetIntegerv(gli, GR_GL_MAX_RENDERBUFFER_SIZE, &fMaxRenderTargetSize);
     // Our render targets are always created with textures as the color
     // attachment, hence this min:
     fMaxRenderTargetSize = SkTMin(fMaxTextureSize, fMaxRenderTargetSize);
 
-    fPathRenderingSupport = ctxInfo.hasExtension("GL_NV_path_rendering");
-
-    if (fPathRenderingSupport) {
-        if (kGL_GrGLStandard == standard) {
-            // We need one of the two possible texturing methods: using fixed function pipeline
-            // (PathTexGen, texcoords, ...)  or using the newer NVPR API additions that support
-            // setting individual fragment inputs with ProgramPathFragmentInputGen. The API
-            // additions are detected by checking the existence of the function. Eventually we may
-            // choose to remove the fixed function codepath.
-            // Set fMaxFixedFunctionTextureCoords = 0 here if you want to force
-            // ProgramPathFragmentInputGen usage on desktop.
-            fPathRenderingSupport = ctxInfo.hasExtension("GL_EXT_direct_state_access") &&
-                (fMaxFixedFunctionTextureCoords > 0 ||
-                 ((ctxInfo.version() >= GR_GL_VER(4,3) ||
-                   ctxInfo.hasExtension("GL_ARB_program_interface_query")) &&
-                  gli->fFunctions.fProgramPathFragmentInputGen));
-            if (fPathRenderingSupport) {
-                fNvprSupport = gli->fFunctions.fProgramPathFragmentInputGen ? kNormal_NvprSupport :
-                                                                              kLegacy_NvprSupport;
-            }
-        } else {
-            fPathRenderingSupport = ctxInfo.version() >= GR_GL_VER(3,1);
-            fNvprSupport = fPathRenderingSupport ? kNormal_NvprSupport : kNone_NvprSupport;
-        }
-    } else {
-        fNvprSupport = kNone_NvprSupport;
-    }
+    fFBMixedSamplesSupport = ctxInfo.hasExtension("GL_NV_framebuffer_mixed_samples");
 
     fGpuTracingSupport = ctxInfo.hasExtension("GL_EXT_debug_marker");
 
-    // For now these two are equivalent but we could have dst read in shader via some other method
-    fDstReadInShaderSupport = fFBFetchSupport;
-
     // Disable scratch texture reuse on Mali and Adreno devices
     fReuseScratchTextures = kARM_GrGLVendor != ctxInfo.vendor() &&
                             kQualcomm_GrGLVendor != ctxInfo.vendor();
 
-    // Enable supported shader-related caps
-    if (kGL_GrGLStandard == standard) {
-        fDualSourceBlendingSupport = ctxInfo.version() >= GR_GL_VER(3,3) ||
-                                     ctxInfo.hasExtension("GL_ARB_blend_func_extended");
-        fShaderDerivativeSupport = true;
-        // we don't support GL_ARB_geometry_shader4, just GL 3.2+ GS
-        fGeometryShaderSupport = ctxInfo.version() >= GR_GL_VER(3,2) &&
-                                 ctxInfo.glslGeneration() >= k150_GrGLSLGeneration;
-    } else {
-        fShaderDerivativeSupport = ctxInfo.version() >= GR_GL_VER(3, 0) ||
-                                   ctxInfo.hasExtension("GL_OES_standard_derivatives");
-    }
-
     if (GrGLCaps::kES_IMG_MsToTexture_MSFBOType == fMSFBOType) {
         GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES_IMG, &fMaxSampleCount);
     } else if (GrGLCaps::kNone_MSFBOType != fMSFBOType) {
@@ -432,7 +369,7 @@
     this->initConfigTexturableTable(ctxInfo, gli);
     this->initConfigRenderableTable(ctxInfo);
 
-    this->initShaderPrecisionTable(ctxInfo, gli);
+    reinterpret_cast<GrGLSLCaps*>(fShaderCaps.get())->init(ctxInfo, gli, *this);
 
     return true;
 }
@@ -798,7 +735,7 @@
 }
 
 namespace {
-const GrGLuint kUnknownBitCount = GrGLStencilBuffer::kUnknownBitCount;
+const GrGLuint kUnknownBitCount = GrGLStencilAttachment::kUnknownBitCount;
 }
 
 void GrGLCaps::initStencilFormats(const GrGLContextInfo& ctxInfo) {
@@ -855,88 +792,9 @@
     fStencilVerifiedColorConfigs.push_back_n(fStencilFormats.count());
 }
 
-static GrGLenum precision_to_gl_float_type(GrSLPrecision p) {
-    switch (p) {
-        case kLow_GrSLPrecision:
-            return GR_GL_LOW_FLOAT;
-        case kMedium_GrSLPrecision:
-            return GR_GL_MEDIUM_FLOAT;
-        case kHigh_GrSLPrecision:
-            return GR_GL_HIGH_FLOAT;
-    }
-    SkFAIL("Unknown precision.");
-    return -1;
-}
-
-static GrGLenum shader_type_to_gl_shader(GrShaderType type) {
-    switch (type) {
-        case kVertex_GrShaderType:
-            return GR_GL_VERTEX_SHADER;
-        case kGeometry_GrShaderType:
-            return GR_GL_GEOMETRY_SHADER;
-        case kFragment_GrShaderType:
-            return GR_GL_FRAGMENT_SHADER;
-    }
-    SkFAIL("Unknown shader type.");
-    return -1;
-}
-
-void GrGLCaps::initShaderPrecisionTable(const GrGLContextInfo& ctxInfo, const GrGLInterface* intf) {
-    if (kGLES_GrGLStandard == ctxInfo.standard() || ctxInfo.version() >= GR_GL_VER(4,1) ||
-        ctxInfo.hasExtension("GL_ARB_ES2_compatibility")) {
-        for (int s = 0; s < kGrShaderTypeCount; ++s) {
-            if (kGeometry_GrShaderType != s) {
-                GrShaderType shaderType = static_cast<GrShaderType>(s);
-                GrGLenum glShader = shader_type_to_gl_shader(shaderType);
-                PrecisionInfo* first = NULL;
-                fShaderPrecisionVaries = false;
-                for (int p = 0; p < kGrSLPrecisionCount; ++p) {
-                    GrSLPrecision precision = static_cast<GrSLPrecision>(p);
-                    GrGLenum glPrecision = precision_to_gl_float_type(precision);
-                    GrGLint range[2];
-                    GrGLint bits;
-                    GR_GL_GetShaderPrecisionFormat(intf, glShader, glPrecision, range, &bits);
-                    if (bits) {
-                        fFloatPrecisions[s][p].fLogRangeLow = range[0];
-                        fFloatPrecisions[s][p].fLogRangeHigh = range[1];
-                        fFloatPrecisions[s][p].fBits = bits;
-                        if (!first) {
-                            first = &fFloatPrecisions[s][p];
-                        } else if (!fShaderPrecisionVaries) {
-                            fShaderPrecisionVaries = (*first != fFloatPrecisions[s][p]);
-                        }
-                    }
-                }
-            }
-        }
-    } else {
-        // We're on a desktop GL that doesn't have precision info. Assume they're all 32bit float.
-        fShaderPrecisionVaries = false;
-        for (int s = 0; s < kGrShaderTypeCount; ++s) {
-            if (kGeometry_GrShaderType != s) {
-                for (int p = 0; p < kGrSLPrecisionCount; ++p) {
-                    fFloatPrecisions[s][p].fLogRangeLow = 127;
-                    fFloatPrecisions[s][p].fLogRangeHigh = 127;
-                    fFloatPrecisions[s][p].fBits = 23;
-                }
-            }
-        }
-    }
-    // GetShaderPrecisionFormat doesn't accept GL_GEOMETRY_SHADER as a shader type. Assume they're
-    // the same as the vertex shader. Only fragment shaders were ever allowed to omit support for
-    // highp. GS was added after GetShaderPrecisionFormat was added to the list of features that
-    // are recommended against.
-    if (fGeometryShaderSupport) {
-        for (int p = 0; p < kGrSLPrecisionCount; ++p) {
-            fFloatPrecisions[kGeometry_GrShaderType][p] = fFloatPrecisions[kVertex_GrShaderType][p];
-        }
-    }
-}
-
-
 void GrGLCaps::markColorConfigAndStencilFormatAsVerified(
                                     GrPixelConfig config,
-                                    const GrGLStencilBuffer::Format& format) {
+                                    const GrGLStencilAttachment::Format& format) {
 #if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
     return;
 #endif
@@ -959,7 +817,7 @@
 
 bool GrGLCaps::isColorConfigAndStencilFormatVerified(
                                 GrPixelConfig config,
-                                const GrGLStencilBuffer::Format& format) const {
+                                const GrGLStencilAttachment::Format& format) const {
 #if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
     return false;
 #endif
@@ -1033,14 +891,10 @@
 
     r.appendf("Core Profile: %s\n", (fIsCoreProfile ? "YES" : "NO"));
     r.appendf("MSAA Type: %s\n", kMSFBOExtStr[fMSFBOType]);
-    r.appendf("FB Fetch Support: %s\n", (fFBFetchSupport ? "YES" : "NO"));
     r.appendf("Invalidate FB Type: %s\n", kInvalidateFBTypeStr[fInvalidateFBType]);
     r.appendf("Map Buffer Type: %s\n", kMapBufferTypeStr[fMapBufferType]);
     r.appendf("Max FS Uniform Vectors: %d\n", fMaxFragmentUniformVectors);
     r.appendf("Max FS Texture Units: %d\n", fMaxFragmentTextureUnits);
-    if (!fIsCoreProfile) {
-        r.appendf("Max Fixed Function Texture Coords: %d\n", fMaxFixedFunctionTextureCoords);
-    }
     r.appendf("Max Vertex Attributes: %d\n", fMaxVertexAttributes);
     r.appendf("Support RGBA8 Render Buffer: %s\n", (fRGBA8RenderbufferSupport ? "YES": "NO"));
     r.appendf("BGRA is an internal format: %s\n", (fBGRAIsInternalFormat ? "YES": "NO"));
@@ -1061,6 +915,244 @@
     r.appendf("Use non-VBO for dynamic data: %s\n",
              (fUseNonVBOVertexAndIndexDynamicData ? "YES" : "NO"));
     r.appendf("Full screen clear is free: %s\n", (fFullClearIsFree ? "YES" : "NO"));
-    r.appendf("Drops tile on zero divide: %s\n", (fDropsTileOnZeroDivide ? "YES" : "NO"));
     return r;
 }
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+GrGLSLCaps::GrGLSLCaps() {
+    this->reset();
+}
+
+
+void GrGLSLCaps::reset() {
+    INHERITED::reset();
+
+    fDropsTileOnZeroDivide = false;
+    fFBFetchSupport = false;
+    fFBFetchNeedsCustomOutput = false;
+    fAdvBlendEqInteraction = kNotSupported_AdvBlendEqInteraction;
+    fFBFetchColorName = NULL;
+    fFBFetchExtensionString = NULL;
+}
+
+GrGLSLCaps::GrGLSLCaps(const GrGLSLCaps& caps) : GrShaderCaps() {
+    *this = caps;
+}
+
+GrGLSLCaps& GrGLSLCaps::operator= (const GrGLSLCaps& caps) {
+    INHERITED::operator=(caps);
+    fDropsTileOnZeroDivide = caps.fDropsTileOnZeroDivide;
+    fFBFetchSupport = caps.fFBFetchSupport;
+    fFBFetchNeedsCustomOutput = caps.fFBFetchNeedsCustomOutput;
+    fAdvBlendEqInteraction = caps.fAdvBlendEqInteraction;
+    fFBFetchColorName = caps.fFBFetchColorName;
+    fFBFetchExtensionString = caps.fFBFetchExtensionString;
+
+    return *this;
+}
+
+bool GrGLSLCaps::init(const GrGLContextInfo& ctxInfo,
+                      const GrGLInterface* gli,
+                      const GrGLCaps& glCaps) {
+    this->reset();
+    if (!ctxInfo.isInitialized()) {
+        return false;
+    }
+
+    GrGLStandard standard = ctxInfo.standard();
+    GrGLVersion version = ctxInfo.version();
+
+    /**************************************************************************
+    * Caps specific to GrGLSLCaps
+    **************************************************************************/
+
+    if (kGLES_GrGLStandard == standard) {
+        if (ctxInfo.hasExtension("GL_EXT_shader_framebuffer_fetch")) {
+            fFBFetchNeedsCustomOutput = (version >= GR_GL_VER(3, 0));
+            fFBFetchSupport = true;
+            fFBFetchColorName = "gl_LastFragData[0]";
+            fFBFetchExtensionString = "GL_EXT_shader_framebuffer_fetch";
+        }
+        else if (ctxInfo.hasExtension("GL_NV_shader_framebuffer_fetch")) {
+            // Actually, we haven't seen an ES3.0 device with this extension yet, so we don't know
+            fFBFetchNeedsCustomOutput = false;
+            fFBFetchSupport = true;
+            fFBFetchColorName = "gl_LastFragData[0]";
+            fFBFetchExtensionString = "GL_NV_shader_framebuffer_fetch";
+        }
+        else if (ctxInfo.hasExtension("GL_ARM_shader_framebuffer_fetch")) {
+            // The arm extension also requires an additional flag which we will set onResetContext
+            fFBFetchNeedsCustomOutput = false;
+            fFBFetchSupport = true;
+            fFBFetchColorName = "gl_LastFragColorARM";
+            fFBFetchExtensionString = "GL_ARM_shader_framebuffer_fetch";
+        }
+    }
+
+    // Adreno GPUs have a tendency to drop tiles when there is a divide-by-zero in a shader
+    fDropsTileOnZeroDivide = kQualcomm_GrGLVendor == ctxInfo.vendor();
+
+    /**************************************************************************
+    * GrShaderCaps fields
+    **************************************************************************/
+
+    fPathRenderingSupport = ctxInfo.hasExtension("GL_NV_path_rendering");
+
+    if (fPathRenderingSupport) {
+        if (kGL_GrGLStandard == standard) {
+            // We only support v1.3+ of GL_NV_path_rendering which allows us to
+            // set individual fragment inputs with ProgramPathFragmentInputGen. The API
+            // additions are detected by checking the existence of the function.
+            fPathRenderingSupport = ctxInfo.hasExtension("GL_EXT_direct_state_access") &&
+                ((ctxInfo.version() >= GR_GL_VER(4, 3) ||
+                ctxInfo.hasExtension("GL_ARB_program_interface_query")) &&
+                gli->fFunctions.fProgramPathFragmentInputGen);
+        }
+        else {
+            fPathRenderingSupport = ctxInfo.version() >= GR_GL_VER(3, 1);
+        }
+    }
+
+    // For now these two are equivalent but we could have dst read in shader via some other method
+    fDstReadInShaderSupport = fFBFetchSupport;
+
+    // Enable supported shader-related caps
+    if (kGL_GrGLStandard == standard) {
+        fDualSourceBlendingSupport = ctxInfo.version() >= GR_GL_VER(3, 3) ||
+            ctxInfo.hasExtension("GL_ARB_blend_func_extended");
+        fShaderDerivativeSupport = true;
+        // we don't support GL_ARB_geometry_shader4, just GL 3.2+ GS
+        fGeometryShaderSupport = ctxInfo.version() >= GR_GL_VER(3, 2) &&
+            ctxInfo.glslGeneration() >= k150_GrGLSLGeneration;
+    }
+    else {
+        fShaderDerivativeSupport = ctxInfo.version() >= GR_GL_VER(3, 0) ||
+            ctxInfo.hasExtension("GL_OES_standard_derivatives");
+    }
+
+    if (glCaps.advancedBlendEquationSupport()) {
+        bool coherent = glCaps.advancedCoherentBlendEquationSupport();
+        if (ctxInfo.hasExtension(coherent ? "GL_NV_blend_equation_advanced_coherent"
+                                          : "GL_NV_blend_equation_advanced")) {
+            fAdvBlendEqInteraction = kAutomatic_AdvBlendEqInteraction;
+        } else {
+            fAdvBlendEqInteraction = kGeneralEnable_AdvBlendEqInteraction;
+            // TODO: Use the following on any platform where "blend_support_all_equations" is slow.
+            //fAdvBlendEqInteraction = kSpecificEnables_AdvBlendEqInteraction;
+        }
+    }
+
+    this->initShaderPrecisionTable(ctxInfo, gli);
+
+    return true;
+}
+
+SkString GrGLSLCaps::dump() const {
+    SkString r = INHERITED::dump();
+
+    static const char* kAdvBlendEqInteractionStr[] = {
+        "Not Supported",
+        "Automatic",
+        "General Enable",
+        "Specific Enables",
+    };
+    GR_STATIC_ASSERT(0 == kNotSupported_AdvBlendEqInteraction);
+    GR_STATIC_ASSERT(1 == kAutomatic_AdvBlendEqInteraction);
+    GR_STATIC_ASSERT(2 == kGeneralEnable_AdvBlendEqInteraction);
+    GR_STATIC_ASSERT(3 == kSpecificEnables_AdvBlendEqInteraction);
+    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kAdvBlendEqInteractionStr) == kLast_AdvBlendEqInteraction + 1);
+
+    r.appendf("--- GLSL-Specific ---\n");
+
+    r.appendf("FB Fetch Support: %s\n", (fFBFetchSupport ? "YES" : "NO"));
+    r.appendf("Drops tile on zero divide: %s\n", (fDropsTileOnZeroDivide ? "YES" : "NO"));
+    r.appendf("Advanced blend equation interaction: %s\n",
+              kAdvBlendEqInteractionStr[fAdvBlendEqInteraction]);
+    return r;
+}
+
+static GrGLenum precision_to_gl_float_type(GrSLPrecision p) {
+    switch (p) {
+    case kLow_GrSLPrecision:
+        return GR_GL_LOW_FLOAT;
+    case kMedium_GrSLPrecision:
+        return GR_GL_MEDIUM_FLOAT;
+    case kHigh_GrSLPrecision:
+        return GR_GL_HIGH_FLOAT;
+    }
+    SkFAIL("Unknown precision.");
+    return -1;
+}
+
+static GrGLenum shader_type_to_gl_shader(GrShaderType type) {
+    switch (type) {
+    case kVertex_GrShaderType:
+        return GR_GL_VERTEX_SHADER;
+    case kGeometry_GrShaderType:
+        return GR_GL_GEOMETRY_SHADER;
+    case kFragment_GrShaderType:
+        return GR_GL_FRAGMENT_SHADER;
+    }
+    SkFAIL("Unknown shader type.");
+    return -1;
+}
+
+void GrGLSLCaps::initShaderPrecisionTable(const GrGLContextInfo& ctxInfo,
+                                          const GrGLInterface* intf) {
+    if (kGLES_GrGLStandard == ctxInfo.standard() || ctxInfo.version() >= GR_GL_VER(4, 1) ||
+        ctxInfo.hasExtension("GL_ARB_ES2_compatibility")) {
+        for (int s = 0; s < kGrShaderTypeCount; ++s) {
+            if (kGeometry_GrShaderType != s) {
+                GrShaderType shaderType = static_cast<GrShaderType>(s);
+                GrGLenum glShader = shader_type_to_gl_shader(shaderType);
+                PrecisionInfo* first = NULL;
+                fShaderPrecisionVaries = false;
+                for (int p = 0; p < kGrSLPrecisionCount; ++p) {
+                    GrSLPrecision precision = static_cast<GrSLPrecision>(p);
+                    GrGLenum glPrecision = precision_to_gl_float_type(precision);
+                    GrGLint range[2];
+                    GrGLint bits;
+                    GR_GL_GetShaderPrecisionFormat(intf, glShader, glPrecision, range, &bits);
+                    if (bits) {
+                        fFloatPrecisions[s][p].fLogRangeLow = range[0];
+                        fFloatPrecisions[s][p].fLogRangeHigh = range[1];
+                        fFloatPrecisions[s][p].fBits = bits;
+                        if (!first) {
+                            first = &fFloatPrecisions[s][p];
+                        }
+                        else if (!fShaderPrecisionVaries) {
+                            fShaderPrecisionVaries = (*first != fFloatPrecisions[s][p]);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    else {
+        // We're on a desktop GL that doesn't have precision info. Assume they're all 32bit float.
+        fShaderPrecisionVaries = false;
+        for (int s = 0; s < kGrShaderTypeCount; ++s) {
+            if (kGeometry_GrShaderType != s) {
+                for (int p = 0; p < kGrSLPrecisionCount; ++p) {
+                    fFloatPrecisions[s][p].fLogRangeLow = 127;
+                    fFloatPrecisions[s][p].fLogRangeHigh = 127;
+                    fFloatPrecisions[s][p].fBits = 23;
+                }
+            }
+        }
+    }
+    // GetShaderPrecisionFormat doesn't accept GL_GEOMETRY_SHADER as a shader type. Assume they're
+    // the same as the vertex shader. Only fragment shaders were ever allowed to omit support for
+    // highp. GS was added after GetShaderPrecisionFormat was added to the list of features that
+    // are recommended against.
+    if (fGeometryShaderSupport) {
+        for (int p = 0; p < kGrSLPrecisionCount; ++p) {
+            fFloatPrecisions[kGeometry_GrShaderType][p] = fFloatPrecisions[kVertex_GrShaderType][p];
+        }
+    }
+}
+
+
+
+
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 48aeb0b..25c7889 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -10,12 +10,13 @@
 #define GrGLCaps_DEFINED
 
 #include "GrDrawTargetCaps.h"
-#include "GrGLStencilBuffer.h"
+#include "GrGLStencilAttachment.h"
 #include "SkChecksum.h"
 #include "SkTHash.h"
 #include "SkTArray.h"
 
 class GrGLContextInfo;
+class GrGLSLCaps;
 
 /**
  * Stores some capabilities of a GL context. Most are determined by the GL
@@ -26,7 +27,7 @@
 public:
     SK_DECLARE_INST_COUNT(GrGLCaps)
 
-    typedef GrGLStencilBuffer::Format StencilFormat;
+    typedef GrGLStencilAttachment::Format StencilFormat;
 
     /**
      * The type of MSAA for FBOs supported. Different extensions have different
@@ -99,7 +100,7 @@
     /**
      * Resets the caps such that nothing is supported.
      */
-    void reset() SK_OVERRIDE;
+    void reset() override;
 
     /**
      * Initializes the GrGLCaps to the set of features supported in the current
@@ -132,7 +133,7 @@
      */
     void markColorConfigAndStencilFormatAsVerified(
                     GrPixelConfig config,
-                    const GrGLStencilBuffer::Format& format);
+                    const GrGLStencilAttachment::Format& format);
 
     /**
      * Call to check whether color config / stencil format pair has already
@@ -140,7 +141,7 @@
      */
     bool isColorConfigAndStencilFormatVerified(
                     GrPixelConfig config,
-                    const GrGLStencilBuffer::Format& format) const;
+                    const GrGLStencilAttachment::Format& format) const;
 
     /**
      * Reports the type of MSAA FBO support.
@@ -165,18 +166,7 @@
                kES_EXT_MsToTexture_MSFBOType == fMSFBOType;
     }
 
-    /**
-     * Some helper functions for encapsulating various extensions to read FB Buffer on openglES
-     *
-     * TODO(joshualitt) On desktop opengl 4.2+ we can achieve something similar to this effect
-     */
-    bool fbFetchSupport() const { return fFBFetchSupport; }
-
-    bool fbFetchNeedsCustomOutput() const { return fFBFetchNeedsCustomOutput; }
-
-    const char* fbFetchColorName() const { return fFBFetchColorName; }
-
-    const char* fbFetchExtensionString() const { return fFBFetchExtensionString; }
+    bool fbMixedSamplesSupport() const { return fFBMixedSamplesSupport; }
 
     InvalidateFBType invalidateFBType() const { return fInvalidateFBType; }
 
@@ -201,9 +191,6 @@
     /// maximum number of texture units accessible in the fragment shader.
     int maxFragmentTextureUnits() const { return fMaxFragmentTextureUnits; }
 
-    /// maximum number of fixed-function texture coords, or zero if no fixed-function.
-    int maxFixedFunctionTextureCoords() const { return fMaxFixedFunctionTextureCoords; }
-
     /// ES requires an extension to support RGBA8 in RenderBufferStorage
     bool rgba8RenderbufferSupport() const { return fRGBA8RenderbufferSupport; }
 
@@ -266,16 +253,10 @@
 
     bool fullClearIsFree() const { return fFullClearIsFree; }
 
-    bool dropsTileOnZeroDivide() const { return fDropsTileOnZeroDivide; }
-    
-    bool bindFBOToReadAndDrawForAddingAttachments() const {
-        return fBindFBOToReadAndDrawForAddingAttachments;
-    }
-
     /**
      * Returns a string containing the caps info.
      */
-    SkString dump() const SK_OVERRIDE;
+    SkString dump() const override;
 
     /**
      * LATC can appear under one of three possible names. In order to know
@@ -290,17 +271,7 @@
 
     LATCAlias latcAlias() const { return fLATCAlias; }
 
-    /**
-     * Which type of path rendering is supported, if any
-     * TODO delete this when we only support normal non-legacy nvpr
-     */
-    enum NvprSupport {
-        kNone_NvprSupport,
-        kLegacy_NvprSupport,
-        kNormal_NvprSupport,
-    };
-
-    NvprSupport nvprSupport() const { return fNvprSupport; }
+    GrGLSLCaps* glslCaps() const { return reinterpret_cast<GrGLSLCaps*>(fShaderCaps.get()); }
 
 private:
     /**
@@ -346,9 +317,6 @@
     void initConfigRenderableTable(const GrGLContextInfo&);
     void initConfigTexturableTable(const GrGLContextInfo&, const GrGLInterface*);
 
-    // Must be called after fGeometryShaderSupport is initialized.
-    void initShaderPrecisionTable(const GrGLContextInfo&, const GrGLInterface*);
-
     bool doReadPixelsSupported(const GrGLInterface* intf, GrGLenum format, GrGLenum type) const;
 
     // tracks configs that have been verified to pass the FBO completeness when
@@ -364,13 +332,11 @@
     int fMaxFragmentUniformVectors;
     int fMaxVertexAttributes;
     int fMaxFragmentTextureUnits;
-    int fMaxFixedFunctionTextureCoords;
 
     MSFBOType           fMSFBOType;
     InvalidateFBType    fInvalidateFBType;
     MapBufferType       fMapBufferType;
     LATCAlias           fLATCAlias;
-    NvprSupport         fNvprSupport;
 
     bool fRGBA8RenderbufferSupport : 1;
     bool fBGRAIsInternalFormat : 1;
@@ -390,13 +356,7 @@
     bool fUseNonVBOVertexAndIndexDynamicData : 1;
     bool fIsCoreProfile : 1;
     bool fFullClearIsFree : 1;
-    bool fDropsTileOnZeroDivide : 1;
-    bool fFBFetchSupport : 1;
-    bool fFBFetchNeedsCustomOutput : 1;
-    bool fBindFBOToReadAndDrawForAddingAttachments : 1;
-
-    const char* fFBFetchColorName;
-    const char* fFBFetchExtensionString;
+    bool fFBMixedSamplesSupport : 1;
 
     struct ReadPixelsSupportedFormat {
         GrGLenum fFormat;
@@ -408,15 +368,97 @@
                 && fType      == rhs.fType
                 && fFboFormat == rhs.fFboFormat;
         }
-        static uint32_t Hash(const ReadPixelsSupportedFormat& r) {
-            return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&r), sizeof(r));
-        }
     };
-
-    mutable SkTHashMap<ReadPixelsSupportedFormat, bool, ReadPixelsSupportedFormat::Hash>
-        fReadPixelsSupportedCache;
+    mutable SkTHashMap<ReadPixelsSupportedFormat, bool> fReadPixelsSupportedCache;
 
     typedef GrDrawTargetCaps INHERITED;
 };
 
+
+class GrGLSLCaps : public GrShaderCaps {
+public:
+    SK_DECLARE_INST_COUNT(GrGLSLCaps)
+
+    /**
+    * Indicates how GLSL must interact with advanced blend equations. The KHR extension requires
+    * special layout qualifiers in the fragment shader.
+    */
+    enum AdvBlendEqInteraction {
+        kNotSupported_AdvBlendEqInteraction,     //<! No _blend_equation_advanced extension
+        kAutomatic_AdvBlendEqInteraction,        //<! No interaction required
+        kGeneralEnable_AdvBlendEqInteraction,    //<! layout(blend_support_all_equations) out
+        kSpecificEnables_AdvBlendEqInteraction,  //<! Specific layout qualifiers per equation
+
+        kLast_AdvBlendEqInteraction = kSpecificEnables_AdvBlendEqInteraction
+    };
+
+    /**
+     * Creates a GrGLSLCaps that advertises no support for any extensions,
+     * formats, etc. Call init to initialize from a GrGLContextInfo.
+     */
+    GrGLSLCaps();
+    ~GrGLSLCaps() override {}
+
+    GrGLSLCaps(const GrGLSLCaps& caps);
+
+    GrGLSLCaps& operator = (const GrGLSLCaps& caps);
+
+    /**
+     * Resets the caps such that nothing is supported.
+     */
+    void reset() override;
+
+    /**
+     * Initializes the GrGLSLCaps to the set of features supported in the current
+     * OpenGL context accessible via ctxInfo.
+     */
+    bool init(const GrGLContextInfo&, const GrGLInterface*, const GrGLCaps&);
+
+    /**
+     * Some helper functions for encapsulating various extensions to read FB Buffer on openglES
+     *
+     * TODO(joshualitt) On desktop opengl 4.2+ we can achieve something similar to this effect
+     */
+    bool fbFetchSupport() const { return fFBFetchSupport; }
+
+    bool fbFetchNeedsCustomOutput() const { return fFBFetchNeedsCustomOutput; }
+
+    const char* fbFetchColorName() const { return fFBFetchColorName; }
+
+    const char* fbFetchExtensionString() const { return fFBFetchExtensionString; }
+
+    bool dropsTileOnZeroDivide() const { return fDropsTileOnZeroDivide; }
+
+    AdvBlendEqInteraction advBlendEqInteraction() const { return fAdvBlendEqInteraction; }
+
+    bool mustEnableAdvBlendEqs() const {
+        return fAdvBlendEqInteraction >= kGeneralEnable_AdvBlendEqInteraction;
+    }
+
+    bool mustEnableSpecificAdvBlendEqs() const {
+        return fAdvBlendEqInteraction == kSpecificEnables_AdvBlendEqInteraction;
+    }
+
+    /**
+    * Returns a string containing the caps info.
+    */
+    SkString dump() const override;
+
+private:
+    // Must be called after fGeometryShaderSupport is initialized.
+    void initShaderPrecisionTable(const GrGLContextInfo&, const GrGLInterface*);
+
+    bool fDropsTileOnZeroDivide : 1;
+    bool fFBFetchSupport : 1;
+    bool fFBFetchNeedsCustomOutput : 1;
+
+    const char* fFBFetchColorName;
+    const char* fFBFetchExtensionString;
+
+    AdvBlendEqInteraction fAdvBlendEqInteraction;
+
+    typedef GrShaderCaps INHERITED;
+};
+
+
 #endif
diff --git a/src/gpu/gl/GrGLCreateNullInterface.cpp b/src/gpu/gl/GrGLCreateNullInterface.cpp
index c86a0bd..512c672 100644
--- a/src/gpu/gl/GrGLCreateNullInterface.cpp
+++ b/src/gpu/gl/GrGLCreateNullInterface.cpp
@@ -357,6 +357,7 @@
     functions->fBindTexture = nullGLBindTexture;
     functions->fBindVertexArray = nullGLBindVertexArray;
     functions->fBlendColor = noOpGLBlendColor;
+    functions->fBlendEquation = noOpGLBlendEquation;
     functions->fBlendFunc = noOpGLBlendFunc;
     functions->fBufferData = nullGLBufferData;
     functions->fBufferSubData = noOpGLBufferSubData;
diff --git a/src/gpu/gl/GrGLDefines.h b/src/gpu/gl/GrGLDefines.h
index fc44252..511659c 100644
--- a/src/gpu/gl/GrGLDefines.h
+++ b/src/gpu/gl/GrGLDefines.h
@@ -45,6 +45,28 @@
 /*      GL_GEQUAL */
 /*      GL_ALWAYS */
 
+/* Basic OpenGL blend equations */
+#define GR_GL_FUNC_ADD                       0x8006
+#define GR_GL_FUNC_SUBTRACT                  0x800A
+#define GR_GL_FUNC_REVERSE_SUBTRACT          0x800B
+
+/* GL_KHR_blend_equation_advanced */
+#define GR_GL_SCREEN                         0x9295
+#define GR_GL_OVERLAY                        0x9296
+#define GR_GL_DARKEN                         0x9297
+#define GR_GL_LIGHTEN                        0x9298
+#define GR_GL_COLORDODGE                     0x9299
+#define GR_GL_COLORBURN                      0x929A
+#define GR_GL_HARDLIGHT                      0x929B
+#define GR_GL_SOFTLIGHT                      0x929C
+#define GR_GL_DIFFERENCE                     0x929E
+#define GR_GL_EXCLUSION                      0x92A0
+#define GR_GL_MULTIPLY                       0x9294
+#define GR_GL_HSL_HUE                        0x92AD
+#define GR_GL_HSL_SATURATION                 0x92AE
+#define GR_GL_HSL_COLOR                      0x92AF
+#define GR_GL_HSL_LUMINOSITY                 0x92B0
+
 /* BlendingFactorDest */
 #define GR_GL_ZERO                           0
 #define GR_GL_ONE                            1
@@ -211,80 +233,80 @@
 #define GR_GL_COMPRESSED_TEXTURE_FORMATS     0x86A3
 
 /* Compressed Texture Formats */
-#define GR_GL_COMPRESSED_RGB_S3TC_DXT1_EXT         0x83F0
-#define GR_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT        0x83F1
-#define GR_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT        0x83F2
-#define GR_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT        0x83F3
+#define GR_GL_COMPRESSED_RGB_S3TC_DXT1_EXT             0x83F0
+#define GR_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT            0x83F1
+#define GR_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT            0x83F2
+#define GR_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT            0x83F3
 
-#define GR_GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG      0x8C00
-#define GR_GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG      0x8C01
-#define GR_GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG     0x8C02
-#define GR_GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG     0x8C03
+#define GR_GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG          0x8C00
+#define GR_GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG          0x8C01
+#define GR_GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG         0x8C02
+#define GR_GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG         0x8C03
 
-#define GR_GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG     0x9137
-#define GR_GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG     0x9138
+#define GR_GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG         0x9137
+#define GR_GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG         0x9138
 
-#define GR_GL_COMPRESSED_RGB8_ETC1                 0x8D64
+#define GR_GL_COMPRESSED_ETC1_RGB8                     0x8D64
 
-#define GR_GL_COMPRESSED_R11                       0x9270
-#define GR_GL_COMPRESSED_SIGNED_R11                0x9271
-#define GR_GL_COMPRESSED_RG11                      0x9272
-#define GR_GL_COMPRESSED_SIGNED_RG11               0x9273
+#define GR_GL_COMPRESSED_R11_EAC                       0x9270
+#define GR_GL_COMPRESSED_SIGNED_R11_EAC                0x9271
+#define GR_GL_COMPRESSED_RG11_EAC                      0x9272
+#define GR_GL_COMPRESSED_SIGNED_RG11_EAC               0x9273
 
-#define GR_GL_COMPRESSED_RGB8_ETC2                 0x9274
-#define GR_GL_COMPRESSED_SRGB8                     0x9275
-#define GR_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1  0x9276
-#define GR_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1 0x9277
-#define GR_GL_COMPRESSED_RGBA8_ETC2                0x9278
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ETC2         0x9279
+#define GR_GL_COMPRESSED_RGB8_ETC2                     0x9274
+#define GR_GL_COMPRESSED_SRGB8                         0x9275
+#define GR_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1      0x9276
+#define GR_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1     0x9277
+#define GR_GL_COMPRESSED_RGBA8_ETC2                    0x9278
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ETC2             0x9279
 
-#define GR_GL_COMPRESSED_LUMINANCE_LATC1           0x8C70
-#define GR_GL_COMPRESSED_SIGNED_LUMINANCE_LATC1    0x8C71
-#define GR_GL_COMPRESSED_LUMINANCE_ALPHA_LATC2     0x8C72
-#define GR_GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2 0x8C73
+#define GR_GL_COMPRESSED_LUMINANCE_LATC1               0x8C70
+#define GR_GL_COMPRESSED_SIGNED_LUMINANCE_LATC1        0x8C71
+#define GR_GL_COMPRESSED_LUMINANCE_ALPHA_LATC2         0x8C72
+#define GR_GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2  0x8C73
 
-#define GR_GL_COMPRESSED_RED_RGTC1                 0x8DBB
-#define GR_GL_COMPRESSED_SIGNED_RED_RGTC1          0x8DBC
-#define GR_GL_COMPRESSED_RED_GREEN_RGTC2           0x8DBD
-#define GR_GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2    0x8DBE
+#define GR_GL_COMPRESSED_RED_RGTC1                     0x8DBB
+#define GR_GL_COMPRESSED_SIGNED_RED_RGTC1              0x8DBC
+#define GR_GL_COMPRESSED_RED_GREEN_RGTC2               0x8DBD
+#define GR_GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2        0x8DBE
 
-#define GR_GL_COMPRESSED_3DC_X                     0x87F9
-#define GR_GL_COMPRESSED_3DC_XY                    0x87FA
+#define GR_GL_COMPRESSED_3DC_X                         0x87F9
+#define GR_GL_COMPRESSED_3DC_XY                        0x87FA
 
-#define GR_GL_COMPRESSED_RGBA_BPTC_UNORM           0x8E8C
-#define GR_GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM     0x8E8D
-#define GR_GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT     0x8E8E
-#define GR_GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT   0x8E8F
+#define GR_GL_COMPRESSED_RGBA_BPTC_UNORM               0x8E8C
+#define GR_GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM         0x8E8D
+#define GR_GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT         0x8E8E
+#define GR_GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT       0x8E8F
 
-#define GR_GL_COMPRESSED_RGBA_ASTC_4x4             0x93B0
-#define GR_GL_COMPRESSED_RGBA_ASTC_5x4             0x93B1
-#define GR_GL_COMPRESSED_RGBA_ASTC_5x5             0x93B2
-#define GR_GL_COMPRESSED_RGBA_ASTC_6x5             0x93B3
-#define GR_GL_COMPRESSED_RGBA_ASTC_6x6             0x93B4
-#define GR_GL_COMPRESSED_RGBA_ASTC_8x5             0x93B5
-#define GR_GL_COMPRESSED_RGBA_ASTC_8x6             0x93B6
-#define GR_GL_COMPRESSED_RGBA_ASTC_8x8             0x93B7
-#define GR_GL_COMPRESSED_RGBA_ASTC_10x5            0x93B8
-#define GR_GL_COMPRESSED_RGBA_ASTC_10x6            0x93B9
-#define GR_GL_COMPRESSED_RGBA_ASTC_10x8            0x93BA
-#define GR_GL_COMPRESSED_RGBA_ASTC_10x10           0x93BB
-#define GR_GL_COMPRESSED_RGBA_ASTC_12x10           0x93BC
-#define GR_GL_COMPRESSED_RGBA_ASTC_12x12           0x93BD
+#define GR_GL_COMPRESSED_RGBA_ASTC_4x4_KHR             0x93B0
+#define GR_GL_COMPRESSED_RGBA_ASTC_5x4_KHR             0x93B1
+#define GR_GL_COMPRESSED_RGBA_ASTC_5x5_KHR             0x93B2
+#define GR_GL_COMPRESSED_RGBA_ASTC_6x5_KHR             0x93B3
+#define GR_GL_COMPRESSED_RGBA_ASTC_6x6_KHR             0x93B4
+#define GR_GL_COMPRESSED_RGBA_ASTC_8x5_KHR             0x93B5
+#define GR_GL_COMPRESSED_RGBA_ASTC_8x6_KHR             0x93B6
+#define GR_GL_COMPRESSED_RGBA_ASTC_8x8_KHR             0x93B7
+#define GR_GL_COMPRESSED_RGBA_ASTC_10x5_KHR            0x93B8
+#define GR_GL_COMPRESSED_RGBA_ASTC_10x6_KHR            0x93B9
+#define GR_GL_COMPRESSED_RGBA_ASTC_10x8_KHR            0x93BA
+#define GR_GL_COMPRESSED_RGBA_ASTC_10x10_KHR           0x93BB
+#define GR_GL_COMPRESSED_RGBA_ASTC_12x10_KHR           0x93BC
+#define GR_GL_COMPRESSED_RGBA_ASTC_12x12_KHR           0x93BD
 
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4     0x93D0
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4     0x93D1
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5     0x93D2
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5     0x93D3
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6     0x93D4
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5     0x93D5
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6     0x93D6
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8     0x93D7
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5    0x93D8
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6    0x93D9
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8    0x93DA
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10   0x93DB
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10   0x93DC
-#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12   0x93DD
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR     0x93D0
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR     0x93D1
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR     0x93D2
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR     0x93D3
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR     0x93D4
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR     0x93D5
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR     0x93D6
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR     0x93D7
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR    0x93D8
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR    0x93D9
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR    0x93DA
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR   0x93DB
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR   0x93DC
+#define GR_GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR   0x93DD
 
 /* HintMode */
 #define GR_GL_DONT_CARE                      0x1100
diff --git a/src/gpu/gl/GrGLGeometryProcessor.cpp b/src/gpu/gl/GrGLGeometryProcessor.cpp
index 79bcb4c..60c0043 100644
--- a/src/gpu/gl/GrGLGeometryProcessor.cpp
+++ b/src/gpu/gl/GrGLGeometryProcessor.cpp
@@ -89,23 +89,6 @@
     }
 }
 
-void
-GrGLGeometryProcessor::setTransformData(const GrPrimitiveProcessor& primProc,
-                                        const GrGLProgramDataManager& pdman,
-                                        int index,
-                                        const SkTArray<const GrCoordTransform*, true>& transforms) {
-    SkSTArray<2, Transform, true>& procTransforms = fInstalledTransforms[index];
-    int numTransforms = transforms.count();
-    for (int t = 0; t < numTransforms; ++t) {
-        SkASSERT(procTransforms[t].fHandle.isValid());
-        const SkMatrix& transform = GetTransformMatrix(primProc.localMatrix(), *transforms[t]);
-        if (!procTransforms[t].fCurrentValue.cheapEqualTo(transform)) {
-            pdman.setSkMatrix(procTransforms[t].fHandle.convertToUniformHandle(), transform);
-            procTransforms[t].fCurrentValue = transform;
-        }
-    }
-}
-
 void GrGLGeometryProcessor::setupPosition(GrGLGPBuilder* pb,
                                           GrGPArgs* gpArgs,
                                           const char* posName,
diff --git a/src/gpu/gl/GrGLGeometryProcessor.h b/src/gpu/gl/GrGLGeometryProcessor.h
index 2630bde..dfb2b90 100644
--- a/src/gpu/gl/GrGLGeometryProcessor.h
+++ b/src/gpu/gl/GrGLGeometryProcessor.h
@@ -20,21 +20,34 @@
 class GrGLGeometryProcessor : public GrGLPrimitiveProcessor {
 public:
     /* Any general emit code goes in the base class emitCode.  Subclasses override onEmitCode */
-    void emitCode(EmitArgs&) SK_OVERRIDE;
+    void emitCode(EmitArgs&) override;
 
-    void setTransformData(const GrPrimitiveProcessor&,
-                          const GrGLProgramDataManager&,
-                          int index,
-                          const SkTArray<const GrCoordTransform*, true>& transforms);
+    // By default we use the identity matrix
+    virtual void setTransformData(const GrPrimitiveProcessor&,
+                                  const GrGLProgramDataManager& pdman,
+                                  int index,
+                                  const SkTArray<const GrCoordTransform*, true>& transforms) {
+        this->setTransformDataMatrix(SkMatrix::I(), pdman, index, transforms);
+    }
+
+    // A helper which subclasses can use if needed
+    template <class GeometryProcessor>
+    void setTransformDataHelper(const GrPrimitiveProcessor& primProc,
+                                const GrGLProgramDataManager& pdman,
+                                int index,
+                                const SkTArray<const GrCoordTransform*, true>& transforms) {
+        const GeometryProcessor& gp = primProc.cast<GeometryProcessor>();
+        this->setTransformDataMatrix(gp.localMatrix(), pdman, index, transforms);
+    }
 
 protected:
-    // Many GrGeometryProcessors do not need explicit local coords
+    // A helper for subclasses which don't have an explicit local matrix
     void emitTransforms(GrGLGPBuilder* gp,
                         const GrShaderVar& posVar,
-                        const SkMatrix& localMatrix,
+                        const char* localCoords,
                         const TransformsIn& tin,
                         TransformsOut* tout) {
-        this->emitTransforms(gp, posVar, posVar.c_str(), localMatrix, tin, tout);
+        this->emitTransforms(gp, posVar, localCoords, SkMatrix::I(), tin, tout);
     }
 
     void emitTransforms(GrGLGPBuilder*,
@@ -54,7 +67,7 @@
     void setupPosition(GrGLGPBuilder* pb,
                        GrGPArgs* gpArgs,
                        const char* posName,
-                       const SkMatrix& mat);
+                       const SkMatrix& mat = SkMatrix::I());
 
     static uint32_t ComputePosKey(const SkMatrix& mat) {
         if (mat.isIdentity()) {
@@ -67,6 +80,22 @@
     }
 
 private:
+    void setTransformDataMatrix(const SkMatrix& localMatrix,
+                                const GrGLProgramDataManager& pdman,
+                                int index,
+                                const SkTArray<const GrCoordTransform*, true>& transforms) {
+        SkSTArray<2, Transform, true>& procTransforms = fInstalledTransforms[index];
+        int numTransforms = transforms.count();
+        for (int t = 0; t < numTransforms; ++t) {
+            SkASSERT(procTransforms[t].fHandle.isValid());
+            const SkMatrix& transform = GetTransformMatrix(localMatrix, *transforms[t]);
+            if (!procTransforms[t].fCurrentValue.cheapEqualTo(transform)) {
+                pdman.setSkMatrix(procTransforms[t].fHandle.convertToUniformHandle(), transform);
+                procTransforms[t].fCurrentValue = transform;
+            }
+        }
+    }
+
     virtual void onEmitCode(EmitArgs&, GrGPArgs*) = 0;
 
     typedef GrGLPrimitiveProcessor INHERITED;
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 51fc42b..6d97804 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -7,7 +7,7 @@
 
 
 #include "GrGLGpu.h"
-#include "GrGLStencilBuffer.h"
+#include "GrGLStencilAttachment.h"
 #include "GrGLTextureRenderTarget.h"
 #include "GrGpuResourcePriv.h"
 #include "GrPipeline.h"
@@ -16,6 +16,7 @@
 #include "GrTemplates.h"
 #include "GrTexturePriv.h"
 #include "GrTypes.h"
+#include "GrVertices.h"
 #include "SkStrokeRec.h"
 #include "SkTemplates.h"
 
@@ -38,6 +39,49 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 
+static const GrGLenum gXfermodeEquation2Blend[] = {
+    // Basic OpenGL blend equations.
+    GR_GL_FUNC_ADD,
+    GR_GL_FUNC_SUBTRACT,
+    GR_GL_FUNC_REVERSE_SUBTRACT,
+
+    // GL_KHR_blend_equation_advanced.
+    GR_GL_SCREEN,
+    GR_GL_OVERLAY,
+    GR_GL_DARKEN,
+    GR_GL_LIGHTEN,
+    GR_GL_COLORDODGE,
+    GR_GL_COLORBURN,
+    GR_GL_HARDLIGHT,
+    GR_GL_SOFTLIGHT,
+    GR_GL_DIFFERENCE,
+    GR_GL_EXCLUSION,
+    GR_GL_MULTIPLY,
+    GR_GL_HSL_HUE,
+    GR_GL_HSL_SATURATION,
+    GR_GL_HSL_COLOR,
+    GR_GL_HSL_LUMINOSITY
+};
+GR_STATIC_ASSERT(0 == kAdd_GrBlendEquation);
+GR_STATIC_ASSERT(1 == kSubtract_GrBlendEquation);
+GR_STATIC_ASSERT(2 == kReverseSubtract_GrBlendEquation);
+GR_STATIC_ASSERT(3 == kScreen_GrBlendEquation);
+GR_STATIC_ASSERT(4 == kOverlay_GrBlendEquation);
+GR_STATIC_ASSERT(5 == kDarken_GrBlendEquation);
+GR_STATIC_ASSERT(6 == kLighten_GrBlendEquation);
+GR_STATIC_ASSERT(7 == kColorDodge_GrBlendEquation);
+GR_STATIC_ASSERT(8 == kColorBurn_GrBlendEquation);
+GR_STATIC_ASSERT(9 == kHardLight_GrBlendEquation);
+GR_STATIC_ASSERT(10 == kSoftLight_GrBlendEquation);
+GR_STATIC_ASSERT(11 == kDifference_GrBlendEquation);
+GR_STATIC_ASSERT(12 == kExclusion_GrBlendEquation);
+GR_STATIC_ASSERT(13 == kMultiply_GrBlendEquation);
+GR_STATIC_ASSERT(14 == kHSLHue_GrBlendEquation);
+GR_STATIC_ASSERT(15 == kHSLSaturation_GrBlendEquation);
+GR_STATIC_ASSERT(16 == kHSLColor_GrBlendEquation);
+GR_STATIC_ASSERT(17 == kHSLLuminosity_GrBlendEquation);
+GR_STATIC_ASSERT(SK_ARRAY_COUNT(gXfermodeEquation2Blend) == kGrBlendEquationCnt);
+
 static const GrGLenum gXfermodeCoeff2Blend[] = {
     GR_GL_ZERO,
     GR_GL_ONE,
@@ -85,8 +129,7 @@
         false,
     };
     return gCoeffReferencesBlendConst[coeff];
-    GR_STATIC_ASSERT(kTotalGrBlendCoeffCount ==
-                     SK_ARRAY_COUNT(gCoeffReferencesBlendConst));
+    GR_STATIC_ASSERT(kGrBlendCoeffCnt == SK_ARRAY_COUNT(gCoeffReferencesBlendConst));
 
     GR_STATIC_ASSERT(0 == kZero_GrBlendCoeff);
     GR_STATIC_ASSERT(1 == kOne_GrBlendCoeff);
@@ -109,8 +152,7 @@
     GR_STATIC_ASSERT(17 == kIS2A_GrBlendCoeff);
 
     // assertion for gXfermodeCoeff2Blend have to be in GrGpu scope
-    GR_STATIC_ASSERT(kTotalGrBlendCoeffCount ==
-                     SK_ARRAY_COUNT(gXfermodeCoeff2Blend));
+    GR_STATIC_ASSERT(kGrBlendCoeffCnt == SK_ARRAY_COUNT(gXfermodeCoeff2Blend));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -151,8 +193,11 @@
 
     fLastSuccessfulStencilFmtIdx = 0;
     fHWProgramID = 0;
+    fTempSrcFBOID = 0;
+    fTempDstFBOID = 0;
+    fStencilClearFBOID = 0;
 
-    if (this->glCaps().pathRenderingSupport()) {
+    if (this->glCaps().shaderCaps()->pathRenderingSupport()) {
         fPathRendering.reset(new GrGLPathRendering(this));
     }
 }
@@ -165,17 +210,14 @@
         GL_CALL(UseProgram(0));
     }
 
-    if (fTempSrcFBO) {
-        fTempSrcFBO->release(this->glInterface());
-        fTempSrcFBO.reset(NULL);
+    if (0 != fTempSrcFBOID) {
+        GL_CALL(DeleteFramebuffers(1, &fTempSrcFBOID));
     }
-    if (fTempDstFBO) {
-        fTempDstFBO->release(this->glInterface());
-        fTempDstFBO.reset(NULL);
+    if (0 != fTempDstFBOID) {
+        GL_CALL(DeleteFramebuffers(1, &fTempDstFBOID));
     }
-    if (fStencilClearFBO) {
-        fStencilClearFBO->release(this->glInterface());
-        fStencilClearFBO.reset(NULL);
+    if (0 != fStencilClearFBOID) {
+        GL_CALL(DeleteFramebuffers(1, &fStencilClearFBOID));
     }
 
     delete fProgramCache;
@@ -185,20 +227,10 @@
     INHERITED::contextAbandoned();
     fProgramCache->abandon();
     fHWProgramID = 0;
-    if (fTempSrcFBO) {
-        fTempSrcFBO->abandon();
-        fTempSrcFBO.reset(NULL);
-    }
-    if (fTempDstFBO) {
-        fTempDstFBO->abandon();
-        fTempDstFBO.reset(NULL);
-    }
-    if (fStencilClearFBO) {
-        fStencilClearFBO->abandon();
-        fStencilClearFBO.reset(NULL);
-    }
-
-    if (this->glCaps().pathRenderingSupport()) {
+    fTempSrcFBOID = 0;
+    fTempDstFBOID = 0;
+    fStencilClearFBOID = 0;
+    if (this->glCaps().shaderCaps()->pathRenderingSupport()) {
         this->glPathRendering()->abandonGpuResources();
     }
 }
@@ -341,13 +373,11 @@
     }
 
     if (resetBits & kRenderTarget_GrGLBackendState) {
-        for (size_t i = 0; i < SK_ARRAY_COUNT(fHWFBOBinding); ++i) {
-            fHWFBOBinding[i].invalidate();
-        }
+        fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID;
     }
 
     if (resetBits & kPathRendering_GrGLBackendState) {
-        if (this->caps()->pathRenderingSupport()) {
+        if (this->caps()->shaderCaps()->pathRenderingSupport()) {
             this->glPathRendering()->resetContext();
         }
     }
@@ -373,9 +403,7 @@
     }
 }
 
-namespace {
-
-GrSurfaceOrigin resolve_origin(GrSurfaceOrigin origin, bool renderTarget) {
+static GrSurfaceOrigin resolve_origin(GrSurfaceOrigin origin, bool renderTarget) {
     // By default, GrRenderTargets are GL's normal orientation so that they
     // can be drawn to by the outside world without the client having
     // to render upside down.
@@ -386,8 +414,6 @@
     }
 }
 
-}
-
 GrTexture* GrGLGpu::onWrapBackendTexture(const GrBackendTextureDesc& desc) {
     if (!this->configToGLFormats(desc.fConfig, false, NULL, NULL, NULL)) {
         return NULL;
@@ -413,7 +439,7 @@
     surfDesc.fWidth = desc.fWidth;
     surfDesc.fHeight = desc.fHeight;
     surfDesc.fConfig = desc.fConfig;
-    surfDesc.fSampleCnt = desc.fSampleCnt;
+    surfDesc.fSampleCnt = SkTMin(desc.fSampleCnt, this->caps()->maxSampleCount());
     bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrBackendTextureFlag);
     // FIXME:  this should be calling resolve_origin(), but Chrome code is currently
     // assuming the old behaviour, which is that backend textures are always
@@ -428,7 +454,8 @@
     GrGLTexture* texture = NULL;
     if (renderTarget) {
         GrGLRenderTarget::IDDesc rtIDDesc;
-        if (!this->createRenderTargetObjects(surfDesc, false, idDesc.fTextureID, &rtIDDesc)) {
+        if (!this->createRenderTargetObjects(surfDesc, GrGpuResource::kUncached_LifeCycle,
+                                             idDesc.fTextureID, &rtIDDesc)) {
             return NULL;
         }
         texture = SkNEW_ARGS(GrGLTextureRenderTarget, (this, surfDesc, idDesc, rtIDDesc));
@@ -444,9 +471,9 @@
 
 GrRenderTarget* GrGLGpu::onWrapBackendRenderTarget(const GrBackendRenderTargetDesc& wrapDesc) {
     GrGLRenderTarget::IDDesc idDesc;
-    GrGLuint fboID = static_cast<GrGLuint>(wrapDesc.fRenderTargetHandle);
-    idDesc.fRenderFBO.reset(SkNEW_ARGS(GrGLFBO, (fboID)));
+    idDesc.fRTFBOID = static_cast<GrGLuint>(wrapDesc.fRenderTargetHandle);
     idDesc.fMSColorRenderbufferID = 0;
+    idDesc.fTexFBOID = GrGLRenderTarget::kUnresolvableFBOID;
     idDesc.fLifeCycle = GrGpuResource::kWrapped_LifeCycle;
 
     GrSurfaceDesc desc;
@@ -454,25 +481,25 @@
     desc.fFlags = kCheckAllocation_GrSurfaceFlag;
     desc.fWidth = wrapDesc.fWidth;
     desc.fHeight = wrapDesc.fHeight;
-    desc.fSampleCnt = wrapDesc.fSampleCnt;
+    desc.fSampleCnt = SkTMin(wrapDesc.fSampleCnt, this->caps()->maxSampleCount());
     desc.fOrigin = resolve_origin(wrapDesc.fOrigin, true);
 
     GrRenderTarget* tgt = SkNEW_ARGS(GrGLRenderTarget, (this, desc, idDesc));
     if (wrapDesc.fStencilBits) {
-        GrGLStencilBuffer::IDDesc sbDesc;
-        GrGLStencilBuffer::Format format;
-        format.fInternalFormat = GrGLStencilBuffer::kUnknownInternalFormat;
+        GrGLStencilAttachment::IDDesc sbDesc;
+        GrGLStencilAttachment::Format format;
+        format.fInternalFormat = GrGLStencilAttachment::kUnknownInternalFormat;
         format.fPacked = false;
         format.fStencilBits = wrapDesc.fStencilBits;
         format.fTotalBits = wrapDesc.fStencilBits;
-        GrGLStencilBuffer* sb = SkNEW_ARGS(GrGLStencilBuffer,
+        GrGLStencilAttachment* sb = SkNEW_ARGS(GrGLStencilAttachment,
                                            (this,
                                             sbDesc,
                                             desc.fWidth,
                                             desc.fHeight,
                                             desc.fSampleCnt,
                                             format));
-        tgt->renderTargetPriv().didAttachStencilBuffer(sb);
+        tgt->renderTargetPriv().didAttachStencilAttachment(sb);
         sb->unref();
     }
     return tgt;
@@ -820,40 +847,41 @@
             SkFAIL("Shouldn't be here if we don't support multisampled renderbuffers.");
             break;
     }
-    return (GR_GL_NO_ERROR == CHECK_ALLOC_ERROR(ctx.interface()));;
+    return (GR_GL_NO_ERROR == CHECK_ALLOC_ERROR(ctx.interface()));
 }
 
-bool GrGLGpu::createRenderTargetObjects(const GrSurfaceDesc& desc, bool budgeted, GrGLuint texID,
+bool GrGLGpu::createRenderTargetObjects(const GrSurfaceDesc& desc,
+                                        GrGpuResource::LifeCycle lifeCycle,
+                                        GrGLuint texID,
                                         GrGLRenderTarget::IDDesc* idDesc) {
     idDesc->fMSColorRenderbufferID = 0;
-    idDesc->fLifeCycle = budgeted ? GrGpuResource::kCached_LifeCycle :
-                                    GrGpuResource::kUncached_LifeCycle;
+    idDesc->fRTFBOID = 0;
+    idDesc->fTexFBOID = 0;
+    idDesc->fLifeCycle = lifeCycle;
 
     GrGLenum status;
 
     GrGLenum msColorFormat = 0; // suppress warning
-    GrGLenum fboTarget = 0; // suppress warning
 
     if (desc.fSampleCnt > 0 && GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType()) {
         goto FAILED;
     }
 
-    idDesc->fTextureFBO.reset(SkNEW_ARGS(GrGLFBO, (this->glInterface())));
-    if (!idDesc->fTextureFBO->isValid()) {
+    GL_CALL(GenFramebuffers(1, &idDesc->fTexFBOID));
+    if (!idDesc->fTexFBOID) {
         goto FAILED;
     }
 
+
     // If we are using multisampling we will create two FBOS. We render to one and then resolve to
     // the texture bound to the other. The exception is the IMG multisample extension. With this
     // extension the texture is multisampled when rendered to and then auto-resolves it when it is
     // rendered from.
     if (desc.fSampleCnt > 0 && this->glCaps().usesMSAARenderBuffers()) {
-        idDesc->fRenderFBO.reset(SkNEW_ARGS(GrGLFBO, (this->glInterface())));
-        if (!idDesc->fRenderFBO->isValid()) {
-            goto FAILED;
-        }
+        GL_CALL(GenFramebuffers(1, &idDesc->fRTFBOID));
         GL_CALL(GenRenderbuffers(1, &idDesc->fMSColorRenderbufferID));
-        if (!idDesc->fMSColorRenderbufferID ||
+        if (!idDesc->fRTFBOID ||
+            !idDesc->fMSColorRenderbufferID ||
             !this->configToGLFormats(desc.fConfig,
                                      // ES2 and ES3 require sized internal formats for rb storage.
                                      kGLES_GrGLStandard == this->glStandard(),
@@ -863,10 +891,12 @@
             goto FAILED;
         }
     } else {
-        idDesc->fRenderFBO.reset(SkRef(idDesc->fTextureFBO.get()));
+        idDesc->fRTFBOID = idDesc->fTexFBOID;
     }
 
-    if (idDesc->fRenderFBO != idDesc->fTextureFBO) {
+    // below here we may bind the FBO
+    fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID;
+    if (idDesc->fRTFBOID != idDesc->fTexFBOID) {
         SkASSERT(desc.fSampleCnt > 0);
         GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, idDesc->fMSColorRenderbufferID));
         if (!renderbuffer_storage_msaa(fGLContext,
@@ -875,11 +905,12 @@
                                        desc.fWidth, desc.fHeight)) {
             goto FAILED;
         }
-        fboTarget = this->bindFBOForAddingAttachments(idDesc->fRenderFBO);
-        GL_CALL(FramebufferRenderbuffer(fboTarget,
-                                        GR_GL_COLOR_ATTACHMENT0,
-                                        GR_GL_RENDERBUFFER,
-                                        idDesc->fMSColorRenderbufferID));
+        fStats.incRenderTargetBinds();
+        GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, idDesc->fRTFBOID));
+        GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                      GR_GL_COLOR_ATTACHMENT0,
+                                      GR_GL_RENDERBUFFER,
+                                      idDesc->fMSColorRenderbufferID));
         if ((desc.fFlags & kCheckAllocation_GrSurfaceFlag) ||
             !this->glCaps().isConfigVerifiedColorAttachment(desc.fConfig)) {
             GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
@@ -889,22 +920,23 @@
             fGLContext.caps()->markConfigAsValidColorAttachment(desc.fConfig);
         }
     }
-    fboTarget = this->bindFBOForAddingAttachments(idDesc->fTextureFBO);
+    fStats.incRenderTargetBinds();
+    GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, idDesc->fTexFBOID));
 
     if (this->glCaps().usesImplicitMSAAResolve() && desc.fSampleCnt > 0) {
-        GL_CALL(FramebufferTexture2DMultisample(fboTarget,
+        GL_CALL(FramebufferTexture2DMultisample(GR_GL_FRAMEBUFFER,
                                                 GR_GL_COLOR_ATTACHMENT0,
                                                 GR_GL_TEXTURE_2D,
                                                 texID, 0, desc.fSampleCnt));
     } else {
-        GL_CALL(FramebufferTexture2D(fboTarget,
+        GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER,
                                      GR_GL_COLOR_ATTACHMENT0,
                                      GR_GL_TEXTURE_2D,
                                      texID, 0));
     }
     if ((desc.fFlags & kCheckAllocation_GrSurfaceFlag) ||
         !this->glCaps().isConfigVerifiedColorAttachment(desc.fConfig)) {
-        GL_CALL_RET(status, CheckFramebufferStatus(fboTarget));
+        GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
         if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
             goto FAILED;
         }
@@ -917,11 +949,11 @@
     if (idDesc->fMSColorRenderbufferID) {
         GL_CALL(DeleteRenderbuffers(1, &idDesc->fMSColorRenderbufferID));
     }
-    if (idDesc->fRenderFBO) {
-        idDesc->fRenderFBO->release(this->glInterface());
+    if (idDesc->fRTFBOID != idDesc->fTexFBOID) {
+        GL_CALL(DeleteFramebuffers(1, &idDesc->fRTFBOID));
     }
-    if (idDesc->fTextureFBO) {
-        idDesc->fTextureFBO->release(this->glInterface());
+    if (idDesc->fTexFBOID) {
+        GL_CALL(DeleteFramebuffers(1, &idDesc->fTexFBOID));
     }
     return false;
 }
@@ -938,13 +970,9 @@
 }
 #endif
 
-GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& origDesc, bool budgeted,
+GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc,
+                                    GrGpuResource::LifeCycle lifeCycle,
                                     const void* srcData, size_t rowBytes) {
-
-    GrSurfaceDesc desc = origDesc;
-
-    // Attempt to catch un- or wrongly initialized sample counts;
-    SkASSERT(desc.fSampleCnt >= 0 && desc.fSampleCnt <= 64);
     // We fail if the MSAA was requested and is not available.
     if (GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType() && desc.fSampleCnt) {
         //SkDebugf("MSAA RT requested but not supported on this platform.");
@@ -953,31 +981,9 @@
 
     bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
 
-    // If the sample count exceeds the max then we clamp it.
-    desc.fSampleCnt = SkTMin(desc.fSampleCnt, this->caps()->maxSampleCount());
-    desc.fOrigin = resolve_origin(desc.fOrigin, renderTarget);
-
-    if (GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType() && desc.fSampleCnt) {
-        //SkDebugf("MSAA RT requested but not supported on this platform.");
-        return return_null_texture();
-    }
-
-    if (renderTarget) {
-        int maxRTSize = this->caps()->maxRenderTargetSize();
-        if (desc.fWidth > maxRTSize || desc.fHeight > maxRTSize) {
-            return return_null_texture();
-        }
-    } else {
-        int maxSize = this->caps()->maxTextureSize();
-        if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
-            return return_null_texture();
-        }
-    }
-
     GrGLTexture::IDDesc idDesc;
     GL_CALL(GenTextures(1, &idDesc.fTextureID));
-    idDesc.fLifeCycle = budgeted ? GrGpuResource::kCached_LifeCycle :
-                                   GrGpuResource::kUncached_LifeCycle;
+    idDesc.fLifeCycle = lifeCycle;
 
     if (!idDesc.fTextureID) {
         return return_null_texture();
@@ -1028,7 +1034,7 @@
         GL_CALL(BindTexture(GR_GL_TEXTURE_2D, 0));
         GrGLRenderTarget::IDDesc rtIDDesc;
 
-        if (!this->createRenderTargetObjects(desc, budgeted, idDesc.fTextureID, &rtIDDesc)) {
+        if (!this->createRenderTargetObjects(desc, lifeCycle, idDesc.fTextureID, &rtIDDesc)) {
             GL_CALL(DeleteTextures(1, &idDesc.fTextureID));
             return return_null_texture();
         }
@@ -1044,30 +1050,17 @@
     return tex;
 }
 
-GrTexture* GrGLGpu::onCreateCompressedTexture(const GrSurfaceDesc& origDesc, bool budgeted,
+GrTexture* GrGLGpu::onCreateCompressedTexture(const GrSurfaceDesc& desc,
+                                              GrGpuResource::LifeCycle lifeCycle,
                                               const void* srcData) {
-
-    if(SkToBool(origDesc.fFlags & kRenderTarget_GrSurfaceFlag) || origDesc.fSampleCnt > 0) {
-        return return_null_texture();
-    }
-
     // Make sure that we're not flipping Y.
-    GrSurfaceOrigin texOrigin = resolve_origin(origDesc.fOrigin, false);
-    if (kBottomLeft_GrSurfaceOrigin == texOrigin) {
-        return return_null_texture();
-    }
-    GrSurfaceDesc desc = origDesc;
-    desc.fOrigin = texOrigin;
-
-    int maxSize = this->caps()->maxTextureSize();
-    if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
+    if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) {
         return return_null_texture();
     }
 
     GrGLTexture::IDDesc idDesc;
     GL_CALL(GenTextures(1, &idDesc.fTextureID));
-    idDesc.fLifeCycle = budgeted ? GrGpuResource::kCached_LifeCycle :
-                                   GrGpuResource::kUncached_LifeCycle;
+    idDesc.fLifeCycle = lifeCycle;
 
     if (!idDesc.fTextureID) {
         return return_null_texture();
@@ -1116,10 +1109,10 @@
 
 namespace {
 
-const GrGLuint kUnknownBitCount = GrGLStencilBuffer::kUnknownBitCount;
+const GrGLuint kUnknownBitCount = GrGLStencilAttachment::kUnknownBitCount;
 
 void inline get_stencil_rb_sizes(const GrGLInterface* gl,
-                                 GrGLStencilBuffer::Format* format) {
+                                 GrGLStencilAttachment::Format* format) {
 
     // we shouldn't ever know one size and not the other
     SkASSERT((kUnknownBitCount == format->fStencilBits) ==
@@ -1140,7 +1133,7 @@
 }
 }
 
-bool GrGLGpu::createStencilBufferForRenderTarget(GrRenderTarget* rt, int width, int height) {
+bool GrGLGpu::createStencilAttachmentForRenderTarget(GrRenderTarget* rt, int width, int height) {
     // All internally created RTs are also textures. We don't create
     // SBs for a client's standalone RT (that is a RT that isn't also a texture).
     SkASSERT(rt->asTexture());
@@ -1148,7 +1141,7 @@
     SkASSERT(height >= rt->height());
 
     int samples = rt->numSamples();
-    GrGLStencilBuffer::IDDesc sbDesc;
+    GrGLStencilAttachment::IDDesc sbDesc;
 
     int stencilFmtCnt = this->glCaps().stencilFormats().count();
     for (int i = 0; i < stencilFmtCnt; ++i) {
@@ -1180,67 +1173,69 @@
             created = (GR_GL_NO_ERROR == check_alloc_error(rt->desc(), this->glInterface()));
         }
         if (created) {
-            fStats.incStencilBufferCreates();
+            fStats.incStencilAttachmentCreates();
             // After sized formats we attempt an unsized format and take
             // whatever sizes GL gives us. In that case we query for the size.
-            GrGLStencilBuffer::Format format = sFmt;
+            GrGLStencilAttachment::Format format = sFmt;
             get_stencil_rb_sizes(this->glInterface(), &format);
-            SkAutoTUnref<GrGLStencilBuffer> sb(SkNEW_ARGS(GrGLStencilBuffer,
+            SkAutoTUnref<GrGLStencilAttachment> sb(SkNEW_ARGS(GrGLStencilAttachment,
                                                   (this, sbDesc, width, height, samples, format)));
-            if (this->attachStencilBufferToRenderTarget(sb, rt)) {
+            if (this->attachStencilAttachmentToRenderTarget(sb, rt)) {
                 fLastSuccessfulStencilFmtIdx = sIdx;
-                rt->renderTargetPriv().didAttachStencilBuffer(sb);
-
+                rt->renderTargetPriv().didAttachStencilAttachment(sb);
+// This work around is currently breaking on windows 7 hd2000 bot when we bind a color buffer
+#if 0
                 // Clear the stencil buffer. We use a special purpose FBO for this so that the
                 // entire stencil buffer is cleared, even if it is attached to an FBO with a
                 // smaller color target.
-                if (!fStencilClearFBO) {
-                    fStencilClearFBO.reset(SkNEW_ARGS(GrGLFBO, (this->glInterface())));
+                if (0 == fStencilClearFBOID) {
+                    GL_CALL(GenFramebuffers(1, &fStencilClearFBOID));
                 }
-                SkASSERT(fStencilClearFBO->isValid());
-                GrGLenum fboTarget = this->bindFBOForAddingAttachments(fStencilClearFBO);
 
-                GL_CALL(FramebufferRenderbuffer(fboTarget,
+                GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fStencilClearFBOID));
+                fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID;
+                fStats.incRenderTargetBinds();
+                GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                                 GR_GL_STENCIL_ATTACHMENT,
                                                 GR_GL_RENDERBUFFER, sbDesc.fRenderbufferID));
                 if (sFmt.fPacked) {
-                    GL_CALL(FramebufferRenderbuffer(fboTarget,
+                    GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                                     GR_GL_DEPTH_ATTACHMENT,
                                                     GR_GL_RENDERBUFFER, sbDesc.fRenderbufferID));
                 }
 
                 GL_CALL(ClearStencil(0));
-                // At least some versions of the SGX 54x driver can't handle clearing a stencil
-                // buffer without a color buffer and will crash.
-                GrGLuint tempRB = 0;
-                if (kPowerVR54x_GrGLRenderer == this->ctxInfo().renderer()) {
-                    GL_CALL(GenRenderbuffers(1, &tempRB));
-                    GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, tempRB));
+                // Many GL implementations seem to have trouble with clearing an FBO with only
+                // a stencil buffer.
+                GrGLuint tempRB;
+                GL_CALL(GenRenderbuffers(1, &tempRB));
+                GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, tempRB));
+                if (samples > 0) {
+                    renderbuffer_storage_msaa(fGLContext, samples, GR_GL_RGBA8, width, height);
+                } else {
                     GL_CALL(RenderbufferStorage(GR_GL_RENDERBUFFER, GR_GL_RGBA8, width, height));
-                    GL_CALL(FramebufferRenderbuffer(fboTarget,
-                                                    GR_GL_COLOR_ATTACHMENT0,
-                                                    GR_GL_RENDERBUFFER, tempRB));
                 }
+                GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                                GR_GL_COLOR_ATTACHMENT0,
+                                                GR_GL_RENDERBUFFER, tempRB));
 
                 GL_CALL(Clear(GR_GL_STENCIL_BUFFER_BIT));
 
-                if (tempRB) {
-                    GL_CALL(FramebufferRenderbuffer(fboTarget,
-                                                    GR_GL_COLOR_ATTACHMENT0,
-                                                    GR_GL_RENDERBUFFER, 0));
-                    GL_CALL(DeleteRenderbuffers(1, &tempRB));
-                }
+                GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                                GR_GL_COLOR_ATTACHMENT0,
+                                                GR_GL_RENDERBUFFER, 0));
+                GL_CALL(DeleteRenderbuffers(1, &tempRB));
 
                 // Unbind the SB from the FBO so that we don't keep it alive.
-                GL_CALL(FramebufferRenderbuffer(fboTarget,
+                GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                                 GR_GL_STENCIL_ATTACHMENT,
                                                 GR_GL_RENDERBUFFER, 0));
                 if (sFmt.fPacked) {
-                    GL_CALL(FramebufferRenderbuffer(fboTarget,
+                    GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                                     GR_GL_DEPTH_ATTACHMENT,
                                                     GR_GL_RENDERBUFFER, 0));
                 }
-
+#endif
                 return true;
             }
             // Remove the scratch key from this resource so we don't grab it from the cache ever
@@ -1254,10 +1249,13 @@
     return false;
 }
 
-bool GrGLGpu::attachStencilBufferToRenderTarget(GrStencilBuffer* sb, GrRenderTarget* rt) {
+bool GrGLGpu::attachStencilAttachmentToRenderTarget(GrStencilAttachment* sb, GrRenderTarget* rt) {
     GrGLRenderTarget* glrt = static_cast<GrGLRenderTarget*>(rt);
+
+    GrGLuint fbo = glrt->renderFBOID();
+
     if (NULL == sb) {
-        if (rt->renderTargetPriv().getStencilBuffer()) {
+        if (rt->renderTargetPriv().getStencilAttachment()) {
             GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                             GR_GL_STENCIL_ATTACHMENT,
                                             GR_GL_RENDERBUFFER, 0));
@@ -1272,20 +1270,21 @@
         }
         return true;
     } else {
-        GrGLStencilBuffer* glsb = static_cast<GrGLStencilBuffer*>(sb);
+        GrGLStencilAttachment* glsb = static_cast<GrGLStencilAttachment*>(sb);
         GrGLuint rb = glsb->renderbufferID();
 
-        GrGLenum fboTarget = this->bindFBOForAddingAttachments(glrt->renderFBO());
-
-        GL_CALL(FramebufferRenderbuffer(fboTarget,
+        fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID;
+        fStats.incRenderTargetBinds();
+        GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fbo));
+        GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                         GR_GL_STENCIL_ATTACHMENT,
                                         GR_GL_RENDERBUFFER, rb));
         if (glsb->format().fPacked) {
-            GL_CALL(FramebufferRenderbuffer(fboTarget,
+            GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                             GR_GL_DEPTH_ATTACHMENT,
                                             GR_GL_RENDERBUFFER, rb));
         } else {
-            GL_CALL(FramebufferRenderbuffer(fboTarget,
+            GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                             GR_GL_DEPTH_ATTACHMENT,
                                             GR_GL_RENDERBUFFER, 0));
         }
@@ -1294,13 +1293,13 @@
         if (!this->glCaps().isColorConfigAndStencilFormatVerified(rt->config(), glsb->format())) {
             GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
             if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
-                GL_CALL(FramebufferRenderbuffer(fboTarget,
-                                                GR_GL_STENCIL_ATTACHMENT,
-                                                GR_GL_RENDERBUFFER, 0));
+                GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                              GR_GL_STENCIL_ATTACHMENT,
+                                              GR_GL_RENDERBUFFER, 0));
                 if (glsb->format().fPacked) {
-                    GL_CALL(FramebufferRenderbuffer(fboTarget,
-                                                    GR_GL_DEPTH_ATTACHMENT,
-                                                    GR_GL_RENDERBUFFER, 0));
+                    GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                                  GR_GL_DEPTH_ATTACHMENT,
+                                                  GR_GL_RENDERBUFFER, 0));
                 }
                 return false;
             } else {
@@ -1409,7 +1408,7 @@
     this->disableScissor();
 }
 
-bool GrGLGpu::flushGLState(const DrawArgs& args, bool isLineDraw) {
+bool GrGLGpu::flushGLState(const DrawArgs& args) {
     GrXferProcessor::BlendInfo blendInfo;
     const GrPipeline& pipeline = *args.fPipeline;
     args.fPipeline->getXferProcessor()->getBlendInfo(&blendInfo);
@@ -1420,7 +1419,7 @@
 
     fCurrentProgram.reset(fProgramCache->getProgram(args));
     if (NULL == fCurrentProgram.get()) {
-        SkDEBUGFAIL("Failed to create program!");
+        GrContextDebugf(this->getContext(), "Failed to create program!\n");
         return false;
     }
 
@@ -1441,30 +1440,30 @@
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(pipeline.getRenderTarget());
     this->flushStencil(pipeline.getStencil());
     this->flushScissor(pipeline.getScissorState(), glRT->getViewport(), glRT->origin());
-    this->flushHWAAState(glRT, pipeline.isHWAntialiasState(), isLineDraw);
+    this->flushHWAAState(glRT, pipeline.isHWAntialiasState());
 
     // This must come after textures are flushed because a texture may need
     // to be msaa-resolved (which will modify bound FBO state).
-    this->prepareToDrawToRenderTarget(glRT, NULL);
+    this->flushRenderTarget(glRT, NULL);
 
     return true;
 }
 
 void GrGLGpu::setupGeometry(const GrPrimitiveProcessor& primProc,
-                            const GrDrawTarget::DrawInfo& info,
+                            const GrNonInstancedVertices& vertices,
                             size_t* indexOffsetInBytes) {
     GrGLVertexBuffer* vbuf;
-    vbuf = (GrGLVertexBuffer*) info.vertexBuffer();
+    vbuf = (GrGLVertexBuffer*) vertices.vertexBuffer();
 
     SkASSERT(vbuf);
     SkASSERT(!vbuf->isMapped());
 
     GrGLIndexBuffer* ibuf = NULL;
-    if (info.isIndexed()) {
+    if (vertices.isIndexed()) {
         SkASSERT(indexOffsetInBytes);
 
         *indexOffsetInBytes = 0;
-        ibuf = (GrGLIndexBuffer*)info.indexBuffer();
+        ibuf = (GrGLIndexBuffer*)vertices.indexBuffer();
 
         SkASSERT(ibuf);
         SkASSERT(!ibuf->isMapped());
@@ -1478,7 +1477,7 @@
 
         GrGLsizei stride = static_cast<GrGLsizei>(primProc.getVertexStride());
 
-        size_t vertexOffsetInBytes = stride * info.startVertex();
+        size_t vertexOffsetInBytes = stride * vertices.startVertex();
 
         vertexOffsetInBytes += vbuf->baseOffset();
 
@@ -1542,7 +1541,7 @@
         }
     }
 
-    this->prepareToDrawToRenderTarget(glRT, rect);
+    this->flushRenderTarget(glRT, rect);
     GrScissorState scissorState;
     if (rect) {
         scissorState.set(*rect);
@@ -1570,36 +1569,40 @@
     }
 
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(renderTarget);
-    GrGLenum fboTarget = this->bindFBO(kDraw_FBOBinding, glRT->renderFBO());
+    if (renderTarget->getUniqueID() != fHWBoundRenderTargetUniqueID) {
+        fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID;
+        fStats.incRenderTargetBinds();
+        GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, glRT->renderFBOID()));
+    }
     switch (this->glCaps().invalidateFBType()) {
         case GrGLCaps::kNone_InvalidateFBType:
             SkFAIL("Should never get here.");
             break;
         case GrGLCaps::kInvalidate_InvalidateFBType:
-            if (glRT->renderFBO()->isDefaultFramebuffer()) {
+            if (0 == glRT->renderFBOID()) {
                 //  When rendering to the default framebuffer the legal values for attachments
                 //  are GL_COLOR, GL_DEPTH, GL_STENCIL, ... rather than the various FBO attachment
                 //  types.
                 static const GrGLenum attachments[] = { GR_GL_COLOR };
-                GL_CALL(InvalidateFramebuffer(fboTarget, SK_ARRAY_COUNT(attachments),
+                GL_CALL(InvalidateFramebuffer(GR_GL_FRAMEBUFFER, SK_ARRAY_COUNT(attachments),
                         attachments));
             } else {
                 static const GrGLenum attachments[] = { GR_GL_COLOR_ATTACHMENT0 };
-                GL_CALL(InvalidateFramebuffer(fboTarget, SK_ARRAY_COUNT(attachments),
+                GL_CALL(InvalidateFramebuffer(GR_GL_FRAMEBUFFER, SK_ARRAY_COUNT(attachments),
                         attachments));
             }
             break;
         case GrGLCaps::kDiscard_InvalidateFBType: {
-            if (glRT->renderFBO()->isDefaultFramebuffer()) {
+            if (0 == glRT->renderFBOID()) {
                 //  When rendering to the default framebuffer the legal values for attachments
                 //  are GL_COLOR, GL_DEPTH, GL_STENCIL, ... rather than the various FBO attachment
                 //  types. See glDiscardFramebuffer() spec.
                 static const GrGLenum attachments[] = { GR_GL_COLOR };
-                GL_CALL(DiscardFramebuffer(fboTarget, SK_ARRAY_COUNT(attachments),
+                GL_CALL(DiscardFramebuffer(GR_GL_FRAMEBUFFER, SK_ARRAY_COUNT(attachments),
                         attachments));
             } else {
                 static const GrGLenum attachments[] = { GR_GL_COLOR_ATTACHMENT0 };
-                GL_CALL(DiscardFramebuffer(fboTarget, SK_ARRAY_COUNT(attachments),
+                GL_CALL(DiscardFramebuffer(GR_GL_FRAMEBUFFER, SK_ARRAY_COUNT(attachments),
                         attachments));
             }
             break;
@@ -1614,7 +1617,7 @@
         return;
     }
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(target);
-    this->prepareToDrawToRenderTarget(glRT, &SkIRect::EmptyIRect());
+    this->flushRenderTarget(glRT, &SkIRect::EmptyIRect());
 
     this->disableScissor();
 
@@ -1627,7 +1630,7 @@
 void GrGLGpu::onClearStencilClip(GrRenderTarget* target, const SkIRect& rect, bool insideClip) {
     SkASSERT(target);
 
-    GrStencilBuffer* sb = target->renderTargetPriv().getStencilBuffer();
+    GrStencilAttachment* sb = target->renderTargetPriv().getStencilAttachment();
     // this should only be called internally when we know we have a
     // stencil buffer.
     SkASSERT(sb);
@@ -1650,7 +1653,7 @@
         value = 0;
     }
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(target);
-    this->prepareToDrawToRenderTarget(glRT, &SkIRect::EmptyIRect());
+    this->flushRenderTarget(glRT, &SkIRect::EmptyIRect());
 
     GrScissorState scissorState;
     scissorState.set(rect);
@@ -1720,13 +1723,22 @@
 
     // resolve the render target if necessary
     GrGLRenderTarget* tgt = static_cast<GrGLRenderTarget*>(target);
-    if (tgt->getResolveType() == GrGLRenderTarget::kCantResolve_ResolveType) {
-        return false;
+    switch (tgt->getResolveType()) {
+        case GrGLRenderTarget::kCantResolve_ResolveType:
+            return false;
+        case GrGLRenderTarget::kAutoResolves_ResolveType:
+            this->flushRenderTarget(static_cast<GrGLRenderTarget*>(target), &SkIRect::EmptyIRect());
+            break;
+        case GrGLRenderTarget::kCanResolve_ResolveType:
+            this->onResolveRenderTarget(tgt);
+            // we don't track the state of the READ FBO ID.
+            fStats.incRenderTargetBinds();
+            GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER,
+                                    tgt->textureFBOID()));
+            break;
+        default:
+            SkFAIL("Unknown resolve type");
     }
-    if (tgt->getResolveType() == GrGLRenderTarget::kCanResolve_ResolveType) {
-        this->onResolveRenderTarget(tgt);
-    }
-    this->bindFBO(kRead_FBOBinding, tgt->textureFBO());
 
     const GrGLIRect& glvp = tgt->getViewport();
 
@@ -1791,8 +1803,7 @@
             }
         }
     } else {
-        SkASSERT(readDst != buffer);
-        SkASSERT(rowBytes != tightRowBytes);
+        SkASSERT(readDst != buffer);        SkASSERT(rowBytes != tightRowBytes);
         // copy from readDst to buffer while flipping y
         // const int halfY = height >> 1;
         const char* src = reinterpret_cast<const char*>(readDst);
@@ -1813,67 +1824,34 @@
     return true;
 }
 
-GrGLenum GrGLGpu::bindFBO(FBOBinding binding, const GrGLFBO* fbo) {
-    SkASSERT(fbo);
-    SkASSERT(fbo->isValid());
-    GrGLenum target;
-    HWFBOBinding* hwFBOState;
-    if (!this->glCaps().usesMSAARenderBuffers()) {
-        target = GR_GL_FRAMEBUFFER;
-        hwFBOState = &fHWFBOBinding[0];
-    } else {
-        target = kDraw_FBOBinding == binding ? GR_GL_DRAW_FRAMEBUFFER : GR_GL_READ_FRAMEBUFFER;
-        hwFBOState = &fHWFBOBinding[binding];
-    }
+void GrGLGpu::flushRenderTarget(GrGLRenderTarget* target, const SkIRect* bound) {
 
-    if (hwFBOState->fFBO != fbo) {
-        fStats.incRenderTargetBinds();
-        GL_CALL(BindFramebuffer(target, fbo->fboID()));
-        hwFBOState->fFBO.reset(SkRef(fbo));
-    }
-    return target;
-}
-
-GrGLenum GrGLGpu::bindFBOForAddingAttachments(const GrGLFBO* fbo) {
-    if (this->glCaps().bindFBOToReadAndDrawForAddingAttachments()) {
-        if (fHWFBOBinding[kDraw_FBOBinding].fFBO != fbo ||
-            fHWFBOBinding[kRead_FBOBinding].fFBO != fbo) {
-            fStats.incRenderTargetBinds();
-            GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fbo->fboID()));
-            fHWFBOBinding[kDraw_FBOBinding].fFBO.reset(SkRef(fbo));
-            fHWFBOBinding[kRead_FBOBinding].fFBO.reset(SkRef(fbo));
-        }
-        return GR_GL_FRAMEBUFFER;
-    } else {
-        return this->bindFBO(kDraw_FBOBinding, fbo);
-    }
-}
-
-void GrGLGpu::setViewport(const GrGLIRect& viewport) {
-    if (viewport != fHWViewport) {
-        viewport.pushToGLViewport(this->glInterface());
-        fHWViewport = viewport;
-    }
-}
-
-void GrGLGpu::prepareToDrawToRenderTarget(GrGLRenderTarget* target, const SkIRect* bound) {
     SkASSERT(target);
-    this->bindFBO(kDraw_FBOBinding, target->renderFBO());
 
+    uint32_t rtID = target->getUniqueID();
+    if (fHWBoundRenderTargetUniqueID != rtID) {
+        fStats.incRenderTargetBinds();
+        GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, target->renderFBOID()));
 #ifdef SK_DEBUG
-    // don't do this check in Chromium -- this is causing
-    // lots of repeated command buffer flushes when the compositor is
-    // rendering with Ganesh, which is really slow; even too slow for
-    // Debug mode.
-    if (!this->glContext().isChromium()) {
-        GrGLenum status;
-        GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
-        if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
-            SkDebugf("GrGLGpu::flushRenderTarget glCheckFramebufferStatus %x\n", status);
+        // don't do this check in Chromium -- this is causing
+        // lots of repeated command buffer flushes when the compositor is
+        // rendering with Ganesh, which is really slow; even too slow for
+        // Debug mode.
+        if (!this->glContext().isChromium()) {
+            GrGLenum status;
+            GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+            if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+                SkDebugf("GrGLGpu::flushRenderTarget glCheckFramebufferStatus %x\n", status);
+            }
+        }
+#endif
+        fHWBoundRenderTargetUniqueID = rtID;
+        const GrGLIRect& vp = target->getViewport();
+        if (fHWViewport != vp) {
+            vp.pushToGLViewport(this->glInterface());
+            fHWViewport = vp;
         }
     }
-#endif
-    this->setViewport(target->getViewport());
     if (NULL == bound || !bound->isEmpty()) {
         target->flagAsNeedingResolve(bound);
     }
@@ -1915,29 +1893,31 @@
     #endif
 #endif
 
-void GrGLGpu::onDraw(const DrawArgs& args, const GrDrawTarget::DrawInfo& info) {
-    if (!this->flushGLState(args, GrIsPrimTypeLines(info.primitiveType()))) {
+void GrGLGpu::onDraw(const DrawArgs& args, const GrNonInstancedVertices& vertices) {
+    if (!this->flushGLState(args)) {
         return;
     }
 
     size_t indexOffsetInBytes = 0;
-    this->setupGeometry(*args.fPrimitiveProcessor, info, &indexOffsetInBytes);
+    this->setupGeometry(*args.fPrimitiveProcessor, vertices, &indexOffsetInBytes);
 
-    SkASSERT((size_t)info.primitiveType() < SK_ARRAY_COUNT(gPrimitiveType2GLMode));
+    SkASSERT((size_t)vertices.primitiveType() < SK_ARRAY_COUNT(gPrimitiveType2GLMode));
 
-    if (info.isIndexed()) {
+    if (vertices.isIndexed()) {
         GrGLvoid* indices =
-            reinterpret_cast<GrGLvoid*>(indexOffsetInBytes + sizeof(uint16_t) * info.startIndex());
+            reinterpret_cast<GrGLvoid*>(indexOffsetInBytes + sizeof(uint16_t) *
+                                        vertices.startIndex());
         // info.startVertex() was accounted for by setupGeometry.
-        GL_CALL(DrawElements(gPrimitiveType2GLMode[info.primitiveType()],
-                             info.indexCount(),
+        GL_CALL(DrawElements(gPrimitiveType2GLMode[vertices.primitiveType()],
+                             vertices.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()));
+        GL_CALL(DrawArrays(gPrimitiveType2GLMode[vertices.primitiveType()], 0,
+                           vertices.vertexCount()));
     }
 #if SWAP_PER_DRAW
     glFlush();
@@ -1961,15 +1941,15 @@
     SkISize size = SkISize::Make(rt->width(), rt->height());
     this->glPathRendering()->setProjectionMatrix(*state.fViewMatrix, size, rt->origin());
     this->flushScissor(*state.fScissor, rt->getViewport(), rt->origin());
-    this->flushHWAAState(rt, state.fUseHWAA, false);
-    this->prepareToDrawToRenderTarget(rt, NULL);
+    this->flushHWAAState(rt, state.fUseHWAA);
+    this->flushRenderTarget(rt, NULL);
 
     fPathRendering->stencilPath(path, *state.fStencil);
 }
 
 void GrGLGpu::onDrawPath(const DrawArgs& args, const GrPath* path,
                          const GrStencilSettings& stencil) {
-    if (!this->flushGLState(args, false)) {
+    if (!this->flushGLState(args)) {
         return;
     }
     fPathRendering->drawPath(path, stencil);
@@ -1983,7 +1963,7 @@
                           GrDrawTarget::PathTransformType transformType,
                            int count,
                            const GrStencilSettings& stencil) {
-    if (!this->flushGLState(args, false)) {
+    if (!this->flushGLState(args)) {
         return;
     }
     fPathRendering->drawPaths(pathRange, indices, indexType, transformValues,
@@ -1995,9 +1975,14 @@
     if (rt->needsResolve()) {
         // Some extensions automatically resolves the texture when it is read.
         if (this->glCaps().usesMSAARenderBuffers()) {
-            SkASSERT(rt->textureFBO() != rt->renderFBO());
-            this->bindFBO(kRead_FBOBinding, rt->renderFBO());
-            this->bindFBO(kDraw_FBOBinding, rt->textureFBO());
+            SkASSERT(rt->textureFBOID() != rt->renderFBOID());
+            fStats.incRenderTargetBinds();
+            fStats.incRenderTargetBinds();
+            GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER, rt->renderFBOID()));
+            GL_CALL(BindFramebuffer(GR_GL_DRAW_FRAMEBUFFER, rt->textureFBOID()));
+            // make sure we go through flushRenderTarget() since we've modified
+            // the bound DRAW FBO ID.
+            fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID;
             const GrGLIRect& vp = rt->getViewport();
             const SkIRect dirtyRect = rt->getResolveRect();
 
@@ -2113,28 +2098,19 @@
     }
 }
 
-void GrGLGpu::flushHWAAState(GrRenderTarget* rt, bool useHWAA, bool isLineDraw) {
-// At least some ATI linux drivers will render GL_LINES incorrectly when MSAA state is enabled but
-// the target is not multisampled. Single pixel wide lines are rendered thicker than 1 pixel wide.
-#if 0
-    // Replace RT_HAS_MSAA with this definition once this driver bug is no longer a relevant concern
-    #define RT_HAS_MSAA rt->isMultisampled()
-#else
-    #define RT_HAS_MSAA (rt->isMultisampled() || isLineDraw)
-#endif
+void GrGLGpu::flushHWAAState(GrRenderTarget* rt, bool useHWAA) {
+    SkASSERT(!useHWAA || rt->isMultisampled());
 
     if (kGL_GrGLStandard == this->glStandard()) {
-        if (RT_HAS_MSAA) {
-            if (useHWAA) {
-                if (kYes_TriState != fMSAAEnabled) {
-                    GL_CALL(Enable(GR_GL_MULTISAMPLE));
-                    fMSAAEnabled = kYes_TriState;
-                }
-            } else {
-                if (kNo_TriState != fMSAAEnabled) {
-                    GL_CALL(Disable(GR_GL_MULTISAMPLE));
-                    fMSAAEnabled = kNo_TriState;
-                }
+        if (useHWAA) {
+            if (kYes_TriState != fMSAAEnabled) {
+                GL_CALL(Enable(GR_GL_MULTISAMPLE));
+                fMSAAEnabled = kYes_TriState;
+            }
+        } else {
+            if (kNo_TriState != fMSAAEnabled) {
+                GL_CALL(Disable(GR_GL_MULTISAMPLE));
+                fMSAAEnabled = kNo_TriState;
             }
         }
     }
@@ -2142,39 +2118,55 @@
 
 void GrGLGpu::flushBlend(const GrXferProcessor::BlendInfo& blendInfo) {
     // Any optimization to disable blending should have already been applied and
-    // tweaked the coeffs to (1, 0).
-    
+    // tweaked the equation to "add" or "subtract", and the coeffs to (1, 0).
+
+    GrBlendEquation equation = blendInfo.fEquation;
     GrBlendCoeff srcCoeff = blendInfo.fSrcBlend;
     GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
-    bool blendOff = kOne_GrBlendCoeff == srcCoeff && kZero_GrBlendCoeff == dstCoeff;
+    bool blendOff = (kAdd_GrBlendEquation == equation || kSubtract_GrBlendEquation == equation) &&
+                    kOne_GrBlendCoeff == srcCoeff && kZero_GrBlendCoeff == dstCoeff;
     if (blendOff) {
         if (kNo_TriState != fHWBlendState.fEnabled) {
             GL_CALL(Disable(GR_GL_BLEND));
             fHWBlendState.fEnabled = kNo_TriState;
         }
-    } else {
-        if (kYes_TriState != fHWBlendState.fEnabled) {
-            GL_CALL(Enable(GR_GL_BLEND));
-            fHWBlendState.fEnabled = kYes_TriState;
-        }
-        if (fHWBlendState.fSrcCoeff != srcCoeff ||
-            fHWBlendState.fDstCoeff != dstCoeff) {
-            GL_CALL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff],
-                              gXfermodeCoeff2Blend[dstCoeff]));
-            fHWBlendState.fSrcCoeff = srcCoeff;
-            fHWBlendState.fDstCoeff = dstCoeff;
-        }
-        GrColor blendConst = blendInfo.fBlendConstant;
-        if ((BlendCoeffReferencesConstant(srcCoeff) ||
-             BlendCoeffReferencesConstant(dstCoeff)) &&
-            (!fHWBlendState.fConstColorValid ||
-             fHWBlendState.fConstColor != blendConst)) {
-            GrGLfloat c[4];
-            GrColorToRGBAFloat(blendConst, c);
-            GL_CALL(BlendColor(c[0], c[1], c[2], c[3]));
-            fHWBlendState.fConstColor = blendConst;
-            fHWBlendState.fConstColorValid = true;
-        }
+        return;
+    }
+
+    if (kYes_TriState != fHWBlendState.fEnabled) {
+        GL_CALL(Enable(GR_GL_BLEND));
+        fHWBlendState.fEnabled = kYes_TriState;
+    }
+
+    if (fHWBlendState.fEquation != equation) {
+        GL_CALL(BlendEquation(gXfermodeEquation2Blend[equation]));
+        fHWBlendState.fEquation = equation;
+    }
+
+    if (GrBlendEquationIsAdvanced(equation)) {
+        SkASSERT(this->caps()->advancedBlendEquationSupport());
+        // Advanced equations have no other blend state.
+        return;
+    }
+
+    if (fHWBlendState.fSrcCoeff != srcCoeff ||
+        fHWBlendState.fDstCoeff != dstCoeff) {
+        GL_CALL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff],
+                          gXfermodeCoeff2Blend[dstCoeff]));
+        fHWBlendState.fSrcCoeff = srcCoeff;
+        fHWBlendState.fDstCoeff = dstCoeff;
+    }
+
+    GrColor blendConst = blendInfo.fBlendConstant;
+    if ((BlendCoeffReferencesConstant(srcCoeff) ||
+         BlendCoeffReferencesConstant(dstCoeff)) &&
+        (!fHWBlendState.fConstColorValid ||
+         fHWBlendState.fConstColor != blendConst)) {
+        GrGLfloat c[4];
+        GrColorToRGBAFloat(blendConst, c);
+        GL_CALL(BlendColor(c[0], c[1], c[2], c[3]));
+        fHWBlendState.fConstColor = blendConst;
+        fHWBlendState.fConstColorValid = true;
     }
 }
 
@@ -2449,7 +2441,7 @@
             }
             break;
         case kETC1_GrPixelConfig:
-            *internalFormat = GR_GL_COMPRESSED_RGB8_ETC1;
+            *internalFormat = GR_GL_COMPRESSED_ETC1_RGB8;
             break;
         case kLATC_GrPixelConfig:
             switch(this->glCaps().latcAlias()) {
@@ -2465,11 +2457,11 @@
             }
             break;
         case kR11_EAC_GrPixelConfig:
-            *internalFormat = GR_GL_COMPRESSED_R11;
+            *internalFormat = GR_GL_COMPRESSED_R11_EAC;
             break;
 
         case kASTC_12x12_GrPixelConfig:
-            *internalFormat = GR_GL_COMPRESSED_RGBA_ASTC_12x12;
+            *internalFormat = GR_GL_COMPRESSED_RGBA_ASTC_12x12_KHR;
             break;
 
         case kRGBA_float_GrPixelConfig:
@@ -2560,13 +2552,13 @@
     const GrGLRenderTarget* dstRT = static_cast<const GrGLRenderTarget*>(dst->asRenderTarget());
     // If dst is multisampled (and uses an extension where there is a separate MSAA renderbuffer)
     // then we don't want to copy to the texture but to the MSAA buffer.
-    if (dstRT && dstRT->renderFBO() != dstRT->textureFBO()) {
+    if (dstRT && dstRT->renderFBOID() != dstRT->textureFBOID()) {
         return false;
     }
     const GrGLRenderTarget* srcRT = static_cast<const GrGLRenderTarget*>(src->asRenderTarget());
     // If the src is multisampled (and uses an extension where there is a separate MSAA
     // renderbuffer) then it is an invalid operation to call CopyTexSubImage
-    if (srcRT && srcRT->renderFBO() != srcRT->textureFBO()) {
+    if (srcRT && srcRT->renderFBOID() != srcRT->textureFBOID()) {
         return false;
     }
     if (gpu->glCaps().isConfigRenderable(src->config(), src->desc().fSampleCnt > 0) &&
@@ -2583,31 +2575,22 @@
 
 // If a temporary FBO was created, its non-zero ID is returned. The viewport that the copy rect is
 // relative to is output.
-GrGLGpu::FBOBinding GrGLGpu::bindSurfaceAsFBOForCopy(GrSurface* surface, FBOBinding binding,
-                                                     GrGLIRect* viewport) {
+GrGLuint GrGLGpu::bindSurfaceAsFBO(GrSurface* surface, GrGLenum fboTarget, GrGLIRect* viewport,
+                                   TempFBOTarget tempFBOTarget) {
     GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(surface->asRenderTarget());
     if (NULL == rt) {
         SkASSERT(surface->asTexture());
         GrGLuint texID = static_cast<GrGLTexture*>(surface->asTexture())->textureID();
-        GrGLFBO* tempFBO;
+        GrGLuint* tempFBOID;
+        tempFBOID = kSrc_TempFBOTarget == tempFBOTarget ? &fTempSrcFBOID : &fTempDstFBOID;
 
-        if (kRead_FBOBinding == binding) {
-            if (!fTempSrcFBO) {
-                fTempSrcFBO.reset(SkNEW_ARGS(GrGLFBO, (this->glInterface())));
-                SkASSERT(fTempSrcFBO->isValid());
-            }
-            tempFBO = fTempSrcFBO;
-        } else {
-            SkASSERT(kDraw_FBOBinding == binding);
-            if (!fTempDstFBO) {
-                fTempDstFBO.reset(SkNEW_ARGS(GrGLFBO, (this->glInterface())));
-                SkASSERT(fTempDstFBO->isValid());
-            }
-            tempFBO = fTempDstFBO;
+        if (0 == *tempFBOID) {
+            GR_GL_CALL(this->glInterface(), GenFramebuffers(1, tempFBOID));
         }
 
-        GrGLenum target = this->bindFBO(binding, tempFBO);
-        GR_GL_CALL(this->glInterface(), FramebufferTexture2D(target,
+        fStats.incRenderTargetBinds();
+        GR_GL_CALL(this->glInterface(), BindFramebuffer(fboTarget, *tempFBOID));
+        GR_GL_CALL(this->glInterface(), FramebufferTexture2D(fboTarget,
                                                              GR_GL_COLOR_ATTACHMENT0,
                                                              GR_GL_TEXTURE_2D,
                                                              texID,
@@ -2616,21 +2599,18 @@
         viewport->fBottom = 0;
         viewport->fWidth = surface->width();
         viewport->fHeight = surface->height();
-        return binding;
+        return *tempFBOID;
     } else {
-        this->bindFBO(binding, rt->renderFBO());
+        GrGLuint tempFBOID = 0;
+        fStats.incRenderTargetBinds();
+        GR_GL_CALL(this->glInterface(), BindFramebuffer(fboTarget, rt->renderFBOID()));
         *viewport = rt->getViewport();
-        return kInvalidFBOBinding;
+        return tempFBOID;
     }
 }
 
-void GrGLGpu::unbindSurfaceAsFBOForCopy(FBOBinding binding) {
-    if (kInvalidFBOBinding == binding) {
-        return;
-    }
-    GrGLFBO* tempFBO = kDraw_FBOBinding == binding ? fTempSrcFBO : fTempDstFBO;
-    GrGLenum target = this->bindFBO(binding, tempFBO);
-    GR_GL_CALL(this->glInterface(), FramebufferTexture2D(target,
+void GrGLGpu::unbindTextureFromFBO(GrGLenum fboTarget) {
+    GR_GL_CALL(this->glInterface(), FramebufferTexture2D(fboTarget,
                                                          GR_GL_COLOR_ATTACHMENT0,
                                                          GR_GL_TEXTURE_2D,
                                                          0,
@@ -2661,7 +2641,7 @@
     }
 
     const GrGLRenderTarget* srcRT = static_cast<const GrGLRenderTarget*>(src->asRenderTarget());
-    if (srcRT && srcRT->renderFBO() != srcRT->textureFBO()) {
+    if (srcRT && srcRT->renderFBOID() != srcRT->textureFBOID()) {
         // It's illegal to call CopyTexSubImage2D on a MSAA renderbuffer. Set up for FBO blit or
         // fail.
         if (this->caps()->isConfigRenderable(src->config(), false)) {
@@ -2686,10 +2666,13 @@
                           const SkIPoint& dstPoint) {
     bool copied = false;
     if (can_copy_texsubimage(dst, src, this)) {
+        GrGLuint srcFBO;
         GrGLIRect srcVP;
-        FBOBinding srcFBOBinding = this->bindSurfaceAsFBOForCopy(src, kRead_FBOBinding, &srcVP);
+        srcFBO = this->bindSurfaceAsFBO(src, GR_GL_FRAMEBUFFER, &srcVP, kSrc_TempFBOTarget);
         GrGLTexture* dstTex = static_cast<GrGLTexture*>(dst->asTexture());
         SkASSERT(dstTex);
+        // We modified the bound FBO
+        fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID;
         GrGLIRect srcGLRect;
         srcGLRect.setRelativeTo(srcVP,
                                 srcRect.fLeft,
@@ -2711,7 +2694,9 @@
                                   srcGLRect.fLeft, srcGLRect.fBottom,
                                   srcGLRect.fWidth, srcGLRect.fHeight));
         copied = true;
-        this->unbindSurfaceAsFBOForCopy(srcFBOBinding);
+        if (srcFBO) {
+            this->unbindTextureFromFBO(GR_GL_FRAMEBUFFER);
+        }
     } else if (can_blit_framebuffer(dst, src, this)) {
         SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY,
                                             srcRect.width(), srcRect.height());
@@ -2721,11 +2706,16 @@
         }
 
         if (!selfOverlap) {
+            GrGLuint dstFBO;
+            GrGLuint srcFBO;
             GrGLIRect dstVP;
             GrGLIRect srcVP;
-            FBOBinding dstFBOBinding = this->bindSurfaceAsFBOForCopy(dst, kDraw_FBOBinding, &dstVP);
-            FBOBinding srcFBOBinding = this->bindSurfaceAsFBOForCopy(src, kRead_FBOBinding, &srcVP);
-
+            dstFBO = this->bindSurfaceAsFBO(dst, GR_GL_DRAW_FRAMEBUFFER, &dstVP,
+                                            kDst_TempFBOTarget);
+            srcFBO = this->bindSurfaceAsFBO(src, GR_GL_READ_FRAMEBUFFER, &srcVP,
+                                            kSrc_TempFBOTarget);
+            // We modified the bound FBO
+            fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID;
             GrGLIRect srcGLRect;
             GrGLIRect dstGLRect;
             srcGLRect.setRelativeTo(srcVP,
@@ -2763,8 +2753,12 @@
                                     dstGLRect.fLeft + dstGLRect.fWidth,
                                     dstGLRect.fBottom + dstGLRect.fHeight,
                                     GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST));
-            this->unbindSurfaceAsFBOForCopy(dstFBOBinding);
-            this->unbindSurfaceAsFBOForCopy(srcFBOBinding);
+            if (dstFBO) {
+                this->unbindTextureFromFBO(GR_GL_DRAW_FRAMEBUFFER);
+            }
+            if (srcFBO) {
+                this->unbindTextureFromFBO(GR_GL_READ_FRAMEBUFFER);
+            }
             copied = true;
         }
     }
@@ -2793,6 +2787,28 @@
     return false;
 }
 
+void GrGLGpu::xferBarrier(GrRenderTarget* rt, GrXferBarrierType type) {
+    switch (type) {
+        case kTexture_GrXferBarrierType: {
+            GrGLRenderTarget* glrt = static_cast<GrGLRenderTarget*>(rt);
+            if (glrt->textureFBOID() != glrt->renderFBOID()) {
+                // The render target uses separate storage so no need for glTextureBarrier.
+                // FIXME: The render target will resolve automatically when its texture is bound,
+                // but we could resolve only the bounds that will be read if we do it here instead.
+                return;
+            }
+            SkASSERT(this->caps()->textureBarrierSupport());
+            GL_CALL(TextureBarrier());
+            return;
+        }
+        case kBlend_GrXferBarrierType:
+            SkASSERT(GrDrawTargetCaps::kAdvanced_BlendEquationSupport ==
+                     this->caps()->blendEquationSupport());
+            GL_CALL(BlendBarrier());
+            return;
+    }
+}
+
 void GrGLGpu::didAddGpuTraceMarker() {
     if (this->caps()->gpuTracingSupport()) {
         const GrTraceMarkerSet& markerArray = this->getActiveTraceMarkers();
@@ -2826,14 +2842,13 @@
 
     // We use a vertex array if we're on a core profile and the verts are in a VBO.
     if (gpu->glCaps().isCoreProfile() && !vbuffer->isCPUBacked()) {
-        if (NULL == fVBOVertexArray || fVBOVertexArray->wasDestroyed()) {
-            SkSafeUnref(fVBOVertexArray);
+        if (!fVBOVertexArray) {
             GrGLuint arrayID;
             GR_GL_CALL(gpu->glInterface(), GenVertexArrays(1, &arrayID));
             int attrCount = gpu->glCaps().maxVertexAttributes();
-            fVBOVertexArray = SkNEW_ARGS(GrGLVertexArray, (gpu, arrayID, attrCount));
+            fVBOVertexArray = SkNEW_ARGS(GrGLVertexArray, (arrayID, attrCount));
         }
-        attribState = fVBOVertexArray->bindWithIndexBuffer(ibuffer);
+        attribState = fVBOVertexArray->bindWithIndexBuffer(gpu, ibuffer);
     } else {
         if (ibuffer) {
             this->setIndexBufferIDOnDefaultVertexArray(gpu, ibuffer->bufferID());
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 807ed87..3323ece 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -14,7 +14,7 @@
 #include "GrGLPathRendering.h"
 #include "GrGLProgram.h"
 #include "GrGLRenderTarget.h"
-#include "GrGLStencilBuffer.h"
+#include "GrGLStencilAttachment.h"
 #include "GrGLTexture.h"
 #include "GrGLVertexArray.h"
 #include "GrGLVertexBuffer.h"
@@ -24,6 +24,7 @@
 #include "SkTypes.h"
 
 class GrPipeline;
+class GrNonInstancedVertices;
 
 #ifdef SK_DEVELOPER
 #define PROGRAM_CACHE_STATS
@@ -32,9 +33,9 @@
 class GrGLGpu : public GrGpu {
 public:
     GrGLGpu(const GrGLContext& ctx, GrContext* context);
-    ~GrGLGpu() SK_OVERRIDE;
+    ~GrGLGpu() override;
 
-    void contextAbandoned() SK_OVERRIDE;
+    void contextAbandoned() override;
 
     const GrGLContext& glContext() const { return fGLContext; }
 
@@ -46,11 +47,11 @@
     const GrGLCaps& glCaps() const { return *fGLContext.caps(); }
 
     GrGLPathRendering* glPathRendering() {
-        SkASSERT(glCaps().pathRenderingSupport());
+        SkASSERT(glCaps().shaderCaps()->pathRenderingSupport());
         return static_cast<GrGLPathRendering*>(pathRendering());
     }
 
-    void discard(GrRenderTarget*) SK_OVERRIDE;
+    void discard(GrRenderTarget*) override;
 
     // Used by GrGLProgram and GrGLPathTexGenProgramEffects to configure OpenGL
     // state.
@@ -58,18 +59,18 @@
 
     // GrGpu overrides
     GrPixelConfig preferredReadPixelsConfig(GrPixelConfig readConfig,
-                                            GrPixelConfig surfaceConfig) const SK_OVERRIDE;
+                                            GrPixelConfig surfaceConfig) const override;
     GrPixelConfig preferredWritePixelsConfig(GrPixelConfig writeConfig,
-                                             GrPixelConfig surfaceConfig) const SK_OVERRIDE;
-    bool canWriteTexturePixels(const GrTexture*, GrPixelConfig srcConfig) const SK_OVERRIDE;
+                                             GrPixelConfig surfaceConfig) const override;
+    bool canWriteTexturePixels(const GrTexture*, GrPixelConfig srcConfig) const override;
     bool readPixelsWillPayForYFlip(GrRenderTarget* renderTarget,
                                    int left, int top,
                                    int width, int height,
                                    GrPixelConfig config,
-                                   size_t rowBytes) const SK_OVERRIDE;
-    bool fullReadPixelsIsFasterThanPartial() const SK_OVERRIDE;
+                                   size_t rowBytes) const override;
+    bool fullReadPixelsIsFasterThanPartial() const override;
 
-    bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) SK_OVERRIDE;
+    bool initCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) override;
 
     // These functions should be used to bind GL objects. They track the GL state and skip redundant
     // bindings. Making the equivalent glBind calls directly will confuse the state tracking.
@@ -98,55 +99,59 @@
     bool copySurface(GrSurface* dst,
                      GrSurface* src,
                      const SkIRect& srcRect,
-                     const SkIPoint& dstPoint) SK_OVERRIDE;
+                     const SkIPoint& dstPoint) override;
 
     bool canCopySurface(const GrSurface* dst,
                         const GrSurface* src,
                         const SkIRect& srcRect,
-                        const SkIPoint& dstPoint) SK_OVERRIDE;
+                        const SkIPoint& dstPoint) override;
+
+    void xferBarrier(GrRenderTarget*, GrXferBarrierType) override;
 
     void buildProgramDesc(GrProgramDesc*,
                           const GrPrimitiveProcessor&,
                           const GrPipeline&,
-                          const GrBatchTracker&) const SK_OVERRIDE;
+                          const GrBatchTracker&) const override;
 
 private:
     // GrGpu overrides
-    void onResetContext(uint32_t resetBits) SK_OVERRIDE;
+    void onResetContext(uint32_t resetBits) override;
 
-    GrTexture* onCreateTexture(const GrSurfaceDesc& desc, bool budgeted, const void* srcData,
-                               size_t rowBytes) SK_OVERRIDE;
-    GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc, bool budgeted,
-                                         const void* srcData) SK_OVERRIDE;
-    GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) SK_OVERRIDE;
-    GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) SK_OVERRIDE;
-    GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) SK_OVERRIDE;
-    GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) SK_OVERRIDE;
-    bool createStencilBufferForRenderTarget(GrRenderTarget* rt, int width, int height) SK_OVERRIDE;
-    bool attachStencilBufferToRenderTarget(GrStencilBuffer* sb, GrRenderTarget* rt) SK_OVERRIDE;
+    GrTexture* onCreateTexture(const GrSurfaceDesc& desc, GrGpuResource::LifeCycle lifeCycle,
+                               const void* srcData, size_t rowBytes) override;
+    GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc,
+                                         GrGpuResource::LifeCycle lifeCycle,
+                                         const void* srcData) override;
+    GrVertexBuffer* onCreateVertexBuffer(size_t size, bool dynamic) override;
+    GrIndexBuffer* onCreateIndexBuffer(size_t size, bool dynamic) override;
+    GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) override;
+    GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) override;
+    bool createStencilAttachmentForRenderTarget(GrRenderTarget* rt, int width, int height) override;
+    bool attachStencilAttachmentToRenderTarget(GrStencilAttachment* sb,
+                                               GrRenderTarget* rt) override;
 
     void onClear(GrRenderTarget*, const SkIRect* rect, GrColor color,
-                 bool canIgnoreRect) SK_OVERRIDE;
+                 bool canIgnoreRect) override;
 
-    void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) SK_OVERRIDE;
+    void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) override;
 
     bool onReadPixels(GrRenderTarget* target,
                       int left, int top,
                       int width, int height,
                       GrPixelConfig,
                       void* buffer,
-                      size_t rowBytes) SK_OVERRIDE;
+                      size_t rowBytes) override;
 
     bool onWriteTexturePixels(GrTexture* texture,
                               int left, int top, int width, int height,
                               GrPixelConfig config, const void* buffer,
-                              size_t rowBytes) SK_OVERRIDE;
+                              size_t rowBytes) override;
 
-    void onResolveRenderTarget(GrRenderTarget* target) SK_OVERRIDE;
+    void onResolveRenderTarget(GrRenderTarget* target) override;
 
-    void onDraw(const DrawArgs&, const GrDrawTarget::DrawInfo&) SK_OVERRIDE;
-    void onStencilPath(const GrPath*, const StencilPathState&) SK_OVERRIDE;
-    void onDrawPath(const DrawArgs&, const GrPath*, const GrStencilSettings&) SK_OVERRIDE;
+    void onDraw(const DrawArgs&, const GrNonInstancedVertices&) override;
+    void onStencilPath(const GrPath*, const StencilPathState&) override;
+    void onDrawPath(const DrawArgs&, const GrPath*, const GrStencilSettings&) override;
     void onDrawPaths(const DrawArgs&,
                      const GrPathRange*,
                      const void* indices,
@@ -154,27 +159,25 @@
                      const float transformValues[],
                      GrDrawTarget::PathTransformType,
                      int count,
-                     const GrStencilSettings&) SK_OVERRIDE;
+                     const GrStencilSettings&) override;
 
-    void clearStencil(GrRenderTarget*) SK_OVERRIDE;
+    void clearStencil(GrRenderTarget*) override;
 
     // GrDrawTarget overrides
-    void didAddGpuTraceMarker() SK_OVERRIDE;
-    void didRemoveGpuTraceMarker() SK_OVERRIDE;
+    void didAddGpuTraceMarker() override;
+    void didRemoveGpuTraceMarker() override;
 
     // binds texture unit in GL
     void setTextureUnit(int unitIdx);
 
     // Flushes state from GrPipeline to GL. Returns false if the state couldn't be set.
-    // TODO we only have need to know if this is a line draw for flushing AA state on some buggy
-    // hardware.  Evaluate if this is really necessary anymore
-    bool flushGLState(const DrawArgs&, bool isLineDraw);
+    bool flushGLState(const DrawArgs&);
 
     // Sets up vertex attribute pointers and strides. On return indexOffsetInBytes gives the offset
-    // an into the index buffer. It does not account for drawInfo.startIndex() but rather the start
+    // an into the index buffer. It does not account for vertices.startIndex() but rather the start
     // index is relative to the returned offset.
     void setupGeometry(const GrPrimitiveProcessor&,
-                       const GrDrawTarget::DrawInfo& info,
+                       const GrNonInstancedVertices& vertices,
                        size_t* indexOffsetInBytes);
 
     // Subclasses should call this to flush the blend state.
@@ -246,31 +249,12 @@
     // ensures that such operations don't negatively interact with tracking bound textures.
     void setScratchTextureUnit();
 
-    // Binds the render target, sets the viewport, tracks dirty are for resolve, and tracks whether
-    // mip maps need rebuilding. bounds is region that may be modified by the draw. NULL means whole
-    // target. Can be an empty rect.
-    void prepareToDrawToRenderTarget(GrGLRenderTarget*, const SkIRect* bounds);
-
-    // On older GLs there may not be separate FBO bindings for draw and read. In that case these
-    // alias each other.
-    enum FBOBinding {
-        kDraw_FBOBinding, // drawing or dst of blit
-        kRead_FBOBinding, // src of blit, read pixels.
-
-        kLast_FBOBinding = kRead_FBOBinding
-    };
-    static const int kFBOBindingCnt = kLast_FBOBinding + 1;
-
-    // binds the FBO and returns the GL enum of the framebuffer target it was bound to.
-    GrGLenum bindFBO(FBOBinding, const GrGLFBO*);
-    // This version invokes a workaround for a bug in Chromium. It should be called before
-    // attachments are changed on a FBO.
-    GrGLenum bindFBOForAddingAttachments(const GrGLFBO*);
-
-    void setViewport(const GrGLIRect& viewport);
+    // bounds is region that may be modified and therefore has to be resolved.
+    // NULL means whole target. Can be an empty rect.
+    void flushRenderTarget(GrGLRenderTarget*, const SkIRect* bounds);
 
     void flushStencil(const GrStencilSettings&);
-    void flushHWAAState(GrRenderTarget* rt, bool useHWAA, bool isLineDraw);
+    void flushHWAAState(GrRenderTarget* rt, bool useHWAA);
 
     bool configToGLFormats(GrPixelConfig config,
                            bool getSizedInternal,
@@ -297,17 +281,18 @@
                                  int left = 0, int top = 0,
                                  int width = -1, int height = -1);
 
-    bool createRenderTargetObjects(const GrSurfaceDesc&, bool budgeted, GrGLuint texID, 
-                                   GrGLRenderTarget::IDDesc*);
+    bool createRenderTargetObjects(const GrSurfaceDesc&, GrGpuResource::LifeCycle lifeCycle,
+                                   GrGLuint texID, GrGLRenderTarget::IDDesc*);
 
-    static const FBOBinding kInvalidFBOBinding = static_cast<FBOBinding>(-1);
+    enum TempFBOTarget {
+        kSrc_TempFBOTarget,
+        kDst_TempFBOTarget
+    };
 
-    // Binds a surface as an FBO. A temporary FBO ID may be used if the surface is not already
-    // a render target. Afterwards unbindSurfaceAsFBOForCopy must be called with the value returned.
-    FBOBinding bindSurfaceAsFBOForCopy(GrSurface*, FBOBinding, GrGLIRect* viewport);
+    GrGLuint bindSurfaceAsFBO(GrSurface* surface, GrGLenum fboTarget, GrGLIRect* viewport,
+                              TempFBOTarget tempFBOTarget);
 
-    // Must be matched with bindSurfaceAsFBOForCopy.
-    void unbindSurfaceAsFBOForCopy(FBOBinding);
+    void unbindTextureFromFBO(GrGLenum fboTarget);
 
     GrGLContext fGLContext;
 
@@ -327,9 +312,10 @@
         kUnknown_TriState
     };
 
-    SkAutoTUnref<GrGLFBO> fTempSrcFBO;
-    SkAutoTUnref<GrGLFBO> fTempDstFBO;
-    SkAutoTUnref<GrGLFBO> fStencilClearFBO;
+    GrGLuint                    fTempSrcFBOID;
+    GrGLuint                    fTempDstFBOID;
+
+    GrGLuint                    fStencilClearFBOID;
 
     // last scissor / viewport scissor state seen by the GL.
     struct {
@@ -350,7 +336,7 @@
     public:
         HWGeometryState() { fVBOVertexArray = NULL; this->invalidate(); }
 
-        ~HWGeometryState() { SkSafeUnref(fVBOVertexArray); }
+        ~HWGeometryState() { SkDELETE(fVBOVertexArray); }
 
         void invalidate() {
             fBoundVertexArrayIDIsValid = false;
@@ -452,6 +438,7 @@
     } fHWGeometryState;
 
     struct {
+        GrBlendEquation fEquation;
         GrBlendCoeff    fSrcCoeff;
         GrBlendCoeff    fDstCoeff;
         GrColor         fConstColor;
@@ -459,8 +446,9 @@
         TriState        fEnabled;
 
         void invalidate() {
-            fSrcCoeff = kInvalid_GrBlendCoeff;
-            fDstCoeff = kInvalid_GrBlendCoeff;
+            fEquation = static_cast<GrBlendEquation>(-1);
+            fSrcCoeff = static_cast<GrBlendCoeff>(-1);
+            fDstCoeff = static_cast<GrBlendCoeff>(-1);
             fConstColorValid = false;
             fEnabled = kUnknown_TriState;
         }
@@ -475,14 +463,9 @@
     GrPipelineBuilder::DrawFace fHWDrawFace;
     TriState                    fHWWriteToColor;
     TriState                    fHWDitherEnabled;
+    uint32_t                    fHWBoundRenderTargetUniqueID;
     SkTArray<uint32_t, true>    fHWBoundTextureUniqueIDs;
 
-    // Track fbo binding state.
-    struct HWFBOBinding {
-        SkAutoTUnref<const GrGLFBO> fFBO;
-        void invalidate() { fFBO.reset(NULL); }
-    } fHWFBOBinding[kFBOBindingCnt];
-
     ///@}
 
     // we record what stencil format worked last time to hopefully exit early
diff --git a/src/gpu/gl/GrGLIndexBuffer.h b/src/gpu/gl/GrGLIndexBuffer.h
index 3ff746e..4ca6360 100644
--- a/src/gpu/gl/GrGLIndexBuffer.h
+++ b/src/gpu/gl/GrGLIndexBuffer.h
@@ -31,13 +31,13 @@
     }
 
 protected:
-    void onAbandon() SK_OVERRIDE;
-    void onRelease() SK_OVERRIDE;
+    void onAbandon() override;
+    void onRelease() override;
 
 private:
-    void* onMap() SK_OVERRIDE;
-    void onUnmap() SK_OVERRIDE;
-    bool onUpdateData(const void* src, size_t srcSizeInBytes) SK_OVERRIDE;
+    void* onMap() override;
+    void onUnmap() override;
+    bool onUpdateData(const void* src, size_t srcSizeInBytes) override;
 
     GrGLGpu* getGpuGL() const {
         SkASSERT(!this->wasDestroyed());
diff --git a/src/gpu/gl/GrGLInterface.cpp b/src/gpu/gl/GrGLInterface.cpp
index dba0059..89f1f9b 100644
--- a/src/gpu/gl/GrGLInterface.cpp
+++ b/src/gpu/gl/GrGLInterface.cpp
@@ -114,8 +114,9 @@
         NULL == fFunctions.fBindAttribLocation ||
         NULL == fFunctions.fBindBuffer ||
         NULL == fFunctions.fBindTexture ||
+        NULL == fFunctions.fBlendColor ||      // -> GL >= 1.4 or extension, ES >= 2.0
+        NULL == fFunctions.fBlendEquation ||   // -> GL >= 1.4 or extension, ES >= 2.0
         NULL == fFunctions.fBlendFunc ||
-        NULL == fFunctions.fBlendColor ||      // -> GL >= 1.4, ES >= 2.0 or extension
         NULL == fFunctions.fBufferData ||
         NULL == fFunctions.fBufferSubData ||
         NULL == fFunctions.fClear ||
@@ -306,6 +307,28 @@
         }
     }
 
+    // glTextureBarrier is part of desktop 4.5. There are also ARB and NV extensions.
+    if (kGL_GrGLStandard == fStandard) {
+        if (glVer >= GR_GL_VER(4,5) ||
+            fExtensions.has("GL_ARB_texture_barrier") ||
+            fExtensions.has("GL_NV_texture_barrier")) {
+            if (NULL == fFunctions.fTextureBarrier) {
+                RETURN_FALSE_INTERFACE
+            }
+        }
+    } else if (fExtensions.has("GL_NV_texture_barrier")) {
+        if (NULL == fFunctions.fTextureBarrier) {
+            RETURN_FALSE_INTERFACE
+        }
+    }
+
+    if (fExtensions.has("GL_KHR_blend_equation_advanced") ||
+        fExtensions.has("GL_NV_blend_equation_advanced")) {
+        if (NULL == fFunctions.fBlendBarrier) {
+            RETURN_FALSE_INTERFACE
+        }
+    }
+
     if (fExtensions.has("GL_EXT_discard_framebuffer")) {
 // FIXME: Remove this once Chromium is updated to provide this function
 #if 0
@@ -505,5 +528,11 @@
         }
     }
 
+    if (fExtensions.has("GL_NV_framebuffer_mixed_samples")) {
+        if (NULL == fFunctions.fCoverageModulation) {
+            RETURN_FALSE_INTERFACE
+        }
+    }
+
     return true;
 }
diff --git a/src/gpu/gl/GrGLNameAllocator.cpp b/src/gpu/gl/GrGLNameAllocator.cpp
index 9d60162..94792f1 100644
--- a/src/gpu/gl/GrGLNameAllocator.cpp
+++ b/src/gpu/gl/GrGLNameAllocator.cpp
@@ -133,7 +133,7 @@
         this->updateStats();
     }
 
-    SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) SK_OVERRIDE {
+    SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) override {
         // Try allocating the range inside fLeft's internal gaps.
         fLeft.reset(fLeft->internalAllocate(outName));
         if (0 != *outName) {
@@ -160,7 +160,7 @@
         return this->takeRef();
     }
 
-    SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) SK_OVERRIDE {
+    SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) override {
         fLeft.reset(fLeft->removeLeftmostContiguousRange(removedCount));
         if (NULL == fLeft) {
             return fRight.detach();
@@ -169,7 +169,7 @@
         return this->rebalance();
     }
 
-    GrGLuint appendNames(GrGLuint count) SK_OVERRIDE {
+    GrGLuint appendNames(GrGLuint count) override {
         SkASSERT(fEnd + count > fEnd); // Check for integer wrap.
         GrGLuint name = fRight->appendNames(count);
         SkASSERT(fRight->end() == fEnd + count);
@@ -177,7 +177,7 @@
         return name;
     }
 
-    GrGLuint prependNames(GrGLuint count) SK_OVERRIDE {
+    GrGLuint prependNames(GrGLuint count) override {
         SkASSERT(fFirst > count); // We can't allocate at or below 0.
         GrGLuint name = fLeft->prependNames(count);
         SkASSERT(fLeft->first() == fFirst - count);
@@ -185,7 +185,7 @@
         return name;
     }
 
-    SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) SK_OVERRIDE {
+    SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) override {
         if (name < fLeft->end()) {
             fLeft.reset(fLeft->free(name));
             if (NULL == fLeft) {
@@ -280,30 +280,30 @@
         fHeight = 0;
     }
 
-    SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) SK_OVERRIDE {
+    SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) override {
         *outName = 0; // No internal gaps, we are contiguous.
         return this->takeRef();
     }
 
-    SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) SK_OVERRIDE {
+    SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) override {
         *removedCount = fEnd - fFirst;
         return NULL;
     }
 
-    GrGLuint appendNames(GrGLuint count) SK_OVERRIDE {
+    GrGLuint appendNames(GrGLuint count) override {
         SkASSERT(fEnd + count > fEnd); // Check for integer wrap.
         GrGLuint name = fEnd;
         fEnd += count;
         return name;
     }
 
-    GrGLuint prependNames(GrGLuint count) SK_OVERRIDE {
+    GrGLuint prependNames(GrGLuint count) override {
         SkASSERT(fFirst > count); // We can't allocate at or below 0.
         fFirst -= count;
         return fFirst;
     }
 
-    SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) SK_OVERRIDE {
+    SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) override {
         if (name < fFirst || name >= fEnd) {
           // Not-allocated names are silently ignored.
           return this->takeRef();
diff --git a/src/gpu/gl/GrGLNoOpInterface.cpp b/src/gpu/gl/GrGLNoOpInterface.cpp
index 38ad65e..f588046 100644
--- a/src/gpu/gl/GrGLNoOpInterface.cpp
+++ b/src/gpu/gl/GrGLNoOpInterface.cpp
@@ -27,7 +27,6 @@
     "GL_ARB_timer_query",
     "GL_ARB_draw_buffers",
     "GL_ARB_occlusion_query",
-    "GL_EXT_blend_color",
     "GL_EXT_stencil_wrap"
 };
 
@@ -48,15 +47,18 @@
 }
 }
 
+GrGLvoid GR_GL_FUNCTION_TYPE noOpGLBindFragDataLocation(GrGLuint program,
+                                                        GrGLuint colorNumber,
+                                                        const GrGLchar* name) {
+}
+
 GrGLvoid GR_GL_FUNCTION_TYPE noOpGLBlendColor(GrGLclampf red,
                                               GrGLclampf green,
                                               GrGLclampf blue,
                                               GrGLclampf alpha) {
 }
 
-GrGLvoid GR_GL_FUNCTION_TYPE noOpGLBindFragDataLocation(GrGLuint program,
-                                                        GrGLuint colorNumber,
-                                                        const GrGLchar* name) {
+GrGLvoid GR_GL_FUNCTION_TYPE noOpGLBlendEquation(GrGLenum mode) {
 }
 
 GrGLvoid GR_GL_FUNCTION_TYPE noOpGLBlendFunc(GrGLenum sfactor,
@@ -440,8 +442,7 @@
 
 GrGLenum GR_GL_FUNCTION_TYPE noOpGLCheckFramebufferStatus(GrGLenum target) {
 
-    GrAlwaysAssert(GR_GL_FRAMEBUFFER == target || GR_GL_READ_FRAMEBUFFER == target ||
-                   GR_GL_DRAW_FRAMEBUFFER == target);
+    GrAlwaysAssert(GR_GL_FRAMEBUFFER == target);
 
     return GR_GL_FRAMEBUFFER_COMPLETE;
 }
diff --git a/src/gpu/gl/GrGLNoOpInterface.h b/src/gpu/gl/GrGLNoOpInterface.h
index c7bc9d8..b803b46 100644
--- a/src/gpu/gl/GrGLNoOpInterface.h
+++ b/src/gpu/gl/GrGLNoOpInterface.h
@@ -13,14 +13,16 @@
 
 // These are constants/functions that are common to the Null and Debug GL interface implementations.
 
+GrGLvoid GR_GL_FUNCTION_TYPE noOpGLBindFragDataLocation(GrGLuint program,
+                                                        GrGLuint colorNumber,
+                                                        const GrGLchar* name);
+
 GrGLvoid GR_GL_FUNCTION_TYPE noOpGLBlendColor(GrGLclampf red,
                                               GrGLclampf green,
                                               GrGLclampf blue,
                                               GrGLclampf alpha);
 
-GrGLvoid GR_GL_FUNCTION_TYPE noOpGLBindFragDataLocation(GrGLuint program,
-                                                        GrGLuint colorNumber,
-                                                        const GrGLchar* name);
+GrGLvoid GR_GL_FUNCTION_TYPE noOpGLBlendEquation(GrGLenum mode);
 
 GrGLvoid GR_GL_FUNCTION_TYPE noOpGLBlendFunc(GrGLenum sfactor,
                                              GrGLenum dfactor);
diff --git a/src/gpu/gl/GrGLPath.h b/src/gpu/gl/GrGLPath.h
index f048f85..f02f705 100644
--- a/src/gpu/gl/GrGLPath.h
+++ b/src/gpu/gl/GrGLPath.h
@@ -31,12 +31,12 @@
     GrGLuint pathID() const { return fPathID; }
 
 protected:
-    void onRelease() SK_OVERRIDE;
-    void onAbandon() SK_OVERRIDE;
+    void onRelease() override;
+    void onAbandon() override;
 
 private:
     // TODO: Figure out how to get an approximate size of the path in Gpu memory.
-    size_t onGpuMemorySize() const SK_OVERRIDE { return 100; }
+    size_t onGpuMemorySize() const override { return 100; }
 
     GrGLuint fPathID;
 
diff --git a/src/gpu/gl/GrGLPathProcessor.cpp b/src/gpu/gl/GrGLPathProcessor.cpp
index 3f3e5d9..e8c10a3 100644
--- a/src/gpu/gl/GrGLPathProcessor.cpp
+++ b/src/gpu/gl/GrGLPathProcessor.cpp
@@ -16,7 +16,7 @@
 
 void GrGLPathProcessor::emitCode(EmitArgs& args) {
     GrGLGPBuilder* pb = args.fPB;
-    GrGLGPFragmentBuilder* fs = args.fPB->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fs = args.fPB->getFragmentShaderBuilder();
     const PathBatchTracker& local = args.fBT.cast<PathBatchTracker>();
 
     // emit transforms
@@ -41,7 +41,7 @@
 
 void GrGLPathProcessor::GenKey(const GrPathProcessor&,
                                const GrBatchTracker& bt,
-                               const GrGLCaps&,
+                               const GrGLSLCaps&,
                                GrProcessorKeyBuilder* b) {
     const PathBatchTracker& local = bt.cast<PathBatchTracker>();
     b->add32(local.fInputColorType | local.fInputCoverageType << 16);
@@ -59,59 +59,8 @@
     }
 }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-void GrGLLegacyPathProcessor::emitTransforms(GrGLGPBuilder*, const TransformsIn& tin,
-                                             TransformsOut* tout) {
-    tout->push_back_n(tin.count());
-    fInstalledTransforms.push_back_n(tin.count());
-    for (int i = 0; i < tin.count(); i++) {
-        const ProcCoords& coordTransforms = tin[i];
-        int texCoordIndex = this->addTexCoordSets(coordTransforms.count());
-
-        // Use the first uniform location as the texcoord index.
-        fInstalledTransforms[i].push_back_n(1);
-        fInstalledTransforms[i][0].fHandle = ShaderVarHandle(texCoordIndex);
-
-        SkString name;
-        for (int t = 0; t < coordTransforms.count(); ++t) {
-            GrSLType type = coordTransforms[t]->getMatrix().hasPerspective() ? kVec3f_GrSLType :
-                                                                               kVec2f_GrSLType;
-
-            name.printf("%s(gl_TexCoord[%i])", GrGLSLTypeString(type), texCoordIndex++);
-            SkNEW_APPEND_TO_TARRAY(&(*tout)[i], GrGLProcessor::TransformedCoords, (name, type));
-        }
-    }
-}
-
-void GrGLLegacyPathProcessor::setTransformData(
-        const GrPrimitiveProcessor& primProc,
-        int index,
-        const SkTArray<const GrCoordTransform*, true>& transforms,
-        GrGLPathRendering* glpr,
-        GrGLuint) {
-    // We've hidden the texcoord index in the first entry of the transforms array for each
-    // effect
-    int texCoordIndex = fInstalledTransforms[index][0].fHandle.handle();
-    for (int t = 0; t < transforms.count(); ++t) {
-        const SkMatrix& transform = GetTransformMatrix(primProc.localMatrix(), *transforms[t]);
-        GrGLPathRendering::PathTexGenComponents components =
-                GrGLPathRendering::kST_PathTexGenComponents;
-        if (transform.hasPerspective()) {
-            components = GrGLPathRendering::kSTR_PathTexGenComponents;
-        }
-        glpr->enablePathTexGen(texCoordIndex++, components, transform);
-    }
-}
-
-void GrGLLegacyPathProcessor::didSetData(GrGLPathRendering* glpr) {
-    glpr->flushPathTexGenSettings(fTexCoordSetCnt);
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-void GrGLNormalPathProcessor::emitTransforms(GrGLGPBuilder* pb, const TransformsIn& tin,
-                                             TransformsOut* tout) {
+void GrGLPathProcessor::emitTransforms(GrGLGPBuilder* pb, const TransformsIn& tin,
+                                       TransformsOut* tout) {
     tout->push_back_n(tin.count());
     fInstalledTransforms.push_back_n(tin.count());
     for (int i = 0; i < tin.count(); i++) {
@@ -140,7 +89,7 @@
     }
 }
 
-void GrGLNormalPathProcessor::resolveSeparableVaryings(GrGLGpu* gpu, GrGLuint programId) {
+void GrGLPathProcessor::resolveSeparableVaryings(GrGLGpu* gpu, GrGLuint programId) {
     int count = fSeparableVaryingInfos.count();
     for (int i = 0; i < count; ++i) {
         GrGLint location;
@@ -153,17 +102,18 @@
     }
 }
 
-void GrGLNormalPathProcessor::setTransformData(
+void GrGLPathProcessor::setTransformData(
         const GrPrimitiveProcessor& primProc,
         int index,
         const SkTArray<const GrCoordTransform*, true>& coordTransforms,
         GrGLPathRendering* glpr,
         GrGLuint programID) {
+    const GrPathProcessor& pathProc = primProc.cast<GrPathProcessor>();
     SkSTArray<2, Transform, true>& transforms = fInstalledTransforms[index];
     int numTransforms = transforms.count();
     for (int t = 0; t < numTransforms; ++t) {
         SkASSERT(transforms[t].fHandle.isValid());
-        const SkMatrix& transform = GetTransformMatrix(primProc.localMatrix(),
+        const SkMatrix& transform = GetTransformMatrix(pathProc.localMatrix(),
                                                        *coordTransforms[t]);
         if (transforms[t].fCurrentValue.cheapEqualTo(transform)) {
             continue;
diff --git a/src/gpu/gl/GrGLPathProcessor.h b/src/gpu/gl/GrGLPathProcessor.h
index 368f1f9..d6fc170 100644
--- a/src/gpu/gl/GrGLPathProcessor.h
+++ b/src/gpu/gl/GrGLPathProcessor.h
@@ -20,83 +20,30 @@
 
     static void GenKey(const GrPathProcessor&,
                        const GrBatchTracker& bt,
-                       const GrGLCaps&,
+                       const GrGLSLCaps&,
                        GrProcessorKeyBuilder* b);
 
-    void emitCode(EmitArgs&) SK_OVERRIDE;
+    void emitCode(EmitArgs&) override;
 
-    virtual void emitTransforms(GrGLGPBuilder*, const TransformsIn&, TransformsOut*) = 0;
+    void emitTransforms(GrGLGPBuilder*, const TransformsIn&, TransformsOut*);
 
-    virtual void resolveSeparableVaryings(GrGLGpu* gpu, GrGLuint programId) {}
+    void resolveSeparableVaryings(GrGLGpu* gpu, GrGLuint programId);
 
     void setData(const GrGLProgramDataManager&,
                  const GrPrimitiveProcessor&,
-                 const GrBatchTracker&) SK_OVERRIDE;
+                 const GrBatchTracker&) override;
 
-    virtual void setTransformData(const GrPrimitiveProcessor&,
-                                  int index,
-                                  const SkTArray<const GrCoordTransform*, true>& transforms,
-                                  GrGLPathRendering*,
-                                  GrGLuint programID) = 0;
+    void setTransformData(const GrPrimitiveProcessor&,
+                          int index,
+                          const SkTArray<const GrCoordTransform*, true>& transforms,
+                          GrGLPathRendering*,
+                          GrGLuint programID);
 
     virtual void didSetData(GrGLPathRendering*) {}
 
 private:
     UniformHandle fColorUniform;
     GrColor fColor;
-
-    typedef GrGLPrimitiveProcessor INHERITED;
-};
-
-class GrGLLegacyPathProcessor : public GrGLPathProcessor {
-public:
-    GrGLLegacyPathProcessor(const GrPathProcessor& pathProc, const GrBatchTracker& bt,
-                            int maxTexCoords)
-        : INHERITED(pathProc, bt)
-        , fTexCoordSetCnt(0) {
-        SkDEBUGCODE(fMaxTexCoords = maxTexCoords;)
-    }
-
-    int addTexCoordSets(int count) {
-        int firstFreeCoordSet = fTexCoordSetCnt;
-        fTexCoordSetCnt += count;
-        SkASSERT(fMaxTexCoords >= fTexCoordSetCnt);
-        return firstFreeCoordSet;
-    }
-
-    void emitTransforms(GrGLGPBuilder*, const TransformsIn& tin, TransformsOut* tout) SK_OVERRIDE;
-
-    void setTransformData(const GrPrimitiveProcessor& primProc,
-                          int index,
-                          const SkTArray<const GrCoordTransform*, true>& transforms,
-                          GrGLPathRendering* glpr,
-                          GrGLuint) SK_OVERRIDE;
-
-    void didSetData(GrGLPathRendering* glpr) SK_OVERRIDE;
-
-private:
-    SkDEBUGCODE(int fMaxTexCoords;)
-    int fTexCoordSetCnt;
-
-    typedef GrGLPathProcessor INHERITED;
-};
-
-class GrGLNormalPathProcessor : public GrGLPathProcessor {
-public:
-    GrGLNormalPathProcessor(const GrPathProcessor& pathProc, const GrBatchTracker& bt)
-        : INHERITED(pathProc, bt) {}
-
-    void emitTransforms(GrGLGPBuilder* pb, const TransformsIn& tin,TransformsOut* tout) SK_OVERRIDE;
-
-    void resolveSeparableVaryings(GrGLGpu* gpu, GrGLuint programId);
-
-    void setTransformData(const GrPrimitiveProcessor& primProc,
-                          int index,
-                          const SkTArray<const GrCoordTransform*, true>& coordTransforms,
-                          GrGLPathRendering* glpr,
-                          GrGLuint programID) SK_OVERRIDE;
-
-private:
     struct SeparableVaryingInfo {
         GrSLType      fType;
         GrGLShaderVar fVariable;
@@ -107,7 +54,7 @@
 
     SeparableVaryingInfoArray fSeparableVaryingInfos;
 
-    typedef GrGLPathProcessor INHERITED;
+    typedef GrGLPrimitiveProcessor INHERITED;
 };
 
 #endif
diff --git a/src/gpu/gl/GrGLPathRange.h b/src/gpu/gl/GrGLPathRange.h
index ce13a3a..5cba9c7 100644
--- a/src/gpu/gl/GrGLPathRange.h
+++ b/src/gpu/gl/GrGLPathRange.h
@@ -42,13 +42,13 @@
     GrGLuint basePathID() const { return fBasePathID; }
 
 protected:
-    void onInitPath(int index, const SkPath&) const SK_OVERRIDE;
+    void onInitPath(int index, const SkPath&) const override;
 
-    void onRelease() SK_OVERRIDE;
-    void onAbandon() SK_OVERRIDE;
+    void onRelease() override;
+    void onAbandon() override;
 
 private:
-    size_t onGpuMemorySize() const SK_OVERRIDE { return fGpuMemorySize; }
+    size_t onGpuMemorySize() const override { return fGpuMemorySize; }
 
     GrGLuint fBasePathID;
     mutable size_t fGpuMemorySize;
diff --git a/src/gpu/gl/GrGLPathRendering.cpp b/src/gpu/gl/GrGLPathRendering.cpp
index a915214..8a0b35c 100644
--- a/src/gpu/gl/GrGLPathRendering.cpp
+++ b/src/gpu/gl/GrGLPathRendering.cpp
@@ -72,9 +72,7 @@
     fCaps.glyphLoadingSupport =
         NULL != glInterface->fFunctions.fPathMemoryGlyphIndexArray;
 
-    if (!fCaps.fragmentInputGenSupport) {
-        fHWPathTexGenSettings.reset(fGpu->glCaps().maxFixedFunctionTextureCoords());
-    }
+    SkASSERT(fCaps.fragmentInputGenSupport);
 }
 
 GrGLPathRendering::~GrGLPathRendering() {
@@ -87,19 +85,9 @@
 void GrGLPathRendering::resetContext() {
     fHWProjectionMatrixState.invalidate();
     // we don't use the model view matrix.
-    GrGLenum matrixMode =
-            fGpu->glCaps().nvprSupport() == GrGLCaps::kNormal_NvprSupport ? GR_GL_PATH_MODELVIEW :
-                                                                            GR_GL_MODELVIEW;
-    GL_CALL(MatrixLoadIdentity(matrixMode));
+    GL_CALL(MatrixLoadIdentity(GR_GL_PATH_MODELVIEW));
 
-    if (!caps().fragmentInputGenSupport) {
-        for (int i = 0; i < fGpu->glCaps().maxFixedFunctionTextureCoords(); ++i) {
-            GL_CALL(PathTexGen(GR_GL_TEXTURE0 + i, GR_GL_NONE, 0, NULL));
-            fHWPathTexGenSettings[i].fMode = GR_GL_NONE;
-            fHWPathTexGenSettings[i].fNumComponents = 0;
-        }
-        fHWActivePathTexGenSets = 0;
-    }
+    SkASSERT(fCaps.fragmentInputGenSupport);
     fHWPathStencilSettings.invalidate();
 }
 
@@ -209,7 +197,7 @@
                                   const void* indices, PathIndexType indexType,
                                   const float transformValues[], PathTransformType transformType,
                                   int count, const GrStencilSettings& stencilSettings) {
-    SkASSERT(fGpu->caps()->pathRenderingSupport());
+    SkASSERT(fGpu->caps()->shaderCaps()->pathRenderingSupport());
 
     GrGLuint baseID = static_cast<const GrGLPathRange*>(pathRange)->basePathID();
 
@@ -242,76 +230,6 @@
     }
 }
 
-void GrGLPathRendering::enablePathTexGen(int unitIdx, PathTexGenComponents components,
-                                         const GrGLfloat* coefficients) {
-    SkASSERT(components >= kS_PathTexGenComponents &&
-             components <= kSTR_PathTexGenComponents);
-    SkASSERT(fGpu->glCaps().maxFixedFunctionTextureCoords() >= unitIdx);
-
-    if (GR_GL_OBJECT_LINEAR == fHWPathTexGenSettings[unitIdx].fMode &&
-        components == fHWPathTexGenSettings[unitIdx].fNumComponents &&
-        !memcmp(coefficients, fHWPathTexGenSettings[unitIdx].fCoefficients,
-                3 * components * sizeof(GrGLfloat))) {
-        return;
-    }
-
-    fGpu->setTextureUnit(unitIdx);
-
-    fHWPathTexGenSettings[unitIdx].fNumComponents = components;
-    GL_CALL(PathTexGen(GR_GL_TEXTURE0 + unitIdx, GR_GL_OBJECT_LINEAR, components, coefficients));
-
-    memcpy(fHWPathTexGenSettings[unitIdx].fCoefficients, coefficients,
-           3 * components * sizeof(GrGLfloat));
-}
-
-void GrGLPathRendering::enablePathTexGen(int unitIdx, PathTexGenComponents components,
-                                         const SkMatrix& matrix) {
-    GrGLfloat coefficients[3 * 3];
-    SkASSERT(components >= kS_PathTexGenComponents &&
-             components <= kSTR_PathTexGenComponents);
-
-    coefficients[0] = SkScalarToFloat(matrix[SkMatrix::kMScaleX]);
-    coefficients[1] = SkScalarToFloat(matrix[SkMatrix::kMSkewX]);
-    coefficients[2] = SkScalarToFloat(matrix[SkMatrix::kMTransX]);
-
-    if (components >= kST_PathTexGenComponents) {
-        coefficients[3] = SkScalarToFloat(matrix[SkMatrix::kMSkewY]);
-        coefficients[4] = SkScalarToFloat(matrix[SkMatrix::kMScaleY]);
-        coefficients[5] = SkScalarToFloat(matrix[SkMatrix::kMTransY]);
-    }
-
-    if (components >= kSTR_PathTexGenComponents) {
-        coefficients[6] = SkScalarToFloat(matrix[SkMatrix::kMPersp0]);
-        coefficients[7] = SkScalarToFloat(matrix[SkMatrix::kMPersp1]);
-        coefficients[8] = SkScalarToFloat(matrix[SkMatrix::kMPersp2]);
-    }
-
-    this->enablePathTexGen(unitIdx, components, coefficients);
-}
-
-void GrGLPathRendering::flushPathTexGenSettings(int numUsedTexCoordSets) {
-    SkASSERT(fGpu->glCaps().maxFixedFunctionTextureCoords() >= numUsedTexCoordSets);
-
-    // Only write the inactive path tex gens, since active path tex gens were
-    // written when they were enabled.
-
-    SkDEBUGCODE(
-        for (int i = 0; i < numUsedTexCoordSets; i++) {
-            SkASSERT(0 != fHWPathTexGenSettings[i].fNumComponents);
-        }
-    );
-
-    for (int i = numUsedTexCoordSets; i < fHWActivePathTexGenSets; i++) {
-        SkASSERT(0 != fHWPathTexGenSettings[i].fNumComponents);
-
-        fGpu->setTextureUnit(i);
-        GL_CALL(PathTexGen(GR_GL_TEXTURE0 + i, GR_GL_NONE, 0, NULL));
-        fHWPathTexGenSettings[i].fNumComponents = 0;
-    }
-
-    fHWActivePathTexGenSets = numUsedTexCoordSets;
-}
-
 void GrGLPathRendering::setProgramPathFragmentInputTransform(GrGLuint program, GrGLint location,
                                                              GrGLenum genMode, GrGLint components,
                                                              const SkMatrix& matrix) {
@@ -342,7 +260,7 @@
                                             const SkISize& renderTargetSize,
                                             GrSurfaceOrigin renderTargetOrigin) {
 
-    SkASSERT(fGpu->glCaps().pathRenderingSupport());
+    SkASSERT(fGpu->glCaps().shaderCaps()->pathRenderingSupport());
 
     if (renderTargetOrigin == fHWProjectionMatrixState.fRenderTargetOrigin &&
         renderTargetSize == fHWProjectionMatrixState.fRenderTargetSize &&
@@ -356,10 +274,7 @@
 
     GrGLfloat glMatrix[4 * 4];
     fHWProjectionMatrixState.getRTAdjustedGLMatrix<4>(glMatrix);
-     GrGLenum matrixMode =
-             fGpu->glCaps().nvprSupport() == GrGLCaps::kNormal_NvprSupport ? GR_GL_PATH_PROJECTION :
-                                                                             GR_GL_PROJECTION;
-     GL_CALL(MatrixLoadf(matrixMode, glMatrix));
+    GL_CALL(MatrixLoadf(GR_GL_PATH_PROJECTION, glMatrix));
 }
 
 GrGLuint GrGLPathRendering::genPaths(GrGLsizei range) {
diff --git a/src/gpu/gl/GrGLPathRendering.h b/src/gpu/gl/GrGLPathRendering.h
index ac56a92..25e8ef0 100644
--- a/src/gpu/gl/GrGLPathRendering.h
+++ b/src/gpu/gl/GrGLPathRendering.h
@@ -33,17 +33,17 @@
     virtual ~GrGLPathRendering();
 
     // GrPathRendering implementations.
-    GrPath* createPath(const SkPath&, const SkStrokeRec&) SK_OVERRIDE;
+    GrPath* createPath(const SkPath&, const SkStrokeRec&) override;
     virtual GrPathRange* createPathRange(GrPathRange::PathGenerator*,
-                                         const SkStrokeRec&) SK_OVERRIDE;
+                                         const SkStrokeRec&) override;
     virtual GrPathRange* createGlyphs(const SkTypeface*,
                                       const SkDescriptor*,
-                                      const SkStrokeRec&) SK_OVERRIDE;
-    void stencilPath(const GrPath*, const GrStencilSettings&) SK_OVERRIDE;
-    void drawPath(const GrPath*, const GrStencilSettings&) SK_OVERRIDE;
+                                      const SkStrokeRec&) override;
+    void stencilPath(const GrPath*, const GrStencilSettings&) override;
+    void drawPath(const GrPath*, const GrStencilSettings&) override;
     virtual void drawPaths(const GrPathRange*, const void* indices, PathIndexType,
                            const float transformValues[], PathTransformType, int count,
-                           const GrStencilSettings&) SK_OVERRIDE;
+                           const GrStencilSettings&) override;
 
     /* Called when the 3D context state is unknown. */
     void resetContext();
@@ -54,34 +54,6 @@
      */
     void abandonGpuResources();
 
-
-    enum TexturingMode {
-        FixedFunction_TexturingMode,
-        SeparableShaders_TexturingMode
-    };
-
-    /** Specifies whether texturing should use fixed fuction pipe or separable shaders
-     * Specifies whether texturing should use fixed fuction pipe or whether
-     * it is ok to use normal vertex and fragment shaders, and for path rendering
-     * populate fragment shaders with setProgramPathFragmentInputTransform.
-     * The fixed function mode will be removed once the other mode is more widely
-     * available.
-     */
-    TexturingMode texturingMode() const  {
-        return caps().fragmentInputGenSupport ?
-            SeparableShaders_TexturingMode : FixedFunction_TexturingMode;
-    }
-
-    // Functions for fixed function texturing support.
-    enum PathTexGenComponents {
-        kS_PathTexGenComponents = 1,
-        kST_PathTexGenComponents = 2,
-        kSTR_PathTexGenComponents = 3
-    };
-    void enablePathTexGen(int unitIdx, PathTexGenComponents, const GrGLfloat* coefficients);
-    void enablePathTexGen(int unitIdx, PathTexGenComponents, const SkMatrix& matrix);
-    void flushPathTexGenSettings(int numUsedTexCoordSets);
-
     // Functions for "separable shader" texturing support.
     void setProgramPathFragmentInputTransform(GrGLuint program, GrGLint location,
                                               GrGLenum genMode, GrGLint components,
@@ -168,8 +140,6 @@
         GrGLint   fNumComponents;
         GrGLfloat fCoefficients[3 * 3];
     };
-    int fHWActivePathTexGenSets;
-    SkTArray<PathTexGenData, true> fHWPathTexGenSettings;
 };
 
 #endif
diff --git a/src/gpu/gl/GrGLPrimitiveProcessor.cpp b/src/gpu/gl/GrGLPrimitiveProcessor.cpp
index 2bb595a..3213ffa 100644
--- a/src/gpu/gl/GrGLPrimitiveProcessor.cpp
+++ b/src/gpu/gl/GrGLPrimitiveProcessor.cpp
@@ -37,7 +37,7 @@
                                               const char* outputName,
                                               const GrGeometryProcessor::Attribute* colorAttr,
                                               UniformHandle* colorUniform) {
-    GrGLGPFragmentBuilder* fs = pb->getFragmentShaderBuilder();
+    GrGLFragmentBuilder* fs = pb->getFragmentShaderBuilder();
     if (kUniform_GrGPInput == inputType) {
         SkASSERT(colorUniform);
         const char* stagedLocalVarName;
diff --git a/src/gpu/gl/GrGLProcessor.h b/src/gpu/gl/GrGLProcessor.h
index a4fad75..017585d 100644
--- a/src/gpu/gl/GrGLProcessor.h
+++ b/src/gpu/gl/GrGLProcessor.h
@@ -17,7 +17,7 @@
     include/gpu/GrProcessor.h. Objects of type GrGLProcessor are responsible for emitting the
     GLSL code that implements a GrProcessor and for uploading uniforms at draw time. If they don't
     always emit the same GLSL code, they must have a function:
-        static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*)
+        static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*)
     that is used to implement a program cache. When two GrProcessors produce the same key this means
     that their GrGLProcessors would emit the same GLSL code.
 
@@ -111,7 +111,7 @@
     // TODO update this to pass in GrFragmentProcessor
     virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) {}
 
-    static void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*) {}
+    static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) {}
 
 private:
     typedef GrGLProcessor INHERITED;
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 2f6b784..8ae581c 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -206,6 +206,7 @@
     const GrRenderTarget* rt = pipeline.getRenderTarget();
     SkISize size;
     size.set(rt->width(), rt->height());
-    fGpu->glPathRendering()->setProjectionMatrix(primProc.viewMatrix(),
+    const GrPathProcessor& pathProc = primProc.cast<GrPathProcessor>();
+    fGpu->glPathRendering()->setProjectionMatrix(pathProc.viewMatrix(),
                                                  size, rt->origin());
 }
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index e49fbab..3c97e0f 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -175,11 +175,11 @@
                     GrGLInstalledFragProcs* fragmentProcessors);
 
 private:
-    void didSetData() SK_OVERRIDE;
+    void didSetData() override;
     virtual void setTransformData(const GrPrimitiveProcessor&,
                                   const GrPendingFragmentStage&,
                                   int index,
-                                  GrGLInstalledFragProc*) SK_OVERRIDE;
+                                  GrGLInstalledFragProc*) override;
     virtual void onSetRenderTargetState(const GrPrimitiveProcessor&, const GrPipeline&);
 
     friend class GrGLNvprProgramBuilder;
diff --git a/src/gpu/gl/GrGLProgramDataManager.cpp b/src/gpu/gl/GrGLProgramDataManager.cpp
index 9cd8d57..ef2f59e 100644
--- a/src/gpu/gl/GrGLProgramDataManager.cpp
+++ b/src/gpu/gl/GrGLProgramDataManager.cpp
@@ -49,7 +49,7 @@
     // FIXME: We still insert a single sampler uniform for every stage. If the shader does not
     // reference the sampler then the compiler may have optimized it out. Uncomment this assert
     // once stages insert their own samplers.
-    // SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    // this->printUnused(uni);
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(), Uniform1i(uni.fFSLocation, texUnit));
     }
@@ -62,7 +62,7 @@
     const Uniform& uni = fUniforms[u.toProgramDataIndex()];
     SkASSERT(uni.fType == kFloat_GrSLType);
     SkASSERT(GrGLShaderVar::kNonArray == uni.fArrayCount);
-    SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    SkDEBUGCODE(this->printUnused(uni);)
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(), Uniform1f(uni.fFSLocation, v0));
     }
@@ -81,7 +81,7 @@
     // This assert fires in some instances of the two-pt gradient for its VSParams.
     // Once the uniform manager is responsible for inserting the duplicate uniform
     // arrays in VS and FS driver bug workaround, this can be enabled.
-    //SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    // this->printUni(uni);
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(), Uniform1fv(uni.fFSLocation, arrayCount, v));
     }
@@ -94,7 +94,7 @@
     const Uniform& uni = fUniforms[u.toProgramDataIndex()];
     SkASSERT(uni.fType == kVec2f_GrSLType);
     SkASSERT(GrGLShaderVar::kNonArray == uni.fArrayCount);
-    SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    SkDEBUGCODE(this->printUnused(uni);)
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(), Uniform2f(uni.fFSLocation, v0, v1));
     }
@@ -110,7 +110,7 @@
     SkASSERT(uni.fType == kVec2f_GrSLType);
     SkASSERT(arrayCount > 0);
     ASSERT_ARRAY_UPLOAD_IN_BOUNDS(uni, arrayCount);
-    SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    SkDEBUGCODE(this->printUnused(uni);)
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(), Uniform2fv(uni.fFSLocation, arrayCount, v));
     }
@@ -123,7 +123,7 @@
     const Uniform& uni = fUniforms[u.toProgramDataIndex()];
     SkASSERT(uni.fType == kVec3f_GrSLType);
     SkASSERT(GrGLShaderVar::kNonArray == uni.fArrayCount);
-    SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    SkDEBUGCODE(this->printUnused(uni);)
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(), Uniform3f(uni.fFSLocation, v0, v1, v2));
     }
@@ -139,7 +139,7 @@
     SkASSERT(uni.fType == kVec3f_GrSLType);
     SkASSERT(arrayCount > 0);
     ASSERT_ARRAY_UPLOAD_IN_BOUNDS(uni, arrayCount);
-    SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    SkDEBUGCODE(this->printUnused(uni);)
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(), Uniform3fv(uni.fFSLocation, arrayCount, v));
     }
@@ -156,7 +156,7 @@
     const Uniform& uni = fUniforms[u.toProgramDataIndex()];
     SkASSERT(uni.fType == kVec4f_GrSLType);
     SkASSERT(GrGLShaderVar::kNonArray == uni.fArrayCount);
-    SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    SkDEBUGCODE(this->printUnused(uni);)
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(), Uniform4f(uni.fFSLocation, v0, v1, v2, v3));
     }
@@ -172,7 +172,7 @@
     SkASSERT(uni.fType == kVec4f_GrSLType);
     SkASSERT(arrayCount > 0);
     ASSERT_ARRAY_UPLOAD_IN_BOUNDS(uni, arrayCount);
-    SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    SkDEBUGCODE(this->printUnused(uni);)
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(), Uniform4fv(uni.fFSLocation, arrayCount, v));
     }
@@ -185,7 +185,7 @@
     const Uniform& uni = fUniforms[u.toProgramDataIndex()];
     SkASSERT(uni.fType == kMat33f_GrSLType);
     SkASSERT(GrGLShaderVar::kNonArray == uni.fArrayCount);
-    SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    SkDEBUGCODE(this->printUnused(uni);)
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(), UniformMatrix3fv(uni.fFSLocation, 1, false, matrix));
     }
@@ -198,7 +198,7 @@
     const Uniform& uni = fUniforms[u.toProgramDataIndex()];
     SkASSERT(uni.fType == kMat44f_GrSLType);
     SkASSERT(GrGLShaderVar::kNonArray == uni.fArrayCount);
-    SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    SkDEBUGCODE(this->printUnused(uni);)
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(), UniformMatrix4fv(uni.fFSLocation, 1, false, matrix));
     }
@@ -214,7 +214,7 @@
     SkASSERT(uni.fType == kMat33f_GrSLType);
     SkASSERT(arrayCount > 0);
     ASSERT_ARRAY_UPLOAD_IN_BOUNDS(uni, arrayCount);
-    SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    SkDEBUGCODE(this->printUnused(uni);)
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(),
                    UniformMatrix3fv(uni.fFSLocation, arrayCount, false, matrices));
@@ -232,7 +232,7 @@
     SkASSERT(uni.fType == kMat44f_GrSLType);
     SkASSERT(arrayCount > 0);
     ASSERT_ARRAY_UPLOAD_IN_BOUNDS(uni, arrayCount);
-    SkASSERT(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    SkDEBUGCODE(this->printUnused(uni);)
     if (kUnusedUniform != uni.fFSLocation) {
         GR_GL_CALL(fGpu->glInterface(),
                    UniformMatrix4fv(uni.fFSLocation, arrayCount, false, matrices));
@@ -257,3 +257,11 @@
     };
     this->setMatrix3f(u, mt);
 }
+
+#ifdef SK_DEBUG
+void GrGLProgramDataManager::printUnused(const Uniform& uni) const {
+    if (kUnusedUniform == uni.fFSLocation && kUnusedUniform == uni.fVSLocation) {
+        GrContextDebugf(fGpu->getContext(), "Unused uniform in shader\n");
+    }
+}
+#endif
diff --git a/src/gpu/gl/GrGLProgramDataManager.h b/src/gpu/gl/GrGLProgramDataManager.h
index f893d18..f154313 100644
--- a/src/gpu/gl/GrGLProgramDataManager.h
+++ b/src/gpu/gl/GrGLProgramDataManager.h
@@ -106,6 +106,8 @@
         );
     };
 
+    SkDEBUGCODE(void printUnused(const Uniform&) const;)
+
     SkTArray<Uniform, true> fUniforms;
     GrGLGpu* fGpu;
 
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 73e02b6..34cb114 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -96,40 +96,45 @@
     // bindings in use or other descriptor field settings) it should be set
     // to a canonical value to avoid duplicate programs with different keys.
 
+    GrGLProgramDesc* glDesc = (GrGLProgramDesc*) desc;
+
     GR_STATIC_ASSERT(0 == kProcessorKeysOffset % sizeof(uint32_t));
     // Make room for everything up to the effect keys.
-    desc->fKey.reset();
-    desc->fKey.push_back_n(kProcessorKeysOffset);
+    glDesc->key().reset();
+    glDesc->key().push_back_n(kProcessorKeysOffset);
 
-    GrProcessorKeyBuilder b(&desc->fKey);
+    GrProcessorKeyBuilder b(&glDesc->key());
 
-    primProc.getGLProcessorKey(batchTracker, gpu->glCaps(), &b);
+    primProc.getGLProcessorKey(batchTracker, *gpu->glCaps().glslCaps(), &b);
+    //**** use glslCaps here?
     if (!get_meta_key(primProc, gpu->glCaps(), 0, &b)) {
-        desc->fKey.reset();
+        glDesc->key().reset();
         return false;
     }
 
     for (int s = 0; s < pipeline.numFragmentStages(); ++s) {
         const GrPendingFragmentStage& fps = pipeline.getFragmentStage(s);
         const GrFragmentProcessor& fp = *fps.processor();
-        fp.getGLProcessorKey(gpu->glCaps(), &b);
+        fp.getGLProcessorKey(*gpu->glCaps().glslCaps(), &b);
+        //**** use glslCaps here?
         if (!get_meta_key(fp, gpu->glCaps(), primProc.getTransformKey(fp.coordTransforms()), &b)) {
-            desc->fKey.reset();
+            glDesc->key().reset();
             return false;
         }
     }
 
     const GrXferProcessor& xp = *pipeline.getXferProcessor();
-    xp.getGLProcessorKey(gpu->glCaps(), &b);
+    xp.getGLProcessorKey(*gpu->glCaps().glslCaps(), &b);
+    //**** use glslCaps here?
     if (!get_meta_key(xp, gpu->glCaps(), 0, &b)) {
-        desc->fKey.reset();
+        glDesc->key().reset();
         return false;
     }
 
     // --------DO NOT MOVE HEADER ABOVE THIS LINE--------------------------------------------------
     // Because header is a pointer into the dynamic array, we can't push any new data into the key
     // below here.
-    KeyHeader* header = desc->atOffset<KeyHeader, kHeaderOffset>();
+    KeyHeader* header = glDesc->atOffset<KeyHeader, kHeaderOffset>();
 
     // make sure any padding in the header is zeroed.
     memset(header, 0, kHeaderSize);
@@ -141,9 +146,9 @@
     } else {
         header->fFragPosKey = 0;
     }
-
+    header->fSnapVerticesToPixelCenters = pipeline.snapVerticesToPixelCenters();
     header->fColorEffectCnt = pipeline.numColorFragmentStages();
     header->fCoverageEffectCnt = pipeline.numCoverageFragmentStages();
-    desc->finalize();
+    glDesc->finalize();
     return true;
 }
diff --git a/src/gpu/gl/GrGLProgramDesc.h b/src/gpu/gl/GrGLProgramDesc.h
index f237155..a10b0e8 100644
--- a/src/gpu/gl/GrGLProgramDesc.h
+++ b/src/gpu/gl/GrGLProgramDesc.h
@@ -14,6 +14,11 @@
 #include "GrTypesPriv.h"
 
 class GrGLGpu;
+class GrGLProgramDescBuilder;
+
+class GrGLProgramDesc : public GrProgramDesc {
+    friend class GrGLProgramDescBuilder;
+};
 
 /**
  * This class can be used to build a GrProgramDesc.  It also provides helpers for accessing
@@ -30,7 +35,7 @@
     //    Each processor's key is a variable length array of uint32_t.
     enum {
         // Part 3.
-        kHeaderOffset = GrProgramDesc::kHeaderOffset,
+        kHeaderOffset = GrGLProgramDesc::kHeaderOffset,
         kHeaderSize = SkAlign4(sizeof(KeyHeader)),
         // Part 4.
         // This is the offset into the backenend specific part of the key, which includes
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
index 8b9bb98..c9e65d4 100644
--- a/src/gpu/gl/GrGLRenderTarget.cpp
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -9,20 +9,8 @@
 
 #include "GrGLGpu.h"
 
-void GrGLFBO::release(const GrGLInterface* gl) {
-    SkASSERT(gl);
-    if (this->isValid()) {
-        GR_GL_CALL(gl, DeleteFramebuffers(1, &fID));
-        fIsValid = false;
-    }
-}
-
-void GrGLFBO::abandon() { fIsValid = false; }
-
-//////////////////////////////////////////////////////////////////////////////
-
-#define GLGPU static_cast<GrGLGpu*>(this->getGpu())
-#define GL_CALL(X) GR_GL_CALL(GLGPU->glInterface(), X)
+#define GPUGL static_cast<GrGLGpu*>(this->getGpu())
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
 
 // Because this class is virtually derived from GrSurface we must explicitly call its constructor.
 GrGLRenderTarget::GrGLRenderTarget(GrGLGpu* gpu, const GrSurfaceDesc& desc, const IDDesc& idDesc)
@@ -40,10 +28,8 @@
 }
 
 void GrGLRenderTarget::init(const GrSurfaceDesc& desc, const IDDesc& idDesc) {
-    fRenderFBO.reset(SkRef(idDesc.fRenderFBO.get()));
-    fTextureFBO.reset(SkSafeRef(idDesc.fTextureFBO.get()));
-    SkASSERT(fRenderFBO->isValid());
-    SkASSERT(!fTextureFBO || fTextureFBO->isValid());
+    fRTFBOID                = idDesc.fRTFBOID;
+    fTexFBOID               = idDesc.fTexFBOID;
     fMSColorRenderbufferID  = idDesc.fMSColorRenderbufferID;
     fIsWrapped              = kWrapped_LifeCycle == idDesc.fLifeCycle;
 
@@ -53,59 +39,48 @@
     fViewport.fHeight = desc.fHeight;
 
     // We own one color value for each MSAA sample.
-    fColorValuesPerPixel = SkTMax(1, fDesc.fSampleCnt);
-    if (fTextureFBO && fTextureFBO != fRenderFBO) {
+    int colorValuesPerPixel = SkTMax(1, fDesc.fSampleCnt);
+    if (fTexFBOID != fRTFBOID) {
         // If we own the resolve buffer then that is one more sample per pixel.
-        fColorValuesPerPixel += 1;
-    } 
-}
-
-size_t GrGLRenderTarget::onGpuMemorySize() const {
+        colorValuesPerPixel += 1;
+    } else if (fTexFBOID != 0) {
+        // For auto-resolving FBOs, the MSAA buffer is free.
+        colorValuesPerPixel = 1;
+    }
     SkASSERT(kUnknown_GrPixelConfig != fDesc.fConfig);
     SkASSERT(!GrPixelConfigIsCompressed(fDesc.fConfig));
     size_t colorBytes = GrBytesPerPixel(fDesc.fConfig);
     SkASSERT(colorBytes > 0);
-    return fColorValuesPerPixel * fDesc.fWidth * fDesc.fHeight * colorBytes;
+    fGpuMemorySize = colorValuesPerPixel * fDesc.fWidth * fDesc.fHeight * colorBytes;
+}
+
+size_t GrGLRenderTarget::onGpuMemorySize() const {
+    return fGpuMemorySize;
 }
 
 void GrGLRenderTarget::onRelease() {
     if (!fIsWrapped) {
-        const GrGLInterface* gl = GLGPU->glInterface();
-        if (fRenderFBO) {
-            fRenderFBO->release(gl);
-            fRenderFBO.reset(NULL);
+        if (fTexFBOID) {
+            GL_CALL(DeleteFramebuffers(1, &fTexFBOID));
         }
-        if (fTextureFBO) {
-            fTextureFBO->release(gl);
-            fTextureFBO.reset(NULL);
+        if (fRTFBOID && fRTFBOID != fTexFBOID) {
+            GL_CALL(DeleteFramebuffers(1, &fRTFBOID));
         }
         if (fMSColorRenderbufferID) {
             GL_CALL(DeleteRenderbuffers(1, &fMSColorRenderbufferID));
-            fMSColorRenderbufferID = 0;
         }
-    } else {
-        if (fRenderFBO) {
-            fRenderFBO->abandon();
-            fRenderFBO.reset(NULL);
-        }
-        if (fTextureFBO) {
-            fTextureFBO->abandon();
-            fTextureFBO.reset(NULL);
-        }
-        fMSColorRenderbufferID  = 0;
     }
+    fRTFBOID                = 0;
+    fTexFBOID               = 0;
+    fMSColorRenderbufferID  = 0;
+    fIsWrapped              = false;
     INHERITED::onRelease();
 }
 
 void GrGLRenderTarget::onAbandon() {
-    if (fRenderFBO) {
-        fRenderFBO->abandon();
-        fRenderFBO.reset(NULL);
-    }
-    if (fTextureFBO) {
-        fTextureFBO->abandon();
-        fTextureFBO.reset(NULL);
-    }
+    fRTFBOID                = 0;
+    fTexFBOID               = 0;
     fMSColorRenderbufferID  = 0;
+    fIsWrapped              = false;
     INHERITED::onAbandon();
 }
diff --git a/src/gpu/gl/GrGLRenderTarget.h b/src/gpu/gl/GrGLRenderTarget.h
index 485ae4a..7c0c08c 100644
--- a/src/gpu/gl/GrGLRenderTarget.h
+++ b/src/gpu/gl/GrGLRenderTarget.h
@@ -15,59 +15,15 @@
 
 class GrGLGpu;
 
-/** Represents a GL FBO object. It has a gen ID which is valid whenever the FBO ID owned by the
-    object is valid. The gen IDs are not recycled after FBOs are freed, unlike FBO IDs, and so
-    can be used to uniquely identity FBO ID instantiations. If this object owns an FBO ID, the ID
-    must be deleted or abandoned before this object is freed. FBO IDs should never be owned by
-    more than one instance. */
-class GrGLFBO : public SkNVRefCnt<GrGLFBO> {
-public:
-    SK_DECLARE_INST_COUNT(GrGLFBO);
-
-    /** Initializes to an FBO. The FBO should already be valid in the relevant GL context. */
-    GrGLFBO(GrGLint id) : fID(id), fIsValid(true) {}
-
-    /** Initializes to an FBO ID generated using the interface. */
-    GrGLFBO(const GrGLInterface* gl) {
-        GR_GL_CALL(gl, GenFramebuffers(1, &fID));
-        fIsValid = SkToBool(fID);
-    }
-
-    ~GrGLFBO() { SkASSERT(!this->isValid()); }
-
-    /** Has this object been released or abandoned? */
-    bool isValid() const { return fIsValid; }
-    
-    GrGLint fboID() const { SkASSERT(this->isValid()); return fID; }
-
-    bool isDefaultFramebuffer() const { return fIsValid && 0 == fID; }
-
-    /** Give up ownership of the FBO ID owned by this object without deleting it. */
-    void abandon();
-
-    /** Delete and give up ownership of the the FBO ID if it is valid. */
-    void release(const GrGLInterface*);
-
-private:
-    static uint32_t NextGenID() {
-        static int32_t gGenID = SK_InvalidGenID + 1;
-        return static_cast<uint32_t>(sk_atomic_inc(&gGenID));
-    }
-
-    GrGLuint    fID;
-    bool        fIsValid;
-
-    typedef SkRefCnt INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-/** GL-specific subclass of GrRenderTarget. */
 class GrGLRenderTarget : public GrRenderTarget {
 public:
+    // set fTexFBOID to this value to indicate that it is multisampled but
+    // Gr doesn't know how to resolve it.
+    enum { kUnresolvableFBOID = 0 };
+
     struct IDDesc {
-        SkAutoTUnref<GrGLFBO>       fRenderFBO;
-        SkAutoTUnref<GrGLFBO>       fTextureFBO;
+        GrGLuint                    fRTFBOID;
+        GrGLuint                    fTexFBOID;
         GrGLuint                    fMSColorRenderbufferID;
         GrGpuResource::LifeCycle    fLifeCycle;
     };
@@ -77,33 +33,21 @@
     void setViewport(const GrGLIRect& rect) { fViewport = rect; }
     const GrGLIRect& getViewport() const { return fViewport; }
 
-    // For multisampled renderbuffer render targets, these will return different GrGLFBO objects. If
-    // the render target is not texturable, textureFBO() returns NULL. If the render target auto
-    // resolves to a texture, the same object is returned.
-
-    // FBO that should be rendered into. Always non-NULL unless this resource is destroyed
-    // (this->wasDestroyed()).
-    const GrGLFBO* renderFBO() const {
-        SkASSERT(fRenderFBO && fRenderFBO->isValid());
-        return fRenderFBO;
-    }
-
-    // FBO that has the target's texture ID attached. The return value may be:
-    //      * NULL when this render target is not a texture,
-    //      * the same as renderFBO() when this surface is not multisampled or auto-resolves,
-    //      * or different than renderFBO() when it requires explicit resolving via
-    //        glBlitFramebuffer.
-    const GrGLFBO* textureFBO() const {
-        SkASSERT(!fTextureFBO || fTextureFBO->isValid());
-        return fTextureFBO;
-    }
+    // The following two functions return the same ID when a
+    // texture/render target is multisampled, and different IDs when
+    // it is.
+    // FBO ID used to render into
+    GrGLuint renderFBOID() const { return fRTFBOID; }
+    // FBO ID that has texture ID attached.
+    GrGLuint textureFBOID() const { return fTexFBOID; }
 
     // override of GrRenderTarget
-    ResolveType getResolveType() const SK_OVERRIDE {
-        if (!this->isMultisampled() || this->renderFBO() == this->textureFBO()) {
+    ResolveType getResolveType() const override {
+        if (!this->isMultisampled() ||
+            fRTFBOID == fTexFBOID) {
             // catches FBO 0 and non MSAA case
             return kAutoResolves_ResolveType;
-        } else if (!this->textureFBO()) {
+        } else if (kUnresolvableFBOID == fTexFBOID) {
             return kCantResolve_ResolveType;
         } else {
             return kCanResolve_ResolveType;
@@ -111,7 +55,7 @@
     }
 
     /** When we don't own the FBO ID we don't attempt to modify its attachments. */
-    bool canAttemptStencilAttachment() const SK_OVERRIDE { return !fIsWrapped; }
+    bool canAttemptStencilAttachment() const override { return !fIsWrapped; }
 
 protected:
     // The public constructor registers this object with the cache. However, only the most derived
@@ -122,30 +66,29 @@
 
     void init(const GrSurfaceDesc&, const IDDesc&);
 
-    void onAbandon() SK_OVERRIDE;
-    void onRelease() SK_OVERRIDE;
+    void onAbandon() override;
+    void onRelease() override;
 
     // In protected because subclass GrGLTextureRenderTarget calls this version.
-    size_t onGpuMemorySize() const SK_OVERRIDE;
+    size_t onGpuMemorySize() const override;
 
 private:
-    SkAutoTUnref<GrGLFBO>   fRenderFBO;
-    SkAutoTUnref<GrGLFBO>   fTextureFBO;
-    GrGLuint                fMSColorRenderbufferID;
+    GrGLuint      fRTFBOID;
+    GrGLuint      fTexFBOID;
+    GrGLuint      fMSColorRenderbufferID;
 
     // We track this separately from GrGpuResource because this may be both a texture and a render
     // target, and the texture may be wrapped while the render target is not.
-    bool                    fIsWrapped;
+    bool fIsWrapped;
 
     // when we switch to this render target we want to set the viewport to
     // only render 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)
-    GrGLIRect               fViewport;
+    GrGLIRect fViewport;
 
-    // onGpuMemorySize() needs to know what how many color values are owned per pixel. However,
-    // abandon and release zero out the IDs and the cache needs to know the size even after those
-    // actions.
-    uint8_t                 fColorValuesPerPixel;
+    // onGpuMemorySize() needs to know the VRAM footprint of the FBO(s). However, abandon and
+    // release zero out the IDs and the cache needs to know the size even after those actions.
+    size_t fGpuMemorySize;
 
     typedef GrRenderTarget INHERITED;
 };
diff --git a/src/gpu/gl/GrGLStencilBuffer.cpp b/src/gpu/gl/GrGLStencilAttachment.cpp
similarity index 79%
rename from src/gpu/gl/GrGLStencilBuffer.cpp
rename to src/gpu/gl/GrGLStencilAttachment.cpp
index b6dfbb5..4ea08c8 100644
--- a/src/gpu/gl/GrGLStencilBuffer.cpp
+++ b/src/gpu/gl/GrGLStencilAttachment.cpp
@@ -6,10 +6,10 @@
  */
 
 
-#include "GrGLStencilBuffer.h"
+#include "GrGLStencilAttachment.h"
 #include "GrGLGpu.h"
 
-size_t GrGLStencilBuffer::onGpuMemorySize() const {
+size_t GrGLStencilAttachment::onGpuMemorySize() const {
     uint64_t size = this->width();
     size *= this->height();
     size *= fFormat.fTotalBits;
@@ -17,7 +17,7 @@
     return static_cast<size_t>(size / 8);
 }
 
-void GrGLStencilBuffer::onRelease() {
+void GrGLStencilAttachment::onRelease() {
     if (0 != fRenderbufferID && !this->isWrapped()) {
         GrGLGpu* gpuGL = (GrGLGpu*) this->getGpu();
         const GrGLInterface* gl = gpuGL->glInterface();
@@ -28,7 +28,7 @@
     INHERITED::onRelease();
 }
 
-void GrGLStencilBuffer::onAbandon() {
+void GrGLStencilAttachment::onAbandon() {
     fRenderbufferID = 0;
 
     INHERITED::onAbandon();
diff --git a/src/gpu/gl/GrGLStencilBuffer.h b/src/gpu/gl/GrGLStencilAttachment.h
similarity index 74%
rename from src/gpu/gl/GrGLStencilBuffer.h
rename to src/gpu/gl/GrGLStencilAttachment.h
index 75e98a0..fd1214e 100644
--- a/src/gpu/gl/GrGLStencilBuffer.h
+++ b/src/gpu/gl/GrGLStencilAttachment.h
@@ -6,13 +6,13 @@
  */
 
 
-#ifndef GrGLStencilBuffer_DEFINED
-#define GrGLStencilBuffer_DEFINED
+#ifndef GrGLStencilAttachment_DEFINED
+#define GrGLStencilAttachment_DEFINED
 
 #include "gl/GrGLInterface.h"
-#include "GrStencilBuffer.h"
+#include "GrStencilAttachment.h"
 
-class GrGLStencilBuffer : public GrStencilBuffer {
+class GrGLStencilAttachment : public GrStencilAttachment {
 public:
     static const GrGLenum kUnknownInternalFormat = ~0U;
     static const GrGLuint kUnknownBitCount = ~0U;
@@ -29,12 +29,12 @@
         GrGpuResource::LifeCycle fLifeCycle;
     };
 
-    GrGLStencilBuffer(GrGpu* gpu,
+    GrGLStencilAttachment(GrGpu* gpu,
                       const IDDesc& idDesc,
                       int width, int height,
                       int sampleCnt,
                       const Format& format)
-        : GrStencilBuffer(gpu, idDesc.fLifeCycle, width, height, format.fStencilBits, sampleCnt)
+        : GrStencilAttachment(gpu, idDesc.fLifeCycle, width, height, format.fStencilBits, sampleCnt)
         , fFormat(format)
         , fRenderbufferID(idDesc.fRenderbufferID) {
         this->registerWithCache();
@@ -48,11 +48,11 @@
 
 protected:
     // overrides of GrResource
-    void onRelease() SK_OVERRIDE;
-    void onAbandon() SK_OVERRIDE;
+    void onRelease() override;
+    void onAbandon() override;
 
 private:
-    size_t onGpuMemorySize() const SK_OVERRIDE;
+    size_t onGpuMemorySize() const override;
 
     Format fFormat;
     // may be zero for external SBs associated with external RTs
@@ -60,7 +60,7 @@
     // us how many bits of stencil there are).
     GrGLuint fRenderbufferID;
 
-    typedef GrStencilBuffer INHERITED;
+    typedef GrStencilAttachment INHERITED;
 };
 
 #endif
diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h
index c64865fcd..de3cf3f 100644
--- a/src/gpu/gl/GrGLTexture.h
+++ b/src/gpu/gl/GrGLTexture.h
@@ -13,6 +13,7 @@
 #include "GrTexture.h"
 #include "GrGLUtil.h"
 
+class GrGLGpu;
 
 class GrGLTexture : public GrTexture {
 
@@ -33,9 +34,9 @@
 
     GrGLTexture(GrGLGpu*, const GrSurfaceDesc&, const IDDesc&);
 
-    GrBackendObject getTextureHandle() const SK_OVERRIDE;
+    GrBackendObject getTextureHandle() const override;
 
-    void textureParamsModified() SK_OVERRIDE { fTexParams.invalidate(); }
+    void textureParamsModified() override { fTexParams.invalidate(); }
 
     // These functions are used to track the texture parameters associated with the texture.
     const TexParams& getCachedTexParams(GrGpu::ResetTimestamp* timestamp) const {
@@ -60,8 +61,8 @@
 
     void init(const GrSurfaceDesc&, const IDDesc&);
 
-    void onAbandon() SK_OVERRIDE;
-    void onRelease() SK_OVERRIDE;
+    void onAbandon() override;
+    void onRelease() override;
 
 private:
     TexParams                       fTexParams;
diff --git a/src/gpu/gl/GrGLTextureRenderTarget.h b/src/gpu/gl/GrGLTextureRenderTarget.h
index e2dd14c..a913499 100644
--- a/src/gpu/gl/GrGLTextureRenderTarget.h
+++ b/src/gpu/gl/GrGLTextureRenderTarget.h
@@ -35,19 +35,19 @@
     }
 
 protected:
-    void onAbandon() SK_OVERRIDE {
+    void onAbandon() override {
         GrGLRenderTarget::onAbandon();
         GrGLTexture::onAbandon();
     }
 
-    void onRelease() SK_OVERRIDE {
+    void onRelease() override {
         GrGLRenderTarget::onRelease();
         GrGLTexture::onRelease();
     }
 
 private:
     // GrGLRenderTarget accounts for the texture's memory and any MSAA renderbuffer's memory.
-    size_t onGpuMemorySize() const SK_OVERRIDE {
+    size_t onGpuMemorySize() const override {
         return GrGLRenderTarget::onGpuMemorySize();
     }
 
diff --git a/src/gpu/gl/GrGLVertexArray.cpp b/src/gpu/gl/GrGLVertexArray.cpp
index bb409c6..e20dbb5 100644
--- a/src/gpu/gl/GrGLVertexArray.cpp
+++ b/src/gpu/gl/GrGLVertexArray.cpp
@@ -8,8 +8,6 @@
 #include "GrGLVertexArray.h"
 #include "GrGLGpu.h"
 
-#define GPUGL static_cast<GrGLGpu*>(this->getGpu())
-#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X);
 
 void GrGLAttribArrayState::set(const GrGLGpu* gpu,
                                int index,
@@ -68,42 +66,27 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-GrGLVertexArray::GrGLVertexArray(GrGLGpu* gpu, GrGLint id, int attribCount)
-    : INHERITED(gpu, kCached_LifeCycle)
-    , fID(id)
+GrGLVertexArray::GrGLVertexArray(GrGLint id, int attribCount)
+    : fID(id)
     , fAttribArrays(attribCount)
     , fIndexBufferIDIsValid(false) {
-    this->registerWithCache();
 }
 
-void GrGLVertexArray::onAbandon() {
-    fID = 0;
-    INHERITED::onAbandon();
-}
-
-void GrGLVertexArray::onRelease() {
-    if (0 != fID) {
-        GL_CALL(DeleteVertexArrays(1, &fID));
-        GPUGL->notifyVertexArrayDelete(fID);
-        fID = 0;
-    }
-    INHERITED::onRelease();
-}
-
-GrGLAttribArrayState* GrGLVertexArray::bind() {
+GrGLAttribArrayState* GrGLVertexArray::bind(GrGLGpu* gpu) {
     if (0 == fID) {
         return NULL;
     }
-    GPUGL->bindVertexArray(fID);
+    gpu->bindVertexArray(fID);
     return &fAttribArrays;
 }
 
-GrGLAttribArrayState* GrGLVertexArray::bindWithIndexBuffer(const GrGLIndexBuffer* buffer) {
-    GrGLAttribArrayState* state = this->bind();
+GrGLAttribArrayState* GrGLVertexArray::bindWithIndexBuffer(GrGLGpu* gpu,
+                                                           const GrGLIndexBuffer* buffer) {
+    GrGLAttribArrayState* state = this->bind(gpu);
     if (state && buffer) {
         GrGLuint bufferID = buffer->bufferID();
-        if (!fIndexBufferIDIsValid || bufferID != fIndexBufferID) {
-            GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, bufferID));
+        if (!fIndexBufferIDIsValid || bufferID != fIndexBufferID) {            
+            GR_GL_CALL(gpu->glInterface(), BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, bufferID));
             fIndexBufferIDIsValid = true;
             fIndexBufferID = bufferID;
         }
diff --git a/src/gpu/gl/GrGLVertexArray.h b/src/gpu/gl/GrGLVertexArray.h
index 4143ad4..a8feb73 100644
--- a/src/gpu/gl/GrGLVertexArray.h
+++ b/src/gpu/gl/GrGLVertexArray.h
@@ -8,11 +8,9 @@
 #ifndef GrGLVertexArray_DEFINED
 #define GrGLVertexArray_DEFINED
 
-#include "GrGpuResource.h"
 #include "GrTypesPriv.h"
 #include "gl/GrGLDefines.h"
 #include "gl/GrGLFunctions.h"
-
 #include "SkTArray.h"
 
 class GrGLVertexBuffer;
@@ -134,22 +132,22 @@
  * This class represents an OpenGL vertex array object. It manages the lifetime of the vertex array
  * and is used to track the state of the vertex array to avoid redundant GL calls.
  */
-class GrGLVertexArray : public GrGpuResource {
+class GrGLVertexArray {
 public:
-    GrGLVertexArray(GrGLGpu* gpu, GrGLint id, int attribCount);
+    GrGLVertexArray(GrGLint id, int attribCount);
 
     /**
      * Binds this vertex array. If the ID has been deleted or abandoned then NULL is returned.
      * Otherwise, the GrGLAttribArrayState that is tracking this vertex array's attrib bindings is
      * returned.
      */
-    GrGLAttribArrayState* bind();
+    GrGLAttribArrayState* bind(GrGLGpu*);
 
     /**
      * This is a version of the above function that also binds an index buffer to the vertex
      * array object.
      */
-    GrGLAttribArrayState* bindWithIndexBuffer(const GrGLIndexBuffer* indexBuffer);
+    GrGLAttribArrayState* bindWithIndexBuffer(GrGLGpu* gpu, const GrGLIndexBuffer*);
 
     void notifyIndexBufferDelete(GrGLuint bufferID);
 
@@ -161,20 +159,11 @@
 
     void invalidateCachedState();
 
-protected:
-    size_t onGpuMemorySize() const SK_OVERRIDE { return 0; }
-
-    void onAbandon() SK_OVERRIDE;
-
-    void onRelease() SK_OVERRIDE;
-
 private:
     GrGLuint                fID;
     GrGLAttribArrayState    fAttribArrays;
     GrGLuint                fIndexBufferID;
     bool                    fIndexBufferIDIsValid;
-
-    typedef GrGpuResource INHERITED;
 };
 
 #endif
diff --git a/src/gpu/gl/GrGLVertexBuffer.h b/src/gpu/gl/GrGLVertexBuffer.h
index c428f4d..3b0c5c1 100644
--- a/src/gpu/gl/GrGLVertexBuffer.h
+++ b/src/gpu/gl/GrGLVertexBuffer.h
@@ -31,13 +31,13 @@
     }
 
 protected:
-    void onAbandon() SK_OVERRIDE;
-    void onRelease() SK_OVERRIDE;
+    void onAbandon() override;
+    void onRelease() override;
 
 private:
-    void* onMap() SK_OVERRIDE;
-    void onUnmap() SK_OVERRIDE;
-    bool onUpdateData(const void* src, size_t srcSizeInBytes) SK_OVERRIDE;
+    void* onMap() override;
+    void onUnmap() override;
+    bool onUpdateData(const void* src, size_t srcSizeInBytes) override;
 
     GrGLGpu* getGpuGL() const {
         SkASSERT(!this->wasDestroyed());
diff --git a/src/gpu/gl/GrGLXferProcessor.cpp b/src/gpu/gl/GrGLXferProcessor.cpp
index b7bf301..657da41 100644
--- a/src/gpu/gl/GrGLXferProcessor.cpp
+++ b/src/gpu/gl/GrGLXferProcessor.cpp
@@ -15,13 +15,16 @@
     if (args.fXP.getDstCopyTexture()) {
         bool topDown = kTopLeft_GrSurfaceOrigin == args.fXP.getDstCopyTexture()->origin();
 
-        GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
+        GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
 
-        // We don't think any shaders actually output negative coverage, but just as a safety check
-        // for floating point precision errors we compare with <= here
-        fsBuilder->codeAppendf("if (all(lessThanEqual(%s, vec4(0)))) {"
-                               "    discard;"
-                               "}", args.fInputCoverage);
+        if (args.fXP.readsCoverage()) {
+            // We don't think any shaders actually output negative coverage, but just as a safety
+            // check for floating point precision errors we compare with <= here
+            fsBuilder->codeAppendf("if (all(lessThanEqual(%s, vec4(0)))) {"
+                                   "    discard;"
+                                   "}", args.fInputCoverage);
+        }
+
         const char* dstColor = fsBuilder->dstColor();
 
         const char* dstCopyTopLeftName;
diff --git a/src/gpu/gl/SkNullGLContext.cpp b/src/gpu/gl/SkNullGLContext.cpp
index 42dafc5..cf07cb3 100644
--- a/src/gpu/gl/SkNullGLContext.cpp
+++ b/src/gpu/gl/SkNullGLContext.cpp
@@ -337,7 +337,7 @@
 class NullInterface : public GrGLInterface {
 public:
     NullInterface(State* state) : fState(SkRef(state)) {}
-    ~NullInterface() SK_OVERRIDE {
+    ~NullInterface() override {
         fState->unref();
     }
     State* fState;
@@ -360,6 +360,7 @@
     functions->fBindTexture = nullGLBindTexture;
     functions->fBindVertexArray = nullGLBindVertexArray;
     functions->fBlendColor = noOpGLBlendColor;
+    functions->fBlendEquation = noOpGLBlendEquation;
     functions->fBlendFunc = noOpGLBlendFunc;
     functions->fBufferData = nullGLBufferData;
     functions->fBufferSubData = noOpGLBufferSubData;
diff --git a/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp b/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp
index 99219d0..4569cc8 100644
--- a/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp
+++ b/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp
@@ -30,6 +30,8 @@
         return (GrGLFuncPtr) glBindTexture;
     } else if (0 == strcmp("glBlendColor", name)) {
         return (GrGLFuncPtr) glBlendColor;
+    } else if (0 == strcmp("glBlendEquation", name)) {
+        return (GrGLFuncPtr) glBlendEquation;
     } else if (0 == strcmp("glBlendFunc", name)) {
         return (GrGLFuncPtr) glBlendFunc;
     } else if (0 == strcmp("glBufferData", name)) {
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
index c739f11..044752f 100644
--- a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.cpp
@@ -36,10 +36,51 @@
     }
 }
 
+static const char* specific_layout_qualifier_name(GrBlendEquation equation) {
+    SkASSERT(GrBlendEquationIsAdvanced(equation));
+
+    static const char* kLayoutQualifierNames[] = {
+        "blend_support_screen",
+        "blend_support_overlay",
+        "blend_support_darken",
+        "blend_support_lighten",
+        "blend_support_colordodge",
+        "blend_support_colorburn",
+        "blend_support_hardlight",
+        "blend_support_softlight",
+        "blend_support_difference",
+        "blend_support_exclusion",
+        "blend_support_multiply",
+        "blend_support_hsl_hue",
+        "blend_support_hsl_saturation",
+        "blend_support_hsl_color",
+        "blend_support_hsl_luminosity"
+    };
+    return kLayoutQualifierNames[equation - kFirstAdvancedGrBlendEquation];
+
+    GR_STATIC_ASSERT(0 == kScreen_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(1 == kOverlay_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(2 == kDarken_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(3 == kLighten_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(4 == kColorDodge_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(5 == kColorBurn_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(6 == kHardLight_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(7 == kSoftLight_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(8 == kDifference_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(9 == kExclusion_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(10 == kMultiply_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(11 == kHSLHue_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(12 == kHSLSaturation_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(13 == kHSLColor_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(14 == kHSLLuminosity_GrBlendEquation - kFirstAdvancedGrBlendEquation);
+    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kLayoutQualifierNames) ==
+                     kGrBlendEquationCnt - kFirstAdvancedGrBlendEquation);
+}
+
 GrGLFragmentShaderBuilder::DstReadKey
 GrGLFragmentShaderBuilder::KeyForDstRead(const GrTexture* dstCopy, const GrGLCaps& caps) {
     uint32_t key = kYesDstRead_DstReadKeyBit;
-    if (caps.fbFetchSupport()) {
+    if (caps.glslCaps()->fbFetchSupport()) {
         return key;
     }
     SkASSERT(dstCopy);
@@ -79,7 +120,7 @@
     switch (feature) {
         case kStandardDerivatives_GLSLFeature: {
             GrGLGpu* gpu = fProgramBuilder->gpu();
-            if (!gpu->glCaps().shaderDerivativeSupport()) {
+            if (!gpu->glCaps().shaderCaps()->shaderDerivativeSupport()) {
                 return false;
             }
             if (kGLES_GrGLStandard == gpu->glStandard() &&
@@ -166,13 +207,13 @@
     fHasReadDstColor = true;
 
     GrGLGpu* gpu = fProgramBuilder->gpu();
-    if (gpu->glCaps().fbFetchSupport()) {
+    if (gpu->glCaps().glslCaps()->fbFetchSupport()) {
         this->addFeature(1 << (GrGLFragmentShaderBuilder::kLastGLSLPrivateFeature + 1),
-                         gpu->glCaps().fbFetchExtensionString());
+                         gpu->glCaps().glslCaps()->fbFetchExtensionString());
 
         // Some versions of this extension string require declaring custom color output on ES 3.0+
-        const char* fbFetchColorName = gpu->glCaps().fbFetchColorName();
-        if (gpu->glCaps().fbFetchNeedsCustomOutput()) {
+        const char* fbFetchColorName = gpu->glCaps().glslCaps()->fbFetchColorName();
+        if (gpu->glCaps().glslCaps()->fbFetchNeedsCustomOutput()) {
             this->enableCustomOutput();
             fOutputs[fCustomColorOutputIndex].setTypeModifier(GrShaderVar::kInOut_TypeModifier);
             fbFetchColorName = declared_color_output_name();
@@ -183,6 +224,23 @@
     } 
 }
 
+void GrGLFragmentShaderBuilder::enableAdvancedBlendEquationIfNeeded(GrBlendEquation equation) {
+    SkASSERT(GrBlendEquationIsAdvanced(equation));
+
+    const GrGLSLCaps& caps = *fProgramBuilder->gpu()->glCaps().glslCaps();
+    if (!caps.mustEnableAdvBlendEqs()) {
+        return;
+    }
+
+    this->addFeature(1 << kBlendEquationAdvanced_GLSLPrivateFeature,
+                     "GL_KHR_blend_equation_advanced");
+    if (caps.mustEnableSpecificAdvBlendEqs()) {
+        this->addLayoutQualifier(specific_layout_qualifier_name(equation), kOut_InterfaceQualifier);
+    } else {
+        this->addLayoutQualifier("blend_support_all_equations", kOut_InterfaceQualifier);
+    }
+}
+
 void GrGLFragmentShaderBuilder::enableCustomOutput() {
     if (!fHasCustomColorOutput) {
         fHasCustomColorOutput = true;
@@ -215,6 +273,7 @@
     append_default_precision_qualifier(kDefault_GrSLPrecision,
                                        gpu->glStandard(),
                                        &this->precisionQualifier());
+    this->compileAndAppendLayoutQualifiers();
     fProgramBuilder->appendUniformDecls(GrGLProgramBuilder::kFragment_Visibility,
                                         &this->uniforms());
     this->appendDecls(fInputs, &this->inputs());
diff --git a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
index f294257..1ab7af6 100644
--- a/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLFragmentShaderBuilder.h
@@ -15,10 +15,10 @@
 /*
  * This base class encapsulates the functionality which the GP uses to build fragment shaders
  */
-class GrGLGPFragmentBuilder : public GrGLShaderBuilder {
+class GrGLFragmentBuilder : public GrGLShaderBuilder {
 public:
-    GrGLGPFragmentBuilder(GrGLProgramBuilder* program) : INHERITED(program) {}
-    virtual ~GrGLGPFragmentBuilder() {}
+    GrGLFragmentBuilder(GrGLProgramBuilder* program) : INHERITED(program) {}
+    virtual ~GrGLFragmentBuilder() {}
     /**
      * Use of these features may require a GLSL extension to be enabled. Shaders may not compile
      * if code is added that uses one of these features without calling enableFeature()
@@ -48,7 +48,7 @@
     virtual const char* fragmentPosition() = 0;
 
 private:
-    friend class GrGLNormalPathProcessor;
+    friend class GrGLPathProcessor;
 
     typedef GrGLShaderBuilder INHERITED;
 };
@@ -58,20 +58,25 @@
  * this builder to create their shader.  Because this is the only shader builder the FP sees, we
  * just call it FPShaderBuilder
  */
-class GrGLFPFragmentBuilder : public GrGLGPFragmentBuilder {
+class GrGLXPFragmentBuilder : public GrGLFragmentBuilder {
 public:
-    GrGLFPFragmentBuilder(GrGLProgramBuilder* program) : INHERITED(program) {}
+    GrGLXPFragmentBuilder(GrGLProgramBuilder* program) : INHERITED(program) {}
 
     /** Returns the variable name that holds the color of the destination pixel. This may be NULL if
         no effect advertised that it will read the destination. */
     virtual const char* dstColor() = 0;
 
+    /** Adds any necessary layout qualifiers in order to legalize the supplied blend equation with
+        this shader. It is only legal to call this method with an advanced blend equation, and only
+        if these equations are supported. */
+    virtual void enableAdvancedBlendEquationIfNeeded(GrBlendEquation) = 0;
+
 private:
-    typedef GrGLGPFragmentBuilder INHERITED;
+    typedef GrGLFragmentBuilder INHERITED;
 };
 
 // TODO rename to Fragment Builder
-class GrGLFragmentShaderBuilder : public GrGLFPFragmentBuilder {
+class GrGLFragmentShaderBuilder : public GrGLXPFragmentBuilder {
 public:
     typedef uint8_t DstReadKey;
     typedef uint8_t FragPosKey;
@@ -89,11 +94,13 @@
     GrGLFragmentShaderBuilder(GrGLProgramBuilder* program, uint8_t fragPosKey);
 
     // true public interface, defined explicitly in the abstract interfaces above
-    bool enableFeature(GLSLFeature) SK_OVERRIDE;
+    bool enableFeature(GLSLFeature) override;
     virtual SkString ensureFSCoords2D(const GrGLProcessor::TransformedCoordsArray& coords,
-                                      int index) SK_OVERRIDE;
-    const char* fragmentPosition() SK_OVERRIDE;
-    const char* dstColor() SK_OVERRIDE;
+                                      int index) override;
+    const char* fragmentPosition() override;
+    const char* dstColor() override;
+
+    void enableAdvancedBlendEquationIfNeeded(GrBlendEquation) override;
 
 private:
     // Private public interface, used by GrGLProgramBuilder to build a fragment shader
@@ -123,7 +130,8 @@
      */
     enum GLSLPrivateFeature {
         kFragCoordConventions_GLSLPrivateFeature = kLastGLSLFeature + 1,
-        kLastGLSLPrivateFeature = kFragCoordConventions_GLSLPrivateFeature
+        kBlendEquationAdvanced_GLSLPrivateFeature,
+        kLastGLSLPrivateFeature = kBlendEquationAdvanced_GLSLPrivateFeature
     };
 
     // Interpretation of DstReadKey when generating code
@@ -156,7 +164,7 @@
 
     friend class GrGLProgramBuilder;
 
-    typedef GrGLFPFragmentBuilder INHERITED;
+    typedef GrGLXPFragmentBuilder INHERITED;
 };
 
 #endif
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index bad1d63..28e1fba 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -14,6 +14,7 @@
 #include "gl/GrGLSLPrettyPrint.h"
 #include "gl/GrGLUniformHandle.h"
 #include "gl/GrGLXferProcessor.h"
+#include "GrAutoLocaleSetter.h"
 #include "GrCoordTransform.h"
 #include "GrGLProgramBuilder.h"
 #include "GrTexture.h"
@@ -30,7 +31,7 @@
     GrGLNvprProgramBuilder(GrGLGpu* gpu, const DrawArgs& args)
         : INHERITED(gpu, args) {}
 
-    GrGLProgram* createProgram(GrGLuint programID) SK_OVERRIDE {
+    GrGLProgram* createProgram(GrGLuint programID) override {
         // this is just for nvpr es, which has separable varyings that are plugged in after
         // building
         GrGLPathProcessor* pathProc =
@@ -53,6 +54,8 @@
 const int GrGLProgramBuilder::kVarsPerBlock = 8;
 
 GrGLProgram* GrGLProgramBuilder::CreateProgram(const DrawArgs& args, GrGLGpu* gpu) {
+    GrAutoLocaleSetter als("C");
+
     // create a builder.  This will be handed off to effects so they can use it to add
     // uniforms, varyings, textures, etc
     SkAutoTDelete<GrGLProgramBuilder> builder(CreateProgramBuilder(args, gpu));
@@ -64,7 +67,9 @@
     GrGLSLExpr4 inputColor;
     GrGLSLExpr4 inputCoverage;
 
-    pb->emitAndInstallProcs(&inputColor, &inputCoverage);
+    if (!pb->emitAndInstallProcs(&inputColor, &inputCoverage)) {
+        return NULL;
+    }
 
     return pb->finalize();
 }
@@ -72,7 +77,7 @@
 GrGLProgramBuilder* GrGLProgramBuilder::CreateProgramBuilder(const DrawArgs& args,
                                                              GrGLGpu* gpu) {
     if (args.fPrimitiveProcessor->isPathRendering()) {
-        SkASSERT(gpu->glCaps().pathRenderingSupport() &&
+        SkASSERT(gpu->glCaps().shaderCaps()->pathRenderingSupport() &&
                  !args.fPrimitiveProcessor->willUseGeoShader() &&
                  args.fPrimitiveProcessor->numAttribs() == 0);
         return SkNEW_ARGS(GrGLNvprProgramBuilder, (gpu, args));
@@ -186,9 +191,12 @@
     return fGpu->ctxInfo();
 }
 
-void GrGLProgramBuilder::emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr4* inputCoverage) {
+bool GrGLProgramBuilder::emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr4* inputCoverage) {
     // First we loop over all of the installed processors and collect coord transforms.  These will
     // be sent to the GrGLPrimitiveProcessor in its emitCode function
+    const GrPrimitiveProcessor& primProc = this->primitiveProcessor();
+    int totalTextures = primProc.numTextures();
+    const int maxTextureUnits = fGpu->glCaps().maxFragmentTextureUnits();
     SkSTArray<8, GrGLProcessor::TransformedCoordsArray> outCoords;
     for (int i = 0; i < this->pipeline().numFragmentStages(); i++) {
         const GrFragmentProcessor* processor = this->pipeline().getFragmentStage(i).processor();
@@ -196,9 +204,14 @@
         for (int t = 0; t < processor->numTransforms(); t++) {
             procCoords.push_back(&processor->coordTransform(t));
         }
+
+        totalTextures += processor->numTextures();
+        if (totalTextures >= maxTextureUnits) {
+            GrContextDebugf(fGpu->getContext(), "Program would use too many texture units\n");
+            return false;
+        }
     }
 
-    const GrPrimitiveProcessor& primProc = this->primitiveProcessor();
     this->emitAndInstallProc(primProc, inputColor, inputCoverage);
 
     fFragmentProcessors.reset(SkNEW(GrGLInstalledFragProcs));
@@ -207,6 +220,7 @@
     this->emitAndInstallFragProcs(this->pipeline().numColorFragmentStages(), numProcs,
                                   inputCoverage);
     this->emitAndInstallXferProc(*this->pipeline().getXferProcessor(), *inputColor, *inputCoverage);
+    return true;
 }
 
 void GrGLProgramBuilder::emitAndInstallFragProcs(int procOffset,
@@ -299,7 +313,7 @@
     fGeometryProcessor = SkNEW(GrGLInstalledGeoProc);
 
     const GrBatchTracker& bt = this->batchTracker();
-    fGeometryProcessor->fGLProc.reset(gp.createGLInstance(bt, fGpu->glCaps()));
+    fGeometryProcessor->fGLProc.reset(gp.createGLInstance(bt, *fGpu->glCaps().glslCaps()));
 
     SkSTArray<4, GrGLProcessor::TextureSampler> samplers(gp.numTextures());
     this->emitSamplers(gp, &samplers, fGeometryProcessor);
@@ -392,19 +406,15 @@
     // compile shaders and bind attributes / uniforms
     SkTDArray<GrGLuint> shadersToDelete;
 
-    // Legacy nvpr will not compile with a vertex shader, but newer nvpr requires a dummy vertex
-    // shader
-    bool useNvpr = primitiveProcessor().isPathRendering();
-    if (!(useNvpr && fGpu->glCaps().nvprSupport() == GrGLCaps::kLegacy_NvprSupport)) {
-        if (!fVS.compileAndAttachShaders(programID, &shadersToDelete)) {
-            this->cleanupProgram(programID, shadersToDelete);
-            return NULL;
-        }
+    if (!fVS.compileAndAttachShaders(programID, &shadersToDelete)) {
+        this->cleanupProgram(programID, shadersToDelete);
+        return NULL;
+    }
 
-        // Non fixed function NVPR actually requires a vertex shader to compile
-        if (!useNvpr) {
-            fVS.bindVertexAttributes(programID);
-        }
+    // NVPR actually requires a vertex shader to compile
+    bool useNvpr = primitiveProcessor().isPathRendering();
+    if (!useNvpr) {
+        fVS.bindVertexAttributes(programID);
     }
 
     if (!fFS.compileAndAttachShaders(programID, &shadersToDelete)) {
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index 46d2816..5805cfc 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -155,7 +155,7 @@
                                          const char* output) = 0;
 
     // TODO rename getFragmentBuilder
-    virtual GrGLGPFragmentBuilder* getFragmentShaderBuilder() = 0;
+    virtual GrGLFragmentBuilder* getFragmentShaderBuilder() = 0;
     virtual GrGLVertexBuilder* getVertexShaderBuilder() = 0;
 
     /*
@@ -166,7 +166,7 @@
 /* a specializations for FPs. Lets the user add uniforms and FS code */
 class GrGLFPBuilder : public virtual GrGLUniformBuilder {
 public:
-    virtual GrGLFPFragmentBuilder* getFragmentShaderBuilder() = 0;
+    virtual GrGLFragmentBuilder* getFragmentShaderBuilder() = 0;
 
     /*
      * *NOTE* NO MEMBERS ALLOWED, MULTIPLE INHERITANCE
@@ -176,7 +176,7 @@
 /* a specializations for XPs. Lets the user add uniforms and FS code */
 class GrGLXPBuilder : public virtual GrGLUniformBuilder {
 public:
-    virtual GrGLFPFragmentBuilder* getFragmentShaderBuilder() = 0;
+    virtual GrGLXPFragmentBuilder* getFragmentShaderBuilder() = 0;
 
     /*
      * *NOTE* NO MEMBERS ALLOWED, MULTIPLE INHERITANCE
@@ -234,30 +234,30 @@
                                   GrSLPrecision precision,
                                   const char* name,
                                   int arrayCount,
-                                  const char** outName) SK_OVERRIDE;
+                                  const char** outName) override;
 
-    const GrGLShaderVar& getUniformVariable(UniformHandle u) const SK_OVERRIDE {
+    const GrGLShaderVar& getUniformVariable(UniformHandle u) const override {
         return fUniforms[u.toShaderBuilderIndex()].fVariable;
     }
 
-    const char* getUniformCStr(UniformHandle u) const SK_OVERRIDE {
+    const char* getUniformCStr(UniformHandle u) const override {
         return this->getUniformVariable(u).c_str();
     }
 
-    const GrGLContextInfo& ctxInfo() const SK_OVERRIDE;
+    const GrGLContextInfo& ctxInfo() const override;
 
-    GrGLGpu* gpu() const SK_OVERRIDE { return fGpu; }
+    GrGLGpu* gpu() const override { return fGpu; }
 
-    GrGLFPFragmentBuilder* getFragmentShaderBuilder() SK_OVERRIDE { return &fFS; }
-    GrGLVertexBuilder* getVertexShaderBuilder() SK_OVERRIDE { return &fVS; }
+    GrGLXPFragmentBuilder* getFragmentShaderBuilder() override { return &fFS; }
+    GrGLVertexBuilder* getVertexShaderBuilder() override { return &fVS; }
 
     void addVarying(
             const char* name,
             GrGLVarying*,
-            GrSLPrecision fsPrecision = kDefault_GrSLPrecision) SK_OVERRIDE;
+            GrSLPrecision fsPrecision = kDefault_GrSLPrecision) override;
 
     void addPassThroughAttribute(const GrPrimitiveProcessor::Attribute*,
-                                 const char* output) SK_OVERRIDE;
+                                 const char* output) override;
 
 
     // Handles for program uniforms (other than per-effect uniforms)
@@ -290,8 +290,7 @@
     // Generates a possibly mangled name for a stage variable and writes it to the fragment shader.
     // If GrGLSLExpr4 has a valid name then it will use that instead
     void nameExpression(GrGLSLExpr4*, const char* baseName);
-    void emitAndInstallProcs(GrGLSLExpr4* inputColor,
-                             GrGLSLExpr4* inputCoverage);
+    bool emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr4* inputCoverage);
     void emitAndInstallFragProcs(int procOffset, int numProcs, GrGLSLExpr4* inOut);
     void emitAndInstallProc(const GrPendingFragmentStage&,
                             int index,
diff --git a/src/gpu/gl/builders/GrGLShaderBuilder.cpp b/src/gpu/gl/builders/GrGLShaderBuilder.cpp
index c1bc959..67fbe24 100644
--- a/src/gpu/gl/builders/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLShaderBuilder.cpp
@@ -172,6 +172,33 @@
                           kVec2f_GrSLType);
 }
 
+void GrGLShaderBuilder::addLayoutQualifier(const char* param, InterfaceQualifier interface) {
+    SkASSERT(fProgramBuilder->gpu()->glslGeneration() >= k330_GrGLSLGeneration ||
+             fProgramBuilder->gpu()->glCaps().glslCaps()->mustEnableAdvBlendEqs());
+    fLayoutParams[interface].push_back() = param;
+}
+
+void GrGLShaderBuilder::compileAndAppendLayoutQualifiers() {
+    static const char* interfaceQualifierNames[] = {
+        "out"
+    };
+
+    for (int interface = 0; interface <= kLastInterfaceQualifier; ++interface) {
+        const SkTArray<SkString>& params = fLayoutParams[interface];
+        if (params.empty()) {
+            continue;
+        }
+        this->layoutQualifiers().appendf("layout(%s", params[0].c_str());
+        for (int i = 1; i < params.count(); ++i) {
+            this->layoutQualifiers().appendf(", %s", params[i].c_str());
+        }
+        this->layoutQualifiers().appendf(") %s;\n", interfaceQualifierNames[interface]);
+    }
+
+    GR_STATIC_ASSERT(0 == GrGLShaderBuilder::kOut_InterfaceQualifier);
+    GR_STATIC_ASSERT(SK_ARRAY_COUNT(interfaceQualifierNames) == kLastInterfaceQualifier + 1);
+}
+
 bool
 GrGLShaderBuilder::finalize(GrGLuint programId, GrGLenum type, SkTDArray<GrGLuint>* shaderIds) {
     SkASSERT(!fFinalized);
diff --git a/src/gpu/gl/builders/GrGLShaderBuilder.h b/src/gpu/gl/builders/GrGLShaderBuilder.h
index 3b06c05..0416dbe 100644
--- a/src/gpu/gl/builders/GrGLShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLShaderBuilder.h
@@ -139,6 +139,22 @@
      */
     void addFeature(uint32_t featureBit, const char* extensionName);
 
+    enum InterfaceQualifier {
+        kOut_InterfaceQualifier,
+        kLastInterfaceQualifier = kOut_InterfaceQualifier
+    };
+
+    /*
+     * A low level function to build default layout qualifiers.
+     *
+     *   e.g. layout(param1, param2, ...) out;
+     *
+     * GLSL allows default layout qualifiers for in, out, and uniform.
+     */
+    void addLayoutQualifier(const char* param, InterfaceQualifier);
+
+    void compileAndAppendLayoutQualifiers();
+
     void nextStage() {
         fShaderStrings.push_back();
         fCompilerStrings.push_back(this->code().c_str());
@@ -149,6 +165,7 @@
     SkString& versionDecl() { return fShaderStrings[kVersionDecl]; }
     SkString& extensions() { return fShaderStrings[kExtensions]; }
     SkString& precisionQualifier() { return fShaderStrings[kPrecisionQualifier]; }
+    SkString& layoutQualifiers() { return fShaderStrings[kLayoutQualifiers]; }
     SkString& uniforms() { return fShaderStrings[kUniforms]; }
     SkString& inputs() { return fShaderStrings[kInputs]; }
     SkString& outputs() { return fShaderStrings[kOutputs]; }
@@ -161,6 +178,7 @@
         kVersionDecl,
         kExtensions,
         kPrecisionQualifier,
+        kLayoutQualifiers,
         kUniforms,
         kInputs,
         kOutputs,
@@ -180,6 +198,7 @@
     VarArray fInputs;
     VarArray fOutputs;
     uint32_t fFeaturesAddedMask;
+    SkSTArray<1, SkString> fLayoutParams[kLastInterfaceQualifier + 1];
     int fCodeIndex;
     bool fFinalized;
 
diff --git a/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp b/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
index 00e96a5..f484497 100644
--- a/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLVertexShaderBuilder.cpp
@@ -42,11 +42,18 @@
                                         kVec4f_GrSLType, kDefault_GrSLPrecision,
                                         fProgramBuilder->rtAdjustment(),
                                         &fRtAdjustName);
-
-    // Transform from Skia's device coords to GL's normalized device coords. Note that
-    // because we want to "nudge" the device space positions we are converting to 
-    // non-homogeneous NDC.
-    if (kVec3f_GrSLType == posVar.getType()) {
+    if (this->getProgramBuilder()->desc().header().fSnapVerticesToPixelCenters) {
+        if (kVec3f_GrSLType == posVar.getType()) {
+            const char* p = posVar.c_str();
+            this->codeAppendf("{vec2 _posTmp = vec2(%s.x/%s.z, %s.y/%s.z);", p, p, p, p);
+        } else {
+            SkASSERT(kVec2f_GrSLType == posVar.getType());
+            this->codeAppendf("{vec2 _posTmp = %s;", posVar.c_str());
+        }
+        this->codeAppendf("_posTmp = floor(_posTmp) + vec2(0.5, 0.5);"
+                          "gl_Position = vec4(_posTmp.x * %s.x + %s.y, _posTmp.y * %s.z + %s.w, 0, 1);}",
+                          fRtAdjustName, fRtAdjustName, fRtAdjustName, fRtAdjustName);
+    } else if (kVec3f_GrSLType == posVar.getType()) {
         this->codeAppendf("gl_Position = vec4(dot(%s.xz, %s.xy)/%s.z, dot(%s.yz, %s.zw)/%s.z, 0, 1);",
                           posVar.c_str(), fRtAdjustName, posVar.c_str(),
                           posVar.c_str(), fRtAdjustName, posVar.c_str());
@@ -56,9 +63,8 @@
                           posVar.c_str(), fRtAdjustName, fRtAdjustName,
                           posVar.c_str(), fRtAdjustName, fRtAdjustName);
     }
-
-    // We could have the GrGeometryProcessor do this, but its just easier to have it performed here.
-    // If we ever need to set variable pointsize, then we can reinvestigate
+    // We could have the GrGeometryProcessor do this, but its just easier to have it performed
+    // here. If we ever need to set variable pointsize, then we can reinvestigate
     this->codeAppend("gl_PointSize = 1.0;");
 }
 
@@ -75,6 +81,7 @@
 bool
 GrGLVertexBuilder::compileAndAttachShaders(GrGLuint programId, SkTDArray<GrGLuint>* shaderIds) {
     this->versionDecl() = GrGetGLSLVersionDecl(fProgramBuilder->ctxInfo());
+    this->compileAndAppendLayoutQualifiers();
     fProgramBuilder->appendUniformDecls(GrGLProgramBuilder::kVertex_Visibility, &this->uniforms());
     this->appendDecls(fInputs, &this->inputs());
     this->appendDecls(fOutputs, &this->outputs());
diff --git a/src/gpu/gl/builders/GrGLVertexShaderBuilder.h b/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
index 71a60a0..7f7471d 100644
--- a/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
+++ b/src/gpu/gl/builders/GrGLVertexShaderBuilder.h
@@ -9,6 +9,7 @@
 #define GrGLVertexShader_DEFINED
 
 #include "GrGLShaderBuilder.h"
+#include "GrGeometryProcessor.h"
 
 class GrGLVarying;
 
diff --git a/src/gpu/gl/debug/GrBufferObj.h b/src/gpu/gl/debug/GrBufferObj.h
index d0bb83d..3c7a30b 100644
--- a/src/gpu/gl/debug/GrBufferObj.h
+++ b/src/gpu/gl/debug/GrBufferObj.h
@@ -55,7 +55,7 @@
     void setUsage(GrGLint usage) { fUsage = usage; }
     GrGLint getUsage() const     { return fUsage; }
 
-    void deleteAction() SK_OVERRIDE;
+    void deleteAction() override;
 
 protected:
 private:
diff --git a/src/gpu/gl/debug/GrDebugGL.cpp b/src/gpu/gl/debug/GrDebugGL.cpp
index e5b00c1..ad06862 100644
--- a/src/gpu/gl/debug/GrDebugGL.cpp
+++ b/src/gpu/gl/debug/GrDebugGL.cpp
@@ -205,7 +205,6 @@
 
 void GrDebugGL::report() const {
     for (int i = 0; i < fObjects.count(); ++i) {
-        GrAlwaysAssert(0 < fObjects[i]->getHighRefCount());
         if (!fAbandoned) {
             GrAlwaysAssert(0 == fObjects[i]->getRefCount());
             GrAlwaysAssert(fObjects[i]->getDeleted());
diff --git a/src/gpu/gl/debug/GrFakeRefObj.h b/src/gpu/gl/debug/GrFakeRefObj.h
index 84ac499..27e27ce 100644
--- a/src/gpu/gl/debug/GrFakeRefObj.h
+++ b/src/gpu/gl/debug/GrFakeRefObj.h
@@ -22,7 +22,6 @@
 public:
     GrFakeRefObj()
         : fRef(0)
-        , fHighRefCount(0)
         , fMarkedForDeletion(false)
         , fDeleted(false) {
 
@@ -35,9 +34,6 @@
 
     void ref() {
         fRef++;
-        if (fHighRefCount < fRef) {
-            fHighRefCount = fRef;
-        }
     }
     void unref() {
         fRef--;
@@ -51,7 +47,6 @@
         }
     }
     int getRefCount() const             { return fRef; }
-    int getHighRefCount() const         { return fHighRefCount; }
 
     GrGLuint getID() const              { return fID; }
 
@@ -69,7 +64,6 @@
 protected:
 private:
     int         fRef;               // ref count
-    int         fHighRefCount;      // high water mark of the ref count
     GrGLuint    fID;                // globally unique ID
     bool        fMarkedForDeletion;
     // The deleted flag is only set when OpenGL thinks the object is deleted
diff --git a/src/gpu/gl/debug/GrFrameBufferObj.h b/src/gpu/gl/debug/GrFrameBufferObj.h
index 3cfa236..794450c 100644
--- a/src/gpu/gl/debug/GrFrameBufferObj.h
+++ b/src/gpu/gl/debug/GrFrameBufferObj.h
@@ -46,7 +46,7 @@
     void setStencil(GrFBBindableObj *buffer);
     GrFBBindableObj *getStencil()     { return fStencilBuffer; }
 
-    void deleteAction() SK_OVERRIDE {
+    void deleteAction() override {
 
         setColor(NULL);
         setDepth(NULL);
diff --git a/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp
index b4643cc..e60c519 100644
--- a/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp
+++ b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp
@@ -382,8 +382,7 @@
                                                              GrGLenum renderbuffertarget,
                                                              GrGLuint renderBufferID) {
 
-    GrAlwaysAssert(GR_GL_FRAMEBUFFER == target || GR_GL_READ_FRAMEBUFFER == target ||
-                   GR_GL_DRAW_FRAMEBUFFER == target);
+     GrAlwaysAssert(GR_GL_FRAMEBUFFER == target);
      GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment ||
                     GR_GL_DEPTH_ATTACHMENT == attachment ||
                     GR_GL_STENCIL_ATTACHMENT == attachment);
@@ -423,8 +422,7 @@
                                                           GrGLuint textureID,
                                                           GrGLint level) {
 
-    GrAlwaysAssert(GR_GL_FRAMEBUFFER == target || GR_GL_READ_FRAMEBUFFER == target ||
-                   GR_GL_DRAW_FRAMEBUFFER == target);
+     GrAlwaysAssert(GR_GL_FRAMEBUFFER == target);
      GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment ||
                     GR_GL_DEPTH_ATTACHMENT == attachment ||
                     GR_GL_STENCIL_ATTACHMENT == attachment);
@@ -798,7 +796,7 @@
         fWrapped.reset(interface);
     }
 
-    void abandon() const SK_OVERRIDE {
+    void abandon() const override {
         GrDebugGL::abandon();
     }
 
@@ -852,6 +850,7 @@
     functions->fBindTexture = debugGLBindTexture;
     functions->fBindVertexArray = debugGLBindVertexArray;
     functions->fBlendColor = noOpGLBlendColor;
+    functions->fBlendEquation = noOpGLBlendEquation;
     functions->fBlendFunc = noOpGLBlendFunc;
     functions->fBufferData = debugGLBufferData;
     functions->fBufferSubData = noOpGLBufferSubData;
diff --git a/src/gpu/gl/debug/GrProgramObj.h b/src/gpu/gl/debug/GrProgramObj.h
index d06e410..a25341a 100644
--- a/src/gpu/gl/debug/GrProgramObj.h
+++ b/src/gpu/gl/debug/GrProgramObj.h
@@ -24,7 +24,7 @@
 
     void AttachShader(GrShaderObj *shader);
 
-    void deleteAction() SK_OVERRIDE;
+    void deleteAction() override;
 
     // TODO: this flag system won't work w/ multiple contexts!
     void setInUse()         { fInUse = true; }
diff --git a/src/gpu/gl/debug/GrRenderBufferObj.h b/src/gpu/gl/debug/GrRenderBufferObj.h
index 2ad3e2c..8231ef5 100644
--- a/src/gpu/gl/debug/GrRenderBufferObj.h
+++ b/src/gpu/gl/debug/GrRenderBufferObj.h
@@ -25,7 +25,7 @@
     void resetBound()       { fBound = false; }
     bool getBound() const   { return fBound; }
 
-    void deleteAction() SK_OVERRIDE {
+    void deleteAction() override {
 
         this->INHERITED::deleteAction();
     }
diff --git a/src/gpu/gl/debug/GrShaderObj.h b/src/gpu/gl/debug/GrShaderObj.h
index c59d9c9..977920f 100644
--- a/src/gpu/gl/debug/GrShaderObj.h
+++ b/src/gpu/gl/debug/GrShaderObj.h
@@ -24,7 +24,7 @@
     void setType(GrGLenum type)         { fType = type; }
     GrGLenum getType()                  { return fType; }
 
-    void deleteAction() SK_OVERRIDE;
+    void deleteAction() override;
 
 protected:
 private:
diff --git a/src/gpu/gl/debug/GrTextureObj.h b/src/gpu/gl/debug/GrTextureObj.h
index 653e23f..fcf851d 100644
--- a/src/gpu/gl/debug/GrTextureObj.h
+++ b/src/gpu/gl/debug/GrTextureObj.h
@@ -43,7 +43,7 @@
         return 0 != fTextureUnitReferees.count();
     }
 
-    void deleteAction() SK_OVERRIDE;
+    void deleteAction() override;
 
 protected:
 
diff --git a/src/gpu/gl/debug/SkDebugGLContext.h b/src/gpu/gl/debug/SkDebugGLContext.h
index 35e4456..5779532 100644
--- a/src/gpu/gl/debug/SkDebugGLContext.h
+++ b/src/gpu/gl/debug/SkDebugGLContext.h
@@ -12,9 +12,9 @@
 
 class SkDebugGLContext : public SkGLContext {
 public:
-    ~SkDebugGLContext() SK_OVERRIDE;
-    void makeCurrent() const SK_OVERRIDE {}
-    void swapBuffers() const SK_OVERRIDE {}
+    ~SkDebugGLContext() override;
+    void makeCurrent() const override {}
+    void swapBuffers() const override {}
 
     static SkDebugGLContext* Create(GrGLStandard forcedGpuAPI) {
         if (kGLES_GrGLStandard == forcedGpuAPI) {
diff --git a/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp b/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp
index 9f35981..d57f761 100644
--- a/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp
+++ b/src/gpu/gl/egl/SkCreatePlatformGLContext_egl.cpp
@@ -15,9 +15,9 @@
 class EGLGLContext : public SkGLContext  {
 public:
     EGLGLContext(GrGLStandard forcedGpuAPI);
-    ~EGLGLContext() SK_OVERRIDE;
-    void makeCurrent() const SK_OVERRIDE;
-    void swapBuffers() const SK_OVERRIDE;
+    ~EGLGLContext() override;
+    void makeCurrent() const override;
+    void swapBuffers() const override;
 
 private:
     void destroyGLContext();
diff --git a/src/gpu/gl/glx/SkCreatePlatformGLContext_glx.cpp b/src/gpu/gl/glx/SkCreatePlatformGLContext_glx.cpp
index 9efa993..8006d49 100644
--- a/src/gpu/gl/glx/SkCreatePlatformGLContext_glx.cpp
+++ b/src/gpu/gl/glx/SkCreatePlatformGLContext_glx.cpp
@@ -47,9 +47,9 @@
 class GLXGLContext : public SkGLContext {
 public:
     GLXGLContext(GrGLStandard forcedGpuAPI);
-    ~GLXGLContext() SK_OVERRIDE;
-    void makeCurrent() const SK_OVERRIDE;
-    void swapBuffers() const SK_OVERRIDE;
+    ~GLXGLContext() override;
+    void makeCurrent() const override;
+    void swapBuffers() const override;
 
 private:
     void destroyGLContext();
diff --git a/src/gpu/gl/iOS/SkCreatePlatformGLContext_iOS.mm b/src/gpu/gl/iOS/SkCreatePlatformGLContext_iOS.mm
index f900976..8842168 100644
--- a/src/gpu/gl/iOS/SkCreatePlatformGLContext_iOS.mm
+++ b/src/gpu/gl/iOS/SkCreatePlatformGLContext_iOS.mm
@@ -16,9 +16,9 @@
 class IOSGLContext : public SkGLContext {
 public:
     IOSGLContext();
-    ~IOSGLContext() SK_OVERRIDE;
-    void makeCurrent() const SK_OVERRIDE;
-    void swapBuffers() const SK_OVERRIDE;
+    ~IOSGLContext() override;
+    void makeCurrent() const override;
+    void swapBuffers() const override;
 
 private:
     void destroyGLContext();
diff --git a/src/gpu/gl/mac/SkCreatePlatformGLContext_mac.cpp b/src/gpu/gl/mac/SkCreatePlatformGLContext_mac.cpp
index e7f21e0..436c53f 100644
--- a/src/gpu/gl/mac/SkCreatePlatformGLContext_mac.cpp
+++ b/src/gpu/gl/mac/SkCreatePlatformGLContext_mac.cpp
@@ -14,9 +14,9 @@
 class MacGLContext : public SkGLContext {
 public:
     MacGLContext();
-    ~MacGLContext() SK_OVERRIDE;
-    void makeCurrent() const SK_OVERRIDE;
-    void swapBuffers() const SK_OVERRIDE;
+    ~MacGLContext() override;
+    void makeCurrent() const override;
+    void swapBuffers() const override;
 
 private:
     void destroyGLContext();
diff --git a/src/gpu/gl/mesa/SkMesaGLContext.h b/src/gpu/gl/mesa/SkMesaGLContext.h
index df7e0b0..bf0c7e9 100644
--- a/src/gpu/gl/mesa/SkMesaGLContext.h
+++ b/src/gpu/gl/mesa/SkMesaGLContext.h
@@ -17,9 +17,9 @@
     typedef intptr_t Context;
 
 public:
-    ~SkMesaGLContext() SK_OVERRIDE;
-    void makeCurrent() const SK_OVERRIDE;
-    void swapBuffers() const SK_OVERRIDE;
+    ~SkMesaGLContext() override;
+    void makeCurrent() const override;
+    void swapBuffers() const override;
 
     static SkMesaGLContext* Create(GrGLStandard forcedGpuAPI) {
         if (kGLES_GrGLStandard == forcedGpuAPI) {
diff --git a/src/gpu/gl/win/SkCreatePlatformGLContext_win.cpp b/src/gpu/gl/win/SkCreatePlatformGLContext_win.cpp
index 5f3d364..d387ef4 100644
--- a/src/gpu/gl/win/SkCreatePlatformGLContext_win.cpp
+++ b/src/gpu/gl/win/SkCreatePlatformGLContext_win.cpp
@@ -20,9 +20,9 @@
 class WinGLContext : public SkGLContext {
 public:
     WinGLContext(GrGLStandard forcedGpuAPI);
-	~WinGLContext() SK_OVERRIDE;
-    void makeCurrent() const SK_OVERRIDE;
-    void swapBuffers() const SK_OVERRIDE;
+	~WinGLContext() override;
+    void makeCurrent() const override;
+    void swapBuffers() const override;
 
 private:
     void destroyGLContext();
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index f427755..4efbc33 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -25,15 +25,6 @@
     return id;
 }
 
-void SkImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
-    as_IB(this)->onDraw(canvas, x, y, paint);
-}
-
-void SkImage::drawRect(SkCanvas* canvas, const SkRect* src, const SkRect& dst,
-                   const SkPaint* paint) const {
-    as_IB(this)->onDrawRect(canvas, src, dst, paint);
-}
-
 const void* SkImage::peekPixels(SkImageInfo* info, size_t* rowBytes) const {
     SkImageInfo infoStorage;
     size_t rowBytesStorage;
@@ -178,4 +169,16 @@
     return surface->newImageSnapshot();
 }
 
+//////////////////////////////////////////////////////////////////////////////////////
 
+#if !SK_SUPPORT_GPU
+
+SkImage* SkImage::NewFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType) {
+    return NULL;
+}
+
+SkImage* SkImage::NewFromTextureCopy(GrContext*, const GrBackendTextureDesc&, SkAlphaType) {
+    return NULL;
+}
+
+#endif
diff --git a/src/image/SkImagePriv.h b/src/image/SkImagePriv.h
index 1b2ae4f..3a5c59a 100644
--- a/src/image/SkImagePriv.h
+++ b/src/image/SkImagePriv.h
@@ -38,11 +38,6 @@
 // in which case the surface may need to perform a copy-on-write.
 extern const SkPixelRef* SkBitmapImageGetPixelRef(const SkImage* rasterImage);
 
-// Given an image created with NewTexture, return its GrTexture. This
-// may be called to see if the surface and the image share the same GrTexture,
-// in which case the surface may need to perform a copy-on-write.
-extern GrTexture* SkTextureImageGetTexture(SkImage* textureImage);
-
 // When a texture is shared by a surface and an image its budgeted status is that of the
 // surface. This function is used when the surface makes a new texture for itself in order
 // for the orphaned image to determine whether the original texture counts against the
@@ -54,7 +49,4 @@
 // surface needs to perform a copy-on-write
 extern void SkTextureImageSetTexture(SkImage* image, GrTexture* texture);
 
-extern SkImage* SkNewImageFromBitmapTexture(const SkBitmap&, int sampleCountForNewSurfaces,
-                                            SkSurface::Budgeted);
-
 #endif
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 512c80c..5daf419 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -36,9 +36,6 @@
 
     const SkSurfaceProps& props() const { return fProps; }
 
-    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const = 0;
-    virtual void onDrawRect(SkCanvas*, const SkRect* src,
-                                  const SkRect& dst, const SkPaint*) const = 0;
     virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const = 0;
 
     virtual const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const {
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 7ccff17..06ecda1 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -8,66 +8,154 @@
 #include "SkImage_Gpu.h"
 #include "SkCanvas.h"
 #include "GrContext.h"
+#include "SkGpuDevice.h"
 
-SkImage_Gpu::SkImage_Gpu(const SkBitmap& bitmap, int sampleCountForNewSurfaces,
-                         SkSurface::Budgeted budgeted)
-    : INHERITED(bitmap.width(), bitmap.height(), NULL)
-    , fBitmap(bitmap)
+SkImage_Gpu::SkImage_Gpu(int w, int h, SkAlphaType at, GrTexture* tex,
+                         int sampleCountForNewSurfaces, SkSurface::Budgeted budgeted)
+    : INHERITED(w, h, NULL)
+    , fTexture(SkRef(tex))
     , fSampleCountForNewSurfaces(sampleCountForNewSurfaces)
+    , fAlphaType(at)
     , fBudgeted(budgeted)
-{
-    SkASSERT(fBitmap.getTexture());
-}
-
-SkShader* SkImage_Gpu::onNewShader(SkShader::TileMode tileX,
-                                   SkShader::TileMode tileY,
-                                   const SkMatrix* localMatrix) const
-{
-    return SkShader::CreateBitmapShader(fBitmap, tileX, tileY, localMatrix);
-}
-
-void SkImage_Gpu::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
-    canvas->drawBitmap(fBitmap, x, y, paint);
-}
-
-void SkImage_Gpu::onDrawRect(SkCanvas* canvas, const SkRect* src, const SkRect& dst,
-                                   const SkPaint* paint) const {
-    canvas->drawBitmapRectToRect(fBitmap, src, dst, paint);
-}
+    {}
 
 SkSurface* SkImage_Gpu::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) const {
-    GrContext* ctx = this->getTexture()->getContext();
-    // TODO: Change signature of onNewSurface to take a budgeted param.
-    static const SkSurface::Budgeted kBudgeted = SkSurface::kNo_Budgeted;
-    return SkSurface::NewRenderTarget(ctx, kBudgeted, info, fSampleCountForNewSurfaces, &props);
-}
-
-GrTexture* SkImage_Gpu::onGetTexture() const {
-    return fBitmap.getTexture();
-}
-
-bool SkImage_Gpu::getROPixels(SkBitmap* dst) const {
-    return fBitmap.copyTo(dst, kN32_SkColorType);
-}
-
-bool SkImage_Gpu::isOpaque() const {
-    return fBitmap.isOpaque();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkImage* SkNewImageFromBitmapTexture(const SkBitmap& bitmap, int sampleCountForNewSurfaces,
-                                     SkSurface::Budgeted budgeted) {
-    if (0 == bitmap.width() || 0 == bitmap.height() || NULL == bitmap.getTexture()) {
+    GrTexture* tex = this->getTexture();
+    SkASSERT(tex);
+    GrContext* ctx = tex->getContext();
+    if (!ctx) {
+        // the texture may have been abandoned, so we have to check
         return NULL;
     }
-    return SkNEW_ARGS(SkImage_Gpu, (bitmap, sampleCountForNewSurfaces, budgeted));
-}
-
-GrTexture* SkTextureImageGetTexture(SkImage* image) {
-    return ((SkImage_Gpu*)image)->getTexture();
+    // TODO: Change signature of onNewSurface to take a budgeted param.
+    const SkSurface::Budgeted budgeted = SkSurface::kNo_Budgeted;
+    return SkSurface::NewRenderTarget(ctx, budgeted, info, fSampleCountForNewSurfaces, &props);
 }
 
 extern void SkTextureImageApplyBudgetedDecision(SkImage* image) {
-    ((SkImage_Gpu*)image)->applyBudgetDecision();
+    if (image->getTexture()) {
+        ((SkImage_Gpu*)image)->applyBudgetDecision();
+    }
+}
+
+SkShader* SkImage_Gpu::onNewShader(SkShader::TileMode tileX, SkShader::TileMode tileY,
+                                   const SkMatrix* localMatrix) const {
+    SkBitmap bm;
+    GrWrapTextureInBitmap(fTexture, this->width(), this->height(), this->isOpaque(), &bm);
+    return SkShader::CreateBitmapShader(bm, tileX, tileY, localMatrix);
+}
+
+bool SkImage_Gpu::getROPixels(SkBitmap* dst) const {
+    SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
+    if (!dst->tryAllocPixels(SkImageInfo::MakeN32(this->width(), this->height(), at))) {
+        return false;
+    }
+    if (!fTexture->readPixels(0, 0, dst->width(), dst->height(), kSkia8888_GrPixelConfig,
+                              dst->getPixels(), dst->rowBytes())) {
+        return false;
+    }
+    return true;
+}
+
+bool SkImage_Gpu::isOpaque() const {
+    return GrPixelConfigIsOpaque(fTexture->config());
+}
+
+static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
+    switch (info.colorType()) {
+        case kRGBA_8888_SkColorType:
+        case kBGRA_8888_SkColorType:
+            break;
+        default:
+            return; // nothing to do
+    }
+
+    // SkColor is not necesarily RGBA or BGRA, but it is one of them on little-endian,
+    // and in either case, the alpha-byte is always in the same place, so we can safely call
+    // SkPreMultiplyColor()
+    //
+    SkColor* row = (SkColor*)pixels;
+    for (int y = 0; y < info.height(); ++y) {
+        for (int x = 0; x < info.width(); ++x) {
+            row[x] = SkPreMultiplyColor(row[x]);
+        }
+    }
+}
+
+bool SkImage_Gpu::onReadPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
+                               int srcX, int srcY) const {
+    GrPixelConfig config = SkImageInfo2GrPixelConfig(info.colorType(), info.alphaType(),
+                                                     info.profileType());
+    uint32_t flags = 0;
+    if (kUnpremul_SkAlphaType == info.alphaType() && kPremul_SkAlphaType == fAlphaType) {
+        // let the GPU perform this transformation for us
+        flags = GrContext::kUnpremul_PixelOpsFlag;
+    }
+    if (!fTexture->readPixels(srcX, srcY, info.width(), info.height(), config,
+                              pixels, rowBytes, flags)) {
+        return false;
+    }
+    // do we have to manually fix-up the alpha channel?
+    //      src         dst
+    //      unpremul    premul      fix manually
+    //      premul      unpremul    done by kUnpremul_PixelOpsFlag
+    // all other combos need to change.
+    //
+    // Should this be handled by Ganesh? todo:?
+    //
+    if (kPremul_SkAlphaType == info.alphaType() && kUnpremul_SkAlphaType == fAlphaType) {
+        apply_premul(info, pixels, rowBytes);
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkImage* SkImage::NewFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc, SkAlphaType at) {
+    if (desc.fWidth <= 0 || desc.fHeight <= 0) {
+        return NULL;
+    }
+    SkAutoTUnref<GrTexture> tex(ctx->textureProvider()->wrapBackendTexture(desc));
+    if (!tex) {
+        return NULL;
+    }
+    const SkSurface::Budgeted budgeted = SkSurface::kNo_Budgeted;
+    return SkNEW_ARGS(SkImage_Gpu, (desc.fWidth, desc.fHeight, at, tex, 0, budgeted));
+}
+
+SkImage* SkImage::NewFromTextureCopy(GrContext* ctx, const GrBackendTextureDesc& srcDesc,
+                                     SkAlphaType at) {
+    const bool isBudgeted = true;
+    const SkSurface::Budgeted budgeted = SkSurface::kYes_Budgeted;
+
+    if (srcDesc.fWidth <= 0 || srcDesc.fHeight <= 0) {
+        return NULL;
+    }
+    SkAutoTUnref<GrTexture> src(ctx->textureProvider()->wrapBackendTexture(srcDesc));
+    if (!src) {
+        return NULL;
+    }
+
+    GrSurfaceDesc dstDesc;
+    // need to be a rendertarget for readpixels to work, instead of kNone_GrSurfaceFlags
+    dstDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    dstDesc.fOrigin = srcDesc.fOrigin;
+    dstDesc.fWidth = srcDesc.fWidth;
+    dstDesc.fHeight = srcDesc.fHeight;
+    dstDesc.fConfig = srcDesc.fConfig;
+    dstDesc.fSampleCnt = srcDesc.fSampleCnt;
+
+    SkAutoTUnref<GrTexture> dst(ctx->textureProvider()->createTexture(
+                                                                  dstDesc, isBudgeted, NULL, 0));
+    if (!dst) {
+        return NULL;
+    }
+
+    const SkIRect srcR = SkIRect::MakeWH(dstDesc.fWidth, dstDesc.fHeight);
+    const SkIPoint dstP = SkIPoint::Make(0, 0);
+    ctx->copySurface(dst, src, srcR, dstP, GrContext::kFlushWrites_PixelOp);
+
+    const int sampleCount = 0;  // todo: make this an explicit parameter to newSurface()?
+    return SkNEW_ARGS(SkImage_Gpu, (dstDesc.fWidth, dstDesc.fHeight, at, dst, sampleCount,
+                                    budgeted));
 }
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index f4ca064..972cd3c 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -19,35 +19,38 @@
 public:
     SK_DECLARE_INST_COUNT(SkImage_Gpu)
 
-    SkImage_Gpu(const SkBitmap&, int sampleCountForNewSurfaces, SkSurface::Budgeted);
-
-    void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const SK_OVERRIDE;
-    void onDrawRect(SkCanvas*, const SkRect* src, const SkRect& dst,
-                    const SkPaint*) const SK_OVERRIDE;
-    SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const SK_OVERRIDE;
-    GrTexture* onGetTexture() const SK_OVERRIDE;
-    bool getROPixels(SkBitmap*) const SK_OVERRIDE;
-
-    GrTexture* getTexture() const { return fBitmap.getTexture(); }
-
-    SkShader* onNewShader(SkShader::TileMode,
-                                  SkShader::TileMode,
-                                  const SkMatrix* localMatrix) const SK_OVERRIDE;
-
-    bool isOpaque() const SK_OVERRIDE;
+    /**
+     *  An "image" can be a subset/window into a larger texture, so we explicit take the
+     *  width and height.
+     */
+    SkImage_Gpu(int w, int h, SkAlphaType, GrTexture*, int sampleCountForNewSurfaces,
+                SkSurface::Budgeted);
 
     void applyBudgetDecision() const {
+        GrTexture* tex = this->getTexture();
+        SkASSERT(tex);
         if (fBudgeted) {
-            fBitmap.getTexture()->resourcePriv().makeBudgeted();
+            tex->resourcePriv().makeBudgeted();
         } else {
-            fBitmap.getTexture()->resourcePriv().makeUnbudgeted();
+            tex->resourcePriv().makeUnbudgeted();
         }
     }
 
+    bool getROPixels(SkBitmap*) const override;
+    GrTexture* onGetTexture() const override { return fTexture; }
+    SkShader* onNewShader(SkShader::TileMode,
+                          SkShader::TileMode,
+                          const SkMatrix* localMatrix) const override;
+    bool isOpaque() const override;
+    SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const override;
+    bool onReadPixels(const SkImageInfo&, void* dstPixels, size_t dstRowBytes,
+                      int srcX, int srcY) const override;
+
 private:
-    SkBitmap            fBitmap;
-    const int           fSampleCountForNewSurfaces;   // 0 if we don't know
-    SkSurface::Budgeted fBudgeted;
+    SkAutoTUnref<GrTexture> fTexture;
+    const int               fSampleCountForNewSurfaces;   // 0 if we don't know
+    const SkAlphaType       fAlphaType;
+    SkSurface::Budgeted     fBudgeted;
 
     typedef SkImage_Base INHERITED;
 };
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index 1165d14..20ae62c 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -53,12 +53,10 @@
     SkImage_Raster(const SkImageInfo&, SkData*, size_t rb, const SkSurfaceProps*);
     virtual ~SkImage_Raster();
 
-    void onDraw(SkCanvas*, SkScalar, SkScalar, const SkPaint*) const SK_OVERRIDE;
-    void onDrawRect(SkCanvas*, const SkRect*, const SkRect&, const SkPaint*) const SK_OVERRIDE;
-    SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const SK_OVERRIDE;
-    bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY) const SK_OVERRIDE;
-    const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const SK_OVERRIDE;
-    bool getROPixels(SkBitmap*) const SK_OVERRIDE;
+    SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const override;
+    bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY) const override;
+    const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const override;
+    bool getROPixels(SkBitmap*) const override;
 
     // exposed for SkSurface_Raster via SkNewImageFromPixelRef
     SkImage_Raster(const SkImageInfo&, SkPixelRef*, const SkIPoint& pixelRefOrigin, size_t rowBytes,
@@ -66,11 +64,11 @@
 
     SkPixelRef* getPixelRef() const { return fBitmap.pixelRef(); }
 
-    virtual SkShader* onNewShader(SkShader::TileMode,
-                                  SkShader::TileMode,
-                                  const SkMatrix* localMatrix) const SK_OVERRIDE;
+    SkShader* onNewShader(SkShader::TileMode,
+                          SkShader::TileMode,
+                          const SkMatrix* localMatrix) const override;
 
-    bool isOpaque() const SK_OVERRIDE;
+    bool isOpaque() const override;
 
     SkImage_Raster(const SkBitmap& bm, const SkSurfaceProps* props)
         : INHERITED(bm.width(), bm.height(), props)
@@ -120,17 +118,6 @@
     return SkShader::CreateBitmapShader(fBitmap, tileX, tileY, localMatrix);
 }
 
-void SkImage_Raster::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
-    SkBitmap shallowCopy(fBitmap);
-    canvas->drawBitmap(shallowCopy, x, y, paint);
-}
-
-void SkImage_Raster::onDrawRect(SkCanvas* canvas, const SkRect* src, const SkRect& dst,
-                                      const SkPaint* paint) const {
-    SkBitmap shallowCopy(fBitmap);
-    canvas->drawBitmapRectToRect(shallowCopy, src, dst, paint);
-}
-
 SkSurface* SkImage_Raster::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) const {
     return SkSurface::NewRaster(info, &props);
 }
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index 2d3fa2b..a999973 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -188,5 +188,9 @@
     return NULL;
 }
 
+SkSurface* SkSurface::NewWrappedRenderTarget(GrContext*, GrBackendTextureDesc,
+                                             const SkSurfaceProps*) {
+    return NULL;
+}
 
 #endif
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index b94e4e3..fa5daec 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -10,6 +10,7 @@
 #include "SkCanvas.h"
 #include "SkGpuDevice.h"
 #include "SkImage_Base.h"
+#include "SkImage_Gpu.h"
 #include "SkImagePriv.h"
 #include "SkSurface_Base.h"
 
@@ -44,9 +45,15 @@
 }
 
 SkImage* SkSurface_Gpu::onNewImageSnapshot(Budgeted budgeted) {
+    const SkImageInfo info = fDevice->imageInfo();
     const int sampleCount = fDevice->accessRenderTarget()->numSamples();
-    SkImage* image = SkNewImageFromBitmapTexture(fDevice->accessBitmap(false), sampleCount,
-                                                 budgeted);
+    SkImage* image = NULL;
+    GrTexture* tex = fDevice->accessRenderTarget()->asTexture();
+    if (tex) {
+        image = SkNEW_ARGS(SkImage_Gpu,
+                           (info.width(), info.height(), info.alphaType(),
+                            tex, sampleCount, budgeted));
+    }
     if (image) {
         as_IB(image)->initWithProps(this->props());
     }
@@ -67,7 +74,7 @@
     // image because onCopyOnWrite is only called when there is a cached image.
     SkImage* image = this->getCachedImage(kNo_Budgeted);
     SkASSERT(image);
-    if (rt->asTexture() == SkTextureImageGetTexture(image)) {
+    if (rt->asTexture() == image->getTexture()) {
         this->fDevice->replaceRenderTarget(SkSurface::kRetain_ContentChangeMode == mode);
         SkTextureImageApplyBudgetedDecision(image);
     } else if (kDiscard_ContentChangeMode == mode) {
@@ -99,4 +106,24 @@
     return SkNEW_ARGS(SkSurface_Gpu, (device));
 }
 
+SkSurface* SkSurface::NewWrappedRenderTarget(GrContext* context, GrBackendTextureDesc desc,
+                                             const SkSurfaceProps* props) {
+    if (NULL == context) {
+        return NULL;
+    }
+    if (!SkToBool(desc.fFlags & kRenderTarget_GrBackendTextureFlag)) {
+        return NULL;
+    }
+    SkAutoTUnref<GrSurface> surface(context->textureProvider()->wrapBackendTexture(desc));
+    if (!surface) {
+        return NULL;
+    }
+    SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(surface->asRenderTarget(), props,
+                                                         SkGpuDevice::kNeedClear_Flag));
+    if (!device) {
+        return NULL;
+    }
+    return SkNEW_ARGS(SkSurface_Gpu, (device));
+}
+
 #endif
diff --git a/src/image/SkSurface_Gpu.h b/src/image/SkSurface_Gpu.h
index 90c00c4..3ef1741 100644
--- a/src/image/SkSurface_Gpu.h
+++ b/src/image/SkSurface_Gpu.h
@@ -21,13 +21,12 @@
     SkSurface_Gpu(SkGpuDevice*);
     virtual ~SkSurface_Gpu();
 
-    SkCanvas* onNewCanvas() SK_OVERRIDE;
-    SkSurface* onNewSurface(const SkImageInfo&) SK_OVERRIDE;
-    SkImage* onNewImageSnapshot(Budgeted) SK_OVERRIDE;
-    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
-                        const SkPaint*) SK_OVERRIDE;
-    void onCopyOnWrite(ContentChangeMode) SK_OVERRIDE;
-    void onDiscard() SK_OVERRIDE;
+    SkCanvas* onNewCanvas() override;
+    SkSurface* onNewSurface(const SkImageInfo&) override;
+    SkImage* onNewImageSnapshot(Budgeted) override;
+    void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override;
+    void onCopyOnWrite(ContentChangeMode) override;
+    void onDiscard() override;
 
     SkGpuDevice* getDevice() { return fDevice; }
 
diff --git a/src/image/SkSurface_Raster.cpp b/src/image/SkSurface_Raster.cpp
index 9c38640..28b972b 100644
--- a/src/image/SkSurface_Raster.cpp
+++ b/src/image/SkSurface_Raster.cpp
@@ -22,12 +22,11 @@
                      const SkSurfaceProps*);
     SkSurface_Raster(SkPixelRef*, const SkSurfaceProps*);
 
-    SkCanvas* onNewCanvas() SK_OVERRIDE;
-    SkSurface* onNewSurface(const SkImageInfo&) SK_OVERRIDE;
-    SkImage* onNewImageSnapshot(Budgeted) SK_OVERRIDE;
-    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
-                        const SkPaint*) SK_OVERRIDE;
-    void onCopyOnWrite(ContentChangeMode) SK_OVERRIDE;
+    SkCanvas* onNewCanvas() override;
+    SkSurface* onNewSurface(const SkImageInfo&) override;
+    SkImage* onNewImageSnapshot(Budgeted) override;
+    void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override;
+    void onCopyOnWrite(ContentChangeMode) override;
 
 private:
     SkBitmap    fBitmap;
diff --git a/src/images/SkDecodingImageGenerator.cpp b/src/images/SkDecodingImageGenerator.cpp
index f9b9393..2a07308 100644
--- a/src/images/SkDecodingImageGenerator.cpp
+++ b/src/images/SkDecodingImageGenerator.cpp
@@ -37,16 +37,12 @@
                            bool ditherImage);
 
 protected:
-    SkData* onRefEncodedData() SK_OVERRIDE;
-    bool onGetInfo(SkImageInfo* info) SK_OVERRIDE {
-        *info = fInfo;
-        return true;
-    }
-    virtual Result onGetPixels(const SkImageInfo& info,
-                               void* pixels, size_t rowBytes,
-                               SkPMColor ctable[], int* ctableCount) SK_OVERRIDE;
-    virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
-                                 SkYUVColorSpace* colorSpace) SK_OVERRIDE;
+    SkData* onRefEncodedData() override;
+    Result onGetPixels(const SkImageInfo& info,
+                       void* pixels, size_t rowBytes, const Options&,
+                       SkPMColor ctable[], int* ctableCount) override;
+    bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
+                         SkYUVColorSpace* colorSpace) override;
 
 private:
     typedef SkImageGenerator INHERITED;
@@ -116,7 +112,8 @@
         const SkImageInfo& info,
         int sampleSize,
         bool ditherImage)
-    : fData(data)
+    : INHERITED(info)
+    , fData(data)
     , fStream(stream)
     , fInfo(info)
     , fSampleSize(sampleSize)
@@ -148,7 +145,8 @@
 }
 
 SkImageGenerator::Result DecodingImageGenerator::onGetPixels(const SkImageInfo& info,
-        void* pixels, size_t rowBytes, SkPMColor ctableEntries[], int* ctableCount) {
+        void* pixels, size_t rowBytes, const Options& options, SkPMColor ctableEntries[],
+        int* ctableCount) {
     if (fInfo != info) {
         // The caller has specified a different info.  This is an
         // error for this kind of SkImageGenerator.  Use the Options
diff --git a/src/images/SkForceLinking.cpp b/src/images/SkForceLinking.cpp
index 4f604a2..55b7021 100644
--- a/src/images/SkForceLinking.cpp
+++ b/src/images/SkForceLinking.cpp
@@ -26,8 +26,7 @@
         CreateASTCImageDecoder();
 #endif
         // Only link GIF and PNG on platforms that build them. See images.gyp
-#if !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_WIN) && !defined(SK_BUILD_FOR_NACL) \
-        && !defined(SK_BUILD_FOR_IOS)
+#if !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_WIN) && !defined(SK_BUILD_FOR_IOS)
         CreateGIFImageDecoder();
 #endif
 #if !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_WIN) && !defined(SK_BUILD_FOR_IOS)
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index 89a4705..5b3cf32 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -222,7 +222,9 @@
     SkAutoTDelete<SkStreamRewindable> stream(SkStream::NewFromFile(file));
     if (stream.get()) {
         if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) {
-            bm->pixelRef()->setURI(file);
+            if (SkPixelRef* pr = bm->pixelRef()) {
+                pr->setURI(file);
+            }
             return true;
         }
     }
diff --git a/src/images/SkImageDecoder_astc.cpp b/src/images/SkImageDecoder_astc.cpp
index 64cdf43..869acfb 100644
--- a/src/images/SkImageDecoder_astc.cpp
+++ b/src/images/SkImageDecoder_astc.cpp
@@ -19,12 +19,12 @@
 public:
     SkASTCImageDecoder() { }
 
-    Format getFormat() const SK_OVERRIDE {
+    Format getFormat() const override {
         return kASTC_Format;
     }
 
 protected:
-    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
 
 private:
     typedef SkImageDecoder INHERITED;
diff --git a/src/images/SkImageDecoder_ktx.cpp b/src/images/SkImageDecoder_ktx.cpp
index 7248950..837fe58 100644
--- a/src/images/SkImageDecoder_ktx.cpp
+++ b/src/images/SkImageDecoder_ktx.cpp
@@ -36,12 +36,12 @@
 public:
     SkKTXImageDecoder() { }
 
-    Format getFormat() const SK_OVERRIDE {
+    Format getFormat() const override {
         return kKTX_Format;
     }
 
 protected:
-    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
 
 private:
     typedef SkImageDecoder INHERITED;
@@ -251,7 +251,7 @@
 
 class SkKTXImageEncoder : public SkImageEncoder {
 protected:
-    bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
+    bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override;
 
 private:
     virtual bool encodePKM(SkWStream* stream, const SkData *data);
diff --git a/src/images/SkImageDecoder_libbmp.cpp b/src/images/SkImageDecoder_libbmp.cpp
index e28713d..6647e1b 100644
--- a/src/images/SkImageDecoder_libbmp.cpp
+++ b/src/images/SkImageDecoder_libbmp.cpp
@@ -19,12 +19,12 @@
 public:
     SkBMPImageDecoder() {}
 
-    Format getFormat() const SK_OVERRIDE {
+    Format getFormat() const override {
         return kBMP_Format;
     }
 
 protected:
-    Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
+    Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) override;
 
 private:
     typedef SkImageDecoder INHERITED;
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
index b0ed810..5d55a13 100644
--- a/src/images/SkImageDecoder_libgif.cpp
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -19,12 +19,12 @@
 
 class SkGIFImageDecoder : public SkImageDecoder {
 public:
-    Format getFormat() const SK_OVERRIDE {
+    Format getFormat() const override {
         return kGIF_Format;
     }
 
 protected:
-    Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
+    Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) override;
 
 private:
     typedef SkImageDecoder INHERITED;
diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
index 3ca1908..b9b3fba 100644
--- a/src/images/SkImageDecoder_libico.cpp
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -15,12 +15,12 @@
 public:
     SkICOImageDecoder();
 
-    Format getFormat() const SK_OVERRIDE {
+    Format getFormat() const override {
         return kICO_Format;
     }
 
 protected:
-    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
 
 private:
     typedef SkImageDecoder INHERITED;
@@ -76,7 +76,8 @@
 {
     SkAutoMalloc autoMal;
     const size_t length = SkCopyStreamToStorage(&autoMal, stream);
-    if (0 == length) {
+    // Check that the buffer is large enough to read the directory header
+    if (length < 6) {
         return kFailure;
     }
 
@@ -91,8 +92,15 @@
     }
 
     int count = read2Bytes(buf, 4);
+    // Check that there are directory entries
+    if (count < 1) {
+        return kFailure;
+    }
 
-    //need to at least have enough space to hold the initial table of info
+    // Check that buffer is large enough to read directory entries.
+    // We are guaranteed that count is at least 1.  We might as well assume
+    // count is 1 because this deprecated decoder only looks at the first
+    // directory entry.
     if (length < (size_t)(6 + count*16)) {
         return kFailure;
     }
@@ -102,6 +110,7 @@
     //otherwise, they could be used for error checking
     int w = readByte(buf, 6);
     int h = readByte(buf, 7);
+    SkASSERT(w >= 0 && h >= 0);
     int colorCount = readByte(buf, 8);
     //int reservedToo = readByte(buf, 9 + choice*16);   //0
     //int planes = read2Bytes(buf, 10 + choice*16);       //1 - but often 0
@@ -109,6 +118,7 @@
     const size_t size = read4Bytes(buf, 14);           //matters?
     const size_t offset = read4Bytes(buf, 18);
     // promote the sum to 64-bits to avoid overflow
+    // Check that buffer is large enough to read image data
     if (offset > length || size > length || ((uint64_t)offset + size) > length) {
         return kFailure;
     }
@@ -139,6 +149,20 @@
     //int width = read4Bytes(buf, offset+4);              //should == w
     //int height = read4Bytes(buf, offset+8);             //should == 2*h
     //int planesToo = read2Bytes(buf, offset+12);         //should == 1 (does it?)
+    
+    // For ico images, only a byte is used to store each dimension
+    // 0 is used to represent 256
+    if (w == 0) {
+        w = 256;
+    }
+    if (h == 0) {
+        h = 256;
+    }
+
+    // Check that buffer is large enough to read the bit depth
+    if (length < offset + 16) {
+        return kFailure;
+    }
     int bitCount = read2Bytes(buf, offset+14);
 
     void (*placePixel)(const int pixelNo, const unsigned char* buf,
@@ -180,6 +204,12 @@
     //int colorsImportant = read4Bytes(buf, offset+36);   //0
 
     int begin = SkToInt(offset + 40);
+    // Check that the buffer is large enough to read the color table
+    // For bmp-in-icos, there should be 4 bytes per color
+    if (length < (size_t) (begin + 4*colorCount)) {
+        return kFailure;
+    }
+
     //this array represents the colortable
     //if i allow other types of bitmaps, it may actually be used as a part of the bitmap
     SkPMColor* colors = NULL;
@@ -228,6 +258,45 @@
         return kFailure;
     }
 
+    // The AND mask is a 1-bit alpha mask for each pixel that comes after the
+    // XOR mask in the bmp.  If we check that the largest AND offset is safe,
+    // it should mean all other buffer accesses will be at smaller indices and
+    // will therefore be safe.
+    size_t maxAndOffset = andOffset + ((andLineWidth*(h-1)+(w-1)) >> 3);
+    if (length <= maxAndOffset) {
+        return kFailure;
+    }
+
+    // Here we assert that all reads from the buffer using the XOR offset are
+    // less than the AND offset.  This should be guaranteed based on the above
+    // calculations.
+#ifdef SK_DEBUG
+    int maxPixelNum = lineWidth*(h-1)+w-1;
+    int maxByte;
+    switch (bitCount) {
+        case 1:
+            maxByte = maxPixelNum >> 3;
+            break;
+        case 4:
+            maxByte = maxPixelNum >> 1;
+            break;
+        case 8:
+            maxByte = maxPixelNum;
+            break;
+        case 24:
+            maxByte = maxPixelNum * 3 + 2;
+            break;
+        case 32:
+            maxByte = maxPixelNum * 4 + 3;
+            break;
+        default:
+            SkASSERT(false);
+            return kFailure;
+    }
+    int maxXOROffset = xorOffset + maxByte;
+    SkASSERT(maxXOROffset < andOffset);
+#endif
+
     SkAutoLockPixels alp(*bm);
 
     for (int y = 0; y < h; y++)
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
index d32e2a2..c42f214 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -232,19 +232,19 @@
     }
 #endif
 
-    Format getFormat() const SK_OVERRIDE {
+    Format getFormat() const override {
         return kJPEG_Format;
     }
 
 protected:
 #ifdef SK_BUILD_FOR_ANDROID
-    bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
-    bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
+    bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) override;
+    bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) override;
 #endif
-    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
-    virtual bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
-                                    void* planes[3], size_t rowBytes[3],
-                                    SkYUVColorSpace* colorSpace) SK_OVERRIDE;
+    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
+    bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
+                            void* planes[3], size_t rowBytes[3],
+                            SkYUVColorSpace* colorSpace) override;
 
 private:
 #ifdef SK_BUILD_FOR_ANDROID
@@ -768,14 +768,31 @@
                          info.cur_comp_info[component]->downsampled_height);
 }
 
+static bool appears_to_be_yuv(const jpeg_decompress_struct& info) {
+    return (info.jpeg_color_space == JCS_YCbCr)
+        && (DCTSIZE == 8)
+        && (info.num_components == 3)
+        && (info.comps_in_scan >= info.num_components)
+        && (info.scale_denom <= 8)
+        && (info.cur_comp_info[0])
+        && (info.cur_comp_info[1])
+        && (info.cur_comp_info[2])
+        && (info.cur_comp_info[1]->h_samp_factor == 1)
+        && (info.cur_comp_info[1]->v_samp_factor == 1)
+        && (info.cur_comp_info[2]->h_samp_factor == 1)
+        && (info.cur_comp_info[2]->v_samp_factor == 1);
+}
+
 static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3],
                                     SizeType sizeType) {
+    SkASSERT(appears_to_be_yuv(cinfo));
     for (int i = 0; i < 3; ++i) {
         componentSizes[i] = compute_yuv_size(cinfo, i, sizeType);
     }
 }
 
 static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) {
+    SkASSERT(appears_to_be_yuv(cinfo));
     // U size and V size have to be the same if we're calling output_raw_data()
     SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType);
     SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType));
@@ -861,7 +878,6 @@
 #ifdef TIME_DECODE
     SkAutoTime atm("JPEG YUV8 Decode");
 #endif
-
     if (this->getSampleSize() != 1) {
         return false; // Resizing not supported
     }
@@ -888,7 +904,7 @@
         return return_false(cinfo, "read_header YUV8");
     }
 
-    if (cinfo.jpeg_color_space != JCS_YCbCr) {
+    if (!appears_to_be_yuv(cinfo)) {
         // It's not an error to not be encoded in YUV, so no need to use return_false()
         return false;
     }
@@ -920,6 +936,12 @@
         return return_false(cinfo, "start_decompress YUV8");
     }
 
+    // Seems like jpeg_start_decompress is updating our opinion of whether cinfo represents YUV.
+    // Again, not really an error.
+    if (!appears_to_be_yuv(cinfo)) {
+        return false;
+    }
+
     if (!output_raw_data(cinfo, planes, rowBytes)) {
         return return_false(cinfo, "output_raw_data");
     }
@@ -1103,11 +1125,11 @@
 
         if (swapOnly) {
             bm->swap(bitmap);
-        } else {
-            cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
-                       region.width(), region.height(), startX, startY);
+            return true;
         }
-        return true;
+
+        return cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
+                          region.width(), region.height(), startX, startY);
     }
 #endif
 
@@ -1162,11 +1184,10 @@
     }
     if (swapOnly) {
         bm->swap(bitmap);
-    } else {
-        cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
-                   region.width(), region.height(), startX, startY);
+        return true;
     }
-    return true;
+    return cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
+                      region.width(), region.height(), startX, startY);
 }
 #endif
 
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
index c074268..44aa8e2 100644
--- a/src/images/SkImageDecoder_libpng.cpp
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -17,9 +17,12 @@
 #include "SkTemplates.h"
 #include "SkUtils.h"
 #include "transform_scanline.h"
-extern "C" {
+
+#ifdef SKIA_PNG_PREFIXED
+    // this must proceed png.h
+    #include "pngprefix.h"
+#endif
 #include "png.h"
-}
 
 /* These were dropped in libpng >= 1.4 */
 #ifndef png_infopp_NULL
@@ -78,7 +81,7 @@
     SkPNGImageDecoder() {
         fImageIndex = NULL;
     }
-    Format getFormat() const SK_OVERRIDE {
+    Format getFormat() const override {
         return kPNG_Format;
     }
 
@@ -88,16 +91,16 @@
 
 protected:
 #ifdef SK_BUILD_FOR_ANDROID
-    bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
-    bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& region) SK_OVERRIDE;
+    bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) override;
+    bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& region) override;
 #endif
-    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
 
 private:
     SkPNGImageIndex* fImageIndex;
 
     bool onDecodeInit(SkStream* stream, png_structp *png_ptrp, png_infop *info_ptrp);
-    bool decodePalette(png_structp png_ptr, png_infop info_ptr,
+    bool decodePalette(png_structp png_ptr, png_infop info_ptr, int bitDepth,
                        bool * SK_RESTRICT hasAlphap, bool *reallyHasAlphap,
                        SkColorTable **colorTablep);
     bool getBitmapColorType(png_structp, png_infop, SkColorType*, bool* hasAlpha,
@@ -350,7 +353,7 @@
     SkColorTable* colorTable = NULL;
 
     if (pngColorType == PNG_COLOR_TYPE_PALETTE) {
-        decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
+        decodePalette(png_ptr, info_ptr, bitDepth, &hasAlpha, &reallyHasAlpha, &colorTable);
     }
 
     SkAutoUnref aur(colorTable);
@@ -657,7 +660,8 @@
 typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b);
 
 bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
-                                      bool *hasAlphap, bool *reallyHasAlphap,
+                                      int bitDepth, bool *hasAlphap,
+                                      bool *reallyHasAlphap,
                                       SkColorTable **colorTablep) {
     int numPalette;
     png_colorp palette;
@@ -666,13 +670,6 @@
 
     png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
 
-    /*  BUGGY IMAGE WORKAROUND
-
-        We hit some images (e.g. fruit_.png) who contain bytes that are == colortable_count
-        which is a problem since we use the byte as an index. To work around this we grow
-        the colortable by 1 (if its < 256) and duplicate the last color into that slot.
-    */
-    int colorCount = numPalette + (numPalette < 256);
     SkPMColor colorStorage[256];    // worst-case storage
     SkPMColor* colorPtr = colorStorage;
 
@@ -711,9 +708,19 @@
         palette++;
     }
 
-    // see BUGGY IMAGE WORKAROUND comment above
-    if (numPalette < 256) {
-        *colorPtr = colorPtr[-1];
+    /*  BUGGY IMAGE WORKAROUND
+
+        Invalid images could contain pixel values that are greater than the number of palette
+        entries. Since we use pixel values as indices into the palette this could result in reading
+        beyond the end of the palette which could leak the contents of uninitialized memory. To
+        ensure this doesn't happen, we grow the colortable to the maximum size that can be
+        addressed by the bitdepth of the image and fill it with the last palette color or black if
+        the palette is empty (really broken image).
+    */
+    int colorCount = SkTMax(numPalette, 1 << SkTMin(bitDepth, 8));
+    SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0);
+    for (; index < colorCount; index++) {
+        *colorPtr++ = lastColor;
     }
 
     *colorTablep = SkNEW_ARGS(SkColorTable, (colorStorage, colorCount));
@@ -803,7 +810,7 @@
     SkColorTable* colorTable = NULL;
 
     if (pngColorType == PNG_COLOR_TYPE_PALETTE) {
-        decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);
+        decodePalette(png_ptr, info_ptr, bitDepth, &hasAlpha, &reallyHasAlpha, &colorTable);
     }
 
     SkAutoUnref aur(colorTable);
@@ -1097,7 +1104,7 @@
 
 class SkPNGImageEncoder : public SkImageEncoder {
 protected:
-    bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
+    bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override;
 private:
     bool doEncode(SkWStream* stream, const SkBitmap& bm,
                   const bool& hasAlpha, int colorType,
diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp
index d2061db..a7c9610 100644
--- a/src/images/SkImageDecoder_libwebp.cpp
+++ b/src/images/SkImageDecoder_libwebp.cpp
@@ -98,14 +98,14 @@
         fHasAlpha = 0;
     }
 
-    Format getFormat() const SK_OVERRIDE {
+    Format getFormat() const override {
         return kWEBP_Format;
     }
 
 protected:
-    bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
-    bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
-    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) override;
+    bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) override;
+    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
 
 private:
     /**
@@ -395,8 +395,8 @@
     }
 
     if (!directDecode) {
-        cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(),
-                   region.width(), region.height(), rect.x(), rect.y());
+        return cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(),
+                          region.width(), region.height(), rect.x(), rect.y());
     }
     return true;
 }
@@ -583,7 +583,7 @@
 
 class SkWEBPImageEncoder : public SkImageEncoder {
 protected:
-    bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
+    bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override;
 
 private:
     typedef SkImageEncoder INHERITED;
diff --git a/src/images/SkImageDecoder_pkm.cpp b/src/images/SkImageDecoder_pkm.cpp
index c607c6f..9e1a2aa 100644
--- a/src/images/SkImageDecoder_pkm.cpp
+++ b/src/images/SkImageDecoder_pkm.cpp
@@ -19,12 +19,12 @@
 public:
     SkPKMImageDecoder() { }
 
-    Format getFormat() const SK_OVERRIDE {
+    Format getFormat() const override {
         return kPKM_Format;
     }
 
 protected:
-    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
 
 private:
     typedef SkImageDecoder INHERITED;
diff --git a/src/images/SkImageDecoder_wbmp.cpp b/src/images/SkImageDecoder_wbmp.cpp
index 1f506db..b7a6790 100644
--- a/src/images/SkImageDecoder_wbmp.cpp
+++ b/src/images/SkImageDecoder_wbmp.cpp
@@ -17,12 +17,12 @@
 
 class SkWBMPImageDecoder : public SkImageDecoder {
 public:
-    Format getFormat() const SK_OVERRIDE {
+    Format getFormat() const override {
         return kWBMP_Format;
     }
 
 protected:
-    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
+    Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
 
 private:
     typedef SkImageDecoder INHERITED;
@@ -93,7 +93,7 @@
     if (bits > 0) {
         unsigned mask = *src;
         do {
-            *dst++ = (mask >> 7) & 1;;
+            *dst++ = (mask >> 7) & 1;
             mask <<= 1;
         } while (--bits != 0);
     }
diff --git a/src/images/SkImageEncoder_argb.cpp b/src/images/SkImageEncoder_argb.cpp
index 6bc906f..9da15cf 100644
--- a/src/images/SkImageEncoder_argb.cpp
+++ b/src/images/SkImageEncoder_argb.cpp
@@ -13,7 +13,7 @@
 
 class SkARGBImageEncoder : public SkImageEncoder {
 protected:
-    bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
+    bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) override;
 
 private:
     typedef SkImageEncoder INHERITED;
diff --git a/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp
index ff8985a..f79eb6f 100644
--- a/src/images/SkScaledBitmapSampler.cpp
+++ b/src/images/SkScaledBitmapSampler.cpp
@@ -836,7 +836,7 @@
 public:
     DummyDecoder() {}
 protected:
-    Result onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) SK_OVERRIDE {
+    Result onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) override {
         return kFailure;
     }
 };
diff --git a/src/lazy/SkCachingPixelRef.cpp b/src/lazy/SkCachingPixelRef.cpp
index dc53a5d..5fc0d2a 100644
--- a/src/lazy/SkCachingPixelRef.cpp
+++ b/src/lazy/SkCachingPixelRef.cpp
@@ -11,11 +11,12 @@
 
 bool SkCachingPixelRef::Install(SkImageGenerator* generator,
                                 SkBitmap* dst) {
-    SkImageInfo info;
     SkASSERT(dst != NULL);
-    if ((NULL == generator)
-        || !(generator->getInfo(&info))
-        || !dst->setInfo(info)) {
+    if (NULL == generator) {
+        return false;
+    }
+    const SkImageInfo info = generator->getInfo();
+    if (!dst->setInfo(info)) {
         SkDELETE(generator);
         return false;
     }
diff --git a/src/lazy/SkCachingPixelRef.h b/src/lazy/SkCachingPixelRef.h
index a46df14..4889b83 100644
--- a/src/lazy/SkCachingPixelRef.h
+++ b/src/lazy/SkCachingPixelRef.h
@@ -42,11 +42,11 @@
 
 protected:
     virtual ~SkCachingPixelRef();
-    bool onNewLockPixels(LockRec*) SK_OVERRIDE;
-    void onUnlockPixels() SK_OVERRIDE;
-    bool onLockPixelsAreWritable() const SK_OVERRIDE { return false; }
+    bool onNewLockPixels(LockRec*) override;
+    void onUnlockPixels() override;
+    bool onLockPixelsAreWritable() const override { return false; }
 
-    SkData* onRefEncodedData() SK_OVERRIDE {
+    SkData* onRefEncodedData() override {
         return fImageGenerator->refEncodedData();
     }
 
diff --git a/src/lazy/SkDiscardableMemoryPool.cpp b/src/lazy/SkDiscardableMemoryPool.cpp
index 2da8335..5b9d87f 100644
--- a/src/lazy/SkDiscardableMemoryPool.cpp
+++ b/src/lazy/SkDiscardableMemoryPool.cpp
@@ -32,19 +32,19 @@
     DiscardableMemoryPool(size_t budget, SkBaseMutex* mutex = NULL);
     virtual ~DiscardableMemoryPool();
 
-    SkDiscardableMemory* create(size_t bytes) SK_OVERRIDE;
+    SkDiscardableMemory* create(size_t bytes) override;
 
-    size_t getRAMUsed() SK_OVERRIDE;
-    void setRAMBudget(size_t budget) SK_OVERRIDE;
-    size_t getRAMBudget() SK_OVERRIDE { return fBudget; }
+    size_t getRAMUsed() override;
+    void setRAMBudget(size_t budget) override;
+    size_t getRAMBudget() override { return fBudget; }
 
     /** purges all unlocked DMs */
-    void dumpPool() SK_OVERRIDE;
+    void dumpPool() override;
 
     #if SK_LAZY_CACHE_STATS  // Defined in SkDiscardableMemoryPool.h
-    int getCacheHits() SK_OVERRIDE { return fCacheHits; }
-    int getCacheMisses() SK_OVERRIDE { return fCacheMisses; }
-    void resetCacheHitsAndMisses() SK_OVERRIDE {
+    int getCacheHits() override { return fCacheHits; }
+    int getCacheMisses() override { return fCacheMisses; }
+    void resetCacheHitsAndMisses() override {
         fCacheHits = fCacheMisses = 0;
     }
     int          fCacheHits;
@@ -80,9 +80,9 @@
     PoolDiscardableMemory(DiscardableMemoryPool* pool,
                             void* pointer, size_t bytes);
     virtual ~PoolDiscardableMemory();
-    bool lock() SK_OVERRIDE;
-    void* data() SK_OVERRIDE;
-    void unlock() SK_OVERRIDE;
+    bool lock() override;
+    void* data() override;
+    void unlock() override;
     friend class DiscardableMemoryPool;
 private:
     SK_DECLARE_INTERNAL_LLIST_INTERFACE(PoolDiscardableMemory);
@@ -188,9 +188,9 @@
 }
 
 void DiscardableMemoryPool::free(PoolDiscardableMemory* dm) {
+    SkAutoMutexAcquire autoMutexAcquire(fMutex);
     // This is called by dm's destructor.
     if (dm->fPointer != NULL) {
-        SkAutoMutexAcquire autoMutexAcquire(fMutex);
         sk_free(dm->fPointer);
         dm->fPointer = NULL;
         SkASSERT(fUsed >= dm->fBytes);
diff --git a/src/lazy/SkDiscardablePixelRef.cpp b/src/lazy/SkDiscardablePixelRef.cpp
index b6dec1b..1e42042 100644
--- a/src/lazy/SkDiscardablePixelRef.cpp
+++ b/src/lazy/SkDiscardablePixelRef.cpp
@@ -71,7 +71,7 @@
     SkPMColor colors[256];
     int colorCount = 0;
 
-    const SkImageGenerator::Result result = fGenerator->getPixels(info, pixels, fRowBytes,
+    const SkImageGenerator::Result result = fGenerator->getPixels(info, pixels, fRowBytes, NULL,
                                                                   colors, &colorCount);
     switch (result) {
         case SkImageGenerator::kSuccess:
@@ -109,12 +109,12 @@
 
 bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, SkBitmap* dst,
                                   SkDiscardableMemory::Factory* factory) {
-    SkImageInfo info;
     SkAutoTDelete<SkImageGenerator> autoGenerator(generator);
-    if ((NULL == autoGenerator.get())
-        || (!autoGenerator->getInfo(&info))
-        || info.isEmpty()
-        || (!dst->setInfo(info))) {
+    if (NULL == autoGenerator.get()) {
+        return false;
+    }
+    SkImageInfo info = autoGenerator->getInfo();
+    if (info.isEmpty() || !dst->setInfo(info)) {
         return false;
     }
     // Since dst->setInfo() may have changed/fixed-up info, we copy it back from that bitmap
diff --git a/src/lazy/SkDiscardablePixelRef.h b/src/lazy/SkDiscardablePixelRef.h
index 9fef055..4515fd2 100644
--- a/src/lazy/SkDiscardablePixelRef.h
+++ b/src/lazy/SkDiscardablePixelRef.h
@@ -25,11 +25,11 @@
 protected:
     ~SkDiscardablePixelRef();
 
-    bool onNewLockPixels(LockRec*) SK_OVERRIDE;
-    void onUnlockPixels() SK_OVERRIDE;
-    bool onLockPixelsAreWritable() const SK_OVERRIDE { return false; }
+    bool onNewLockPixels(LockRec*) override;
+    void onUnlockPixels() override;
+    bool onLockPixelsAreWritable() const override { return false; }
 
-    SkData* onRefEncodedData() SK_OVERRIDE {
+    SkData* onRefEncodedData() override {
         return fGenerator->refEncodedData();
     }
 
@@ -49,10 +49,10 @@
                           size_t rowBytes,
                           SkDiscardableMemory::Factory* factory);
 
-    virtual bool onGetYUV8Planes(SkISize sizes[3],
-                                 void* planes[3],
-                                 size_t rowBytes[3],
-                                 SkYUVColorSpace* colorSpace) SK_OVERRIDE {
+    bool onGetYUV8Planes(SkISize sizes[3],
+                         void* planes[3],
+                         size_t rowBytes[3],
+                         SkYUVColorSpace* colorSpace) override {
         // If the image was already decoded with lockPixels(), favor not
         // re-decoding to YUV8 planes.
         if (fDiscardableMemory) {
diff --git a/src/opts/Sk4px_NEON.h b/src/opts/Sk4px_NEON.h
new file mode 100644
index 0000000..f0d9b56
--- /dev/null
+++ b/src/opts/Sk4px_NEON.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+inline Sk4px::Sk4px(SkPMColor px) : INHERITED((uint8x16_t)vdupq_n_u32(px)) {}
+
+inline Sk4px Sk4px::Load4(const SkPMColor px[4]) {
+    return Sk16b((uint8x16_t)vld1q_u32(px));
+}
+inline Sk4px Sk4px::Load2(const SkPMColor px[2]) {
+    uint32x2_t px2 = vld1_u32(px);
+    return Sk16b((uint8x16_t)vcombine_u32(px2, px2));
+}
+inline Sk4px Sk4px::Load1(const SkPMColor px[1]) {
+    return Sk16b((uint8x16_t)vdupq_n_u32(*px));
+}
+
+inline void Sk4px::store4(SkPMColor px[4]) const {
+    vst1q_u32(px, (uint32x4_t)this->fVec);
+}
+inline void Sk4px::store2(SkPMColor px[2]) const {
+    vst1_u32(px, (uint32x2_t)vget_low_u8(this->fVec));
+}
+inline void Sk4px::store1(SkPMColor px[1]) const {
+    vst1q_lane_u32(px, (uint32x4_t)this->fVec, 0);
+}
+
+inline Sk4px::Wide Sk4px::widenLo() const {
+    return Sk16h(vmovl_u8(vget_low_u8 (this->fVec)),
+                 vmovl_u8(vget_high_u8(this->fVec)));
+}
+
+inline Sk4px::Wide Sk4px::widenHi() const {
+    return Sk16h(vshll_n_u8(vget_low_u8 (this->fVec), 8),
+                 vshll_n_u8(vget_high_u8(this->fVec), 8));
+}
+
+inline Sk4px::Wide Sk4px::mulWiden(const Sk16b& other) const {
+    return Sk16h(vmull_u8(vget_low_u8 (this->fVec), vget_low_u8 (other.fVec)),
+                 vmull_u8(vget_high_u8(this->fVec), vget_high_u8(other.fVec)));
+}
+
+inline Sk4px Sk4px::Wide::addNarrowHi(const Sk16h& other) const {
+    const Sk4px::Wide o(other);  // Should be no code, but allows us to access fLo, fHi.
+    return Sk16b(vcombine_u8(vaddhn_u16(this->fLo.fVec, o.fLo.fVec),
+                             vaddhn_u16(this->fHi.fVec, o.fHi.fVec)));
+}
+
+inline Sk4px Sk4px::alphas() const {
+    static_assert(SK_A32_SHIFT == 24, "This method assumes little-endian.");
+    auto as = vshrq_n_u32((uint32x4_t)this->fVec, 24);  // ___3 ___2 ___1 ___0
+    as = vorrq_u32(as, vshlq_n_u32(as,  8));            // __33 __22 __11 __11
+    as = vorrq_u32(as, vshlq_n_u32(as, 16));            // 3333 2222 1111 1111
+    return Sk16b((uint8x16_t)as);
+}
+
+inline Sk4px Sk4px::Load4Alphas(const SkAlpha a[4]) {
+    uint8x16_t a8 = vdupq_n_u8(0);                        // ____ ____ ____ ____
+    a8 = vld1q_lane_u8(a+0, a8,  0);                      // ____ ____ ____ ___0
+    a8 = vld1q_lane_u8(a+1, a8,  4);                      // ____ ____ ___1 ___0
+    a8 = vld1q_lane_u8(a+2, a8,  8);                      // ____ ___2 ___1 ___0
+    a8 = vld1q_lane_u8(a+3, a8, 12);                      // ___3 ___2 ___1 ___0
+    auto a32 = (uint32x4_t)a8;                            //
+    a32 = vorrq_u32(a32, vshlq_n_u32(a32,  8));           // __33 __22 __11 __00
+    a32 = vorrq_u32(a32, vshlq_n_u32(a32, 16));           // 3333 2222 1111 0000
+    return Sk16b((uint8x16_t)a32);
+}
+
+inline Sk4px Sk4px::Load2Alphas(const SkAlpha a[2]) {
+    uint8x16_t a8 = vdupq_n_u8(0);                        // ____ ____ ____ ____
+    a8 = vld1q_lane_u8(a+0, a8,  0);                      // ____ ____ ____ ___0
+    a8 = vld1q_lane_u8(a+1, a8,  4);                      // ____ ____ ___1 ___0
+    auto a32 = (uint32x4_t)a8;                            //
+    a32 = vorrq_u32(a32, vshlq_n_u32(a32,  8));           // ____ ____ __11 __00
+    a32 = vorrq_u32(a32, vshlq_n_u32(a32, 16));           // ____ ____ 1111 0000
+    return Sk16b((uint8x16_t)a32);
+}
+
+inline Sk4px Sk4px::zeroColors() const {
+    return Sk16b(vandq_u8(this->fVec, (uint8x16_t)vdupq_n_u32(0xFF << SK_A32_SHIFT)));
+}
+
+inline Sk4px Sk4px::zeroAlphas() const {
+    // vbic(a,b) == a & ~b
+    return Sk16b(vbicq_u8(this->fVec, (uint8x16_t)vdupq_n_u32(0xFF << SK_A32_SHIFT)));
+}
+
diff --git a/src/opts/Sk4px_SSE2.h b/src/opts/Sk4px_SSE2.h
new file mode 100644
index 0000000..9ba5103
--- /dev/null
+++ b/src/opts/Sk4px_SSE2.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+inline Sk4px::Sk4px(SkPMColor px) : INHERITED(_mm_set1_epi32(px)) {}
+
+inline Sk4px Sk4px::Load4(const SkPMColor px[4]) {
+    return Sk16b(_mm_loadu_si128((const __m128i*)px));
+}
+inline Sk4px Sk4px::Load2(const SkPMColor px[2]) {
+    return Sk16b(_mm_loadl_epi64((const __m128i*)px));
+}
+inline Sk4px Sk4px::Load1(const SkPMColor px[1]) { return Sk16b(_mm_cvtsi32_si128(*px)); }
+
+inline void Sk4px::store4(SkPMColor px[4]) const { _mm_storeu_si128((__m128i*)px, this->fVec); }
+inline void Sk4px::store2(SkPMColor px[2]) const { _mm_storel_epi64((__m128i*)px, this->fVec); }
+inline void Sk4px::store1(SkPMColor px[1]) const { *px = _mm_cvtsi128_si32(this->fVec); }
+
+inline Sk4px::Wide Sk4px::widenLo() const {
+    return Sk16h(_mm_unpacklo_epi8(this->fVec, _mm_setzero_si128()),
+                 _mm_unpackhi_epi8(this->fVec, _mm_setzero_si128()));
+}
+
+inline Sk4px::Wide Sk4px::widenHi() const {
+    return Sk16h(_mm_unpacklo_epi8(_mm_setzero_si128(), this->fVec),
+                 _mm_unpackhi_epi8(_mm_setzero_si128(), this->fVec));
+}
+
+inline Sk4px::Wide Sk4px::mulWiden(const Sk16b& other) const {
+    return this->widenLo() * Sk4px(other).widenLo();
+}
+
+inline Sk4px Sk4px::Wide::addNarrowHi(const Sk16h& other) const {
+    Sk4px::Wide r = (*this + other) >> 8;
+    return Sk4px(_mm_packus_epi16(r.fLo.fVec, r.fHi.fVec));
+}
+
+// Load4Alphas and Load2Alphas use possibly-unaligned loads (SkAlpha[] -> uint16_t or uint32_t).
+// These are safe on x86, often with no speed penalty.
+
+#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSSE3
+    inline Sk4px Sk4px::alphas() const {
+        static_assert(SK_A32_SHIFT == 24, "Intel's always little-endian.");
+        __m128i splat = _mm_set_epi8(15,15,15,15, 11,11,11,11, 7,7,7,7, 3,3,3,3);
+        return Sk16b(_mm_shuffle_epi8(this->fVec, splat));
+    }
+
+    inline Sk4px Sk4px::Load4Alphas(const SkAlpha a[4]) {
+        uint32_t as = *(const uint32_t*)a;
+        __m128i splat = _mm_set_epi8(3,3,3,3, 2,2,2,2, 1,1,1,1, 0,0,0,0);
+        return Sk16b(_mm_shuffle_epi8(_mm_cvtsi32_si128(as), splat));
+    }
+#else
+    inline Sk4px Sk4px::alphas() const {
+        static_assert(SK_A32_SHIFT == 24, "Intel's always little-endian.");
+        __m128i as = _mm_srli_epi32(this->fVec, 24);   // ___3 ___2 ___1 ___0
+        as = _mm_or_si128(as, _mm_slli_si128(as, 1));  // __33 __22 __11 __00
+        as = _mm_or_si128(as, _mm_slli_si128(as, 2));  // 3333 2222 1111 0000
+        return Sk16b(as);
+    }
+
+    inline Sk4px Sk4px::Load4Alphas(const SkAlpha a[4]) {
+        __m128i as = _mm_cvtsi32_si128(*(const uint32_t*)a);  // ____ ____ ____ 3210
+        as = _mm_unpacklo_epi8 (as, _mm_setzero_si128());     // ____ ____ _3_2 _1_0
+        as = _mm_unpacklo_epi16(as, _mm_setzero_si128());     // ___3 ___2 ___1 ___0
+        as = _mm_or_si128(as, _mm_slli_si128(as, 1));         // __33 __22 __11 __00
+        as = _mm_or_si128(as, _mm_slli_si128(as, 2));         // 3333 2222 1111 0000
+        return Sk16b(as);
+    }
+#endif
+
+inline Sk4px Sk4px::Load2Alphas(const SkAlpha a[2]) {
+    uint32_t as = *(const uint16_t*)a;   // Aa -> Aa00
+    return Load4Alphas((const SkAlpha*)&as);
+}
+
+inline Sk4px Sk4px::zeroColors() const {
+    return Sk16b(_mm_and_si128(_mm_set1_epi32(0xFF << SK_A32_SHIFT), this->fVec));
+}
+
+inline Sk4px Sk4px::zeroAlphas() const {
+    // andnot(a,b) == ~a & b
+    return Sk16b(_mm_andnot_si128(_mm_set1_epi32(0xFF << SK_A32_SHIFT), this->fVec));
+}
diff --git a/src/opts/Sk4px_none.h b/src/opts/Sk4px_none.h
new file mode 100644
index 0000000..541443d
--- /dev/null
+++ b/src/opts/Sk4px_none.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkUtils.h"
+
+static_assert(sizeof(Sk4px) == 16, "This file uses memcpy / sk_memset32, so exact size matters.");
+
+inline Sk4px::Sk4px(SkPMColor px) {
+    sk_memset32((uint32_t*)this, px, 4);
+}
+
+inline Sk4px Sk4px::Load4(const SkPMColor px[4]) {
+    Sk4px px4 = Sk16b();
+    memcpy(&px4, px, 16);
+    return px4;
+}
+
+inline Sk4px Sk4px::Load2(const SkPMColor px[2]) {
+    Sk4px px2 = Sk16b();
+    memcpy(&px2, px, 8);
+    return px2;
+}
+
+inline Sk4px Sk4px::Load1(const SkPMColor px[1]) {
+    Sk4px px1 = Sk16b();
+    memcpy(&px1, px, 4);
+    return px1;
+}
+
+inline void Sk4px::store4(SkPMColor px[4]) const { memcpy(px, this, 16); }
+inline void Sk4px::store2(SkPMColor px[2]) const { memcpy(px, this,  8); }
+inline void Sk4px::store1(SkPMColor px[1]) const { memcpy(px, this,  4); }
+
+inline Sk4px::Wide Sk4px::widenLo() const {
+    return Sk16h(this->kth< 0>(), this->kth< 1>(), this->kth< 2>(), this->kth< 3>(),
+                 this->kth< 4>(), this->kth< 5>(), this->kth< 6>(), this->kth< 7>(),
+                 this->kth< 8>(), this->kth< 9>(), this->kth<10>(), this->kth<11>(),
+                 this->kth<12>(), this->kth<13>(), this->kth<14>(), this->kth<15>());
+}
+
+inline Sk4px::Wide Sk4px::widenHi() const { return this->widenLo() << 8; }
+
+inline Sk4px::Wide Sk4px::mulWiden(const Sk16b& other) const {
+    return this->widenLo() * Sk4px(other).widenLo();
+}
+
+inline Sk4px Sk4px::Wide::addNarrowHi(const Sk16h& other) const {
+    Sk4px::Wide r = (*this + other) >> 8;
+    return Sk16b(r.kth< 0>(), r.kth< 1>(), r.kth< 2>(), r.kth< 3>(),
+                 r.kth< 4>(), r.kth< 5>(), r.kth< 6>(), r.kth< 7>(),
+                 r.kth< 8>(), r.kth< 9>(), r.kth<10>(), r.kth<11>(),
+                 r.kth<12>(), r.kth<13>(), r.kth<14>(), r.kth<15>());
+}
+
+inline Sk4px Sk4px::alphas() const {
+    static_assert(SK_A32_SHIFT == 24, "This method assumes little-endian.");
+    return Sk16b(this->kth< 3>(), this->kth< 3>(), this->kth< 3>(), this->kth< 3>(),
+                 this->kth< 7>(), this->kth< 7>(), this->kth< 7>(), this->kth< 7>(),
+                 this->kth<11>(), this->kth<11>(), this->kth<11>(), this->kth<11>(),
+                 this->kth<15>(), this->kth<15>(), this->kth<15>(), this->kth<15>());
+}
+
+inline Sk4px Sk4px::Load4Alphas(const SkAlpha a[4]) {
+    return Sk16b(a[0], a[0], a[0], a[0],
+                 a[1], a[1], a[1], a[1],
+                 a[2], a[2], a[2], a[2],
+                 a[3], a[3], a[3], a[3]);
+}
+
+inline Sk4px Sk4px::Load2Alphas(const SkAlpha a[2]) {
+    return Sk16b(a[0], a[0], a[0], a[0],
+                 a[1], a[1], a[1], a[1],
+                 0,0,0,0,
+                 0,0,0,0);
+}
+
+inline Sk4px Sk4px::zeroAlphas() const {
+    static_assert(SK_A32_SHIFT == 24, "This method assumes little-endian.");
+    return Sk16b(this->kth< 0>(), this->kth< 1>(), this->kth< 2>(), 0,
+                 this->kth< 4>(), this->kth< 5>(), this->kth< 6>(), 0,
+                 this->kth< 8>(), this->kth< 9>(), this->kth<10>(), 0,
+                 this->kth<12>(), this->kth<13>(), this->kth<14>(), 0);
+}
+
+inline Sk4px Sk4px::zeroColors() const {
+    static_assert(SK_A32_SHIFT == 24, "This method assumes little-endian.");
+    return Sk16b(0,0,0, this->kth< 3>(),
+                 0,0,0, this->kth< 7>(),
+                 0,0,0, this->kth<11>(),
+                 0,0,0, this->kth<15>());
+}
+
diff --git a/src/opts/SkBitmapProcState_arm_neon.cpp b/src/opts/SkBitmapProcState_arm_neon.cpp
index 33b3627..08b83ea 100644
--- a/src/opts/SkBitmapProcState_arm_neon.cpp
+++ b/src/opts/SkBitmapProcState_arm_neon.cpp
@@ -66,7 +66,17 @@
     SA8_alpha_D32_filter_DXDY_neon,
     SA8_alpha_D32_filter_DXDY_neon,
     SA8_alpha_D32_filter_DX_neon,
-    SA8_alpha_D32_filter_DX_neon
+    SA8_alpha_D32_filter_DX_neon,
+
+    // todo: possibly specialize on opaqueness
+    SG8_alpha_D32_nofilter_DXDY_neon,
+    SG8_alpha_D32_nofilter_DXDY_neon,
+    SG8_alpha_D32_nofilter_DX_neon,
+    SG8_alpha_D32_nofilter_DX_neon,
+    SG8_alpha_D32_filter_DXDY_neon,
+    SG8_alpha_D32_filter_DXDY_neon,
+    SG8_alpha_D32_filter_DX_neon,
+    SG8_alpha_D32_filter_DX_neon,
 };
 
 const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[] = {
@@ -88,7 +98,9 @@
     // Don't support 4444 -> 565
     NULL, NULL, NULL, NULL,
     // Don't support A8 -> 565
-    NULL, NULL, NULL, NULL
+    NULL, NULL, NULL, NULL,
+    // Don't support G8 -> 565 (but we could)
+    NULL, NULL, NULL, NULL,
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/opts/SkBitmapProcState_opts_SSE2.cpp b/src/opts/SkBitmapProcState_opts_SSE2.cpp
index 1f3bbc1..659caf2 100644
--- a/src/opts/SkBitmapProcState_opts_SSE2.cpp
+++ b/src/opts/SkBitmapProcState_opts_SSE2.cpp
@@ -15,7 +15,7 @@
                                    const uint32_t* xy,
                                    int count, uint32_t* colors) {
     SkASSERT(count > 0 && colors != NULL);
-    SkASSERT(s.fFilterLevel != SkPaint::kNone_FilterLevel);
+    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
     SkASSERT(kN32_SkColorType == s.fBitmap->colorType());
     SkASSERT(s.fAlphaScale == 256);
 
@@ -121,7 +121,7 @@
                                   const uint32_t* xy,
                                   int count, uint32_t* colors) {
     SkASSERT(count > 0 && colors != NULL);
-    SkASSERT(s.fFilterLevel != SkPaint::kNone_FilterLevel);
+    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
     SkASSERT(kN32_SkColorType == s.fBitmap->colorType());
     SkASSERT(s.fAlphaScale < 256);
 
@@ -641,7 +641,7 @@
                             const uint32_t* xy,
                             int count, uint16_t* colors) {
     SkASSERT(count > 0 && colors != NULL);
-    SkASSERT(s.fFilterLevel != SkPaint::kNone_FilterLevel);
+    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
     SkASSERT(kN32_SkColorType == s.fBitmap->colorType());
     SkASSERT(s.fBitmap->isOpaque());
 
diff --git a/src/opts/SkBitmapProcState_opts_SSE2.h b/src/opts/SkBitmapProcState_opts_SSE2.h
index 82c5cc8..82bf2cd 100644
--- a/src/opts/SkBitmapProcState_opts_SSE2.h
+++ b/src/opts/SkBitmapProcState_opts_SSE2.h
@@ -16,8 +16,6 @@
 void S32_alpha_D32_filter_DX_SSE2(const SkBitmapProcState& s,
                                   const uint32_t* xy,
                                   int count, uint32_t* colors);
-void Color32_SSE2(SkPMColor dst[], const SkPMColor src[], int count,
-                  SkPMColor color);
 void ClampX_ClampY_filter_scale_SSE2(const SkBitmapProcState& s, uint32_t xy[],
                                      int count, int x, int y);
 void ClampX_ClampY_nofilter_scale_SSE2(const SkBitmapProcState& s,
diff --git a/src/opts/SkBitmapProcState_opts_SSSE3.cpp b/src/opts/SkBitmapProcState_opts_SSSE3.cpp
index 984a65e..bfcd7ca 100644
--- a/src/opts/SkBitmapProcState_opts_SSSE3.cpp
+++ b/src/opts/SkBitmapProcState_opts_SSSE3.cpp
@@ -394,7 +394,7 @@
                                      const uint32_t* xy,
                                      int count, uint32_t* colors) {
     SkASSERT(count > 0 && colors != NULL);
-    SkASSERT(s.fFilterLevel != SkPaint::kNone_FilterLevel);
+    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
     SkASSERT(kN32_SkColorType == s.fBitmap->colorType());
     if (has_alpha) {
         SkASSERT(s.fAlphaScale < 256);
@@ -586,7 +586,7 @@
                                        const uint32_t* xy,
                                        int count, uint32_t* colors) {
     SkASSERT(count > 0 && colors != NULL);
-    SkASSERT(s.fFilterLevel != SkPaint::kNone_FilterLevel);
+    SkASSERT(s.fFilterLevel != kNone_SkFilterQuality);
     SkASSERT(kN32_SkColorType == s.fBitmap->colorType());
     if (has_alpha) {
         SkASSERT(s.fAlphaScale < 256);
diff --git a/src/opts/SkBitmapProcState_opts_arm.cpp b/src/opts/SkBitmapProcState_opts_arm.cpp
index 4a84aee..e6799de 100644
--- a/src/opts/SkBitmapProcState_opts_arm.cpp
+++ b/src/opts/SkBitmapProcState_opts_arm.cpp
@@ -28,7 +28,7 @@
                              int count, uint16_t* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != NULL);
     SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
-    SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
 
     const uint16_t* SK_RESTRICT table = s.fBitmap->getColorTable()->read16BitCache();
     const uint8_t* SK_RESTRICT srcAddr = (const uint8_t*)s.fBitmap->getPixels();
@@ -117,7 +117,7 @@
                                     int count, SkPMColor* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != NULL);
     SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
-    SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
 
     const SkPMColor* SK_RESTRICT table = s.fBitmap->getColorTable()->readColors();
     const uint8_t* SK_RESTRICT srcAddr = (const uint8_t*)s.fBitmap->getPixels();
@@ -201,7 +201,7 @@
 
     switch (fBitmap->colorType()) {
         case kIndex_8_SkColorType:
-            if (justDx && SkPaint::kNone_FilterLevel == fFilterLevel) {
+            if (justDx && kNone_SkFilterQuality == fFilterLevel) {
 #if 0   /* crashing on android device */
                 fSampleProc16 = SI8_D16_nofilter_DX_arm;
                 fShaderProc16 = NULL;
diff --git a/src/opts/SkBitmapProcState_opts_mips_dsp.cpp b/src/opts/SkBitmapProcState_opts_mips_dsp.cpp
index 6051cd8..049519a 100644
--- a/src/opts/SkBitmapProcState_opts_mips_dsp.cpp
+++ b/src/opts/SkBitmapProcState_opts_mips_dsp.cpp
@@ -17,7 +17,7 @@
                                          int count, uint16_t* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != NULL);
     SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
-    SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
     const uint16_t* SK_RESTRICT table = s.fBitmap->getColorTable()->read16BitCache();
     const uint8_t* SK_RESTRICT srcAddr = (const uint8_t*)s.fBitmap->getPixels();
     SkASSERT((unsigned)xy[0] < (unsigned)s.fBitmap->height());
@@ -149,7 +149,7 @@
                                                 int count, SkPMColor* SK_RESTRICT colors) {
     SkASSERT(count > 0 && colors != NULL);
     SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
-    SkASSERT(SkPaint::kNone_FilterLevel == s.fFilterLevel);
+    SkASSERT(kNone_SkFilterQuality == s.fFilterLevel);
     const SkPMColor* SK_RESTRICT table = s.fBitmap->getColorTable()->readColors();
     const uint8_t* SK_RESTRICT srcAddr = (const uint8_t*)s.fBitmap->getPixels();
     srcAddr = (const uint8_t*)((const char*)srcAddr + xy[0] * s.fBitmap->rowBytes());
@@ -378,7 +378,7 @@
 
     switch (fBitmap->colorType()) {
         case kIndex_8_SkColorType:
-            if (justDx && SkPaint::kNone_FilterLevel == fFilterLevel) {
+            if (justDx && kNone_SkFilterQuality == fFilterLevel) {
                 fSampleProc16 = SI8_D16_nofilter_DX_mips_dsp;
                 fShaderProc16 = NULL;
                 if (isOpaque) {
diff --git a/src/opts/SkBlitRow_opts_SSE2.cpp b/src/opts/SkBlitRow_opts_SSE2.cpp
index e830c5f..7f5b677 100644
--- a/src/opts/SkBlitRow_opts_SSE2.cpp
+++ b/src/opts/SkBlitRow_opts_SSE2.cpp
@@ -232,60 +232,72 @@
     }
 }
 
-/* SSE2 version of Color32()
- * portable version is in core/SkBlitRow_D32.cpp
- */
-void Color32_SSE2(SkPMColor dst[], const SkPMColor src[], int count,
-                  SkPMColor color) {
-    if (count <= 0) {
-        return;
-    }
+void Color32A_D565_SSE2(uint16_t dst[], SkPMColor src, int count, int x, int y) {
+    SkASSERT(count > 0);
 
-    if (0 == color) {
-        if (src != dst) {
-            memcpy(dst, src, count * sizeof(SkPMColor));
-        }
-        return;
-    }
+    uint32_t src_expand = (SkGetPackedG32(src) << 24) |
+                          (SkGetPackedR32(src) << 13) |
+                          (SkGetPackedB32(src) << 2);
+    unsigned scale = SkAlpha255To256(0xFF - SkGetPackedA32(src)) >> 3;
 
-    unsigned colorA = SkGetPackedA32(color);
-    if (255 == colorA) {
-        sk_memset32(dst, color, count);
-    } else {
-        unsigned scale = 256 - SkAlpha255To256(colorA);
+    // Check if we have enough pixels to run SIMD
+    if (count >= (int)(8 + (((16 - (size_t)dst) & 0x0F) >> 1))) {
+        __m128i* dst_wide;
+        const __m128i src_R_wide = _mm_set1_epi16(SkGetPackedR32(src) << 2);
+        const __m128i src_G_wide = _mm_set1_epi16(SkGetPackedG32(src) << 3);
+        const __m128i src_B_wide = _mm_set1_epi16(SkGetPackedB32(src) << 2);
+        const __m128i scale_wide = _mm_set1_epi16(scale);
+        const __m128i mask_blue  = _mm_set1_epi16(SK_B16_MASK);
+        const __m128i mask_green = _mm_set1_epi16(SK_G16_MASK << SK_G16_SHIFT);
 
-        if (count >= 4) {
-            SkASSERT(((size_t)dst & 0x03) == 0);
-            while (((size_t)dst & 0x0F) != 0) {
-                *dst = color + SkAlphaMulQ(*src, scale);
-                src++;
-                dst++;
-                count--;
-            }
-
-            const __m128i *s = reinterpret_cast<const __m128i*>(src);
-            __m128i *d = reinterpret_cast<__m128i*>(dst);
-            __m128i color_wide = _mm_set1_epi32(color);
-            while (count >= 4) {
-                __m128i src_pixel = _mm_loadu_si128(s);
-                src_pixel = SkAlphaMulQ_SSE2(src_pixel, scale);
-
-                __m128i result = _mm_add_epi8(color_wide, src_pixel);
-                _mm_store_si128(d, result);
-                s++;
-                d++;
-                count -= 4;
-            }
-            src = reinterpret_cast<const SkPMColor*>(s);
-            dst = reinterpret_cast<SkPMColor*>(d);
-        }
-
-        while (count > 0) {
-            *dst = color + SkAlphaMulQ(*src, scale);
-            src += 1;
+        // Align dst to an even 16 byte address (0-7 pixels)
+        while (((((size_t)dst) & 0x0F) != 0) && (count > 0)) {
+            *dst = SkBlend32_RGB16(src_expand, *dst, scale);
             dst += 1;
             count--;
         }
+
+        dst_wide = reinterpret_cast<__m128i*>(dst);
+        do {
+            // Load eight RGB565 pixels
+            __m128i pixels = _mm_load_si128(dst_wide);
+
+            // Mask out sub-pixels
+            __m128i pixel_R = _mm_srli_epi16(pixels, SK_R16_SHIFT);
+            __m128i pixel_G = _mm_slli_epi16(pixels, SK_R16_BITS);
+            pixel_G = _mm_srli_epi16(pixel_G, SK_R16_BITS + SK_B16_BITS);
+            __m128i pixel_B = _mm_and_si128(pixels, mask_blue);
+
+            // Scale with alpha
+            pixel_R = _mm_mullo_epi16(pixel_R, scale_wide);
+            pixel_G = _mm_mullo_epi16(pixel_G, scale_wide);
+            pixel_B = _mm_mullo_epi16(pixel_B, scale_wide);
+
+            // Add src_X_wide and shift down again
+            pixel_R = _mm_add_epi16(pixel_R, src_R_wide);
+            pixel_R = _mm_srli_epi16(pixel_R, 5);
+            pixel_G = _mm_add_epi16(pixel_G, src_G_wide);
+            pixel_B = _mm_add_epi16(pixel_B, src_B_wide);
+            pixel_B = _mm_srli_epi16(pixel_B, 5);
+
+            // Combine into RGB565 and store
+            pixel_R = _mm_slli_epi16(pixel_R, SK_R16_SHIFT);
+            pixel_G = _mm_and_si128(pixel_G, mask_green);
+            pixels = _mm_or_si128(pixel_R, pixel_G);
+            pixels = _mm_or_si128(pixels, pixel_B);
+            _mm_store_si128(dst_wide, pixels);
+            count -= 8;
+            dst_wide++;
+        } while (count >= 8);
+
+        dst = reinterpret_cast<uint16_t*>(dst_wide);
+    }
+
+    // Small loop to handle remaining pixels.
+    while (count > 0) {
+        *dst = SkBlend32_RGB16(src_expand, *dst, scale);
+        dst += 1;
+        count--;
     }
 }
 
diff --git a/src/opts/SkBlitRow_opts_SSE2.h b/src/opts/SkBlitRow_opts_SSE2.h
index 29fd96e..6c0611f 100644
--- a/src/opts/SkBlitRow_opts_SSE2.h
+++ b/src/opts/SkBlitRow_opts_SSE2.h
@@ -21,6 +21,10 @@
 void S32A_Blend_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst,
                                const SkPMColor* SK_RESTRICT src,
                                int count, U8CPU alpha);
+
+void Color32A_D565_SSE2(uint16_t dst[], SkPMColor src, int count, int x,
+                        int y);
+
 void SkARGB32_A8_BlitMask_SSE2(void* device, size_t dstRB, const void* mask,
                                size_t maskRB, SkColor color,
                                int width, int height);
@@ -42,5 +46,4 @@
 void S32A_D565_Opaque_Dither_SSE2(uint16_t* SK_RESTRICT dst,
                                   const SkPMColor* SK_RESTRICT src,
                                   int count, U8CPU alpha, int x, int y);
-
 #endif
diff --git a/src/opts/SkBlitRow_opts_SSE4.cpp b/src/opts/SkBlitRow_opts_SSE4.cpp
index f4273d2..3649d17 100644
--- a/src/opts/SkBlitRow_opts_SSE4.cpp
+++ b/src/opts/SkBlitRow_opts_SSE4.cpp
@@ -7,14 +7,9 @@
     sk_throw();
 }
 
-void Color32A_D565_SSE4(uint16_t dst[], SkPMColor src, int count, int x, int y) {
-    sk_throw();
-}
-
 #else
 
-#include <smmintrin.h>  // SSE4.1 intrinsics
-
+#include <smmintrin.h>      // SSE4.1 intrinsics
 #include "SkColorPriv.h"
 #include "SkColor_opts_SSE2.h"
 
@@ -66,76 +61,4 @@
     }
 }
 
-static inline uint16_t Color32A_D565_1x(uint16_t dst, unsigned scale, uint32_t src_expand) {
-    uint32_t dst_expand = SkExpand_rgb_16(dst) * scale;
-    return SkCompact_rgb_16((src_expand + dst_expand) >> 5);
-}
-
-void Color32A_D565_SSE4(uint16_t dst[], SkPMColor src, int count, int x, int y) {
-    SkASSERT(count > 0);
-
-    uint32_t src_expand = (SkGetPackedG32(src) << 24) |
-                          (SkGetPackedR32(src) << 13) |
-                          (SkGetPackedB32(src) << 2);
-    unsigned scale = SkAlpha255To256(0xFF - SkGetPackedA32(src)) >> 3;
-
-    // Check if we have enough pixels to run SIMD
-    if (count >= (int)(8 + (((16 - (size_t)dst) & 0x0F) >> 1))) {
-        __m128i* dst_wide;
-        const __m128i src_expand_wide = _mm_set1_epi32(src_expand);
-        const __m128i scale_wide = _mm_set1_epi32(scale);
-        const __m128i mask_green = _mm_set1_epi32(SK_R16_MASK_IN_PLACE |
-                                                  SK_B16_MASK_IN_PLACE |
-                                                 (SK_G16_MASK_IN_PLACE << 16));
-
-        // Align dst to an even 16 byte address (0-7 pixels)
-        while (((((size_t)dst) & 0x0F) != 0) && (count > 0)) {
-            *dst = Color32A_D565_1x(*dst, scale, src_expand);
-            dst += 1;
-            count--;
-        }
-
-        dst_wide = reinterpret_cast<__m128i*>(dst);
-        do {
-            // Load 8 RGB565 pixels
-            __m128i pixels = _mm_load_si128(dst_wide);
-
-            // Duplicate and mask
-            __m128i pixels_high = _mm_unpackhi_epi16(pixels, pixels);
-            pixels_high = _mm_and_si128(mask_green, pixels_high);
-            pixels = _mm_unpacklo_epi16(pixels, pixels);
-            pixels = _mm_and_si128(mask_green, pixels);
-
-            // Scale with alpha
-            pixels_high = _mm_mullo_epi32(pixels_high, scale_wide);
-            pixels = _mm_mullo_epi32(pixels, scale_wide);
-
-            // Add src_expand_wide and shift down again
-            pixels_high = _mm_add_epi32(pixels_high, src_expand_wide);
-            pixels_high = _mm_srli_epi32(pixels_high, 5);
-            pixels = _mm_add_epi32(pixels, src_expand_wide);
-            pixels = _mm_srli_epi32(pixels, 5);
-
-            // Mask
-            pixels_high = _mm_and_si128(mask_green, pixels_high);
-            pixels = _mm_and_si128(mask_green, pixels);
-
-            // Combine into RGB565 and store
-            pixels = _mm_hadd_epi16(pixels, pixels_high);
-            _mm_store_si128(dst_wide, pixels);
-            count -= 8;
-            dst_wide++;
-        } while (count >= 8);
-
-        dst = reinterpret_cast<uint16_t*>(dst_wide);
-    }
-
-    // Small loop to handle remaining pixels.
-    while (count > 0) {
-        *dst = Color32A_D565_1x(*dst, scale, src_expand);
-        dst += 1;
-        count--;
-    }
-}
-
 #endif
diff --git a/src/opts/SkBlitRow_opts_SSE4.h b/src/opts/SkBlitRow_opts_SSE4.h
index 6a57216..577ace6 100644
--- a/src/opts/SkBlitRow_opts_SSE4.h
+++ b/src/opts/SkBlitRow_opts_SSE4.h
@@ -14,8 +14,5 @@
                                 const SkPMColor* SK_RESTRICT,
                                 int count,
                                 U8CPU alpha);
-
-void Color32A_D565_SSE4(uint16_t dst[], SkPMColor src, int count, int x, int y);
-
 #endif
 
diff --git a/src/opts/SkBlitRow_opts_arm.cpp b/src/opts/SkBlitRow_opts_arm.cpp
index a8ab8ce..9dd4b3b 100644
--- a/src/opts/SkBlitRow_opts_arm.cpp
+++ b/src/opts/SkBlitRow_opts_arm.cpp
@@ -390,9 +390,3 @@
     return SK_ARM_NEON_WRAP(sk_blitrow_platform_32_procs_arm)[flags];
 }
 
-///////////////////////////////////////////////////////////////////////////////
-#define Color32_arm  NULL
-SkBlitRow::ColorProc SkBlitRow::PlatformColorProc() {
-    return SK_ARM_NEON_WRAP(Color32_arm);
-}
-
diff --git a/src/opts/SkBlitRow_opts_arm_neon.cpp b/src/opts/SkBlitRow_opts_arm_neon.cpp
index 4465ff6..4a6514a 100644
--- a/src/opts/SkBlitRow_opts_arm_neon.cpp
+++ b/src/opts/SkBlitRow_opts_arm_neon.cpp
@@ -246,13 +246,19 @@
                       "vaddhn.u16 d6, q14, q8                 \n\t"
                       "vshr.u16   q8, q12, #5                 \n\t"
                       "vaddhn.u16 d5, q13, q9                 \n\t"
-                      "vqadd.u8   d6, d6, d0                  \n\t"  // moved up
                       "vaddhn.u16 d4, q12, q8                 \n\t"
                       // intentionally don't calculate alpha
                       // result in d4-d6
 
+            #ifdef SK_PMCOLOR_IS_RGBA
+                      "vqadd.u8   d6, d6, d0                  \n\t"
                       "vqadd.u8   d5, d5, d1                  \n\t"
                       "vqadd.u8   d4, d4, d2                  \n\t"
+            #else
+                      "vqadd.u8   d6, d6, d2                  \n\t"
+                      "vqadd.u8   d5, d5, d1                  \n\t"
+                      "vqadd.u8   d4, d4, d0                  \n\t"
+            #endif
 
                       // pack 8888 {d4-d6} to 0565 q10
                       "vshll.u8   q10, d6, #8                 \n\t"
@@ -326,13 +332,19 @@
                       "vaddhn.u16 d6, q14, q8                 \n\t"
                       "vshr.u16   q8, q12, #5                 \n\t"
                       "vaddhn.u16 d5, q13, q9                 \n\t"
-                      "vqadd.u8   d6, d6, d0                  \n\t"  // moved up
                       "vaddhn.u16 d4, q12, q8                 \n\t"
                       // intentionally don't calculate alpha
                       // result in d4-d6
 
+            #ifdef SK_PMCOLOR_IS_RGBA
+                      "vqadd.u8   d6, d6, d0                  \n\t"
                       "vqadd.u8   d5, d5, d1                  \n\t"
                       "vqadd.u8   d4, d4, d2                  \n\t"
+            #else
+                      "vqadd.u8   d6, d6, d2                  \n\t"
+                      "vqadd.u8   d5, d5, d1                  \n\t"
+                      "vqadd.u8   d4, d4, d0                  \n\t"
+            #endif
 
                       // pack 8888 {d4-d6} to 0565 q10
                       "vshll.u8   q10, d6, #8                 \n\t"
@@ -1667,107 +1679,6 @@
     }
 }
 
-void Color32_arm_neon(SkPMColor* dst, const SkPMColor* src, int count,
-                      SkPMColor color) {
-    if (count <= 0) {
-        return;
-    }
-
-    if (0 == color) {
-        if (src != dst) {
-            memcpy(dst, src, count * sizeof(SkPMColor));
-        }
-        return;
-    }
-
-    unsigned colorA = SkGetPackedA32(color);
-    if (255 == colorA) {
-        sk_memset32(dst, color, count);
-        return;
-    }
-
-    unsigned scale = 256 - SkAlpha255To256(colorA);
-
-    if (count >= 8) {
-        uint32x4_t vcolor;
-        uint8x8_t vscale;
-
-        vcolor = vdupq_n_u32(color);
-
-        // scale numerical interval [0-255], so load as 8 bits
-        vscale = vdup_n_u8(scale);
-
-        do {
-            // load src color, 8 pixels, 4 64 bit registers
-            // (and increment src).
-            uint32x2x4_t vsrc;
-#if defined(SK_CPU_ARM32) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6)))
-            asm (
-                "vld1.32    %h[vsrc], [%[src]]!"
-                : [vsrc] "=w" (vsrc), [src] "+r" (src)
-                : :
-            );
-#else // 64bit targets and Clang
-            vsrc.val[0] = vld1_u32(src);
-            vsrc.val[1] = vld1_u32(src+2);
-            vsrc.val[2] = vld1_u32(src+4);
-            vsrc.val[3] = vld1_u32(src+6);
-            src += 8;
-#endif
-
-            // multiply long by scale, 64 bits at a time,
-            // destination into a 128 bit register.
-            uint16x8x4_t vtmp;
-            vtmp.val[0] = vmull_u8(vreinterpret_u8_u32(vsrc.val[0]), vscale);
-            vtmp.val[1] = vmull_u8(vreinterpret_u8_u32(vsrc.val[1]), vscale);
-            vtmp.val[2] = vmull_u8(vreinterpret_u8_u32(vsrc.val[2]), vscale);
-            vtmp.val[3] = vmull_u8(vreinterpret_u8_u32(vsrc.val[3]), vscale);
-
-            // shift the 128 bit registers, containing the 16
-            // bit scaled values back to 8 bits, narrowing the
-            // results to 64 bit registers.
-            uint8x16x2_t vres;
-            vres.val[0] = vcombine_u8(
-                            vshrn_n_u16(vtmp.val[0], 8),
-                            vshrn_n_u16(vtmp.val[1], 8));
-            vres.val[1] = vcombine_u8(
-                            vshrn_n_u16(vtmp.val[2], 8),
-                            vshrn_n_u16(vtmp.val[3], 8));
-
-            // adding back the color, using 128 bit registers.
-            uint32x4x2_t vdst;
-            vdst.val[0] = vreinterpretq_u32_u8(vres.val[0] +
-                                               vreinterpretq_u8_u32(vcolor));
-            vdst.val[1] = vreinterpretq_u32_u8(vres.val[1] +
-                                               vreinterpretq_u8_u32(vcolor));
-
-            // store back the 8 calculated pixels (2 128 bit
-            // registers), and increment dst.
-#if defined(SK_CPU_ARM32) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6)))
-            asm (
-                "vst1.32    %h[vdst], [%[dst]]!"
-                : [dst] "+r" (dst)
-                : [vdst] "w" (vdst)
-                : "memory"
-            );
-#else // 64bit targets and Clang
-            vst1q_u32(dst, vdst.val[0]);
-            vst1q_u32(dst+4, vdst.val[1]);
-            dst += 8;
-#endif
-            count -= 8;
-
-        } while (count >= 8);
-    }
-
-    while (count > 0) {
-        *dst = color + SkAlphaMulQ(*src, scale);
-        src += 1;
-        dst += 1;
-        count--;
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 const SkBlitRow::Proc16 sk_blitrow_platform_565_procs_arm_neon[] = {
diff --git a/src/opts/SkBlitRow_opts_arm_neon.h b/src/opts/SkBlitRow_opts_arm_neon.h
index 92d58ce..159a466 100644
--- a/src/opts/SkBlitRow_opts_arm_neon.h
+++ b/src/opts/SkBlitRow_opts_arm_neon.h
@@ -13,7 +13,4 @@
 extern const SkBlitRow::ColorProc16 sk_blitrow_platform_565_colorprocs_arm_neon[];
 extern const SkBlitRow::Proc32 sk_blitrow_platform_32_procs_arm_neon[];
 
-extern void Color32_arm_neon(SkPMColor* dst, const SkPMColor* src, int count,
-                             SkPMColor color);
-
 #endif
diff --git a/src/opts/SkBlitRow_opts_mips_dsp.cpp b/src/opts/SkBlitRow_opts_mips_dsp.cpp
index 528eb07..7ef75c4 100644
--- a/src/opts/SkBlitRow_opts_mips_dsp.cpp
+++ b/src/opts/SkBlitRow_opts_mips_dsp.cpp
@@ -953,7 +953,3 @@
 SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) {
     return platform_32_procs_mips_dsp[flags];
 }
-
-SkBlitRow::ColorProc SkBlitRow::PlatformColorProc() {
-    return NULL;
-}
diff --git a/src/opts/SkBlitRow_opts_none.cpp b/src/opts/SkBlitRow_opts_none.cpp
index 87ceb69..10b3b43 100644
--- a/src/opts/SkBlitRow_opts_none.cpp
+++ b/src/opts/SkBlitRow_opts_none.cpp
@@ -20,8 +20,3 @@
 SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) {
     return NULL;
 }
-
-SkBlitRow::ColorProc SkBlitRow::PlatformColorProc() {
-    return NULL;
-}
-
diff --git a/src/opts/SkBlurImage_opts.h b/src/opts/SkBlurImage_opts.h
index d4006f7..68352c5 100644
--- a/src/opts/SkBlurImage_opts.h
+++ b/src/opts/SkBlurImage_opts.h
@@ -14,7 +14,6 @@
                               int leftOffset, int rightOffset, int width, int height);
 
 bool SkBoxBlurGetPlatformProcs(SkBoxBlurProc* boxBlurX,
-                               SkBoxBlurProc* boxBlurY,
                                SkBoxBlurProc* boxBlurXY,
                                SkBoxBlurProc* boxBlurYX);
 #endif
diff --git a/src/opts/SkBlurImage_opts_SSE2.cpp b/src/opts/SkBlurImage_opts_SSE2.cpp
index d2f8882..2ade91d 100644
--- a/src/opts/SkBlurImage_opts_SSE2.cpp
+++ b/src/opts/SkBlurImage_opts_SSE2.cpp
@@ -97,11 +97,9 @@
 } // namespace
 
 bool SkBoxBlurGetPlatformProcs_SSE2(SkBoxBlurProc* boxBlurX,
-                                    SkBoxBlurProc* boxBlurY,
                                     SkBoxBlurProc* boxBlurXY,
                                     SkBoxBlurProc* boxBlurYX) {
     *boxBlurX = SkBoxBlur_SSE2<kX, kX>;
-    *boxBlurY = SkBoxBlur_SSE2<kY, kY>;
     *boxBlurXY = SkBoxBlur_SSE2<kX, kY>;
     *boxBlurYX = SkBoxBlur_SSE2<kY, kX>;
     return true;
diff --git a/src/opts/SkBlurImage_opts_SSE2.h b/src/opts/SkBlurImage_opts_SSE2.h
index db104ba..29cfb43 100644
--- a/src/opts/SkBlurImage_opts_SSE2.h
+++ b/src/opts/SkBlurImage_opts_SSE2.h
@@ -11,7 +11,6 @@
 #include "SkBlurImage_opts.h"
 
 bool SkBoxBlurGetPlatformProcs_SSE2(SkBoxBlurProc* boxBlurX,
-                                    SkBoxBlurProc* boxBlurY,
                                     SkBoxBlurProc* boxBlurXY,
                                     SkBoxBlurProc* boxBlurYX);
 
diff --git a/src/opts/SkBlurImage_opts_SSE4.cpp b/src/opts/SkBlurImage_opts_SSE4.cpp
index 81748af..34da940 100644
--- a/src/opts/SkBlurImage_opts_SSE4.cpp
+++ b/src/opts/SkBlurImage_opts_SSE4.cpp
@@ -27,16 +27,11 @@
  * lower 8 bits of each 32-bit element of an SSE register.
  */
 inline __m128i expand(int a) {
-    const __m128i zero = _mm_setzero_si128();
-
-    // 0 0 0 0   0 0 0 0   0 0 0 0   A R G B
-    __m128i result = _mm_cvtsi32_si128(a);
-
-    // 0 0 0 0   0 0 0 0   0 A 0 R   0 G 0 B
-    result = _mm_unpacklo_epi8(result, zero);
-
-    // 0 0 0 A   0 0 0 R   0 0 0 G   0 0 0 B
-    return _mm_unpacklo_epi16(result, zero);
+    // ARGB -> 0000 0000 0000 ARGB
+    __m128i widened = _mm_cvtsi32_si128(a);
+    // SSE4.1 has xxxx xxxx xxxx ARGB -> 000A 000R 000G 000B as a one-stop-shop instruction.
+    // It can even work from memory, so a smart compiler probably merges in the _mm_cvtsi32_si128().
+    return _mm_cvtepu8_epi32(widened);
 }
 
 template<BlurDirection srcDirection, BlurDirection dstDirection>
@@ -50,9 +45,8 @@
     const int dstStrideY = dstDirection == kX ? width : 1;
     const __m128i scale = _mm_set1_epi32((1 << 24) / kernelSize);
     const __m128i half = _mm_set1_epi32(1 << 23);
-    const __m128i zero = _mm_setzero_si128();
     for (int y = 0; y < height; ++y) {
-        __m128i sum = zero;
+        __m128i sum = _mm_setzero_si128();
         const SkPMColor* p = src;
         for (int i = 0; i < rightBorder; ++i) {
             sum = _mm_add_epi32(sum, expand(*p));
@@ -62,20 +56,20 @@
         const SkPMColor* sptr = src;
         SkColor* dptr = dst;
         for (int x = 0; x < width; ++x) {
-            __m128i result = _mm_mullo_epi32(sum, scale);
+            // TODO(mtklein): We are working in 8.24 here. Drop to 8.8 when the kernel is narrow?
 
-            // sumA*scale+.5 sumB*scale+.5 sumG*scale+.5 sumB*scale+.5
+            // Multiply each component by scale (i.e. divide by kernel size) and add half to round.
+            __m128i result = _mm_mullo_epi32(sum, scale);
             result = _mm_add_epi32(result, half);
 
-            // 0 0 0 A   0 0 0 R   0 0 0 G   0 0 0 B
-            result = _mm_srli_epi32(result, 24);
+            // Now pack the top byte of each 32-bit lane back down into one 32-bit color.
+            // Axxx Rxxx Gxxx Bxxx -> xxxx xxxx xxxx ARGB
+            const char _ = 0;  // Don't care what ends up in these bytes.  Happens to be byte 0.
+            result = _mm_shuffle_epi8(result, _mm_set_epi8(_,_,_,_, _,_,_,_, _,_,_,_, 15,11,7,3));
 
-            // 0 0 0 0   0 0 0 0   0 A 0 R   0 G 0 B
-            result = _mm_packs_epi32(result, zero);
-
-            // 0 0 0 0   0 0 0 0   0 0 0 0   A R G B
-            result = _mm_packus_epi16(result, zero);
             *dptr = _mm_cvtsi128_si32(result);
+
+            // TODO(mtklein): experiment with breaking this loop into 3 parts
             if (x >= leftOffset) {
                 SkColor l = *(sptr - leftOffset * srcStrideX);
                 sum = _mm_sub_epi32(sum, expand(l));
@@ -86,6 +80,7 @@
             }
             sptr += srcStrideX;
             if (srcDirection == kY) {
+                // TODO(mtklein): experiment with moving this prefetch forward
                 _mm_prefetch(reinterpret_cast<const char*>(sptr + (rightOffset + 1) * srcStrideX),
                              _MM_HINT_T0);
             }
@@ -99,11 +94,9 @@
 } // namespace
 
 bool SkBoxBlurGetPlatformProcs_SSE4(SkBoxBlurProc* boxBlurX,
-                                    SkBoxBlurProc* boxBlurY,
                                     SkBoxBlurProc* boxBlurXY,
                                     SkBoxBlurProc* boxBlurYX) {
     *boxBlurX = SkBoxBlur_SSE4<kX, kX>;
-    *boxBlurY = SkBoxBlur_SSE4<kY, kY>;
     *boxBlurXY = SkBoxBlur_SSE4<kX, kY>;
     *boxBlurYX = SkBoxBlur_SSE4<kY, kX>;
     return true;
@@ -112,7 +105,6 @@
 #else // SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE41
 
 bool SkBoxBlurGetPlatformProcs_SSE4(SkBoxBlurProc* boxBlurX,
-                                    SkBoxBlurProc* boxBlurY,
                                     SkBoxBlurProc* boxBlurXY,
                                     SkBoxBlurProc* boxBlurYX) {
     sk_throw();
diff --git a/src/opts/SkBlurImage_opts_SSE4.h b/src/opts/SkBlurImage_opts_SSE4.h
index 9f346a9..3c08778 100644
--- a/src/opts/SkBlurImage_opts_SSE4.h
+++ b/src/opts/SkBlurImage_opts_SSE4.h
@@ -11,7 +11,6 @@
 #include "SkBlurImage_opts.h"
 
 bool SkBoxBlurGetPlatformProcs_SSE4(SkBoxBlurProc* boxBlurX,
-                                    SkBoxBlurProc* boxBlurY,
                                     SkBoxBlurProc* boxBlurXY,
                                     SkBoxBlurProc* boxBlurYX);
 
diff --git a/src/opts/SkBlurImage_opts_arm.cpp b/src/opts/SkBlurImage_opts_arm.cpp
index 10d595a..c03e1aa 100644
--- a/src/opts/SkBlurImage_opts_arm.cpp
+++ b/src/opts/SkBlurImage_opts_arm.cpp
@@ -9,7 +9,6 @@
 #include "SkUtilsArm.h"
 
 bool SkBoxBlurGetPlatformProcs(SkBoxBlurProc* boxBlurX,
-                               SkBoxBlurProc* boxBlurY,
                                SkBoxBlurProc* boxBlurXY,
                                SkBoxBlurProc* boxBlurYX) {
 #if SK_ARM_NEON_IS_NONE
@@ -20,6 +19,6 @@
         return false;
     }
 #endif
-    return SkBoxBlurGetPlatformProcs_NEON(boxBlurX, boxBlurY, boxBlurXY, boxBlurYX);
+    return SkBoxBlurGetPlatformProcs_NEON(boxBlurX, boxBlurXY, boxBlurYX);
 #endif
 }
diff --git a/src/opts/SkBlurImage_opts_neon.cpp b/src/opts/SkBlurImage_opts_neon.cpp
index 760980e..1e60e51 100644
--- a/src/opts/SkBlurImage_opts_neon.cpp
+++ b/src/opts/SkBlurImage_opts_neon.cpp
@@ -177,11 +177,9 @@
 } // namespace
 
 bool SkBoxBlurGetPlatformProcs_NEON(SkBoxBlurProc* boxBlurX,
-                                    SkBoxBlurProc* boxBlurY,
                                     SkBoxBlurProc* boxBlurXY,
                                     SkBoxBlurProc* boxBlurYX) {
     *boxBlurX = SkBoxBlur_NEON<kX, kX>;
-    *boxBlurY = SkBoxBlur_NEON<kY, kY>;
     *boxBlurXY = SkBoxBlur_NEON<kX, kY>;
     *boxBlurYX = SkBoxBlur_NEON<kY, kX>;
     return true;
diff --git a/src/opts/SkBlurImage_opts_neon.h b/src/opts/SkBlurImage_opts_neon.h
index 7fbe963..d5a2b65 100644
--- a/src/opts/SkBlurImage_opts_neon.h
+++ b/src/opts/SkBlurImage_opts_neon.h
@@ -8,6 +8,5 @@
 #include "SkBlurImage_opts.h"
 
 bool SkBoxBlurGetPlatformProcs_NEON(SkBoxBlurProc* boxBlurX,
-                                    SkBoxBlurProc* boxBlurY,
                                     SkBoxBlurProc* boxBlurXY,
                                     SkBoxBlurProc* boxBlurYX);
diff --git a/src/opts/SkBlurImage_opts_none.cpp b/src/opts/SkBlurImage_opts_none.cpp
index 6de6419..7c730ca 100644
--- a/src/opts/SkBlurImage_opts_none.cpp
+++ b/src/opts/SkBlurImage_opts_none.cpp
@@ -8,7 +8,6 @@
 #include "SkBlurImage_opts.h"
 
 bool SkBoxBlurGetPlatformProcs(SkBoxBlurProc* boxBlurX,
-                               SkBoxBlurProc* boxBlurY,
                                SkBoxBlurProc* boxBlurXY,
                                SkBoxBlurProc* boxBlurYX) {
     return false;
diff --git a/src/opts/SkNx_neon.h b/src/opts/SkNx_neon.h
new file mode 100644
index 0000000..08691fe
--- /dev/null
+++ b/src/opts/SkNx_neon.h
@@ -0,0 +1,383 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkNx_neon_DEFINED
+#define SkNx_neon_DEFINED
+
+#include <arm_neon.h>
+
+// Well, this is absurd.  The shifts require compile-time constant arguments.
+
+#define SHIFT8(op, v, bits) switch(bits) { \
+    case  1: return op(v,  1);  case  2: return op(v,  2);  case  3: return op(v,  3); \
+    case  4: return op(v,  4);  case  5: return op(v,  5);  case  6: return op(v,  6); \
+    case  7: return op(v,  7); \
+    } return fVec
+
+#define SHIFT16(op, v, bits) if (bits < 8) { SHIFT8(op, v, bits); } switch(bits) { \
+                                case  8: return op(v,  8);  case  9: return op(v,  9); \
+    case 10: return op(v, 10);  case 11: return op(v, 11);  case 12: return op(v, 12); \
+    case 13: return op(v, 13);  case 14: return op(v, 14);  case 15: return op(v, 15); \
+    } return fVec
+
+#define SHIFT32(op, v, bits) if (bits < 16) { SHIFT16(op, v, bits); } switch(bits) { \
+    case 16: return op(v, 16);  case 17: return op(v, 17);  case 18: return op(v, 18); \
+    case 19: return op(v, 19);  case 20: return op(v, 20);  case 21: return op(v, 21); \
+    case 22: return op(v, 22);  case 23: return op(v, 23);  case 24: return op(v, 24); \
+    case 25: return op(v, 25);  case 26: return op(v, 26);  case 27: return op(v, 27); \
+    case 28: return op(v, 28);  case 29: return op(v, 29);  case 30: return op(v, 30); \
+    case 31: return op(v, 31); } return fVec
+
+template <>
+class SkNb<2, 4> {
+public:
+    SkNb(uint32x2_t vec) : fVec(vec) {}
+
+    SkNb() {}
+    bool allTrue() const { return vget_lane_u32(fVec, 0) && vget_lane_u32(fVec, 1); }
+    bool anyTrue() const { return vget_lane_u32(fVec, 0) || vget_lane_u32(fVec, 1); }
+
+    uint32x2_t fVec;
+};
+
+template <>
+class SkNb<4, 4> {
+public:
+    SkNb(uint32x4_t vec) : fVec(vec) {}
+
+    SkNb() {}
+    bool allTrue() const { return vgetq_lane_u32(fVec, 0) && vgetq_lane_u32(fVec, 1)
+                               && vgetq_lane_u32(fVec, 2) && vgetq_lane_u32(fVec, 3); }
+    bool anyTrue() const { return vgetq_lane_u32(fVec, 0) || vgetq_lane_u32(fVec, 1)
+                               || vgetq_lane_u32(fVec, 2) || vgetq_lane_u32(fVec, 3); }
+
+    uint32x4_t fVec;
+};
+
+template <>
+class SkNf<2, float> {
+    typedef SkNb<2, 4> Nb;
+public:
+    SkNf(float32x2_t vec) : fVec(vec) {}
+
+    SkNf() {}
+    explicit SkNf(float val)           : fVec(vdup_n_f32(val)) {}
+    static SkNf Load(const float vals[2]) { return vld1_f32(vals); }
+    SkNf(float a, float b) { fVec = (float32x2_t) { a, b }; }
+
+    void store(float vals[2]) const { vst1_f32(vals, fVec); }
+
+    SkNf approxInvert() const {
+        float32x2_t est0 = vrecpe_f32(fVec),
+                    est1 = vmul_f32(vrecps_f32(est0, fVec), est0);
+        return est1;
+    }
+    SkNf invert() const {
+        float32x2_t est1 = this->approxInvert().fVec,
+                    est2 = vmul_f32(vrecps_f32(est1, fVec), est1);
+        return est2;
+    }
+
+    SkNf operator + (const SkNf& o) const { return vadd_f32(fVec, o.fVec); }
+    SkNf operator - (const SkNf& o) const { return vsub_f32(fVec, o.fVec); }
+    SkNf operator * (const SkNf& o) const { return vmul_f32(fVec, o.fVec); }
+    SkNf operator / (const SkNf& o) const {
+    #if defined(SK_CPU_ARM64)
+        return vdiv_f32(fVec, o.fVec);
+    #else
+        return vmul_f32(fVec, o.invert().fVec);
+    #endif
+    }
+
+    Nb operator == (const SkNf& o) const { return vceq_f32(fVec, o.fVec); }
+    Nb operator  < (const SkNf& o) const { return vclt_f32(fVec, o.fVec); }
+    Nb operator  > (const SkNf& o) const { return vcgt_f32(fVec, o.fVec); }
+    Nb operator <= (const SkNf& o) const { return vcle_f32(fVec, o.fVec); }
+    Nb operator >= (const SkNf& o) const { return vcge_f32(fVec, o.fVec); }
+    Nb operator != (const SkNf& o) const { return vmvn_u32(vceq_f32(fVec, o.fVec)); }
+
+    static SkNf Min(const SkNf& l, const SkNf& r) { return vmin_f32(l.fVec, r.fVec); }
+    static SkNf Max(const SkNf& l, const SkNf& r) { return vmax_f32(l.fVec, r.fVec); }
+
+    SkNf rsqrt0() const { return vrsqrte_f32(fVec); }
+    SkNf rsqrt1() const {
+        float32x2_t est0 = this->rsqrt0().fVec;
+        return vmul_f32(vrsqrts_f32(fVec, vmul_f32(est0, est0)), est0);
+    }
+    SkNf rsqrt2() const {
+        float32x2_t est1 = this->rsqrt1().fVec;
+        return vmul_f32(vrsqrts_f32(fVec, vmul_f32(est1, est1)), est1);
+    }
+
+    SkNf sqrt() const {
+    #if defined(SK_CPU_ARM64)
+        return vsqrt_f32(fVec);
+    #else
+        return *this * this->rsqrt2();
+    #endif
+    }
+
+    template <int k> float kth() const {
+        SkASSERT(0 <= k && k < 2);
+        return vget_lane_f32(fVec, k&1);
+    }
+
+    float32x2_t fVec;
+};
+
+#if defined(SK_CPU_ARM64)
+template <>
+class SkNb<2, 8> {
+public:
+    SkNb(uint64x2_t vec) : fVec(vec) {}
+
+    SkNb() {}
+    bool allTrue() const { return vgetq_lane_u64(fVec, 0) && vgetq_lane_u64(fVec, 1); }
+    bool anyTrue() const { return vgetq_lane_u64(fVec, 0) || vgetq_lane_u64(fVec, 1); }
+
+    uint64x2_t fVec;
+};
+
+template <>
+class SkNf<2, double> {
+    typedef SkNb<2, 8> Nb;
+public:
+    SkNf(float64x2_t vec) : fVec(vec) {}
+
+    SkNf() {}
+    explicit SkNf(double val)           : fVec(vdupq_n_f64(val))  {}
+    static SkNf Load(const double vals[2]) { return vld1q_f64(vals); }
+    SkNf(double a, double b) { fVec = (float64x2_t) { a, b }; }
+
+    void store(double vals[2]) const { vst1q_f64(vals, fVec); }
+
+    SkNf operator + (const SkNf& o) const { return vaddq_f64(fVec, o.fVec); }
+    SkNf operator - (const SkNf& o) const { return vsubq_f64(fVec, o.fVec); }
+    SkNf operator * (const SkNf& o) const { return vmulq_f64(fVec, o.fVec); }
+    SkNf operator / (const SkNf& o) const { return vdivq_f64(fVec, o.fVec); }
+
+    Nb operator == (const SkNf& o) const { return vceqq_f64(fVec, o.fVec); }
+    Nb operator  < (const SkNf& o) const { return vcltq_f64(fVec, o.fVec); }
+    Nb operator  > (const SkNf& o) const { return vcgtq_f64(fVec, o.fVec); }
+    Nb operator <= (const SkNf& o) const { return vcleq_f64(fVec, o.fVec); }
+    Nb operator >= (const SkNf& o) const { return vcgeq_f64(fVec, o.fVec); }
+    Nb operator != (const SkNf& o) const {
+        return vreinterpretq_u64_u32(vmvnq_u32(vreinterpretq_u32_u64(vceqq_f64(fVec, o.fVec))));
+    }
+
+    static SkNf Min(const SkNf& l, const SkNf& r) { return vminq_f64(l.fVec, r.fVec); }
+    static SkNf Max(const SkNf& l, const SkNf& r) { return vmaxq_f64(l.fVec, r.fVec); }
+
+    SkNf  sqrt() const { return vsqrtq_f64(fVec);  }
+
+    SkNf rsqrt0() const { return vrsqrteq_f64(fVec); }
+    SkNf rsqrt1() const {
+        float64x2_t est0 = this->rsqrt0().fVec;
+        return vmulq_f64(vrsqrtsq_f64(fVec, vmulq_f64(est0, est0)), est0);
+    }
+    SkNf rsqrt2() const {
+        float64x2_t est1 = this->rsqrt1().fVec;
+        return vmulq_f64(vrsqrtsq_f64(fVec, vmulq_f64(est1, est1)), est1);
+    }
+
+    SkNf approxInvert() const {
+        float64x2_t est0 = vrecpeq_f64(fVec),
+                    est1 = vmulq_f64(vrecpsq_f64(est0, fVec), est0);
+        return est1;
+    }
+
+    SkNf invert() const {
+        float64x2_t est1 = this->approxInvert().fVec,
+                    est2 = vmulq_f64(vrecpsq_f64(est1, fVec), est1),
+                    est3 = vmulq_f64(vrecpsq_f64(est2, fVec), est2);
+        return est3;
+    }
+
+    template <int k> double kth() const {
+        SkASSERT(0 <= k && k < 2);
+        return vgetq_lane_f64(fVec, k&1);
+    }
+
+    float64x2_t fVec;
+};
+#endif//defined(SK_CPU_ARM64)
+
+template <>
+class SkNi<4, int> {
+public:
+    SkNi(const int32x4_t& vec) : fVec(vec) {}
+
+    SkNi() {}
+    explicit SkNi(int val) : fVec(vdupq_n_s32(val)) {}
+    static SkNi Load(const int vals[4]) { return vld1q_s32(vals); }
+    SkNi(int a, int b, int c, int d) { fVec = (int32x4_t) { a, b, c, d }; }
+
+    void store(int vals[4]) const { vst1q_s32(vals, fVec); }
+
+    SkNi operator + (const SkNi& o) const { return vaddq_s32(fVec, o.fVec); }
+    SkNi operator - (const SkNi& o) const { return vsubq_s32(fVec, o.fVec); }
+    SkNi operator * (const SkNi& o) const { return vmulq_s32(fVec, o.fVec); }
+
+    SkNi operator << (int bits) const { SHIFT32(vshlq_n_s32, fVec, bits); }
+    SkNi operator >> (int bits) const { SHIFT32(vshrq_n_s32, fVec, bits); }
+
+    template <int k> int kth() const {
+        SkASSERT(0 <= k && k < 4);
+        return vgetq_lane_s32(fVec, k&3);
+    }
+
+    int32x4_t fVec;
+};
+
+template <>
+class SkNf<4, float> {
+    typedef SkNb<4, 4> Nb;
+public:
+    SkNf(float32x4_t vec) : fVec(vec) {}
+
+    SkNf() {}
+    explicit SkNf(float val)           : fVec(vdupq_n_f32(val)) {}
+    static SkNf Load(const float vals[4]) { return vld1q_f32(vals); }
+    SkNf(float a, float b, float c, float d) { fVec = (float32x4_t) { a, b, c, d }; }
+
+    void store(float vals[4]) const { vst1q_f32(vals, fVec); }
+
+    SkNi<4, int> castTrunc() const { return vcvtq_s32_f32(fVec); }
+
+    SkNf approxInvert() const {
+        float32x4_t est0 = vrecpeq_f32(fVec),
+                    est1 = vmulq_f32(vrecpsq_f32(est0, fVec), est0);
+        return est1;
+    }
+    SkNf invert() const {
+        float32x4_t est1 = this->approxInvert().fVec,
+                    est2 = vmulq_f32(vrecpsq_f32(est1, fVec), est1);
+        return est2;
+    }
+
+    SkNf operator + (const SkNf& o) const { return vaddq_f32(fVec, o.fVec); }
+    SkNf operator - (const SkNf& o) const { return vsubq_f32(fVec, o.fVec); }
+    SkNf operator * (const SkNf& o) const { return vmulq_f32(fVec, o.fVec); }
+    SkNf operator / (const SkNf& o) const {
+    #if defined(SK_CPU_ARM64)
+        return vdivq_f32(fVec, o.fVec);
+    #else
+        return vmulq_f32(fVec, o.invert().fVec);
+    #endif
+    }
+
+    Nb operator == (const SkNf& o) const { return vceqq_f32(fVec, o.fVec); }
+    Nb operator  < (const SkNf& o) const { return vcltq_f32(fVec, o.fVec); }
+    Nb operator  > (const SkNf& o) const { return vcgtq_f32(fVec, o.fVec); }
+    Nb operator <= (const SkNf& o) const { return vcleq_f32(fVec, o.fVec); }
+    Nb operator >= (const SkNf& o) const { return vcgeq_f32(fVec, o.fVec); }
+    Nb operator != (const SkNf& o) const { return vmvnq_u32(vceqq_f32(fVec, o.fVec)); }
+
+    static SkNf Min(const SkNf& l, const SkNf& r) { return vminq_f32(l.fVec, r.fVec); }
+    static SkNf Max(const SkNf& l, const SkNf& r) { return vmaxq_f32(l.fVec, r.fVec); }
+
+    SkNf rsqrt0() const { return vrsqrteq_f32(fVec); }
+    SkNf rsqrt1() const {
+        float32x4_t est0 = this->rsqrt0().fVec;
+        return vmulq_f32(vrsqrtsq_f32(fVec, vmulq_f32(est0, est0)), est0);
+    }
+    SkNf rsqrt2() const {
+        float32x4_t est1 = this->rsqrt1().fVec;
+        return vmulq_f32(vrsqrtsq_f32(fVec, vmulq_f32(est1, est1)), est1);
+    }
+
+    SkNf sqrt() const {
+    #if defined(SK_CPU_ARM64)
+        return vsqrtq_f32(fVec);
+    #else
+        return *this * this->rsqrt2();
+    #endif
+    }
+
+    template <int k> float kth() const {
+        SkASSERT(0 <= k && k < 4);
+        return vgetq_lane_f32(fVec, k&3);
+    }
+
+    float32x4_t fVec;
+};
+
+template <>
+class SkNi<8, uint16_t> {
+public:
+    SkNi(const uint16x8_t& vec) : fVec(vec) {}
+
+    SkNi() {}
+    explicit SkNi(uint16_t val) : fVec(vdupq_n_u16(val)) {}
+    static SkNi Load(const uint16_t vals[8]) { return vld1q_u16(vals); }
+
+    SkNi(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
+         uint16_t e, uint16_t f, uint16_t g, uint16_t h) {
+        fVec = (uint16x8_t) { a,b,c,d, e,f,g,h };
+    }
+
+    void store(uint16_t vals[8]) const { vst1q_u16(vals, fVec); }
+
+    SkNi operator + (const SkNi& o) const { return vaddq_u16(fVec, o.fVec); }
+    SkNi operator - (const SkNi& o) const { return vsubq_u16(fVec, o.fVec); }
+    SkNi operator * (const SkNi& o) const { return vmulq_u16(fVec, o.fVec); }
+
+    SkNi operator << (int bits) const { SHIFT16(vshlq_n_u16, fVec, bits); }
+    SkNi operator >> (int bits) const { SHIFT16(vshrq_n_u16, fVec, bits); }
+
+    static SkNi Min(const SkNi& a, const SkNi& b) { return vminq_u16(a.fVec, b.fVec); }
+
+    template <int k> uint16_t kth() const {
+        SkASSERT(0 <= k && k < 8);
+        return vgetq_lane_u16(fVec, k&7);
+    }
+
+    uint16x8_t fVec;
+};
+
+template <>
+class SkNi<16, uint8_t> {
+public:
+    SkNi(const uint8x16_t& vec) : fVec(vec) {}
+
+    SkNi() {}
+    explicit SkNi(uint8_t val) : fVec(vdupq_n_u8(val)) {}
+    static SkNi Load(const uint8_t vals[16]) { return vld1q_u8(vals); }
+
+    SkNi(uint8_t a, uint8_t b, uint8_t c, uint8_t d,
+         uint8_t e, uint8_t f, uint8_t g, uint8_t h,
+         uint8_t i, uint8_t j, uint8_t k, uint8_t l,
+         uint8_t m, uint8_t n, uint8_t o, uint8_t p) {
+        fVec = (uint8x16_t) { a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p };
+    }
+
+    void store(uint8_t vals[16]) const { vst1q_u8(vals, fVec); }
+
+    SkNi saturatedAdd(const SkNi& o) const { return vqaddq_u8(fVec, o.fVec); }
+
+    SkNi operator + (const SkNi& o) const { return vaddq_u8(fVec, o.fVec); }
+    SkNi operator - (const SkNi& o) const { return vsubq_u8(fVec, o.fVec); }
+    SkNi operator * (const SkNi& o) const { return vmulq_u8(fVec, o.fVec); }
+
+    SkNi operator << (int bits) const { SHIFT8(vshlq_n_u8, fVec, bits); }
+    SkNi operator >> (int bits) const { SHIFT8(vshrq_n_u8, fVec, bits); }
+
+    static SkNi Min(const SkNi& a, const SkNi& b) { return vminq_u8(a.fVec, b.fVec); }
+
+    template <int k> uint8_t kth() const {
+        SkASSERT(0 <= k && k < 15);
+        return vgetq_lane_u8(fVec, k&16);
+    }
+
+    uint8x16_t fVec;
+};
+
+#undef SHIFT32
+#undef SHIFT16
+#undef SHIFT8
+
+#endif//SkNx_neon_DEFINED
diff --git a/src/opts/SkNx_sse.h b/src/opts/SkNx_sse.h
new file mode 100644
index 0000000..0e9494c
--- /dev/null
+++ b/src/opts/SkNx_sse.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkNx_sse_DEFINED
+#define SkNx_sse_DEFINED
+
+// This file may assume <= SSE2, but must check SK_CPU_SSE_LEVEL for anything more recent.
+#include <immintrin.h>
+
+template <>
+class SkNb<2, 4> {
+public:
+    SkNb(const __m128i& vec) : fVec(vec) {}
+
+    SkNb() {}
+    bool allTrue() const { return 0xff == (_mm_movemask_epi8(fVec) & 0xff); }
+    bool anyTrue() const { return 0x00 != (_mm_movemask_epi8(fVec) & 0xff); }
+
+    __m128i fVec;
+};
+
+template <>
+class SkNb<4, 4> {
+public:
+    SkNb(const __m128i& vec) : fVec(vec) {}
+
+    SkNb() {}
+    bool allTrue() const { return 0xffff == _mm_movemask_epi8(fVec); }
+    bool anyTrue() const { return 0x0000 != _mm_movemask_epi8(fVec); }
+
+    __m128i fVec;
+};
+
+template <>
+class SkNb<2, 8> {
+public:
+    SkNb(const __m128i& vec) : fVec(vec) {}
+
+    SkNb() {}
+    bool allTrue() const { return 0xffff == _mm_movemask_epi8(fVec); }
+    bool anyTrue() const { return 0x0000 != _mm_movemask_epi8(fVec); }
+
+    __m128i fVec;
+};
+
+
+template <>
+class SkNf<2, float> {
+    typedef SkNb<2, 4> Nb;
+public:
+    SkNf(const __m128& vec) : fVec(vec) {}
+
+    SkNf() {}
+    explicit SkNf(float val) : fVec(_mm_set1_ps(val)) {}
+    static SkNf Load(const float vals[2]) {
+        return _mm_castsi128_ps(_mm_loadl_epi64((const __m128i*)vals));
+    }
+    SkNf(float a, float b) : fVec(_mm_setr_ps(a,b,0,0)) {}
+
+    void store(float vals[2]) const { _mm_storel_pi((__m64*)vals, fVec); }
+
+    SkNf operator + (const SkNf& o) const { return _mm_add_ps(fVec, o.fVec); }
+    SkNf operator - (const SkNf& o) const { return _mm_sub_ps(fVec, o.fVec); }
+    SkNf operator * (const SkNf& o) const { return _mm_mul_ps(fVec, o.fVec); }
+    SkNf operator / (const SkNf& o) const { return _mm_div_ps(fVec, o.fVec); }
+
+    Nb operator == (const SkNf& o) const { return _mm_castps_si128(_mm_cmpeq_ps (fVec, o.fVec)); }
+    Nb operator != (const SkNf& o) const { return _mm_castps_si128(_mm_cmpneq_ps(fVec, o.fVec)); }
+    Nb operator  < (const SkNf& o) const { return _mm_castps_si128(_mm_cmplt_ps (fVec, o.fVec)); }
+    Nb operator  > (const SkNf& o) const { return _mm_castps_si128(_mm_cmpgt_ps (fVec, o.fVec)); }
+    Nb operator <= (const SkNf& o) const { return _mm_castps_si128(_mm_cmple_ps (fVec, o.fVec)); }
+    Nb operator >= (const SkNf& o) const { return _mm_castps_si128(_mm_cmpge_ps (fVec, o.fVec)); }
+
+    static SkNf Min(const SkNf& l, const SkNf& r) { return _mm_min_ps(l.fVec, r.fVec); }
+    static SkNf Max(const SkNf& l, const SkNf& r) { return _mm_max_ps(l.fVec, r.fVec); }
+
+    SkNf  sqrt() const { return _mm_sqrt_ps (fVec);  }
+    SkNf rsqrt0() const { return _mm_rsqrt_ps(fVec); }
+    SkNf rsqrt1() const { return this->rsqrt0(); }
+    SkNf rsqrt2() const { return this->rsqrt1(); }
+
+    SkNf       invert() const { return SkNf(1) / *this; }
+    SkNf approxInvert() const { return _mm_rcp_ps(fVec); }
+
+    template <int k> float kth() const {
+        SkASSERT(0 <= k && k < 2);
+        union { __m128 v; float fs[4]; } pun = {fVec};
+        return pun.fs[k&1];
+    }
+
+    __m128 fVec;
+};
+
+template <>
+class SkNf<2, double> {
+    typedef SkNb<2, 8> Nb;
+public:
+    SkNf(const __m128d& vec) : fVec(vec) {}
+
+    SkNf() {}
+    explicit SkNf(double val)           : fVec( _mm_set1_pd(val) ) {}
+    static SkNf Load(const double vals[2]) { return _mm_loadu_pd(vals); }
+    SkNf(double a, double b) : fVec(_mm_setr_pd(a,b)) {}
+
+    void store(double vals[2]) const { _mm_storeu_pd(vals, fVec); }
+
+    SkNf operator + (const SkNf& o) const { return _mm_add_pd(fVec, o.fVec); }
+    SkNf operator - (const SkNf& o) const { return _mm_sub_pd(fVec, o.fVec); }
+    SkNf operator * (const SkNf& o) const { return _mm_mul_pd(fVec, o.fVec); }
+    SkNf operator / (const SkNf& o) const { return _mm_div_pd(fVec, o.fVec); }
+
+    Nb operator == (const SkNf& o) const { return _mm_castpd_si128(_mm_cmpeq_pd (fVec, o.fVec)); }
+    Nb operator != (const SkNf& o) const { return _mm_castpd_si128(_mm_cmpneq_pd(fVec, o.fVec)); }
+    Nb operator  < (const SkNf& o) const { return _mm_castpd_si128(_mm_cmplt_pd (fVec, o.fVec)); }
+    Nb operator  > (const SkNf& o) const { return _mm_castpd_si128(_mm_cmpgt_pd (fVec, o.fVec)); }
+    Nb operator <= (const SkNf& o) const { return _mm_castpd_si128(_mm_cmple_pd (fVec, o.fVec)); }
+    Nb operator >= (const SkNf& o) const { return _mm_castpd_si128(_mm_cmpge_pd (fVec, o.fVec)); }
+
+    static SkNf Min(const SkNf& l, const SkNf& r) { return _mm_min_pd(l.fVec, r.fVec); }
+    static SkNf Max(const SkNf& l, const SkNf& r) { return _mm_max_pd(l.fVec, r.fVec); }
+
+    SkNf  sqrt() const { return _mm_sqrt_pd(fVec);  }
+    SkNf rsqrt0() const { return _mm_cvtps_pd(_mm_rsqrt_ps(_mm_cvtpd_ps(fVec))); }
+    SkNf rsqrt1() const { return this->rsqrt0(); }
+    SkNf rsqrt2() const { return this->rsqrt1(); }
+
+    SkNf       invert() const { return SkNf(1) / *this; }
+    SkNf approxInvert() const { return _mm_cvtps_pd(_mm_rcp_ps(_mm_cvtpd_ps(fVec))); }
+
+    template <int k> double kth() const {
+        SkASSERT(0 <= k && k < 2);
+        union { __m128d v; double ds[2]; } pun = {fVec};
+        return pun.ds[k&1];
+    }
+
+    __m128d fVec;
+};
+
+template <>
+class SkNi<4, int> {
+public:
+    SkNi(const __m128i& vec) : fVec(vec) {}
+
+    SkNi() {}
+    explicit SkNi(int val) : fVec(_mm_set1_epi32(val)) {}
+    static SkNi Load(const int vals[4]) { return _mm_loadu_si128((const __m128i*)vals); }
+    SkNi(int a, int b, int c, int d) : fVec(_mm_setr_epi32(a,b,c,d)) {}
+
+    void store(int vals[4]) const { _mm_storeu_si128((__m128i*)vals, fVec); }
+
+    SkNi operator + (const SkNi& o) const { return _mm_add_epi32(fVec, o.fVec); }
+    SkNi operator - (const SkNi& o) const { return _mm_sub_epi32(fVec, o.fVec); }
+    SkNi operator * (const SkNi& o) const {
+        __m128i mul20 = _mm_mul_epu32(fVec, o.fVec),
+                mul31 = _mm_mul_epu32(_mm_srli_si128(fVec, 4), _mm_srli_si128(o.fVec, 4));
+        return _mm_unpacklo_epi32(_mm_shuffle_epi32(mul20, _MM_SHUFFLE(0,0,2,0)),
+                                  _mm_shuffle_epi32(mul31, _MM_SHUFFLE(0,0,2,0)));
+    }
+
+    SkNi operator << (int bits) const { return _mm_slli_epi32(fVec, bits); }
+    SkNi operator >> (int bits) const { return _mm_srai_epi32(fVec, bits); }
+
+    template <int k> int kth() const {
+        SkASSERT(0 <= k && k < 4);
+        switch (k) {
+            case 0: return _mm_cvtsi128_si32(fVec);
+            case 1: return _mm_cvtsi128_si32(_mm_srli_si128(fVec,  4));
+            case 2: return _mm_cvtsi128_si32(_mm_srli_si128(fVec,  8));
+            case 3: return _mm_cvtsi128_si32(_mm_srli_si128(fVec, 12));
+            default: SkASSERT(false); return 0;
+        }
+    }
+
+    __m128i fVec;
+};
+
+template <>
+class SkNf<4, float> {
+    typedef SkNb<4, 4> Nb;
+public:
+    SkNf(const __m128& vec) : fVec(vec) {}
+
+    SkNf() {}
+    explicit SkNf(float val)           : fVec( _mm_set1_ps(val) ) {}
+    static SkNf Load(const float vals[4]) { return _mm_loadu_ps(vals); }
+    SkNf(float a, float b, float c, float d) : fVec(_mm_setr_ps(a,b,c,d)) {}
+
+    void store(float vals[4]) const { _mm_storeu_ps(vals, fVec); }
+
+    SkNi<4, int> castTrunc() const { return _mm_cvttps_epi32(fVec); }
+
+    SkNf operator + (const SkNf& o) const { return _mm_add_ps(fVec, o.fVec); }
+    SkNf operator - (const SkNf& o) const { return _mm_sub_ps(fVec, o.fVec); }
+    SkNf operator * (const SkNf& o) const { return _mm_mul_ps(fVec, o.fVec); }
+    SkNf operator / (const SkNf& o) const { return _mm_div_ps(fVec, o.fVec); }
+
+    Nb operator == (const SkNf& o) const { return _mm_castps_si128(_mm_cmpeq_ps (fVec, o.fVec)); }
+    Nb operator != (const SkNf& o) const { return _mm_castps_si128(_mm_cmpneq_ps(fVec, o.fVec)); }
+    Nb operator  < (const SkNf& o) const { return _mm_castps_si128(_mm_cmplt_ps (fVec, o.fVec)); }
+    Nb operator  > (const SkNf& o) const { return _mm_castps_si128(_mm_cmpgt_ps (fVec, o.fVec)); }
+    Nb operator <= (const SkNf& o) const { return _mm_castps_si128(_mm_cmple_ps (fVec, o.fVec)); }
+    Nb operator >= (const SkNf& o) const { return _mm_castps_si128(_mm_cmpge_ps (fVec, o.fVec)); }
+
+    static SkNf Min(const SkNf& l, const SkNf& r) { return _mm_min_ps(l.fVec, r.fVec); }
+    static SkNf Max(const SkNf& l, const SkNf& r) { return _mm_max_ps(l.fVec, r.fVec); }
+
+    SkNf  sqrt() const { return _mm_sqrt_ps (fVec);  }
+    SkNf rsqrt0() const { return _mm_rsqrt_ps(fVec); }
+    SkNf rsqrt1() const { return this->rsqrt0(); }
+    SkNf rsqrt2() const { return this->rsqrt1(); }
+
+    SkNf       invert() const { return SkNf(1) / *this; }
+    SkNf approxInvert() const { return _mm_rcp_ps(fVec); }
+
+    template <int k> float kth() const {
+        SkASSERT(0 <= k && k < 4);
+        union { __m128 v; float fs[4]; } pun = {fVec};
+        return pun.fs[k&3];
+    }
+
+    __m128 fVec;
+};
+
+template <>
+class SkNi<4, uint16_t> {
+public:
+    SkNi(const __m128i& vec) : fVec(vec) {}
+
+    SkNi() {}
+    explicit SkNi(uint16_t val) : fVec(_mm_set1_epi16(val)) {}
+    static SkNi Load(const uint16_t vals[4]) { return _mm_loadl_epi64((const __m128i*)vals); }
+    SkNi(uint16_t a, uint16_t b, uint16_t c, uint16_t d) : fVec(_mm_setr_epi16(a,b,c,d,0,0,0,0)) {}
+
+    void store(uint16_t vals[4]) const { _mm_storel_epi64((__m128i*)vals, fVec); }
+
+    SkNi operator + (const SkNi& o) const { return _mm_add_epi16(fVec, o.fVec); }
+    SkNi operator - (const SkNi& o) const { return _mm_sub_epi16(fVec, o.fVec); }
+    SkNi operator * (const SkNi& o) const { return _mm_mullo_epi16(fVec, o.fVec); }
+
+    SkNi operator << (int bits) const { return _mm_slli_epi16(fVec, bits); }
+    SkNi operator >> (int bits) const { return _mm_srli_epi16(fVec, bits); }
+
+    template <int k> uint16_t kth() const {
+        SkASSERT(0 <= k && k < 4);
+        return _mm_extract_epi16(fVec, k);
+    }
+
+    __m128i fVec;
+};
+
+template <>
+class SkNi<8, uint16_t> {
+public:
+    SkNi(const __m128i& vec) : fVec(vec) {}
+
+    SkNi() {}
+    explicit SkNi(uint16_t val) : fVec(_mm_set1_epi16(val)) {}
+    static SkNi Load(const uint16_t vals[8]) { return _mm_loadu_si128((const __m128i*)vals); }
+    SkNi(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
+         uint16_t e, uint16_t f, uint16_t g, uint16_t h) : fVec(_mm_setr_epi16(a,b,c,d,e,f,g,h)) {}
+
+    void store(uint16_t vals[8]) const { _mm_storeu_si128((__m128i*)vals, fVec); }
+
+    SkNi operator + (const SkNi& o) const { return _mm_add_epi16(fVec, o.fVec); }
+    SkNi operator - (const SkNi& o) const { return _mm_sub_epi16(fVec, o.fVec); }
+    SkNi operator * (const SkNi& o) const { return _mm_mullo_epi16(fVec, o.fVec); }
+
+    SkNi operator << (int bits) const { return _mm_slli_epi16(fVec, bits); }
+    SkNi operator >> (int bits) const { return _mm_srli_epi16(fVec, bits); }
+
+    static SkNi Min(const SkNi& a, const SkNi& b) {
+        // No unsigned _mm_min_epu16, so we'll shift into a space where we can use the
+        // signed version, _mm_min_epi16, then shift back.
+        const uint16_t top = 0x8000; // Keep this separate from _mm_set1_epi16 or MSVC will whine.
+        const __m128i top_8x = _mm_set1_epi16(top);
+        return _mm_add_epi8(top_8x, _mm_min_epi16(_mm_sub_epi8(a.fVec, top_8x),
+                                                  _mm_sub_epi8(b.fVec, top_8x)));
+    }
+
+    template <int k> uint16_t kth() const {
+        SkASSERT(0 <= k && k < 8);
+        return _mm_extract_epi16(fVec, k);
+    }
+
+    __m128i fVec;
+};
+
+template <>
+class SkNi<16, uint8_t> {
+public:
+    SkNi(const __m128i& vec) : fVec(vec) {}
+
+    SkNi() {}
+    explicit SkNi(uint8_t val) : fVec(_mm_set1_epi8(val)) {}
+    static SkNi Load(const uint8_t vals[16]) { return _mm_loadu_si128((const __m128i*)vals); }
+    SkNi(uint8_t a, uint8_t b, uint8_t c, uint8_t d,
+         uint8_t e, uint8_t f, uint8_t g, uint8_t h,
+         uint8_t i, uint8_t j, uint8_t k, uint8_t l,
+         uint8_t m, uint8_t n, uint8_t o, uint8_t p)
+        : fVec(_mm_setr_epi8(a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p)) {}
+
+    void store(uint8_t vals[16]) const { _mm_storeu_si128((__m128i*)vals, fVec); }
+
+    SkNi saturatedAdd(const SkNi& o) const { return _mm_adds_epu8(fVec, o.fVec); }
+
+    SkNi operator + (const SkNi& o) const { return _mm_add_epi8(fVec, o.fVec); }
+    SkNi operator - (const SkNi& o) const { return _mm_sub_epi8(fVec, o.fVec); }
+
+    // SSE cannot multiply or shift vectors of uint8_t.
+    SkNi operator * (const SkNi& o) const { SkASSERT(false); return fVec; }
+    SkNi operator << (int bits) const { SkASSERT(false); return fVec; }
+    SkNi operator >> (int bits) const { SkASSERT(false); return fVec; }
+
+    static SkNi Min(const SkNi& a, const SkNi& b) { return _mm_min_epu8(a.fVec, b.fVec); }
+
+    template <int k> uint8_t kth() const {
+        SkASSERT(0 <= k && k < 16);
+        // SSE4.1 would just `return _mm_extract_epi8(fVec, k)`.  We have to read 16-bits instead.
+        int pair = _mm_extract_epi16(fVec, k/2);
+        return k % 2 == 0 ? pair : (pair >> 8);
+    }
+
+    __m128i fVec;
+};
+
+#endif//SkNx_sse_DEFINED
diff --git a/src/opts/SkPMFloat_SSE2.h b/src/opts/SkPMFloat_SSE2.h
index 7298b4d..88a3803 100644
--- a/src/opts/SkPMFloat_SSE2.h
+++ b/src/opts/SkPMFloat_SSE2.h
@@ -1,10 +1,14 @@
-#include "SkColorPriv.h"
-#include <emmintrin.h>
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 
 // For SkPMFloat(SkPMColor), we widen our 8 bit components (fix8) to 8-bit components in 16 bits
 // (fix8_16), then widen those to 8-bit-in-32-bits (fix8_32), and finally convert those to floats.
 
-// get() and clamped() do the opposite, working from floats to 8-bit-in-32-bit,
+// round() and roundClamp() do the opposite, working from floats to 8-bit-in-32-bit,
 // to 8-bit-in-16-bit, back down to 8-bit components.
 // _mm_packus_epi16() gives us clamping for free while narrowing.
 
@@ -13,17 +17,17 @@
     __m128i fix8    = _mm_set_epi32(0,0,0,c),
             fix8_16 = _mm_unpacklo_epi8 (fix8,    _mm_setzero_si128()),
             fix8_32 = _mm_unpacklo_epi16(fix8_16, _mm_setzero_si128());
-    _mm_store_ps(fColor, _mm_cvtepi32_ps(fix8_32));
+    fVec = _mm_cvtepi32_ps(fix8_32);
     SkASSERT(this->isValid());
 }
 
-inline SkPMColor SkPMFloat::get() const {
-    SkASSERT(this->isValid());
-    return this->clamped();  // Haven't beaten this yet.
+inline SkPMColor SkPMFloat::round() const {
+    return this->roundClamp();  // Haven't beaten this yet.
 }
 
-inline SkPMColor SkPMFloat::clamped() const {
-    __m128i fix8_32 = _mm_cvtps_epi32(_mm_load_ps(fColor)),  // _mm_cvtps_epi32 rounds for us!
+inline SkPMColor SkPMFloat::roundClamp() const {
+    // We don't use _mm_cvtps_epi32, because we want precise control over how 0.5 rounds (up).
+    __m128i fix8_32 = _mm_cvttps_epi32(_mm_add_ps(_mm_set1_ps(0.5f), fVec)),
             fix8_16 = _mm_packus_epi16(fix8_32, fix8_32),
             fix8    = _mm_packus_epi16(fix8_16, fix8_16);
     SkPMColor c = _mm_cvtsi128_si32(fix8);
@@ -31,24 +35,41 @@
     return c;
 }
 
-inline void SkPMFloat::From4PMColors(SkPMFloat floats[4], const SkPMColor colors[4]) {
-    // Haven't beaten this yet.
-    for (int i = 0; i < 4; i++) { floats[i] = FromPMColor(colors[i]); }
+inline SkPMColor SkPMFloat::trunc() const {
+    // Basically, same as roundClamp(), but no rounding.
+    __m128i fix8_32 = _mm_cvttps_epi32(fVec),
+            fix8_16 = _mm_packus_epi16(fix8_32, fix8_32),
+            fix8    = _mm_packus_epi16(fix8_16, fix8_16);
+    SkPMColor c = _mm_cvtsi128_si32(fix8);
+    SkPMColorAssert(c);
+    return c;
 }
 
-inline void SkPMFloat::To4PMColors(SkPMColor colors[4], const SkPMFloat floats[4]) {
-    SkASSERT(floats[0].isValid() && floats[1].isValid()
-          && floats[2].isValid() && floats[3].isValid());
+inline void SkPMFloat::From4PMColors(const SkPMColor colors[4],
+                                     SkPMFloat* a, SkPMFloat* b, SkPMFloat* c, SkPMFloat* d) {
     // Haven't beaten this yet.
-    ClampTo4PMColors(colors, floats);
+    *a = FromPMColor(colors[0]);
+    *b = FromPMColor(colors[1]);
+    *c = FromPMColor(colors[2]);
+    *d = FromPMColor(colors[3]);
 }
 
-inline void SkPMFloat::ClampTo4PMColors(SkPMColor colors[4], const SkPMFloat floats[4]) {
+inline void SkPMFloat::RoundTo4PMColors(
+        const SkPMFloat& a, const SkPMFloat& b, const SkPMFloat&c, const SkPMFloat& d,
+        SkPMColor colors[4]) {
+    // Haven't beaten this yet.
+    RoundClampTo4PMColors(a,b,c,d, colors);
+}
+
+inline void SkPMFloat::RoundClampTo4PMColors(
+        const SkPMFloat& a, const SkPMFloat& b, const SkPMFloat&c, const SkPMFloat& d,
+        SkPMColor colors[4]) {
     // Same as _SSSE3.h's.  We use 3 _mm_packus_epi16() where the naive loop uses 8.
-    __m128i c0 = _mm_cvtps_epi32(_mm_load_ps(floats[0].fColor)),  // _mm_cvtps_epi32 rounds for us!
-            c1 = _mm_cvtps_epi32(_mm_load_ps(floats[1].fColor)),
-            c2 = _mm_cvtps_epi32(_mm_load_ps(floats[2].fColor)),
-            c3 = _mm_cvtps_epi32(_mm_load_ps(floats[3].fColor));
+    // We don't use _mm_cvtps_epi32, because we want precise control over how 0.5 rounds (up).
+    __m128i c0 = _mm_cvttps_epi32(_mm_add_ps(_mm_set1_ps(0.5f), a.fVec)),
+            c1 = _mm_cvttps_epi32(_mm_add_ps(_mm_set1_ps(0.5f), b.fVec)),
+            c2 = _mm_cvttps_epi32(_mm_add_ps(_mm_set1_ps(0.5f), c.fVec)),
+            c3 = _mm_cvttps_epi32(_mm_add_ps(_mm_set1_ps(0.5f), d.fVec));
     __m128i c3210 = _mm_packus_epi16(_mm_packus_epi16(c0, c1),
                                      _mm_packus_epi16(c2, c3));
     _mm_storeu_si128((__m128i*)colors, c3210);
diff --git a/src/opts/SkPMFloat_SSSE3.h b/src/opts/SkPMFloat_SSSE3.h
index ff29617..9ff7356 100644
--- a/src/opts/SkPMFloat_SSSE3.h
+++ b/src/opts/SkPMFloat_SSSE3.h
@@ -1,12 +1,16 @@
-#include "SkColorPriv.h"
-#include <tmmintrin.h>
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 
 // For SkPMFloat(SkPMColor), we widen our 8 bit components (fix8) to 8-bit components in 32 bits
 // (fix8_32), then convert those to floats.
 
-// get() does the opposite, working from floats to 8-bit-in-32-bits, then back to packed 8 bit.
+// round() does the opposite, working from floats to 8-bit-in-32-bits, then back to packed 8 bit.
 
-// clamped() is the same as _SSE2: floats to 8-in-32, to 8-in-16, to packed 8 bit, with
+// roundClamp() is the same as _SSE2: floats to 8-in-32, to 8-in-16, to packed 8 bit, with
 // _mm_packus_epi16() both clamping and narrowing.
 
 inline SkPMFloat::SkPMFloat(SkPMColor c) {
@@ -14,22 +18,26 @@
     const int _ = 255;  // _ means to zero that byte.
     __m128i fix8    = _mm_set_epi32(0,0,0,c),
             fix8_32 = _mm_shuffle_epi8(fix8, _mm_set_epi8(_,_,_,3, _,_,_,2, _,_,_,1, _,_,_,0));
-    _mm_store_ps(fColor, _mm_cvtepi32_ps(fix8_32));
+    fVec = _mm_cvtepi32_ps(fix8_32);
     SkASSERT(this->isValid());
 }
 
-inline SkPMColor SkPMFloat::get() const {
-    SkASSERT(this->isValid());
+inline SkPMColor SkPMFloat::trunc() const {
     const int _ = 255;  // _ means to zero that byte.
-    __m128i fix8_32 = _mm_cvtps_epi32(_mm_load_ps(fColor)),  // _mm_cvtps_epi32 rounds for us!
+    __m128i fix8_32 = _mm_cvttps_epi32(fVec),
             fix8    = _mm_shuffle_epi8(fix8_32, _mm_set_epi8(_,_,_,_, _,_,_,_, _,_,_,_, 12,8,4,0));
     SkPMColor c = _mm_cvtsi128_si32(fix8);
     SkPMColorAssert(c);
     return c;
 }
 
-inline SkPMColor SkPMFloat::clamped() const {
-    __m128i fix8_32 = _mm_cvtps_epi32(_mm_load_ps(fColor)),  // _mm_cvtps_epi32 rounds for us!
+inline SkPMColor SkPMFloat::round() const {
+    return SkPMFloat(Sk4f(0.5f) + *this).trunc();
+}
+
+inline SkPMColor SkPMFloat::roundClamp() const {
+    // We don't use _mm_cvtps_epi32, because we want precise control over how 0.5 rounds (up).
+    __m128i fix8_32 = _mm_cvttps_epi32(_mm_add_ps(_mm_set1_ps(0.5f), fVec)),
             fix8_16 = _mm_packus_epi16(fix8_32, fix8_32),
             fix8    = _mm_packus_epi16(fix8_16, fix8_16);
     SkPMColor c = _mm_cvtsi128_si32(fix8);
@@ -37,22 +45,34 @@
     return c;
 }
 
-inline void SkPMFloat::From4PMColors(SkPMFloat floats[4], const SkPMColor colors[4]) {
+inline void SkPMFloat::From4PMColors(const SkPMColor colors[4],
+                                     SkPMFloat* a, SkPMFloat* b, SkPMFloat* c, SkPMFloat* d) {
     // Haven't beaten this yet.
-    for (int i = 0; i < 4; i++) { floats[i] = FromPMColor(colors[i]); }
+    *a = FromPMColor(colors[0]);
+    *b = FromPMColor(colors[1]);
+    *c = FromPMColor(colors[2]);
+    *d = FromPMColor(colors[3]);
 }
 
-inline void SkPMFloat::To4PMColors(SkPMColor colors[4], const SkPMFloat floats[4]) {
-    // Haven't beaten this yet.  Still faster than ClampTo4PMColors too.
-    for (int i = 0; i < 4; i++) { colors[i] = floats[i].get(); }
+inline void SkPMFloat::RoundTo4PMColors(
+        const SkPMFloat& a, const SkPMFloat& b, const SkPMFloat&c, const SkPMFloat& d,
+        SkPMColor colors[4]) {
+    // Haven't beaten this yet.  Still faster than RoundClampTo4PMColors?
+    colors[0] = a.round();
+    colors[1] = b.round();
+    colors[2] = c.round();
+    colors[3] = d.round();
 }
 
-inline void SkPMFloat::ClampTo4PMColors(SkPMColor colors[4], const SkPMFloat floats[4]) {
+inline void SkPMFloat::RoundClampTo4PMColors(
+        const SkPMFloat& a, const SkPMFloat& b, const SkPMFloat&c, const SkPMFloat& d,
+        SkPMColor colors[4]) {
     // Same as _SSE2.h's.  We use 3 _mm_packus_epi16() where the naive loop uses 8.
-    __m128i c0 = _mm_cvtps_epi32(_mm_load_ps(floats[0].fColor)),  // _mm_cvtps_epi32 rounds for us!
-            c1 = _mm_cvtps_epi32(_mm_load_ps(floats[1].fColor)),
-            c2 = _mm_cvtps_epi32(_mm_load_ps(floats[2].fColor)),
-            c3 = _mm_cvtps_epi32(_mm_load_ps(floats[3].fColor));
+    // We don't use _mm_cvtps_epi32, because we want precise control over how 0.5 rounds (up).
+    __m128i c0 = _mm_cvttps_epi32(_mm_add_ps(_mm_set1_ps(0.5f), a.fVec)),
+            c1 = _mm_cvttps_epi32(_mm_add_ps(_mm_set1_ps(0.5f), b.fVec)),
+            c2 = _mm_cvttps_epi32(_mm_add_ps(_mm_set1_ps(0.5f), c.fVec)),
+            c3 = _mm_cvttps_epi32(_mm_add_ps(_mm_set1_ps(0.5f), d.fVec));
     __m128i c3210 = _mm_packus_epi16(_mm_packus_epi16(c0, c1),
                                      _mm_packus_epi16(c2, c3));
     _mm_storeu_si128((__m128i*)colors, c3210);
diff --git a/src/opts/SkPMFloat_neon.h b/src/opts/SkPMFloat_neon.h
index 036d10d..1c01bc4 100644
--- a/src/opts/SkPMFloat_neon.h
+++ b/src/opts/SkPMFloat_neon.h
@@ -1,26 +1,28 @@
-#include "SkColorPriv.h"
-#include <arm_neon.h>
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 
 // For SkPMFloat(SkPMFColor), we widen our 8 bit components (fix8) to 8-bit components in 16 bits
 // (fix8_16), then widen those to 8-bit-in-32-bits (fix8_32), and finally convert those to floats.
 
-// get() and clamped() do the opposite, working from floats to 8-bit-in-32-bit,
+// round() and roundClamp() do the opposite, working from floats to 8-bit-in-32-bit,
 // to 8-bit-in-16-bit, back down to 8-bit components.
-// clamped() uses vqmovn to clamp while narrowing instead of just narrowing with vmovn.
+// roundClamp() uses vqmovn to clamp while narrowing instead of just narrowing with vmovn.
 
 inline SkPMFloat::SkPMFloat(SkPMColor c) {
     SkPMColorAssert(c);
     uint8x8_t   fix8    = (uint8x8_t)vdup_n_u32(c);
     uint16x8_t  fix8_16 = vmovl_u8(fix8);
     uint32x4_t  fix8_32 = vmovl_u16(vget_low_u16(fix8_16));
-    vst1q_f32(fColor, vcvtq_f32_u32(fix8_32));
+    fVec = vcvtq_f32_u32(fix8_32);
     SkASSERT(this->isValid());
 }
 
-inline SkPMColor SkPMFloat::get() const {
-    SkASSERT(this->isValid());
-    float32x4_t add_half = vaddq_f32(vld1q_f32(fColor), vdupq_n_f32(0.5f));
-    uint32x4_t  fix8_32  = vcvtq_u32_f32(add_half);  // vcvtq_u32_f32 truncates, so round manually
+inline SkPMColor SkPMFloat::trunc() const {
+    uint32x4_t  fix8_32  = vcvtq_u32_f32(fVec);  // vcvtq_u32_f32 truncates
     uint16x4_t  fix8_16  = vmovn_u32(fix8_32);
     uint8x8_t   fix8     = vmovn_u16(vcombine_u16(fix8_16, vdup_n_u16(0)));
     SkPMColor c = vget_lane_u32((uint32x2_t)fix8, 0);
@@ -28,8 +30,12 @@
     return c;
 }
 
-inline SkPMColor SkPMFloat::clamped() const {
-    float32x4_t add_half = vaddq_f32(vld1q_f32(fColor), vdupq_n_f32(0.5f));
+inline SkPMColor SkPMFloat::round() const {
+    return SkPMFloat(Sk4f(0.5f) + *this).trunc();
+}
+
+inline SkPMColor SkPMFloat::roundClamp() const {
+    float32x4_t add_half = vaddq_f32(fVec, vdupq_n_f32(0.5f));
     uint32x4_t  fix8_32  = vcvtq_u32_f32(add_half);  // vcvtq_u32_f32 truncates, so round manually
     uint16x4_t  fix8_16  = vqmovn_u32(fix8_32);
     uint8x8_t   fix8     = vqmovn_u16(vcombine_u16(fix8_16, vdup_n_u16(0)));
@@ -39,14 +45,28 @@
 }
 
 // TODO: we should be able to beat these loops on all three methods.
-inline void SkPMFloat::From4PMColors(SkPMFloat floats[4], const SkPMColor colors[4]) {
-    for (int i = 0; i < 4; i++) { floats[i] = FromPMColor(colors[i]); }
+inline void SkPMFloat::From4PMColors(const SkPMColor colors[4],
+                                     SkPMFloat* a, SkPMFloat* b, SkPMFloat* c, SkPMFloat* d) {
+    *a = FromPMColor(colors[0]);
+    *b = FromPMColor(colors[1]);
+    *c = FromPMColor(colors[2]);
+    *d = FromPMColor(colors[3]);
 }
 
-inline void SkPMFloat::To4PMColors(SkPMColor colors[4], const SkPMFloat floats[4]) {
-    for (int i = 0; i < 4; i++) { colors[i] = floats[i].get(); }
+inline void SkPMFloat::RoundTo4PMColors(
+        const SkPMFloat& a, const SkPMFloat& b, const SkPMFloat&c, const SkPMFloat& d,
+        SkPMColor colors[4]) {
+    colors[0] = a.round();
+    colors[1] = b.round();
+    colors[2] = c.round();
+    colors[3] = d.round();
 }
 
-inline void SkPMFloat::ClampTo4PMColors(SkPMColor colors[4], const SkPMFloat floats[4]) {
-    for (int i = 0; i < 4; i++) { colors[i] = floats[i].clamped(); }
+inline void SkPMFloat::RoundClampTo4PMColors(
+        const SkPMFloat& a, const SkPMFloat& b, const SkPMFloat&c, const SkPMFloat& d,
+        SkPMColor colors[4]) {
+    colors[0] = a.roundClamp();
+    colors[1] = b.roundClamp();
+    colors[2] = c.roundClamp();
+    colors[3] = d.roundClamp();
 }
diff --git a/src/opts/SkPMFloat_none.h b/src/opts/SkPMFloat_none.h
index a33fa7a..50f2427 100644
--- a/src/opts/SkPMFloat_none.h
+++ b/src/opts/SkPMFloat_none.h
@@ -1,4 +1,9 @@
-#include "SkColorPriv.h"
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 
 inline SkPMFloat::SkPMFloat(SkPMColor c) {
     *this = SkPMFloat::FromARGB(SkGetPackedA32(c),
@@ -8,12 +13,17 @@
     SkASSERT(this->isValid());
 }
 
-inline SkPMColor SkPMFloat::get() const {
-    SkASSERT(this->isValid());
-    return SkPackARGB32(this->a()+0.5f, this->r()+0.5f, this->g()+0.5f, this->b()+0.5f);
+inline SkPMColor SkPMFloat::trunc() const {
+    return SkPackARGB32(this->a(), this->r(), this->g(), this->b());
 }
 
-inline SkPMColor SkPMFloat::clamped() const {
+inline SkPMColor SkPMFloat::round() const {
+    SkPMColor c = SkPackARGB32(this->a()+0.5f, this->r()+0.5f, this->g()+0.5f, this->b()+0.5f);
+    SkPMColorAssert(c);
+    return c;
+}
+
+inline SkPMColor SkPMFloat::roundClamp() const {
     float a = this->a(),
           r = this->r(),
           g = this->g(),
@@ -22,17 +32,33 @@
     r = r < 0 ? 0 : (r > 255 ? 255 : r);
     g = g < 0 ? 0 : (g > 255 ? 255 : g);
     b = b < 0 ? 0 : (b > 255 ? 255 : b);
-    return SkPackARGB32(a+0.5f, r+0.5f, g+0.5f, b+0.5f);
+    SkPMColor c = SkPackARGB32(a+0.5f, r+0.5f, g+0.5f, b+0.5f);
+    SkPMColorAssert(c);
+    return c;
 }
 
-inline void SkPMFloat::From4PMColors(SkPMFloat floats[4], const SkPMColor colors[4]) {
-    for (int i = 0; i < 4; i++) { floats[i] = FromPMColor(colors[i]); }
+inline void SkPMFloat::From4PMColors(const SkPMColor colors[4],
+                                     SkPMFloat* a, SkPMFloat* b, SkPMFloat* c, SkPMFloat* d) {
+    *a = FromPMColor(colors[0]);
+    *b = FromPMColor(colors[1]);
+    *c = FromPMColor(colors[2]);
+    *d = FromPMColor(colors[3]);
 }
 
-inline void SkPMFloat::To4PMColors(SkPMColor colors[4], const SkPMFloat floats[4]) {
-    for (int i = 0; i < 4; i++) { colors[i] = floats[i].get(); }
+inline void SkPMFloat::RoundTo4PMColors(
+        const SkPMFloat& a, const SkPMFloat& b, const SkPMFloat&c, const SkPMFloat& d,
+        SkPMColor colors[4]) {
+    colors[0] = a.round();
+    colors[1] = b.round();
+    colors[2] = c.round();
+    colors[3] = d.round();
 }
 
-inline void SkPMFloat::ClampTo4PMColors(SkPMColor colors[4], const SkPMFloat floats[4]) {
-    for (int i = 0; i < 4; i++) { colors[i] = floats[i].clamped(); }
+inline void SkPMFloat::RoundClampTo4PMColors(
+        const SkPMFloat& a, const SkPMFloat& b, const SkPMFloat&c, const SkPMFloat& d,
+        SkPMColor colors[4]) {
+    colors[0] = a.roundClamp();
+    colors[1] = b.roundClamp();
+    colors[2] = c.roundClamp();
+    colors[3] = d.roundClamp();
 }
diff --git a/src/opts/SkUtils_opts_arm.cpp b/src/opts/SkUtils_opts_arm.cpp
index b1c9d0a..d74471f 100644
--- a/src/opts/SkUtils_opts_arm.cpp
+++ b/src/opts/SkUtils_opts_arm.cpp
@@ -8,47 +8,26 @@
 #include "SkUtils.h"
 #include "SkUtilsArm.h"
 
-#if defined(SK_CPU_LENDIAN) && !SK_ARM_NEON_IS_NONE
-extern "C" void memset16_neon(uint16_t dst[], uint16_t value, int count);
-extern "C" void memset32_neon(uint32_t dst[], uint32_t value, int count);
-#endif
-
-#if defined(SK_CPU_LENDIAN)
-extern "C" void arm_memset16(uint16_t* dst, uint16_t value, int count);
-extern "C" void arm_memset32(uint32_t* dst, uint32_t value, int count);
-#endif
+void sk_memset16_neon(uint16_t dst[], uint16_t value, int count);
+void sk_memset32_neon(uint32_t dst[], uint32_t value, int count);
 
 SkMemset16Proc SkMemset16GetPlatformProc() {
-    // FIXME: memset.arm.S is using syntax incompatible with XCode
-#if !defined(SK_CPU_LENDIAN) || defined(SK_BUILD_FOR_IOS)
-    return NULL;
+#if SK_ARM_NEON_IS_ALWAYS
+    return sk_memset16_neon;
 #elif SK_ARM_NEON_IS_DYNAMIC
-    if (sk_cpu_arm_has_neon()) {
-        return memset16_neon;
-    } else {
-        return arm_memset16;
-    }
-#elif SK_ARM_NEON_IS_ALWAYS
-    return memset16_neon;
+    return sk_cpu_arm_has_neon() ? sk_memset16_neon : nullptr;
 #else
-    return arm_memset16;
+    return nullptr;
 #endif
 }
 
 SkMemset32Proc SkMemset32GetPlatformProc() {
-    // FIXME: memset.arm.S is using syntax incompatible with XCode
-#if !defined(SK_CPU_LENDIAN) || defined(SK_BUILD_FOR_IOS)
-    return NULL;
+#if SK_ARM_NEON_IS_ALWAYS
+    return sk_memset32_neon;
 #elif SK_ARM_NEON_IS_DYNAMIC
-    if (sk_cpu_arm_has_neon()) {
-        return memset32_neon;
-    } else {
-        return arm_memset32;
-    }
-#elif SK_ARM_NEON_IS_ALWAYS
-    return memset32_neon;
+    return sk_cpu_arm_has_neon() ? sk_memset32_neon : nullptr;
 #else
-    return arm_memset32;
+    return nullptr;
 #endif
 }
 
diff --git a/src/opts/SkUtils_opts_arm_neon.cpp b/src/opts/SkUtils_opts_arm_neon.cpp
new file mode 100644
index 0000000..b7d0504
--- /dev/null
+++ b/src/opts/SkUtils_opts_arm_neon.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+#include <arm_neon.h>
+
+void sk_memset32_neon(uint32_t dst[], uint32_t value, int count) {
+    uint32x4_t   v4  = vdupq_n_u32(value);
+    uint32x4x4_t v16 = { v4, v4, v4, v4 };
+
+    while (count >= 16) {
+        vst4q_u32(dst, v16);  // This swizzles, but we don't care: all lanes are the same, value.
+        dst   += 16;
+        count -= 16;
+    }
+    SkASSERT(count < 16);
+    switch (count / 4) {
+        case 3: vst1q_u32(dst, v4); dst += 4; count -= 4;
+        case 2: vst1q_u32(dst, v4); dst += 4; count -= 4;
+        case 1: vst1q_u32(dst, v4); dst += 4; count -= 4;
+    }
+    SkASSERT(count < 4);
+    if (count >= 2) {
+        vst1_u32(dst, vget_low_u32(v4));
+        dst   += 2;
+        count -= 2;
+    }
+    SkASSERT(count < 2);
+    if (count > 0) {
+        *dst = value;
+    }
+}
+
+void sk_memset16_neon(uint16_t dst[], uint16_t value, int count) {
+    uint16x8_t   v8  = vdupq_n_u16(value);
+    uint16x8x4_t v32 = { v8, v8, v8, v8 };
+
+    while (count >= 32) {
+        vst4q_u16(dst, v32);  // This swizzles, but we don't care: all lanes are the same, value.
+        dst   += 32;
+        count -= 32;
+    }
+    SkASSERT(count < 32);
+    switch (count / 8) {
+        case 3: vst1q_u16(dst, v8); dst += 8; count -= 8;
+        case 2: vst1q_u16(dst, v8); dst += 8; count -= 8;
+        case 1: vst1q_u16(dst, v8); dst += 8; count -= 8;
+    }
+    SkASSERT(count < 8);
+    if (count >= 4) {
+        vst1_u16(dst, vget_low_u16(v8));
+        dst   += 4;
+        count -= 4;
+    }
+    SkASSERT(count < 4);
+    switch (count) {
+        case 3: *dst++ = value;
+        case 2: *dst++ = value;
+        case 1: *dst   = value;
+    }
+}
+
diff --git a/src/opts/SkXfermode_opts_SSE2.h b/src/opts/SkXfermode_opts_SSE2.h
index 7ccce48..927e5f4 100644
--- a/src/opts/SkXfermode_opts_SSE2.h
+++ b/src/opts/SkXfermode_opts_SSE2.h
@@ -17,10 +17,10 @@
                             void* procSIMD)
         : INHERITED(rec, mode), fProcSIMD(procSIMD) {}
 
-    virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) const SK_OVERRIDE;
-    virtual void xfer16(uint16_t dst[], const SkPMColor src[],
-                        int count, const SkAlpha aa[]) const SK_OVERRIDE;
+    void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                const SkAlpha aa[]) const override;
+    void xfer16(uint16_t dst[], const SkPMColor src[],
+                int count, const SkAlpha aa[]) const override;
 
     SK_TO_STRING_OVERRIDE()
 
diff --git a/src/opts/SkXfermode_opts_arm_neon.h b/src/opts/SkXfermode_opts_arm_neon.h
index f091432..e3c9778 100644
--- a/src/opts/SkXfermode_opts_arm_neon.h
+++ b/src/opts/SkXfermode_opts_arm_neon.h
@@ -9,10 +9,10 @@
                             void* procSIMD)
             : INHERITED(rec, mode), fProcSIMD(procSIMD) {}
 
-    virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) const SK_OVERRIDE;
-    virtual void xfer16(uint16_t* SK_RESTRICT dst, const SkPMColor* SK_RESTRICT src,
-                        int count, const SkAlpha* SK_RESTRICT aa) const SK_OVERRIDE;
+    void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                const SkAlpha aa[]) const override;
+    void xfer16(uint16_t* SK_RESTRICT dst, const SkPMColor* SK_RESTRICT src,
+                int count, const SkAlpha* SK_RESTRICT aa) const override;
 
     SK_TO_STRING_OVERRIDE()
 
diff --git a/src/opts/memset.arm.S b/src/opts/memset.arm.S
deleted file mode 100644
index 8c82f74..0000000
--- a/src/opts/memset.arm.S
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/* Changes:
- * 2010-08-11 Steve McIntyre <steve.mcintyre@arm.com>
- *    Added small changes to the two functions to make them work on the
- *    specified number of 16- or 32-bit values rather than the original
- *    code which was specified as a count of bytes. More verbose comments
- *    to aid future maintenance.
- */
-
-    .text
-    .align 4
-    .syntax unified
-
-    .global arm_memset32
-    .hidden arm_memset32
-    .type   arm_memset32, %function
-    .global arm_memset16
-    .hidden arm_memset16
-    .type   arm_memset16, %function
-
-/*
- * Optimized memset functions for ARM.
- *
- * void arm_memset16(uint16_t* dst, uint16_t value, int count);
- * void arm_memset32(uint32_t* dst, uint32_t value, int count);
- *
- */
-arm_memset16:
-        .fnstart
-        push        {lr}
-
-        /* if count is equal to zero then abort */
-        teq         r2, #0
-        ble         .Lfinish
-
-        /* Multiply count by 2 - go from the number of 16-bit shorts
-         * to the number of bytes desired. */
-        mov         r2, r2, lsl #1
-
-        /* expand the data to 32 bits */
-        orr         r1, r1, r1, lsl #16
-
-        /* align to 32 bits */
-        tst         r0, #2
-        strhne      r1, [r0], #2
-        subne       r2, r2, #2
-
-        /* Now jump into the main loop below. */
-        b           .Lwork_32
-        .fnend
-
-arm_memset32:
-        .fnstart
-        push        {lr}
-
-        /* if count is equal to zero then abort */
-        teq         r2, #0
-        ble         .Lfinish
-
-        /* Multiply count by 4 - go from the number of 32-bit words to
-         * the number of bytes desired. */
-        mov         r2, r2, lsl #2
-
-.Lwork_32:
-        /* Set up registers ready for writing them out. */
-        mov         ip, r1
-        mov         lr, r1
-
-        /* Try to align the destination to a cache line. Assume 32
-         * byte (8 word) cache lines, it's the common case. */
-        rsb         r3, r0, #0
-        ands        r3, r3, #0x1C
-        beq         .Laligned32
-        cmp         r3, r2
-        andhi       r3, r2, #0x1C
-        sub         r2, r2, r3
-
-        /* (Optionally) write any unaligned leading bytes.
-         * (0-28 bytes, length in r3) */
-        movs        r3, r3, lsl #28
-        stmiacs     r0!, {r1, lr}
-        stmiacs     r0!, {r1, lr}
-        stmiami     r0!, {r1, lr}
-        movs        r3, r3, lsl #2
-        strcs       r1, [r0], #4
-
-        /* Now quickly loop through the cache-aligned data. */
-.Laligned32:
-        mov         r3, r1
-1:      subs        r2, r2, #32
-        stmiahs     r0!, {r1,r3,ip,lr}
-        stmiahs     r0!, {r1,r3,ip,lr}
-        bhs         1b
-        add         r2, r2, #32
-
-        /* (Optionally) store any remaining trailing bytes.
-         * (0-30 bytes, length in r2) */
-        movs        r2, r2, lsl #28
-        stmiacs     r0!, {r1,r3,ip,lr}
-        stmiami     r0!, {r1,lr}
-        movs        r2, r2, lsl #2
-        strcs       r1, [r0], #4
-        strhmi      lr, [r0], #2
-
-.Lfinish:
-        pop         {pc}
-        .fnend
diff --git a/src/opts/memset16_neon.S b/src/opts/memset16_neon.S
deleted file mode 100644
index a4c06ee..0000000
--- a/src/opts/memset16_neon.S
+++ /dev/null
@@ -1,144 +0,0 @@
-/***************************************************************************
- * Copyright (c) 2009,2010, Code Aurora Forum. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- ***************************************************************************/
-
-/***************************************************************************
-  Neon memset: Attempts to do a memset with Neon registers if possible,
-     Inputs:
-        s: The buffer to write to
-        c: The integer data to write to the buffer
-        n: The size_t count.
-     Outputs:
-
-***************************************************************************/
-
-
-        .syntax unified
-        .code 32
-        .fpu neon
-        .align 4
-        .globl memset16_neon
-        .hidden memset16_neon
-
-memset16_neon:
-        cmp             r2, #0
-        bxeq            lr
-
-        /* Keep in mind that r2 -- the count argument -- is for the
-         * number of 16-bit items to copy.
-         */
-        lsl             r2, r2, #1
-
-        push            {r0}
-
-        /* If we have < 8 bytes, just do a quick loop to handle that */
-        cmp             r2, #8
-        bgt             memset_gt4
-memset_smallcopy_loop:
-        strh            r1, [r0], #2
-        subs            r2, r2, #2
-        bne             memset_smallcopy_loop
-memset_smallcopy_done:
-        pop             {r0}
-        bx              lr
-
-memset_gt4:
-        /*
-         * Duplicate the r1 lowest 16-bits across r1. The idea is to have
-         * a register with two 16-bit-values we can copy. We do this by
-         * duplicating lowest 16-bits of r1 to upper 16-bits.
-         */
-        orr             r1, r1, r1, lsl #16
-        /*
-         * If we're copying > 64 bytes, then we may want to get
-         * onto a 16-byte boundary to improve speed even more.
-         */
-        cmp             r2, #64
-        blt             memset_route
-        ands            r12, r0, #0xf
-        beq             memset_route
-        /*
-         * Determine the number of bytes to move forward to get to the 16-byte
-         * boundary.  Note that this will be a multiple of 4, since we
-         * already are word-aligned.
-         */
-        rsb             r12, r12, #16
-        sub             r2, r2, r12
-        lsls            r12, r12, #29
-        strmi           r1, [r0], #4
-        strcs           r1, [r0], #4
-        strcs           r1, [r0], #4
-        lsls            r12, r12, #2
-        strhcs          r1, [r0], #2
-memset_route:
-        /*
-         * Decide where to route for the maximum copy sizes.  Note that we
-         * build q0 and q1 depending on if we'll need it, so that's
-         * interwoven here as well.
-         */
-        vdup.u32        d0, r1
-        cmp             r2, #16
-        blt             memset_8
-        vmov            d1, d0
-        cmp             r2, #64
-        blt             memset_16
-        vmov            q1, q0
-        cmp             r2, #128
-        blt             memset_32
-memset_128:
-        mov             r12, r2, lsr #7
-memset_128_loop:
-        vst1.64         {q0, q1}, [r0]!
-        vst1.64         {q0, q1}, [r0]!
-        vst1.64         {q0, q1}, [r0]!
-        vst1.64         {q0, q1}, [r0]!
-        subs            r12, r12, #1
-        bne             memset_128_loop
-        ands            r2, r2, #0x7f
-        beq             memset_end
-memset_32:
-        movs            r12, r2, lsr #5
-        beq             memset_16
-memset_32_loop:
-        subs            r12, r12, #1
-        vst1.64         {q0, q1}, [r0]!
-        bne             memset_32_loop
-        ands            r2, r2, #0x1f
-        beq             memset_end
-memset_16:
-        movs            r12, r2, lsr #4
-        beq             memset_8
-memset_16_loop:
-        subs            r12, r12, #1
-        vst1.32         {q0}, [r0]!
-        bne             memset_16_loop
-        ands            r2, r2, #0xf
-        beq             memset_end
-        /*
-         * memset_8 isn't a loop, since we try to do our loops at 16
-         * bytes and above.  We should loop there, then drop down here
-         * to finish the <16-byte versions.  Same for memset_4 and
-         * memset_1.
-         */
-memset_8:
-        cmp             r2, #8
-        blt             memset_4
-        subs            r2, r2, #8
-        vst1.32         {d0}, [r0]!
-memset_4:
-        cmp             r2, #4
-        blt             memset_2
-        subs            r2, r2, #4
-        str             r1, [r0], #4
-memset_2:
-        cmp             r2, #0
-        ble             memset_end
-        strh            r1, [r0], #2
-memset_end:
-        pop             {r0}
-        bx              lr
-
-        .end
diff --git a/src/opts/memset32_neon.S b/src/opts/memset32_neon.S
deleted file mode 100644
index 7e171bb..0000000
--- a/src/opts/memset32_neon.S
+++ /dev/null
@@ -1,113 +0,0 @@
-/***************************************************************************
- * Copyright (c) 2009,2010, Code Aurora Forum. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- ***************************************************************************/
-
-        .syntax unified
-	.code 32
-	.fpu neon
-	.align 4
-	.globl	memset32_neon
-	.hidden memset32_neon
-
-	/* r0 = buffer, r1 = value, r2 = times to write */
-memset32_neon:
-	cmp		r2, #1
-	streq		r1, [r0], #4
-	bxeq		lr
-
-	cmp		r2, #4
-	bgt		memset32_neon_start
-	cmp		r2, #0
-	bxeq		lr
-memset32_neon_small:
-	str		r1, [r0], #4
-	subs		r2, r2, #1
-	bne		memset32_neon_small
-	bx		lr
-memset32_neon_start:
-	cmp		r2, #16
-	blt		memset32_dropthru
-	vdup.32		q0, r1
-	vmov		q1, q0
-	cmp		r2, #32
-	blt		memset32_16
-	cmp		r2, #64
-	blt		memset32_32
-	cmp		r2, #128
-	blt		memset32_64
-memset32_128:
-	movs		r12, r2, lsr #7
-memset32_loop128:
-	subs		r12, r12, #1
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	bne		memset32_loop128
-	ands		r2, r2, #0x7f
-	bxeq		lr
-memset32_64:
-	movs		r12, r2, lsr #6
-	beq		memset32_32
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	ands		r2, r2, #0x3f
-	bxeq		lr
-memset32_32:
-	movs		r12, r2, lsr #5
-	beq		memset32_16
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-	ands		r2, r2, #0x1f
-	bxeq		lr
-memset32_16:
-	movs		r12, r2, lsr #4
-	beq		memset32_dropthru
-	and		r2, r2, #0xf
-	vst1.64		{q0, q1}, [r0]!
-	vst1.64		{q0, q1}, [r0]!
-memset32_dropthru:
-	rsb		r2, r2, #15
-	add		pc, pc, r2, lsl #2
-	nop
-	str		r1, [r0, #56]
-	str		r1, [r0, #52]
-	str		r1, [r0, #48]
-	str		r1, [r0, #44]
-	str		r1, [r0, #40]
-	str		r1, [r0, #36]
-	str		r1, [r0, #32]
-	str		r1, [r0, #28]
-	str		r1, [r0, #24]
-	str		r1, [r0, #20]
-	str		r1, [r0, #16]
-	str		r1, [r0, #12]
-	str		r1, [r0, #8]
-	str		r1, [r0, #4]
-	str		r1, [r0, #0]
-	bx		lr
-
-	.end
diff --git a/src/opts/opts_check_x86.cpp b/src/opts/opts_check_x86.cpp
index 6b9758c..2597a2b 100644
--- a/src/opts/opts_check_x86.cpp
+++ b/src/opts/opts_check_x86.cpp
@@ -215,14 +215,19 @@
     }
 }
 
-static const SkBlitRow::ColorProc16 platform_565_colorprocs_SSE4[] = {
-    Color32A_D565_SSE4,                 // Color32A_D565,
+static const SkBlitRow::ColorProc16 platform_565_colorprocs_SSE2[] = {
+    Color32A_D565_SSE2,                 // Color32A_D565,
     NULL,                               // Color32A_D565_Dither
 };
 
 SkBlitRow::ColorProc16 SkBlitRow::PlatformColorFactory565(unsigned flags) {
-    if (supports_simd(SK_CPU_SSE_LEVEL_SSE41)) {
-        return platform_565_colorprocs_SSE4[flags];
+/* If you're thinking about writing an SSE4 version of this, do check it's
+ * actually faster on Atom. Our original SSE4 version was slower than this
+ * SSE2 version on Silvermont, and only marginally faster on a Core i7,
+ * mainly due to the MULLD timings.
+ */
+    if (supports_simd(SK_CPU_SSE_LEVEL_SSE2)) {
+        return platform_565_colorprocs_SSE2[flags];
     } else {
         return NULL;
     }
@@ -253,14 +258,6 @@
     }
 }
 
-SkBlitRow::ColorProc SkBlitRow::PlatformColorProc() {
-    if (supports_simd(SK_CPU_SSE_LEVEL_SSE2)) {
-        return Color32_SSE2;
-    } else {
-        return NULL;
-    }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
 SkBlitMask::ColorProc SkBlitMask::PlatformColorProcs(SkColorType dstCT,
@@ -353,20 +350,15 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SkBoxBlurGetPlatformProcs(SkBoxBlurProc* boxBlurX,
-                               SkBoxBlurProc* boxBlurY,
                                SkBoxBlurProc* boxBlurXY,
                                SkBoxBlurProc* boxBlurYX) {
-#ifdef SK_DISABLE_BLUR_DIVISION_OPTIMIZATION
-    return false;
-#else
     if (supports_simd(SK_CPU_SSE_LEVEL_SSE41)) {
-        return SkBoxBlurGetPlatformProcs_SSE4(boxBlurX, boxBlurY, boxBlurXY, boxBlurYX);
+        return SkBoxBlurGetPlatformProcs_SSE4(boxBlurX, boxBlurXY, boxBlurYX);
     }
     else if (supports_simd(SK_CPU_SSE_LEVEL_SSE2)) {
-        return SkBoxBlurGetPlatformProcs_SSE2(boxBlurX, boxBlurY, boxBlurXY, boxBlurYX);
+        return SkBoxBlurGetPlatformProcs_SSE2(boxBlurX, boxBlurXY, boxBlurYX);
     }
     return false;
-#endif
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index c27434f..8bdb70b 100644
--- a/src/pathops/SkAddIntersections.cpp
+++ b/src/pathops/SkAddIntersections.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
 #include "SkPathOpsBounds.h"
 
 #if DEBUG_ADD_INTERSECTING_TS
@@ -70,6 +71,67 @@
     SkDebugf("\n");
 }
 
+static void debugShowConicLineIntersection(int pts, const SkIntersectionHelper& wt,
+        const SkIntersectionHelper& wn, const SkIntersections& i) {
+    SkASSERT(i.used() == pts);
+    if (!pts) {
+        SkDebugf("%s no intersect " CONIC_DEBUG_STR " " LINE_DEBUG_STR "\n",
+                __FUNCTION__, CONIC_DEBUG_DATA(wt.pts(), wt.weight()), LINE_DEBUG_DATA(wn.pts()));
+        return;
+    }
+    SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CONIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__,
+            i[0][0], CONIC_DEBUG_DATA(wt.pts(), wt.weight()), PT_DEBUG_DATA(i, 0));
+    for (int n = 1; n < pts; ++n) {
+        SkDebugf(" " TX_DEBUG_STR(wtTs) " " PT_DEBUG_STR, n, i[0][n], PT_DEBUG_DATA(i, n));
+    }
+    SkDebugf(" wnTs[0]=%g " LINE_DEBUG_STR, i[1][0], LINE_DEBUG_DATA(wn.pts()));
+    for (int n = 1; n < pts; ++n) {
+        SkDebugf(" " TX_DEBUG_STR(wnTs), n, i[1][n]);
+    }
+    SkDebugf("\n");
+}
+
+static void debugShowConicQuadIntersection(int pts, const SkIntersectionHelper& wt,
+        const SkIntersectionHelper& wn, const SkIntersections& i) {
+    SkASSERT(i.used() == pts);
+    if (!pts) {
+        SkDebugf("%s no intersect " CONIC_DEBUG_STR " " QUAD_DEBUG_STR "\n",
+                __FUNCTION__, CONIC_DEBUG_DATA(wt.pts(), wt.weight()), QUAD_DEBUG_DATA(wn.pts()));
+        return;
+    }
+    SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CONIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__,
+            i[0][0], CONIC_DEBUG_DATA(wt.pts(), wt.weight()), PT_DEBUG_DATA(i, 0));
+    for (int n = 1; n < pts; ++n) {
+        SkDebugf(" " TX_DEBUG_STR(wtTs) " " PT_DEBUG_STR, n, i[0][n], PT_DEBUG_DATA(i, n));
+    }
+    SkDebugf(" wnTs[0]=%g " QUAD_DEBUG_STR, i[1][0], QUAD_DEBUG_DATA(wn.pts()));
+    for (int n = 1; n < pts; ++n) {
+        SkDebugf(" " TX_DEBUG_STR(wnTs), n, i[1][n]);
+    }
+    SkDebugf("\n");
+}
+
+static void debugShowConicIntersection(int pts, const SkIntersectionHelper& wt,
+        const SkIntersectionHelper& wn, const SkIntersections& i) {
+    SkASSERT(i.used() == pts);
+    if (!pts) {
+        SkDebugf("%s no intersect " CONIC_DEBUG_STR " " CONIC_DEBUG_STR "\n",
+                __FUNCTION__, CONIC_DEBUG_DATA(wt.pts(), wt.weight()),
+                CONIC_DEBUG_DATA(wn.pts(), wn.weight()));
+        return;
+    }
+    SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CONIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__,
+            i[0][0], CONIC_DEBUG_DATA(wt.pts(), wt.weight()), PT_DEBUG_DATA(i, 0));
+    for (int n = 1; n < pts; ++n) {
+        SkDebugf(" " TX_DEBUG_STR(wtTs) " " PT_DEBUG_STR, n, i[0][n], PT_DEBUG_DATA(i, n));
+    }
+    SkDebugf(" wnTs[0]=%g " CONIC_DEBUG_STR, i[1][0], CONIC_DEBUG_DATA(wn.pts(), wn.weight()));
+    for (int n = 1; n < pts; ++n) {
+        SkDebugf(" " TX_DEBUG_STR(wnTs), n, i[1][n]);
+    }
+    SkDebugf("\n");
+}
+
 static void debugShowCubicLineIntersection(int pts, const SkIntersectionHelper& wt,
         const SkIntersectionHelper& wn, const SkIntersections& i) {
     SkASSERT(i.used() == pts);
@@ -110,6 +172,26 @@
     SkDebugf("\n");
 }
 
+static void debugShowCubicConicIntersection(int pts, const SkIntersectionHelper& wt,
+        const SkIntersectionHelper& wn, const SkIntersections& i) {
+    SkASSERT(i.used() == pts);
+    if (!pts) {
+        SkDebugf("%s no intersect " CUBIC_DEBUG_STR " " CONIC_DEBUG_STR "\n",
+                __FUNCTION__, CUBIC_DEBUG_DATA(wt.pts()), CONIC_DEBUG_DATA(wn.pts(), wn.weight()));
+        return;
+    }
+    SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CUBIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__,
+            i[0][0], CUBIC_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0));
+    for (int n = 1; n < pts; ++n) {
+        SkDebugf(" " TX_DEBUG_STR(wtTs) " " PT_DEBUG_STR, n, i[0][n], PT_DEBUG_DATA(i, n));
+    }
+    SkDebugf(" wnTs[0]=%g " CONIC_DEBUG_STR, i[1][0], CONIC_DEBUG_DATA(wn.pts(), wn.weight()));
+    for (int n = 1; n < pts; ++n) {
+        SkDebugf(" " TX_DEBUG_STR(wnTs), n, i[1][n]);
+    }
+    SkDebugf("\n");
+}
+
 static void debugShowCubicIntersection(int pts, const SkIntersectionHelper& wt,
         const SkIntersectionHelper& wn, const SkIntersections& i) {
     SkASSERT(i.used() == pts);
@@ -130,20 +212,6 @@
     SkDebugf("\n");
 }
 
-static void debugShowCubicIntersection(int pts, const SkIntersectionHelper& wt,
-        const SkIntersections& i) {
-    SkASSERT(i.used() == pts);
-    if (!pts) {
-        SkDebugf("%s no self intersect " CUBIC_DEBUG_STR "\n", __FUNCTION__,
-                CUBIC_DEBUG_DATA(wt.pts()));
-        return;
-    }
-    SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CUBIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__,
-            i[0][0], CUBIC_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0));
-    SkDebugf(" " T_DEBUG_STR(wtTs, 1), i[1][0]);
-    SkDebugf("\n");
-}
-
 #else
 static void debugShowLineIntersection(int , const SkIntersectionHelper& ,
         const SkIntersectionHelper& , const SkIntersections& ) {
@@ -157,6 +225,18 @@
         const SkIntersectionHelper& , const SkIntersections& ) {
 }
 
+static void debugShowConicLineIntersection(int , const SkIntersectionHelper& ,
+        const SkIntersectionHelper& , const SkIntersections& ) {
+}
+
+static void debugShowConicQuadIntersection(int , const SkIntersectionHelper& ,
+        const SkIntersectionHelper& , const SkIntersections& ) {
+}
+
+static void debugShowConicIntersection(int , const SkIntersectionHelper& ,
+        const SkIntersectionHelper& , const SkIntersections& ) {
+}
+
 static void debugShowCubicLineIntersection(int , const SkIntersectionHelper& ,
         const SkIntersectionHelper& , const SkIntersections& ) {
 }
@@ -165,16 +245,17 @@
         const SkIntersectionHelper& , const SkIntersections& ) {
 }
 
-static void debugShowCubicIntersection(int , const SkIntersectionHelper& ,
+static void debugShowCubicConicIntersection(int , const SkIntersectionHelper& ,
         const SkIntersectionHelper& , const SkIntersections& ) {
 }
 
 static void debugShowCubicIntersection(int , const SkIntersectionHelper& ,
-        const SkIntersections& ) {
+        const SkIntersectionHelper& , const SkIntersections& ) {
 }
 #endif
 
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
+        SkChunkAlloc* allocator) {
     if (test != next) {
         if (AlmostLessUlps(test->bounds().fBottom, next->bounds().fTop)) {
             return false;
@@ -186,10 +267,11 @@
     }
     SkIntersectionHelper wt;
     wt.init(test);
-    bool foundCommonContour = test == next;
     do {
         SkIntersectionHelper wn;
         wn.init(next);
+        test->debugValidate();
+        next->debugValidate();
         if (test == next && !wn.startAfter(wt)) {
             continue;
         }
@@ -200,30 +282,35 @@
             int pts = 0;
             SkIntersections ts;
             bool swap = false;
+            SkDQuad quad1, quad2;
+            SkDConic conic1, conic2;
+            SkDCubic cubic1, cubic2;
             switch (wt.segmentType()) {
                 case SkIntersectionHelper::kHorizontalLine_Segment:
                     swap = true;
                     switch (wn.segmentType()) {
                         case SkIntersectionHelper::kHorizontalLine_Segment:
                         case SkIntersectionHelper::kVerticalLine_Segment:
-                        case SkIntersectionHelper::kLine_Segment: {
+                        case SkIntersectionHelper::kLine_Segment:
                             pts = ts.lineHorizontal(wn.pts(), wt.left(),
                                     wt.right(), wt.y(), wt.xFlipped());
                             debugShowLineIntersection(pts, wn, wt, ts);
                             break;
-                        }
-                        case SkIntersectionHelper::kQuad_Segment: {
+                        case SkIntersectionHelper::kQuad_Segment:
                             pts = ts.quadHorizontal(wn.pts(), wt.left(),
                                     wt.right(), wt.y(), wt.xFlipped());
                             debugShowQuadLineIntersection(pts, wn, wt, ts);
                             break;
-                        }
-                        case SkIntersectionHelper::kCubic_Segment: {
+                        case SkIntersectionHelper::kConic_Segment:
+                            pts = ts.conicHorizontal(wn.pts(), wn.weight(), wt.left(),
+                                    wt.right(), wt.y(), wt.xFlipped());
+                            debugShowConicLineIntersection(pts, wn, wt, ts);
+                            break;
+                        case SkIntersectionHelper::kCubic_Segment:
                             pts = ts.cubicHorizontal(wn.pts(), wt.left(),
                                     wt.right(), wt.y(), wt.xFlipped());
                             debugShowCubicLineIntersection(pts, wn, wt, ts);
                             break;
-                        }
                         default:
                             SkASSERT(0);
                     }
@@ -245,6 +332,12 @@
                             debugShowQuadLineIntersection(pts, wn, wt, ts);
                             break;
                         }
+                        case SkIntersectionHelper::kConic_Segment: {
+                            pts = ts.conicVertical(wn.pts(), wn.weight(), wt.top(),
+                                    wt.bottom(), wt.x(), wt.yFlipped());
+                            debugShowConicLineIntersection(pts, wn, wt, ts);
+                            break;
+                        }
                         case SkIntersectionHelper::kCubic_Segment: {
                             pts = ts.cubicVertical(wn.pts(), wt.top(),
                                     wt.bottom(), wt.x(), wt.yFlipped());
@@ -267,23 +360,25 @@
                                     wn.bottom(), wn.x(), wn.yFlipped());
                             debugShowLineIntersection(pts, wt, wn, ts);
                             break;
-                        case SkIntersectionHelper::kLine_Segment: {
+                        case SkIntersectionHelper::kLine_Segment:
                             pts = ts.lineLine(wt.pts(), wn.pts());
                             debugShowLineIntersection(pts, wt, wn, ts);
                             break;
-                        }
-                        case SkIntersectionHelper::kQuad_Segment: {
+                        case SkIntersectionHelper::kQuad_Segment:
                             swap = true;
                             pts = ts.quadLine(wn.pts(), wt.pts());
                             debugShowQuadLineIntersection(pts, wn, wt, ts);
                             break;
-                        }
-                        case SkIntersectionHelper::kCubic_Segment: {
+                        case SkIntersectionHelper::kConic_Segment:
+                            swap = true;
+                            pts = ts.conicLine(wn.pts(), wn.weight(), wt.pts());
+                            debugShowConicLineIntersection(pts, wn, wt, ts);
+                            break;
+                        case SkIntersectionHelper::kCubic_Segment:
                             swap = true;
                             pts = ts.cubicLine(wn.pts(), wt.pts());
-                            debugShowCubicLineIntersection(pts, wn, wt,  ts);
+                            debugShowCubicLineIntersection(pts, wn, wt, ts);
                             break;
-                        }
                         default:
                             SkASSERT(0);
                     }
@@ -300,20 +395,25 @@
                                     wn.bottom(), wn.x(), wn.yFlipped());
                             debugShowQuadLineIntersection(pts, wt, wn, ts);
                             break;
-                        case SkIntersectionHelper::kLine_Segment: {
+                        case SkIntersectionHelper::kLine_Segment:
                             pts = ts.quadLine(wt.pts(), wn.pts());
                             debugShowQuadLineIntersection(pts, wt, wn, ts);
                             break;
-                        }
                         case SkIntersectionHelper::kQuad_Segment: {
-                            pts = ts.quadQuad(wt.pts(), wn.pts());
-                            ts.alignQuadPts(wt.pts(), wn.pts());
+                            pts = ts.intersect(quad1.set(wt.pts()), quad2.set(wn.pts()));
                             debugShowQuadIntersection(pts, wt, wn, ts);
                             break;
                         }
+                        case SkIntersectionHelper::kConic_Segment: {
+                            swap = true;
+                            pts = ts.intersect(conic2.set(wn.pts(), wn.weight()),
+                                    quad1.set(wt.pts()));
+                            debugShowConicQuadIntersection(pts, wn, wt, ts);
+                            break;
+                        }
                         case SkIntersectionHelper::kCubic_Segment: {
                             swap = true;
-                            pts = ts.cubicQuad(wn.pts(), wt.pts());
+                            pts = ts.intersect(cubic2.set(wn.pts()), quad1.set(wt.pts()));
                             debugShowCubicQuadIntersection(pts, wn, wt, ts);
                             break;
                         }
@@ -321,6 +421,43 @@
                             SkASSERT(0);
                     }
                     break;
+                case SkIntersectionHelper::kConic_Segment:
+                    switch (wn.segmentType()) {
+                        case SkIntersectionHelper::kHorizontalLine_Segment:
+                            pts = ts.conicHorizontal(wt.pts(), wt.weight(), wn.left(),
+                                    wn.right(), wn.y(), wn.xFlipped());
+                            debugShowConicLineIntersection(pts, wt, wn, ts);
+                            break;
+                        case SkIntersectionHelper::kVerticalLine_Segment:
+                            pts = ts.conicVertical(wt.pts(), wt.weight(), wn.top(),
+                                    wn.bottom(), wn.x(), wn.yFlipped());
+                            debugShowConicLineIntersection(pts, wt, wn, ts);
+                            break;
+                        case SkIntersectionHelper::kLine_Segment:
+                            pts = ts.conicLine(wt.pts(), wt.weight(), wn.pts());
+                            debugShowConicLineIntersection(pts, wt, wn, ts);
+                            break;
+                        case SkIntersectionHelper::kQuad_Segment: {
+                            pts = ts.intersect(conic1.set(wt.pts(), wt.weight()),
+                                    quad2.set(wn.pts()));
+                            debugShowConicQuadIntersection(pts, wt, wn, ts);
+                            break;
+                        }
+                        case SkIntersectionHelper::kConic_Segment: {
+                            pts = ts.intersect(conic1.set(wt.pts(), wt.weight()),
+                                    conic2.set(wn.pts(), wn.weight()));
+                            debugShowConicIntersection(pts, wt, wn, ts);
+                            break;
+                        }
+                        case SkIntersectionHelper::kCubic_Segment: {
+                            swap = true;
+                            pts = ts.intersect(cubic2.set(wn.pts()),
+                                    conic1.set(wt.pts(), wt.weight()));
+                            debugShowCubicConicIntersection(pts, wn, wt, ts);
+                            break;
+                        }
+                    }
+                    break;
                 case SkIntersectionHelper::kCubic_Segment:
                     switch (wn.segmentType()) {
                         case SkIntersectionHelper::kHorizontalLine_Segment:
@@ -333,18 +470,23 @@
                                     wn.bottom(), wn.x(), wn.yFlipped());
                             debugShowCubicLineIntersection(pts, wt, wn, ts);
                             break;
-                        case SkIntersectionHelper::kLine_Segment: {
+                        case SkIntersectionHelper::kLine_Segment:
                             pts = ts.cubicLine(wt.pts(), wn.pts());
                             debugShowCubicLineIntersection(pts, wt, wn, ts);
                             break;
-                        }
                         case SkIntersectionHelper::kQuad_Segment: {
-                            pts = ts.cubicQuad(wt.pts(), wn.pts());
+                            pts = ts.intersect(cubic1.set(wt.pts()), quad2.set(wn.pts()));
                             debugShowCubicQuadIntersection(pts, wt, wn, ts);
                             break;
                         }
+                        case SkIntersectionHelper::kConic_Segment: {
+                            pts = ts.intersect(cubic1.set(wt.pts()),
+                                    conic2.set(wn.pts(), wn.weight()));
+                            debugShowCubicConicIntersection(pts, wt, wn, ts);
+                            break;
+                        }
                         case SkIntersectionHelper::kCubic_Segment: {
-                            pts = ts.cubicCubic(wt.pts(), wn.pts());
+                            pts = ts.intersect(cubic1.set(wt.pts()), cubic2.set(wn.pts()));
                             debugShowCubicIntersection(pts, wt, wn, ts);
                             break;
                         }
@@ -355,102 +497,53 @@
                 default:
                     SkASSERT(0);
             }
-            if (!foundCommonContour && pts > 0) {
-                test->addCross(next);
-                next->addCross(test);
-                foundCommonContour = true;
-            }
-            // in addition to recording T values, record matching segment
-            if (pts == 2) {
-                if (wn.segmentType() <= SkIntersectionHelper::kLine_Segment
-                        && wt.segmentType() <= SkIntersectionHelper::kLine_Segment) {
-                    if (wt.addCoincident(wn, ts, swap)) {
-                        continue;
-                    }
-                    pts = ts.cleanUpCoincidence();  // prefer (t == 0 or t == 1)
-                } else if (wn.segmentType() >= SkIntersectionHelper::kQuad_Segment
-                        && wt.segmentType() >= SkIntersectionHelper::kQuad_Segment
-                        && ts.isCoincident(0)) {
-                    SkASSERT(ts.coincidentUsed() == 2);
-                    if (wt.addCoincident(wn, ts, swap)) {
-                        continue;
-                    }
-                    pts = ts.cleanUpCoincidence();  // prefer (t == 0 or t == 1)
-                }
-            }
-            if (pts >= 2) {
-                for (int pt = 0; pt < pts - 1; ++pt) {
-                    const SkDPoint& point = ts.pt(pt);
-                    const SkDPoint& next = ts.pt(pt + 1);
-                    if (wt.isPartial(ts[swap][pt], ts[swap][pt + 1], point, next)
-                            && wn.isPartial(ts[!swap][pt], ts[!swap][pt + 1], point, next)) {
-                        if (!wt.addPartialCoincident(wn, ts, pt, swap)) {
-                            // remove extra point if two map to same float values
-                            pts = ts.cleanUpCoincidence();  // prefer (t == 0 or t == 1)
-                        }
-                    }
-                }
-            }
+            int coinIndex = -1;
+            SkOpPtT* coinPtT[2];
             for (int pt = 0; pt < pts; ++pt) {
                 SkASSERT(ts[0][pt] >= 0 && ts[0][pt] <= 1);
                 SkASSERT(ts[1][pt] >= 0 && ts[1][pt] <= 1);
-                SkPoint point = ts.pt(pt).asSkPoint();
-                wt.alignTPt(wn, swap, pt, &ts, &point);
-                int testTAt = wt.addT(wn, point, ts[swap][pt]);
-                int nextTAt = wn.addT(wt, point, ts[!swap][pt]);
-                wt.addOtherT(testTAt, ts[!swap][pt], nextTAt);
-                wn.addOtherT(nextTAt, ts[swap][pt], testTAt);
+                wt.segment()->debugValidate();
+                SkOpPtT* testTAt = wt.segment()->addT(ts[swap][pt], SkOpSegment::kAllowAlias,
+                        allocator);
+                wn.segment()->debugValidate();
+                SkOpPtT* nextTAt = wn.segment()->addT(ts[!swap][pt], SkOpSegment::kAllowAlias,
+                        allocator);
+                testTAt->addOpp(nextTAt);
+                if (testTAt->fPt != nextTAt->fPt) {
+                    testTAt->span()->unaligned();
+                    nextTAt->span()->unaligned();
+                }
+                wt.segment()->debugValidate();
+                wn.segment()->debugValidate();
+                if (!ts.isCoincident(pt)) {
+                    continue;
+                }
+                if (coinIndex < 0) {
+                    coinPtT[0] = testTAt;
+                    coinPtT[1] = nextTAt;
+                    coinIndex = pt;
+                    continue;
+                }
+                if (coinPtT[0]->span() == testTAt->span()) {
+                    coinIndex = -1;
+                    continue;
+                }
+                if (coinPtT[1]->span() == nextTAt->span()) {
+                    coinIndex = -1;  // coincidence span collapsed
+                    continue;
+                }
+                if (swap) {
+                    SkTSwap(coinPtT[0], coinPtT[1]);
+                    SkTSwap(testTAt, nextTAt);
+                }
+                SkASSERT(coinPtT[0]->span()->t() < testTAt->span()->t());
+                coincidence->add(coinPtT[0], testTAt, coinPtT[1], nextTAt, allocator);
+                wt.segment()->debugValidate();
+                wn.segment()->debugValidate();
+                coinIndex = -1;
             }
+            SkASSERT(coinIndex < 0);  // expect coincidence to be paired
         } while (wn.advance());
     } while (wt.advance());
     return true;
 }
-
-void AddSelfIntersectTs(SkOpContour* test) {
-    SkIntersectionHelper wt;
-    wt.init(test);
-    do {
-        if (wt.segmentType() != SkIntersectionHelper::kCubic_Segment) {
-            continue;
-        }
-        SkIntersections ts;
-        int pts = ts.cubic(wt.pts());
-        debugShowCubicIntersection(pts, wt, ts);
-        if (!pts) {
-            continue;
-        }
-        SkASSERT(pts == 1);
-        SkASSERT(ts[0][0] >= 0 && ts[0][0] <= 1);
-        SkASSERT(ts[1][0] >= 0 && ts[1][0] <= 1);
-        SkPoint point = ts.pt(0).asSkPoint();
-        int testTAt = wt.addSelfT(point, ts[0][0]);
-        int nextTAt = wt.addSelfT(point, ts[1][0]);
-        wt.addOtherT(testTAt, ts[1][0], nextTAt);
-        wt.addOtherT(nextTAt, ts[0][0], testTAt);
-    } while (wt.advance());
-}
-
-// resolve any coincident pairs found while intersecting, and
-// see if coincidence is formed by clipping non-concident segments
-bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total) {
-    int contourCount = (*contourList).count();
-    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
-        SkOpContour* contour = (*contourList)[cIndex];
-        contour->resolveNearCoincidence();
-    }
-    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
-        SkOpContour* contour = (*contourList)[cIndex];
-        contour->addCoincidentPoints();
-    }
-    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
-        SkOpContour* contour = (*contourList)[cIndex];
-        if (!contour->calcCoincidentWinding()) {
-            return false;
-        }
-    }
-    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
-        SkOpContour* contour = (*contourList)[cIndex];
-        contour->calcPartialCoincidentWinding();
-    }
-    return true;
-}
diff --git a/src/pathops/SkAddIntersections.h b/src/pathops/SkAddIntersections.h
index 4c1947b..23654e5 100644
--- a/src/pathops/SkAddIntersections.h
+++ b/src/pathops/SkAddIntersections.h
@@ -9,10 +9,10 @@
 
 #include "SkIntersectionHelper.h"
 #include "SkIntersections.h"
-#include "SkTArray.h"
 
-bool AddIntersectTs(SkOpContour* test, SkOpContour* next);
-void AddSelfIntersectTs(SkOpContour* test);
-bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total);
+class SkOpCoincidence;
+
+bool AddIntersectTs(SkOpContour* test, SkOpContour* next, SkOpCoincidence* coincidence,
+        SkChunkAlloc* allocator);
 
 #endif
diff --git a/src/pathops/SkConicLineIntersection.cpp b/src/pathops/SkConicLineIntersection.cpp
new file mode 100644
index 0000000..aa1b12d
--- /dev/null
+++ b/src/pathops/SkConicLineIntersection.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkIntersections.h"
+#include "SkPathOpsConic.h"
+#include "SkPathOpsLine.h"
+
+class LineConicIntersections {
+public:
+    LineConicIntersections(const SkDConic& c, const SkDLine& l, SkIntersections* i)
+        : fConic(c)
+        , fLine(l)
+        , fIntersections(i)
+        , fAllowNear(true) {
+        i->setMax(3);  // allow short partial coincidence plus discrete intersection
+    }
+
+    void allowNear(bool allow) {
+        fAllowNear = allow;
+    }
+
+    int intersectRay(double roots[2]) {
+        return 0;
+    }
+};
+
+int SkIntersections::intersectRay(const SkDConic& conic, const SkDLine& line) {
+    LineConicIntersections c(conic, line, this);
+    fUsed = c.intersectRay(fT[0]);
+    for (int index = 0; index < fUsed; ++index) {
+        fPt[index] = conic.ptAtT(fT[0][index]);
+    }
+    return fUsed;
+}
diff --git a/src/pathops/SkDConicLineIntersection.cpp b/src/pathops/SkDConicLineIntersection.cpp
new file mode 100644
index 0000000..c9d825d
--- /dev/null
+++ b/src/pathops/SkDConicLineIntersection.cpp
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkIntersections.h"
+#include "SkPathOpsConic.h"
+#include "SkPathOpsLine.h"
+
+class LineConicIntersections {
+public:
+    enum PinTPoint {
+        kPointUninitialized,
+        kPointInitialized
+    };
+
+    LineConicIntersections(const SkDConic& c, const SkDLine& l, SkIntersections* i)
+        : fConic(c)
+        , fLine(&l)
+        , fIntersections(i)
+        , fAllowNear(true) {
+        i->setMax(3);  // allow short partial coincidence plus discrete intersection
+    }
+
+    LineConicIntersections(const SkDConic& c)
+        : fConic(c)
+        SkDEBUGPARAMS(fLine(NULL))
+        SkDEBUGPARAMS(fIntersections(NULL))
+        SkDEBUGPARAMS(fAllowNear(false)) {
+    }
+
+    void allowNear(bool allow) {
+        fAllowNear = allow;
+    }
+
+    void checkCoincident() {
+        int last = fIntersections->used() - 1;
+        for (int index = 0; index < last; ) {
+            double conicMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2;
+            SkDPoint conicMidPt = fConic.ptAtT(conicMidT);
+            double t = fLine->nearPoint(conicMidPt, NULL);
+            if (t < 0) {
+                ++index;
+                continue;
+            }
+            if (fIntersections->isCoincident(index)) {
+                fIntersections->removeOne(index);
+                --last;
+            } else if (fIntersections->isCoincident(index + 1)) {
+                fIntersections->removeOne(index + 1);
+                --last;
+            } else {
+                fIntersections->setCoincident(index++);
+            }
+            fIntersections->setCoincident(index);
+        }
+    }
+
+#ifdef SK_DEBUG
+    static bool close_to(double a, double b, const double c[3]) {
+        double max = SkTMax(-SkTMin(SkTMin(c[0], c[1]), c[2]), SkTMax(SkTMax(c[0], c[1]), c[2]));
+        return approximately_zero_when_compared_to(a - b, max);
+    }
+#endif
+    int horizontalIntersect(double axisIntercept, double roots[2]) {
+        double conicVals[] = { fConic[0].fY, fConic[1].fY, fConic[2].fY };
+        return this->validT(conicVals, axisIntercept, roots);
+    }
+
+    int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) {
+        this->addExactHorizontalEndPoints(left, right, axisIntercept);
+        if (fAllowNear) {
+            this->addNearHorizontalEndPoints(left, right, axisIntercept);
+        }
+        double roots[2];
+        int count = this->horizontalIntersect(axisIntercept, roots);
+        for (int index = 0; index < count; ++index) {
+            double conicT = roots[index];
+            SkDPoint pt = fConic.ptAtT(conicT);
+            SkDEBUGCODE_(double conicVals[] = { fConic[0].fY, fConic[1].fY, fConic[2].fY });
+            SkASSERT(close_to(pt.fY, axisIntercept, conicVals));
+            double lineT = (pt.fX - left) / (right - left);
+            if (this->pinTs(&conicT, &lineT, &pt, kPointInitialized)
+                    && this->uniqueAnswer(conicT, pt)) {
+                fIntersections->insert(conicT, lineT, pt);
+            }
+        }
+        if (flipped) {
+            fIntersections->flip();
+        }
+        this->checkCoincident();
+        return fIntersections->used();
+    }
+
+    int intersect() {
+        this->addExactEndPoints();
+        if (fAllowNear) {
+            this->addNearEndPoints();
+        }
+        double rootVals[2];
+        int roots = this->intersectRay(rootVals);
+        for (int index = 0; index < roots; ++index) {
+            double conicT = rootVals[index];
+            double lineT = this->findLineT(conicT);
+            SkDEBUGCODE(SkDPoint conicPt = fConic.ptAtT(conicT));
+            SkDEBUGCODE(SkDPoint linePt = fLine->ptAtT(lineT));
+            SkASSERT(conicPt.approximatelyEqual(linePt));
+            SkDPoint pt;
+            if (this->pinTs(&conicT, &lineT, &pt, kPointUninitialized)
+                    && this->uniqueAnswer(conicT, pt)) {
+                fIntersections->insert(conicT, lineT, pt);
+            }
+        }
+        this->checkCoincident();
+        return fIntersections->used();
+    }
+
+    int intersectRay(double roots[2]) {
+        double adj = (*fLine)[1].fX - (*fLine)[0].fX;
+        double opp = (*fLine)[1].fY - (*fLine)[0].fY;
+        double r[3];
+        for (int n = 0; n < 3; ++n) {
+            r[n] = (fConic[n].fY - (*fLine)[0].fY) * adj - (fConic[n].fX - (*fLine)[0].fX) * opp;
+        }
+        return this->validT(r, 0, roots);
+    }
+
+    int validT(double r[3], double axisIntercept, double roots[2]) {
+        double A = r[2];
+        double B = r[1] * fConic.fWeight - axisIntercept * fConic.fWeight + axisIntercept;
+        double C = r[0];
+        A += C - 2 * B;  // A = a + c - 2*(b*w - xCept*w + xCept)
+        B -= C;  // B = b*w - w * xCept + xCept - a
+        C -= axisIntercept;
+        return SkDQuad::RootsValidT(A, 2 * B, C, roots);
+    }
+
+    int verticalIntersect(double axisIntercept, double roots[2]) {
+        double conicVals[] = { fConic[0].fX, fConic[1].fX, fConic[2].fX };
+        return this->validT(conicVals, axisIntercept, roots);
+    }
+
+    int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) {
+        this->addExactVerticalEndPoints(top, bottom, axisIntercept);
+        if (fAllowNear) {
+            this->addNearVerticalEndPoints(top, bottom, axisIntercept);
+        }
+        double roots[2];
+        int count = this->verticalIntersect(axisIntercept, roots);
+        for (int index = 0; index < count; ++index) {
+            double conicT = roots[index];
+            SkDPoint pt = fConic.ptAtT(conicT);
+            SkDEBUGCODE_(double conicVals[] = { fConic[0].fX, fConic[1].fX, fConic[2].fX });
+            SkASSERT(close_to(pt.fX, axisIntercept, conicVals));
+            double lineT = (pt.fY - top) / (bottom - top);
+            if (this->pinTs(&conicT, &lineT, &pt, kPointInitialized)
+                    && this->uniqueAnswer(conicT, pt)) {
+                fIntersections->insert(conicT, lineT, pt);
+            }
+        }
+        if (flipped) {
+            fIntersections->flip();
+        }
+        this->checkCoincident();
+        return fIntersections->used();
+    }
+
+protected:
+// OPTIMIZE: Functions of the form add .. points are indentical to the conic routines.
+    // add endpoints first to get zero and one t values exactly
+    void addExactEndPoints() {
+        for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
+            double lineT = fLine->exactPoint(fConic[cIndex]);
+            if (lineT < 0) {
+                continue;
+            }
+            double conicT = (double) (cIndex >> 1);
+            fIntersections->insert(conicT, lineT, fConic[cIndex]);
+        }
+    }
+
+    void addNearEndPoints() {
+        for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
+            double conicT = (double) (cIndex >> 1);
+            if (fIntersections->hasT(conicT)) {
+                continue;
+            }
+            double lineT = fLine->nearPoint(fConic[cIndex], NULL);
+            if (lineT < 0) {
+                continue;
+            }
+            fIntersections->insert(conicT, lineT, fConic[cIndex]);
+        }
+        // FIXME: see if line end is nearly on conic
+    }
+
+    void addExactHorizontalEndPoints(double left, double right, double y) {
+        for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
+            double lineT = SkDLine::ExactPointH(fConic[cIndex], left, right, y);
+            if (lineT < 0) {
+                continue;
+            }
+            double conicT = (double) (cIndex >> 1);
+            fIntersections->insert(conicT, lineT, fConic[cIndex]);
+        }
+    }
+
+    void addNearHorizontalEndPoints(double left, double right, double y) {
+        for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
+            double conicT = (double) (cIndex >> 1);
+            if (fIntersections->hasT(conicT)) {
+                continue;
+            }
+            double lineT = SkDLine::NearPointH(fConic[cIndex], left, right, y);
+            if (lineT < 0) {
+                continue;
+            }
+            fIntersections->insert(conicT, lineT, fConic[cIndex]);
+        }
+        // FIXME: see if line end is nearly on conic
+    }
+
+    void addExactVerticalEndPoints(double top, double bottom, double x) {
+        for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
+            double lineT = SkDLine::ExactPointV(fConic[cIndex], top, bottom, x);
+            if (lineT < 0) {
+                continue;
+            }
+            double conicT = (double) (cIndex >> 1);
+            fIntersections->insert(conicT, lineT, fConic[cIndex]);
+        }
+    }
+
+    void addNearVerticalEndPoints(double top, double bottom, double x) {
+        for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) {
+            double conicT = (double) (cIndex >> 1);
+            if (fIntersections->hasT(conicT)) {
+                continue;
+            }
+            double lineT = SkDLine::NearPointV(fConic[cIndex], top, bottom, x);
+            if (lineT < 0) {
+                continue;
+            }
+            fIntersections->insert(conicT, lineT, fConic[cIndex]);
+        }
+        // FIXME: see if line end is nearly on conic
+    }
+
+    double findLineT(double t) {
+        SkDPoint xy = fConic.ptAtT(t);
+        double dx = (*fLine)[1].fX - (*fLine)[0].fX;
+        double dy = (*fLine)[1].fY - (*fLine)[0].fY;
+        if (fabs(dx) > fabs(dy)) {
+            return (xy.fX - (*fLine)[0].fX) / dx;
+        }
+        return (xy.fY - (*fLine)[0].fY) / dy;
+    }
+
+    bool pinTs(double* conicT, double* lineT, SkDPoint* pt, PinTPoint ptSet) {
+        if (!approximately_one_or_less_double(*lineT)) {
+            return false;
+        }
+        if (!approximately_zero_or_more_double(*lineT)) {
+            return false;
+        }
+        double qT = *conicT = SkPinT(*conicT);
+        double lT = *lineT = SkPinT(*lineT);
+        if (lT == 0 || lT == 1 || (ptSet == kPointUninitialized && qT != 0 && qT != 1)) {
+            *pt = (*fLine).ptAtT(lT);
+        } else if (ptSet == kPointUninitialized) {
+            *pt = fConic.ptAtT(qT);
+        }
+        SkPoint gridPt = pt->asSkPoint();
+        if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[0].asSkPoint())) {
+            *pt = (*fLine)[0];
+            *lineT = 0;
+        } else if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[1].asSkPoint())) {
+            *pt = (*fLine)[1];
+            *lineT = 1;
+        }
+        if (fIntersections->used() > 0 && approximately_equal((*fIntersections)[1][0], *lineT)) {
+            return false;
+        }
+        if (gridPt == fConic[0].asSkPoint()) {
+            *pt = fConic[0];
+            *conicT = 0;
+        } else if (gridPt == fConic[2].asSkPoint()) {
+            *pt = fConic[2];
+            *conicT = 1;
+        }
+        return true;
+    }
+
+    bool uniqueAnswer(double conicT, const SkDPoint& pt) {
+        for (int inner = 0; inner < fIntersections->used(); ++inner) {
+            if (fIntersections->pt(inner) != pt) {
+                continue;
+            }
+            double existingConicT = (*fIntersections)[0][inner];
+            if (conicT == existingConicT) {
+                return false;
+            }
+            // check if midway on conic is also same point. If so, discard this
+            double conicMidT = (existingConicT + conicT) / 2;
+            SkDPoint conicMidPt = fConic.ptAtT(conicMidT);
+            if (conicMidPt.approximatelyEqual(pt)) {
+                return false;
+            }
+        }
+#if ONE_OFF_DEBUG
+        SkDPoint qPt = fConic.ptAtT(conicT);
+        SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
+                qPt.fX, qPt.fY);
+#endif
+        return true;
+    }
+
+private:
+    const SkDConic& fConic;
+    const SkDLine* fLine;
+    SkIntersections* fIntersections;
+    bool fAllowNear;
+};
+
+int SkIntersections::horizontal(const SkDConic& conic, double left, double right, double y,
+                                bool flipped) {
+    SkDLine line = {{{ left, y }, { right, y }}};
+    LineConicIntersections c(conic, line, this);
+    return c.horizontalIntersect(y, left, right, flipped);
+}
+
+int SkIntersections::vertical(const SkDConic& conic, double top, double bottom, double x,
+                              bool flipped) {
+    SkDLine line = {{{ x, top }, { x, bottom }}};
+    LineConicIntersections c(conic, line, this);
+    return c.verticalIntersect(x, top, bottom, flipped);
+}
+
+int SkIntersections::intersect(const SkDConic& conic, const SkDLine& line) {
+    LineConicIntersections c(conic, line, this);
+    c.allowNear(fAllowNear);
+    return c.intersect();
+}
+
+int SkIntersections::intersectRay(const SkDConic& conic, const SkDLine& line) {
+    LineConicIntersections c(conic, line, this);
+    fUsed = c.intersectRay(fT[0]);
+    for (int index = 0; index < fUsed; ++index) {
+        fPt[index] = conic.ptAtT(fT[0][index]);
+    }
+    return fUsed;
+}
+
+int SkIntersections::HorizontalIntercept(const SkDConic& conic, SkScalar y, double* roots) {
+    LineConicIntersections c(conic);
+    return c.horizontalIntersect(y, roots);
+}
+
+int SkIntersections::VerticalIntercept(const SkDConic& conic, SkScalar x, double* roots) {
+    LineConicIntersections c(conic);
+    return c.verticalIntersect(x, roots);
+}
diff --git a/src/pathops/SkDCubicIntersection.cpp b/src/pathops/SkDCubicIntersection.cpp
deleted file mode 100644
index 2fb35e1..0000000
--- a/src/pathops/SkDCubicIntersection.cpp
+++ /dev/null
@@ -1,704 +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.
- */
-
-#include "SkIntersections.h"
-#include "SkPathOpsCubic.h"
-#include "SkPathOpsLine.h"
-#include "SkPathOpsPoint.h"
-#include "SkPathOpsQuad.h"
-#include "SkPathOpsRect.h"
-#include "SkReduceOrder.h"
-#include "SkTSort.h"
-
-#if ONE_OFF_DEBUG
-static const double tLimits1[2][2] = {{0.3, 0.4}, {0.8, 0.9}};
-static const double tLimits2[2][2] = {{-0.8, -0.9}, {-0.8, -0.9}};
-#endif
-
-#define DEBUG_QUAD_PART ONE_OFF_DEBUG && 1
-#define DEBUG_QUAD_PART_SHOW_SIMPLE DEBUG_QUAD_PART && 0
-#define SWAP_TOP_DEBUG 0
-
-static const int kCubicToQuadSubdivisionDepth = 8; // slots reserved for cubic to quads subdivision
-
-static int quadPart(const SkDCubic& cubic, double tStart, double tEnd, SkReduceOrder* reducer) {
-    SkDCubic part = cubic.subDivide(tStart, tEnd);
-    SkDQuad quad = part.toQuad();
-    // FIXME: should reduceOrder be looser in this use case if quartic is going to blow up on an
-    // extremely shallow quadratic?
-    int order = reducer->reduce(quad);
-#if DEBUG_QUAD_PART
-    SkDebugf("%s cubic=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
-            " t=(%1.9g,%1.9g)\n", __FUNCTION__, cubic[0].fX, cubic[0].fY,
-            cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
-            cubic[3].fX, cubic[3].fY, tStart, tEnd);
-    SkDebugf("  {{%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}},\n",
-            part[0].fX, part[0].fY, part[1].fX, part[1].fY, part[2].fX, part[2].fY,
-            part[3].fX, part[3].fY, quad[0].fX, quad[0].fY,
-            quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY);
-#if DEBUG_QUAD_PART_SHOW_SIMPLE
-    SkDebugf("%s simple=(%1.9g,%1.9g", __FUNCTION__, reducer->fQuad[0].fX, reducer->fQuad[0].fY);
-    if (order > 1) {
-        SkDebugf(" %1.9g,%1.9g", reducer->fQuad[1].fX, reducer->fQuad[1].fY);
-    }
-    if (order > 2) {
-        SkDebugf(" %1.9g,%1.9g", reducer->fQuad[2].fX, reducer->fQuad[2].fY);
-    }
-    SkDebugf(")\n");
-    SkASSERT(order < 4 && order > 0);
-#endif
-#endif
-    return order;
-}
-
-static void intersectWithOrder(const SkDQuad& simple1, int order1, const SkDQuad& simple2,
-        int order2, SkIntersections& i) {
-    if (order1 == 3 && order2 == 3) {
-        i.intersect(simple1, simple2);
-    } else if (order1 <= 2 && order2 <= 2) {
-        i.intersect((const SkDLine&) simple1, (const SkDLine&) simple2);
-    } else if (order1 == 3 && order2 <= 2) {
-        i.intersect(simple1, (const SkDLine&) simple2);
-    } else {
-        SkASSERT(order1 <= 2 && order2 == 3);
-        i.intersect(simple2, (const SkDLine&) simple1);
-        i.swapPts();
-    }
-}
-
-// this flavor centers potential intersections recursively. In contrast, '2' may inadvertently
-// chase intersections near quadratic ends, requiring odd hacks to find them.
-static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDCubic& cubic2,
-        double t2s, double t2e, double precisionScale, SkIntersections& i) {
-    i.upDepth();
-    SkDCubic c1 = cubic1.subDivide(t1s, t1e);
-    SkDCubic c2 = cubic2.subDivide(t2s, t2e);
-    SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts1;
-    // OPTIMIZE: if c1 == c2, call once (happens when detecting self-intersection)
-    c1.toQuadraticTs(c1.calcPrecision() * precisionScale, &ts1);
-    SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts2;
-    c2.toQuadraticTs(c2.calcPrecision() * 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;
-        SkReduceOrder s1;
-        int o1 = quadPart(cubic1, t1Start, t1, &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;
-            if (&cubic1 == &cubic2 && t1Start >= t2Start) {
-                t2Start = t2;
-                continue;
-            }
-            SkReduceOrder s2;
-            int o2 = quadPart(cubic2, t2Start, t2, &s2);
-        #if ONE_OFF_DEBUG
-            char tab[] = "                  ";
-            if (tLimits1[0][0] >= t1Start && tLimits1[0][1] <= t1
-                    && tLimits1[1][0] >= t2Start && tLimits1[1][1] <= t2) {
-                SkDebugf("%.*s %s t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)", i.depth()*2, tab,
-                        __FUNCTION__, t1Start, t1, t2Start, t2);
-                SkIntersections xlocals;
-                xlocals.allowNear(false);
-                xlocals.allowFlatMeasure(true);
-                intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, xlocals);
-                SkDebugf(" xlocals.fUsed=%d\n", xlocals.used());
-            }
-        #endif
-            SkIntersections locals;
-            locals.allowNear(false);
-            locals.allowFlatMeasure(true);
-            intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, locals);
-            int tCount = locals.used();
-            for (int tIdx = 0; tIdx < tCount; ++tIdx) {
-                double to1 = t1Start + (t1 - t1Start) * locals[0][tIdx];
-                double to2 = t2Start + (t2 - t2Start) * locals[1][tIdx];
-    // if the computed t is not sufficiently precise, iterate
-                SkDPoint p1 = cubic1.ptAtT(to1);
-                SkDPoint p2 = cubic2.ptAtT(to2);
-                if (p1.approximatelyEqual(p2)) {
-    // FIXME: local edge may be coincident -- experiment with not propagating coincidence to caller
-//                    SkASSERT(!locals.isCoincident(tIdx));
-                    if (&cubic1 != &cubic2 || !approximately_equal(to1, to2)) {
-                        if (i.swapped()) {  //  FIXME: insert should respect swap
-                            i.insert(to2, to1, p1);
-                        } else {
-                            i.insert(to1, to2, p1);
-                        }
-                    }
-                } else {
-/*for random cubics, 16 below catches 99.997% of the intersections. To test for the remaining 0.003%
-  look for nearly coincident curves. and check each 1/16th section.
-*/
-                    double offset = precisionScale / 16;  // FIXME: const is arbitrary: test, refine
-                    double c1Bottom = tIdx == 0 ? 0 :
-                            (t1Start + (t1 - t1Start) * locals[0][tIdx - 1] + to1) / 2;
-                    double c1Min = SkTMax(c1Bottom, to1 - offset);
-                    double c1Top = tIdx == tCount - 1 ? 1 :
-                            (t1Start + (t1 - t1Start) * locals[0][tIdx + 1] + to1) / 2;
-                    double c1Max = SkTMin(c1Top, to1 + offset);
-                    double c2Min = SkTMax(0., to2 - offset);
-                    double c2Max = SkTMin(1., to2 + offset);
-                #if ONE_OFF_DEBUG
-                    SkDebugf("%.*s %s 1 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
-                            __FUNCTION__,
-                            c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
-                         && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
-                            to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
-                         && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
-                            c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
-                         && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
-                            to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
-                         && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
-                    SkDebugf("%.*s %s 1 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
-                            " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
-                            i.depth()*2, tab, __FUNCTION__, c1Bottom, c1Top, 0., 1.,
-                            to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
-                    SkDebugf("%.*s %s 1 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
-                            " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
-                            c1Max, c2Min, c2Max);
-                #endif
-                    intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
-                #if ONE_OFF_DEBUG
-                    SkDebugf("%.*s %s 1 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
-                            i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
-                #endif
-                    if (tCount > 1) {
-                        c1Min = SkTMax(0., to1 - offset);
-                        c1Max = SkTMin(1., to1 + offset);
-                        double c2Bottom = tIdx == 0 ? to2 :
-                                (t2Start + (t2 - t2Start) * locals[1][tIdx - 1] + to2) / 2;
-                        double c2Top = tIdx == tCount - 1 ? to2 :
-                                (t2Start + (t2 - t2Start) * locals[1][tIdx + 1] + to2) / 2;
-                        if (c2Bottom > c2Top) {
-                            SkTSwap(c2Bottom, c2Top);
-                        }
-                        if (c2Bottom == to2) {
-                            c2Bottom = 0;
-                        }
-                        if (c2Top == to2) {
-                            c2Top = 1;
-                        }
-                        c2Min = SkTMax(c2Bottom, to2 - offset);
-                        c2Max = SkTMin(c2Top, to2 + offset);
-                    #if ONE_OFF_DEBUG
-                        SkDebugf("%.*s %s 2 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
-                            __FUNCTION__,
-                            c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
-                         && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
-                            to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
-                         && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
-                            c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
-                         && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
-                            to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
-                         && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
-                        SkDebugf("%.*s %s 2 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
-                                " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
-                                i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
-                                to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
-                        SkDebugf("%.*s %s 2 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
-                                " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
-                                c1Max, c2Min, c2Max);
-                    #endif
-                        intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
-                #if ONE_OFF_DEBUG
-                    SkDebugf("%.*s %s 2 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
-                            i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
-                #endif
-                        c1Min = SkTMax(c1Bottom, to1 - offset);
-                        c1Max = SkTMin(c1Top, to1 + offset);
-                    #if ONE_OFF_DEBUG
-                        SkDebugf("%.*s %s 3 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
-                        __FUNCTION__,
-                            c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
-                         && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
-                            to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
-                         && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
-                            c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
-                         && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
-                            to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
-                         && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
-                        SkDebugf("%.*s %s 3 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
-                                " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
-                                i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
-                                to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
-                        SkDebugf("%.*s %s 3 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
-                                " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
-                                c1Max, c2Min, c2Max);
-                    #endif
-                        intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
-                #if ONE_OFF_DEBUG
-                    SkDebugf("%.*s %s 3 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
-                            i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
-                #endif
-                    }
-          //          intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
-                    // FIXME: if no intersection is found, either quadratics intersected where
-                    // cubics did not, or the intersection was missed. In the former case, expect
-                    // the quadratics to be nearly parallel at the point of intersection, and check
-                    // for that.
-                }
-            }
-            t2Start = t2;
-        }
-        t1Start = t1;
-    }
-    i.downDepth();
-}
-
-    // if two ends intersect, check middle for coincidence
-bool SkIntersections::cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2) {
-    if (fUsed < 2) {
-        return false;
-    }
-    int last = fUsed - 1;
-    double tRange1 = fT[0][last] - fT[0][0];
-    double tRange2 = fT[1][last] - fT[1][0];
-    for (int index = 1; index < 5; ++index) {
-        double testT1 = fT[0][0] + tRange1 * index / 5;
-        double testT2 = fT[1][0] + tRange2 * index / 5;
-        SkDPoint testPt1 = c1.ptAtT(testT1);
-        SkDPoint testPt2 = c2.ptAtT(testT2);
-        if (!testPt1.approximatelyEqual(testPt2)) {
-            return false;
-        }
-    }
-    if (fUsed > 2) {
-        fPt[1] = fPt[last];
-        fT[0][1] = fT[0][last];
-        fT[1][1] = fT[1][last];
-        fUsed = 2;
-    }
-    fIsCoincident[0] = fIsCoincident[1] = 0x03;
-    return true;
-}
-
-#define LINE_FRACTION 0.1
-
-// intersect the end of the cubic with the other. Try lines from the end to control and opposite
-// end to determine range of t on opposite cubic.
-bool SkIntersections::cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2) {
-    int t1Index = start ? 0 : 3;
-    double testT = (double) !start;
-    bool swap = swapped();
-    // quad/quad at this point checks to see if exact matches have already been found
-    // cubic/cubic can't reject so easily since cubics can intersect same point more than once
-    SkDLine tmpLine;
-    tmpLine[0] = tmpLine[1] = cubic2[t1Index];
-    tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY;
-    tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX;
-    SkIntersections impTs;
-    impTs.allowNear(false);
-    impTs.allowFlatMeasure(true);
-    impTs.intersectRay(cubic1, tmpLine);
-    for (int index = 0; index < impTs.used(); ++index) {
-        SkDPoint realPt = impTs.pt(index);
-        if (!tmpLine[0].approximatelyEqual(realPt)) {
-            continue;
-        }
-        if (swap) {
-            cubicInsert(testT, impTs[0][index], tmpLine[0], cubic2, cubic1);
-        } else {
-            cubicInsert(impTs[0][index], testT, tmpLine[0], cubic1, cubic2);
-        }
-        return true;
-    }
-    return false;
-}
-
-
-void SkIntersections::cubicInsert(double one, double two, const SkDPoint& pt,
-        const SkDCubic& cubic1, const SkDCubic& cubic2) {
-    for (int index = 0; index < fUsed; ++index) {
-        if (fT[0][index] == one) {
-            double oldTwo = fT[1][index];
-            if (oldTwo == two) {
-                return;
-            }
-            SkDPoint mid = cubic2.ptAtT((oldTwo + two) / 2);
-            if (mid.approximatelyEqual(fPt[index])) {
-                return;
-            }
-        }
-        if (fT[1][index] == two) {
-            SkDPoint mid = cubic1.ptAtT((fT[0][index] + two) / 2);
-            if (mid.approximatelyEqual(fPt[index])) {
-                return;
-            }
-        }
-    }
-    insert(one, two, pt);
-}
-
-void SkIntersections::cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
-                         const SkDRect& bounds2) {
-    SkDLine line;
-    int t1Index = start ? 0 : 3;
-    double testT = (double) !start;
-   // don't bother if the two cubics are connnected
-    static const int kPointsInCubic = 4; // FIXME: move to DCubic, replace '4' with this
-    static const int kMaxLineCubicIntersections = 3;
-    SkSTArray<(kMaxLineCubicIntersections - 1) * kMaxLineCubicIntersections, double, true> tVals;
-    line[0] = cubic1[t1Index];
-    // this variant looks for intersections with the end point and lines parallel to other points
-    for (int index = 0; index < kPointsInCubic; ++index) {
-        if (index == t1Index) {
-            continue;
-        }
-        SkDVector dxy1 = cubic1[index] - line[0];
-        dxy1 /= SkDCubic::gPrecisionUnit;
-        line[1] = line[0] + dxy1;
-        SkDRect lineBounds;
-        lineBounds.setBounds(line);
-        if (!bounds2.intersects(&lineBounds)) {
-            continue;
-        }
-        SkIntersections local;
-        if (!local.intersect(cubic2, line)) {
-            continue;
-        }
-        for (int idx2 = 0; idx2 < local.used(); ++idx2) {
-            double foundT = local[0][idx2];
-            if (approximately_less_than_zero(foundT)
-                    || approximately_greater_than_one(foundT)) {
-                continue;
-            }
-            if (local.pt(idx2).approximatelyEqual(line[0])) {
-                if (swapped()) {  // FIXME: insert should respect swap
-                    insert(foundT, testT, line[0]);
-                } else {
-                    insert(testT, foundT, line[0]);
-                }
-            } else {
-                tVals.push_back(foundT);
-            }
-        }
-    }
-    if (tVals.count() == 0) {
-        return;
-    }
-    SkTQSort<double>(tVals.begin(), tVals.end() - 1);
-    double tMin1 = start ? 0 : 1 - LINE_FRACTION;
-    double tMax1 = start ? LINE_FRACTION : 1;
-    int tIdx = 0;
-    do {
-        int tLast = tIdx;
-        while (tLast + 1 < tVals.count() && roughly_equal(tVals[tLast + 1], tVals[tIdx])) {
-            ++tLast;
-        }
-        double tMin2 = SkTMax(tVals[tIdx] - LINE_FRACTION, 0.0);
-        double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0);
-        int lastUsed = used();
-        if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
-            ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
-        }
-        if (lastUsed == used()) {
-            tMin2 = SkTMax(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0);
-            tMax2 = SkTMin(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0);
-            if (start ? tMax1 < tMin2 : tMax2 < tMin1) {
-                ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
-            }
-        }
-        tIdx = tLast + 1;
-    } while (tIdx < tVals.count());
-    return;
-}
-
-const double CLOSE_ENOUGH = 0.001;
-
-static bool closeStart(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) {
-    if (i[cubicIndex][0] != 0 || i[cubicIndex][1] > CLOSE_ENOUGH) {
-        return false;
-    }
-    pt = cubic.ptAtT((i[cubicIndex][0] + i[cubicIndex][1]) / 2);
-    return true;
-}
-
-static bool closeEnd(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) {
-    int last = i.used() - 1;
-    if (i[cubicIndex][last] != 1 || i[cubicIndex][last - 1] < 1 - CLOSE_ENOUGH) {
-        return false;
-    }
-    pt = cubic.ptAtT((i[cubicIndex][last] + i[cubicIndex][last - 1]) / 2);
-    return true;
-}
-
-static bool only_end_pts_in_common(const SkDCubic& c1, const SkDCubic& c2) {
-// the idea here is to see at minimum do a quick reject by rotating all points
-// to either side of the line formed by connecting the endpoints
-// if the opposite curves points are on the line or on the other side, the
-// curves at most intersect at the endpoints
-    for (int oddMan = 0; oddMan < 4; ++oddMan) {
-        const SkDPoint* endPt[3];
-        for (int opp = 1; opp < 4; ++opp) {
-            int end = oddMan ^ opp;  // choose a value not equal to oddMan
-            endPt[opp - 1] = &c1[end];
-        }
-        for (int triTest = 0; triTest < 3; ++triTest) {
-            double origX = endPt[triTest]->fX;
-            double origY = endPt[triTest]->fY;
-            int oppTest = triTest + 1;
-            if (3 == oppTest) {
-                oppTest = 0;
-            }
-            double adj = endPt[oppTest]->fX - origX;
-            double opp = endPt[oppTest]->fY - origY;
-            if (adj == 0 && opp == 0) {  // if the other point equals the test point, ignore it
-                continue;
-            }
-            double sign = (c1[oddMan].fY - origY) * adj - (c1[oddMan].fX - origX) * opp;
-            if (approximately_zero(sign)) {
-                goto tryNextHalfPlane;
-            }
-            for (int n = 0; n < 4; ++n) {
-                double test = (c2[n].fY - origY) * adj - (c2[n].fX - origX) * opp;
-                if (test * sign > 0 && !precisely_zero(test)) {
-                    goto tryNextHalfPlane;
-                }
-            }
-        }
-        return true;
-tryNextHalfPlane:
-        ;
-    }
-    return false;
-}
-
-int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
-    if (fMax == 0) {
-        fMax = 9;
-    }
-    bool selfIntersect = &c1 == &c2;
-    if (selfIntersect) {
-        if (c1[0].approximatelyEqual(c1[3])) {
-            insert(0, 1, c1[0]);
-            return fUsed;
-        }
-    } else {
-        // OPTIMIZATION: set exact end bits here to avoid cubic exact end later
-        for (int i1 = 0; i1 < 4; i1 += 3) {
-            for (int i2 = 0; i2 < 4; i2 += 3) {
-                if (c1[i1].approximatelyEqual(c2[i2])) {
-                    insert(i1 >> 1, i2 >> 1, c1[i1]);
-                }
-            }
-        }
-    }
-    SkASSERT(fUsed < 4);
-    if (!selfIntersect) {
-        if (only_end_pts_in_common(c1, c2)) {
-            return fUsed;
-        }
-        if (only_end_pts_in_common(c2, c1)) {
-            return fUsed;
-        }
-    }
-    // quad/quad does linear test here -- cubic does not
-    // cubics which are really lines should have been detected in reduce step earlier
-    int exactEndBits = 0;
-    if (selfIntersect) {
-        if (fUsed) {
-            return fUsed;
-        }
-    } else {
-        exactEndBits |= cubicExactEnd(c1, false, c2) << 0;
-        exactEndBits |= cubicExactEnd(c1, true, c2) << 1;
-        swap();
-        exactEndBits |= cubicExactEnd(c2, false, c1) << 2;
-        exactEndBits |= cubicExactEnd(c2, true, c1) << 3;
-        swap();
-    }
-    if (cubicCheckCoincidence(c1, c2)) {
-        SkASSERT(!selfIntersect);
-        return fUsed;
-    }
-    // FIXME: pass in cached bounds from caller
-    SkDRect c2Bounds;
-    c2Bounds.setBounds(c2);
-    if (!(exactEndBits & 4)) {
-        cubicNearEnd(c1, false, c2, c2Bounds);
-    }
-    if (!(exactEndBits & 8)) {
-        if (selfIntersect && fUsed) {
-            return fUsed;
-        }
-        cubicNearEnd(c1, true, c2, c2Bounds);
-        if (selfIntersect && fUsed && ((approximately_less_than_zero(fT[0][0])
-                    && approximately_less_than_zero(fT[1][0]))
-                    || (approximately_greater_than_one(fT[0][0])
-                    && approximately_greater_than_one(fT[1][0])))) {
-            SkASSERT(fUsed == 1);
-            fUsed = 0;
-            return fUsed;
-        }
-    }
-    if (!selfIntersect) {
-        SkDRect c1Bounds;
-        c1Bounds.setBounds(c1);  // OPTIMIZE use setRawBounds ?
-        swap();
-        if (!(exactEndBits & 1)) {
-            cubicNearEnd(c2, false, c1, c1Bounds);
-        }
-        if (!(exactEndBits & 2)) {
-            cubicNearEnd(c2, true, c1, c1Bounds);
-        }
-        swap();
-    }
-    if (cubicCheckCoincidence(c1, c2)) {
-        SkASSERT(!selfIntersect);
-        return fUsed;
-    }
-    SkIntersections i;
-    i.fAllowNear = false;
-    i.fFlatMeasure = true;
-    i.fMax = 9;
-    ::intersect(c1, 0, 1, c2, 0, 1, 1, i);
-    int compCount = i.used();
-    if (compCount) {
-        int exactCount = used();
-        if (exactCount == 0) {
-            *this = i;
-        } else {
-            // at least one is exact or near, and at least one was computed. Eliminate duplicates
-            for (int exIdx = 0; exIdx < exactCount; ++exIdx) {
-                for (int cpIdx = 0; cpIdx < compCount; ) {
-                    if (fT[0][0] == i[0][0] && fT[1][0] == i[1][0]) {
-                        i.removeOne(cpIdx);
-                        --compCount;
-                        continue;
-                    }
-                    double tAvg = (fT[0][exIdx] + i[0][cpIdx]) / 2;
-                    SkDPoint pt = c1.ptAtT(tAvg);
-                    if (!pt.approximatelyEqual(fPt[exIdx])) {
-                        ++cpIdx;
-                        continue;
-                    }
-                    tAvg = (fT[1][exIdx] + i[1][cpIdx]) / 2;
-                    pt = c2.ptAtT(tAvg);
-                    if (!pt.approximatelyEqual(fPt[exIdx])) {
-                        ++cpIdx;
-                        continue;
-                    }
-                    i.removeOne(cpIdx);
-                    --compCount;
-                }
-            }
-            // if mid t evaluates to nearly the same point, skip the t
-            for (int cpIdx = 0; cpIdx < compCount - 1; ) {
-                double tAvg = (fT[0][cpIdx] + i[0][cpIdx + 1]) / 2;
-                SkDPoint pt = c1.ptAtT(tAvg);
-                if (!pt.approximatelyEqual(fPt[cpIdx])) {
-                    ++cpIdx;
-                    continue;
-                }
-                tAvg = (fT[1][cpIdx] + i[1][cpIdx + 1]) / 2;
-                pt = c2.ptAtT(tAvg);
-                if (!pt.approximatelyEqual(fPt[cpIdx])) {
-                    ++cpIdx;
-                    continue;
-                }
-                i.removeOne(cpIdx);
-                --compCount;
-            }
-            // in addition to adding below missing function, think about how to say
-            append(i);
-        }
-    }
-    // If an end point and a second point very close to the end is returned, the second
-    // point may have been detected because the approximate quads
-    // intersected at the end and close to it. Verify that the second point is valid.
-    if (fUsed <= 1) {
-        return fUsed;
-    }
-    SkDPoint pt[2];
-    if (closeStart(c1, 0, *this, pt[0]) && closeStart(c2, 1, *this, pt[1])
-            && pt[0].approximatelyEqual(pt[1])) {
-        removeOne(1);
-    }
-    if (closeEnd(c1, 0, *this, pt[0]) && closeEnd(c2, 1, *this, pt[1])
-            && pt[0].approximatelyEqual(pt[1])) {
-        removeOne(used() - 2);
-    }
-    // vet the pairs of t values to see if the mid value is also on the curve. If so, mark
-    // the span as coincident
-    if (fUsed >= 2 && !coincidentUsed()) {
-        int last = fUsed - 1;
-        int match = 0;
-        for (int index = 0; index < last; ++index) {
-            double mid1 = (fT[0][index] + fT[0][index + 1]) / 2;
-            double mid2 = (fT[1][index] + fT[1][index + 1]) / 2;
-            pt[0] = c1.ptAtT(mid1);
-            pt[1] = c2.ptAtT(mid2);
-            if (pt[0].approximatelyEqual(pt[1])) {
-                match |= 1 << index;
-            }
-        }
-        if (match) {
-#if DEBUG_CONCIDENT
-            if (((match + 1) & match) != 0) {
-                SkDebugf("%s coincident hole\n", __FUNCTION__);
-            }
-#endif
-            // for now, assume that everything from start to finish is coincident
-            if (fUsed > 2) {
-                  fPt[1] = fPt[last];
-                  fT[0][1] = fT[0][last];
-                  fT[1][1] = fT[1][last];
-                  fIsCoincident[0] = 0x03;
-                  fIsCoincident[1] = 0x03;
-                  fUsed = 2;
-            }
-        }
-    }
-    return fUsed;
-}
-
-// Up promote the quad to a cubic.
-// OPTIMIZATION If this is a common use case, optimize by duplicating
-// the intersect 3 loop to avoid the promotion  / demotion code
-int SkIntersections::intersect(const SkDCubic& cubic, const SkDQuad& quad) {
-    fMax = 7;
-    SkDCubic up = quad.toCubic();
-    (void) intersect(cubic, up);
-    return used();
-}
-
-/* http://www.ag.jku.at/compass/compasssample.pdf
-( Self-Intersection Problems and Approximate Implicitization by Jan B. Thomassen
-Centre of Mathematics for Applications, University of Oslo http://www.cma.uio.no janbth@math.uio.no
-SINTEF Applied Mathematics http://www.sintef.no )
-describes a method to find the self intersection of a cubic by taking the gradient of the implicit
-form dotted with the normal, and solving for the roots. My math foo is too poor to implement this.*/
-
-int SkIntersections::intersect(const SkDCubic& c) {
-    fMax = 1;
-    // check to see if x or y end points are the extrema. Are other quick rejects possible?
-    if (c.endsAreExtremaInXOrY()) {
-        return false;
-    }
-    // OPTIMIZATION: could quick reject if neither end point tangent ray intersected the line
-    // segment formed by the opposite end point to the control point
-    (void) intersect(c, c);
-    if (used() > 1) {
-        fUsed = 0;
-    } else if (used() > 0) {
-        if (approximately_equal_double(fT[0][0], fT[1][0])) {
-            fUsed = 0;
-        } else {
-            SkASSERT(used() == 1);
-            if (fT[0][0] > fT[1][0]) {
-                swapPts();
-            }
-        }
-    }
-    return used();
-}
diff --git a/src/pathops/SkDCubicLineIntersection.cpp b/src/pathops/SkDCubicLineIntersection.cpp
index 696c42e..ee1e3d3 100644
--- a/src/pathops/SkDCubicLineIntersection.cpp
+++ b/src/pathops/SkDCubicLineIntersection.cpp
@@ -93,6 +93,29 @@
         fAllowNear = allow;
     }
 
+    void checkCoincident() {
+        int last = fIntersections->used() - 1;
+        for (int index = 0; index < last; ) {
+            double cubicMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2;
+            SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
+            double t = fLine.nearPoint(cubicMidPt, NULL);
+            if (t < 0) {
+                ++index;
+                continue;
+            }
+            if (fIntersections->isCoincident(index)) {
+                fIntersections->removeOne(index);
+                --last;
+            } else if (fIntersections->isCoincident(index + 1)) {
+                fIntersections->removeOne(index + 1);
+                --last;
+            } else {
+                fIntersections->setCoincident(index++);
+            }
+            fIntersections->setCoincident(index);
+        }
+    }
+
     // see parallel routine in line quadratic intersections
     int intersectRay(double roots[3]) {
         double adj = fLine[1].fX - fLine[0].fX;
@@ -112,7 +135,7 @@
                             + (fCubic[n].fX - fLine[0].fX) * adj;
                 }
                 double extremeTs[6];
-                int extrema = SkDCubic::FindExtrema(c[0].fX, c[1].fX, c[2].fX, c[3].fX, extremeTs);
+                int extrema = SkDCubic::FindExtrema(&c[0].fX, extremeTs);
                 count = c.searchRoots(extremeTs, extrema, 0, SkDCubic::kXAxis, roots);
                 break;
             }
@@ -131,32 +154,11 @@
             double cubicT = rootVals[index];
             double lineT = findLineT(cubicT);
             SkDPoint pt;
-            if (pinTs(&cubicT, &lineT, &pt, kPointUninitialized)) {
-    #if ONE_OFF_DEBUG
-                SkDPoint cPt = fCubic.ptAtT(cubicT);
-                SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
-                        cPt.fX, cPt.fY);
-    #endif
-                for (int inner = 0; inner < fIntersections->used(); ++inner) {
-                    if (fIntersections->pt(inner) != pt) {
-                        continue;
-                    }
-                    double existingCubicT = (*fIntersections)[0][inner];
-                    if (cubicT == existingCubicT) {
-                        goto skipInsert;
-                    }
-                    // check if midway on cubic is also same point. If so, discard this
-                    double cubicMidT = (existingCubicT + cubicT) / 2;
-                    SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
-                    if (cubicMidPt.approximatelyEqual(pt)) {
-                        goto skipInsert;
-                    }
-                }
+            if (pinTs(&cubicT, &lineT, &pt, kPointUninitialized) && uniqueAnswer(cubicT, pt)) {
                 fIntersections->insert(cubicT, lineT, pt);
-        skipInsert:
-                ;
             }
         }
+        checkCoincident();
         return fIntersections->used();
     }
 
@@ -169,7 +171,7 @@
             SkDPoint calcPt = c.ptAtT(roots[index]);
             if (!approximately_equal(calcPt.fY, axisIntercept)) {
                 double extremeTs[6];
-                int extrema = SkDCubic::FindExtrema(c[0].fY, c[1].fY, c[2].fY, c[3].fY, extremeTs);
+                int extrema = SkDCubic::FindExtrema(&c[0].fY, extremeTs);
                 count = c.searchRoots(extremeTs, extrema, axisIntercept, SkDCubic::kYAxis, roots);
                 break;
             }
@@ -186,20 +188,43 @@
         int count = HorizontalIntersect(fCubic, axisIntercept, roots);
         for (int index = 0; index < count; ++index) {
             double cubicT = roots[index];
-            SkDPoint pt;
-            pt.fX = fCubic.ptAtT(cubicT).fX;
-            pt.fY = axisIntercept;
+            SkDPoint pt = { fCubic.ptAtT(cubicT).fX,  axisIntercept };
             double lineT = (pt.fX - left) / (right - left);
-            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
+            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
                 fIntersections->insert(cubicT, lineT, pt);
             }
         }
         if (flipped) {
             fIntersections->flip();
         }
+        checkCoincident();
         return fIntersections->used();
     }
 
+        bool uniqueAnswer(double cubicT, const SkDPoint& pt) {
+            for (int inner = 0; inner < fIntersections->used(); ++inner) {
+                if (fIntersections->pt(inner) != pt) {
+                    continue;
+                }
+                double existingCubicT = (*fIntersections)[0][inner];
+                if (cubicT == existingCubicT) {
+                    return false;
+                }
+                // check if midway on cubic is also same point. If so, discard this
+                double cubicMidT = (existingCubicT + cubicT) / 2;
+                SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
+                if (cubicMidPt.approximatelyEqual(pt)) {
+                    return false;
+                }
+            }
+#if ONE_OFF_DEBUG
+            SkDPoint cPt = fCubic.ptAtT(cubicT);
+            SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
+                    cPt.fX, cPt.fY);
+#endif
+            return true;
+        }
+
     static int VerticalIntersect(const SkDCubic& c, double axisIntercept, double roots[3]) {
         double A, B, C, D;
         SkDCubic::Coefficients(&c[0].fX, &A, &B, &C, &D);
@@ -209,7 +234,7 @@
             SkDPoint calcPt = c.ptAtT(roots[index]);
             if (!approximately_equal(calcPt.fX, axisIntercept)) {
                 double extremeTs[6];
-                int extrema = SkDCubic::FindExtrema(c[0].fX, c[1].fX, c[2].fX, c[3].fX, extremeTs);
+                int extrema = SkDCubic::FindExtrema(&c[0].fX, extremeTs);
                 count = c.searchRoots(extremeTs, extrema, axisIntercept, SkDCubic::kXAxis, roots);
                 break;
             }
@@ -226,17 +251,16 @@
         int count = VerticalIntersect(fCubic, axisIntercept, roots);
         for (int index = 0; index < count; ++index) {
             double cubicT = roots[index];
-            SkDPoint pt;
-            pt.fX = axisIntercept;
-            pt.fY = fCubic.ptAtT(cubicT).fY;
+            SkDPoint pt = { axisIntercept, fCubic.ptAtT(cubicT).fY };
             double lineT = (pt.fY - top) / (bottom - top);
-            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) {
+            if (pinTs(&cubicT, &lineT, &pt, kPointInitialized) && uniqueAnswer(cubicT, pt)) {
                 fIntersections->insert(cubicT, lineT, pt);
             }
         }
         if (flipped) {
             fIntersections->flip();
         }
+        checkCoincident();
         return fIntersections->used();
     }
 
@@ -342,7 +366,7 @@
         double lT = *lineT = SkPinT(*lineT);
         SkDPoint lPt = fLine.ptAtT(lT);
         SkDPoint cPt = fCubic.ptAtT(cT);
-        if (!lPt.moreRoughlyEqual(cPt)) {
+        if (!lPt.roughlyEqual(cPt)) {
             return false;
         }
         // FIXME: if points are roughly equal but not approximately equal, need to do
@@ -401,3 +425,13 @@
     }
     return fUsed;
 }
+
+// SkDCubic accessors to Intersection utilities
+
+int SkDCubic::horizontalIntersect(double yIntercept, double roots[3]) const {
+    return LineCubicIntersections::HorizontalIntersect(*this, yIntercept, roots);
+}
+
+int SkDCubic::verticalIntersect(double xIntercept, double roots[3]) const {
+    return LineCubicIntersections::VerticalIntersect(*this, xIntercept, roots);
+}
diff --git a/src/pathops/SkDCubicToQuads.cpp b/src/pathops/SkDCubicToQuads.cpp
index a28564d..272b997 100644
--- a/src/pathops/SkDCubicToQuads.cpp
+++ b/src/pathops/SkDCubicToQuads.cpp
@@ -1,4 +1,11 @@
 /*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
 http://stackoverflow.com/questions/2009160/how-do-i-convert-the-2-control-points-of-a-cubic-curve-to-the-single-control-poi
 */
 
@@ -19,63 +26,12 @@
  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
-
-SkDCubic 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 "SkPathOpsCubic.h"
-#include "SkPathOpsLine.h"
 #include "SkPathOpsQuad.h"
-#include "SkReduceOrder.h"
-#include "SkTArray.h"
-#include "SkTSort.h"
 
-#define USE_CUBIC_END_POINTS 1
-
-static double calc_t_div(const SkDCubic& cubic, double precision, double start) {
-    const double adjust = sqrt(3.) / 36;
-    SkDCubic sub;
-    const SkDCubic* cPtr;
-    if (start == 0) {
-        cPtr = &cubic;
-    } else {
-        // OPTIMIZE: special-case half-split ?
-        sub = cubic.subDivide(start, 1);
-        cPtr = &sub;
-    }
-    const SkDCubic& c = *cPtr;
-    double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX;
-    double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY;
-    double dist = sqrt(dx * dx + dy * dy);
-    double tDiv3 = precision / (adjust * dist);
-    double t = SkDCubeRoot(tDiv3);
-    if (start > 0) {
-        t = start + (1 - start) * t;
-    }
-    return t;
-}
-
+// used for testing only
 SkDQuad SkDCubic::toQuad() const {
     SkDQuad quad;
     quad[0] = fPts[0];
@@ -86,101 +42,3 @@
     quad[2] = fPts[3];
     return quad;
 }
-
-static bool add_simple_ts(const SkDCubic& cubic, double precision, SkTArray<double, true>* ts) {
-    double tDiv = calc_t_div(cubic, precision, 0);
-    if (tDiv >= 1) {
-        return true;
-    }
-    if (tDiv >= 0.5) {
-        ts->push_back(0.5);
-        return true;
-    }
-    return false;
-}
-
-static void addTs(const SkDCubic& cubic, double precision, double start, double end,
-        SkTArray<double, true>* ts) {
-    double tDiv = calc_t_div(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->push_back(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 SkDCubic::toQuadraticTs(double precision, SkTArray<double, true>* ts) const {
-    SkReduceOrder reducer;
-    int order = reducer.reduce(*this, SkReduceOrder::kAllow_Quadratics);
-    if (order < 3) {
-        return;
-    }
-    double inflectT[5];
-    int inflections = findInflections(inflectT);
-    SkASSERT(inflections <= 2);
-    if (!endsAreExtremaInXOrY()) {
-        inflections += findMaxCurvature(&inflectT[inflections]);
-        SkASSERT(inflections <= 5);
-    }
-    SkTQSort<double>(inflectT, &inflectT[inflections - 1]);
-    // OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
-    // own subroutine?
-    while (inflections && approximately_less_than_zero(inflectT[0])) {
-        memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
-    }
-    int start = 0;
-    int next = 1;
-    while (next < inflections) {
-        if (!approximately_equal(inflectT[start], inflectT[next])) {
-            ++start;
-        ++next;
-            continue;
-        }
-        memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
-    }
-
-    while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
-        --inflections;
-    }
-    SkDCubicPair pair;
-    if (inflections == 1) {
-        pair = chopAt(inflectT[0]);
-        int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics);
-        if (orderP1 < 2) {
-            --inflections;
-        } else {
-            int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics);
-            if (orderP2 < 2) {
-                --inflections;
-            }
-        }
-    }
-    if (inflections == 0 && add_simple_ts(*this, precision, ts)) {
-        return;
-    }
-    if (inflections == 1) {
-        pair = chopAt(inflectT[0]);
-        addTs(pair.first(), precision, 0, inflectT[0], ts);
-        addTs(pair.second(), precision, inflectT[0], 1, ts);
-        return;
-    }
-    if (inflections > 1) {
-        SkDCubic part = subDivide(0, inflectT[0]);
-        addTs(part, precision, 0, inflectT[0], ts);
-        int last = inflections - 1;
-        for (int idx = 0; idx < last; ++idx) {
-            part = subDivide(inflectT[idx], inflectT[idx + 1]);
-            addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
-        }
-        part = subDivide(inflectT[last], 1);
-        addTs(part, precision, inflectT[last], 1, ts);
-        return;
-    }
-    addTs(*this, precision, 0, 1, ts);
-}
diff --git a/src/pathops/SkDLineIntersection.cpp b/src/pathops/SkDLineIntersection.cpp
index 8fc673f..5fd8e7f 100644
--- a/src/pathops/SkDLineIntersection.cpp
+++ b/src/pathops/SkDLineIntersection.cpp
@@ -7,45 +7,6 @@
 #include "SkIntersections.h"
 #include "SkPathOpsLine.h"
 
-/* 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
- */
-SkDPoint SkIntersections::Line(const SkDLine& a, const SkDLine& b) {
-    double axLen = a[1].fX - a[0].fX;
-    double ayLen = a[1].fY - a[0].fY;
-    double bxLen = b[1].fX - b[0].fX;
-    double byLen = b[1].fY - b[0].fY;
-    double denom = byLen * axLen - ayLen * bxLen;
-    SkASSERT(denom);
-    double term1 = a[1].fX * a[0].fY - a[1].fY * a[0].fX;
-    double term2 = b[1].fX * b[0].fY - b[1].fY * b[0].fX;
-    SkDPoint p;
-    p.fX = (term1 * bxLen - axLen * term2) / denom;
-    p.fY = (term1 * byLen - ayLen * term2) / denom;
-    return p;
-}
-
-int SkIntersections::cleanUpCoincidence() {
-    do {
-        int last = fUsed - 1;
-        for (int index = 0; index < last; ++index) {
-            if (fT[0][index] == fT[0][index + 1]) {
-                removeOne(index + (int) (fT[1][index] == 0 || fT[1][index] == 1));
-                goto tryAgain;
-            }
-        }
-        for (int index = 0; index < last; ++index) {
-            if (fT[1][index] == fT[1][index + 1]) {
-                removeOne(index + (int) (fT[0][index] == 0 || fT[0][index] == 1));
-                goto tryAgain;
-            }
-        }
-        return fUsed;
-tryAgain: ;
-    } while (true);
-}
-
 void SkIntersections::cleanUpParallelLines(bool parallel) {
     while (fUsed > 2) {
         removeOne(1);
@@ -58,6 +19,9 @@
             removeOne(endMatch);
         }
     }
+    if (fUsed == 2) {
+        fIsCoincident[0] = fIsCoincident[1] = 0x03;
+    }
 }
 
 void SkIntersections::computePoints(const SkDLine& line, int used) {
@@ -81,12 +45,6 @@
     SkDVector ab0 = a[0] - b[0];
     double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX;
     double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX;
-#if 0
-    if (!between(0, numerA, denom) || !between(0, numerB, denom)) {
-        fUsed = 0;
-        return 0;
-    }
-#endif
     numerA /= denom;
     numerB /= denom;
     int used;
@@ -190,7 +148,6 @@
                     }
                     SkASSERT(a[iA] != b[nearer]);
                     SkASSERT(iA == (bNearA[nearer] > 0.5));
-                    fNearlySame[iA] = true;
                     insertNear(iA, nearer, a[iA], b[nearer]);
                     aNearB[iA] = -1;
                     bNearA[nearer] = -1;
@@ -231,22 +188,10 @@
     return 1;
 }
 
-static double horizontal_intercept(const SkDLine& line, double y) {
+double SkIntersections::HorizontalIntercept(const SkDLine& line, double y) {
      return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY));
 }
 
-int SkIntersections::horizontal(const SkDLine& line, double y) {
-    fMax = 2;
-    int horizontalType = horizontal_coincident(line, y);
-    if (horizontalType == 1) {
-        fT[0][0] = horizontal_intercept(line, y);
-    } else if (horizontalType == 2) {
-        fT[0][0] = 0;
-        fT[0][1] = 1;
-    }
-    return fUsed = horizontalType;
-}
-
 int SkIntersections::horizontal(const SkDLine& line, double left, double right,
                                 double y, bool flipped) {
     fMax = 3;  // clean up parallel at the end will limit the result to 2 at the most
@@ -269,7 +214,7 @@
     }
     int result = horizontal_coincident(line, y);
     if (result == 1 && fUsed == 0) {
-        fT[0][0] = horizontal_intercept(line, y);
+        fT[0][0] = HorizontalIntercept(line, y);
         double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX);
         if (between(left, xIntercept, right)) {
             fT[1][0] = (xIntercept - left) / (right - left);
@@ -319,22 +264,10 @@
     return 1;
 }
 
-static double vertical_intercept(const SkDLine& line, double x) {
+double SkIntersections::VerticalIntercept(const SkDLine& line, double x) {
     return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX));
 }
 
-int SkIntersections::vertical(const SkDLine& line, double x) {
-    fMax = 2;
-    int verticalType = vertical_coincident(line, x);
-    if (verticalType == 1) {
-        fT[0][0] = vertical_intercept(line, x);
-    } else if (verticalType == 2) {
-        fT[0][0] = 0;
-        fT[0][1] = 1;
-    }
-    return fUsed = verticalType;
-}
-
 int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
                               double x, bool flipped) {
     fMax = 3;  // cleanup parallel lines will bring this back line
@@ -357,7 +290,7 @@
     }
     int result = vertical_coincident(line, x);
     if (result == 1 && fUsed == 0) {
-        fT[0][0] = vertical_intercept(line, x);
+        fT[0][0] = VerticalIntercept(line, x);
         double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY);
         if (between(top, yIntercept, bottom)) {
             fT[1][0] = (yIntercept - top) / (bottom - top);
@@ -393,14 +326,3 @@
     return fUsed;
 }
 
-// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
-// 4 subs, 2 muls, 1 cmp
-static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) {
-    return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX);
-}
-
-// 16 subs, 8 muls, 6 cmps
-bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) {
-    return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
-            && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
-}
diff --git a/src/pathops/SkDQuadImplicit.cpp b/src/pathops/SkDQuadImplicit.cpp
deleted file mode 100644
index f0f66d1..0000000
--- a/src/pathops/SkDQuadImplicit.cpp
+++ /dev/null
@@ -1,117 +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.
- */
-#include "SkDQuadImplicit.h"
-
-/* from http://tom.cs.byu.edu/~tom/papers/cvgip84.pdf 4.1
- *
- * This paper proves that Syvester's method can compute the implicit form of
- * the quadratic from the parameterized form.
- *
- * Given x = a*t*t + b*t + c  (the parameterized form)
- *       y = d*t*t + e*t + f
- *
- * we want to find an equation of the implicit form:
- *
- * A*x*x + B*x*y + C*y*y + D*x + E*y + F = 0
- *
- * The implicit form can be expressed as a 4x4 determinant, as shown.
- *
- * The resultant obtained by Syvester's method is
- *
- * |   a   b   (c - x)     0     |
- * |   0   a      b     (c - x)  |
- * |   d   e   (f - y)     0     |
- * |   0   d      e     (f - y)  |
- *
- * which expands to
- *
- * d*d*x*x + -2*a*d*x*y + a*a*y*y
- *         + (-2*c*d*d + b*e*d - a*e*e + 2*a*f*d)*x
- *         + (-2*f*a*a + e*b*a - d*b*b + 2*d*c*a)*y
- *         +
- * |   a   b   c   0   |
- * |   0   a   b   c   | == 0.
- * |   d   e   f   0   |
- * |   0   d   e   f   |
- *
- * Expanding the constant determinant results in
- *
- *   | a b c |     | b c 0 |
- * a*| e f 0 | + d*| a b c | ==
- *   | d e f |     | d e f |
- *
- * a*(a*f*f + c*e*e - c*f*d - b*e*f) + d*(b*b*f + c*c*d - c*a*f - c*e*b)
- *
- */
-
-// use the tricky arithmetic path, but leave the original to compare just in case
-static bool straight_forward = false;
-
-SkDQuadImplicit::SkDQuadImplicit(const SkDQuad& q) {
-    double a, b, c;
-    SkDQuad::SetABC(&q[0].fX, &a, &b, &c);
-    double d, e, f;
-    SkDQuad::SetABC(&q[0].fY, &d, &e, &f);
-    // compute the implicit coefficients
-    if (straight_forward) {  // 42 muls, 13 adds
-        fP[kXx_Coeff] = d * d;
-        fP[kXy_Coeff] = -2 * a * d;
-        fP[kYy_Coeff] = a * a;
-        fP[kX_Coeff] = -2*c*d*d + b*e*d - a*e*e + 2*a*f*d;
-        fP[kY_Coeff] = -2*f*a*a + e*b*a - d*b*b + 2*d*c*a;
-        fP[kC_Coeff] = a*(a*f*f + c*e*e - c*f*d - b*e*f)
-                   + d*(b*b*f + c*c*d - c*a*f - c*e*b);
-    } else {  // 26 muls, 11 adds
-        double aa = a * a;
-        double ad = a * d;
-        double dd = d * d;
-        fP[kXx_Coeff] = dd;
-        fP[kXy_Coeff] = -2 * ad;
-        fP[kYy_Coeff] = aa;
-        double be = b * e;
-        double bde = be * d;
-        double cdd = c * dd;
-        double ee = e * e;
-        fP[kX_Coeff] =  -2*cdd + bde - a*ee + 2*ad*f;
-        double aaf = aa * f;
-        double abe = a * be;
-        double ac = a * c;
-        double bb_2ac = b*b - 2*ac;
-        fP[kY_Coeff] = -2*aaf + abe - d*bb_2ac;
-        fP[kC_Coeff] = aaf*f + ac*ee + d*f*bb_2ac - abe*f + c*cdd - c*bde;
-    }
-}
-
- /* Given a pair of quadratics, determine their parametric coefficients.
-  * If the scaled coefficients are nearly equal, then the part of the quadratics
-  * may be coincident.
-  * OPTIMIZATION -- since comparison short-circuits on no match,
-  * lazily compute the coefficients, comparing the easiest to compute first.
-  * xx and yy first; then xy; and so on.
-  */
-bool SkDQuadImplicit::match(const SkDQuadImplicit& p2) const {
-    int first = 0;
-    for (int index = 0; index <= kC_Coeff; ++index) {
-        if (approximately_zero(fP[index]) && approximately_zero(p2.fP[index])) {
-            first += first == index;
-            continue;
-        }
-        if (first == index) {
-            continue;
-        }
-        if (!AlmostDequalUlps(fP[index] * p2.fP[first], fP[first] * p2.fP[index])) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool SkDQuadImplicit::Match(const SkDQuad& quad1, const SkDQuad& quad2) {
-    SkDQuadImplicit i1(quad1);  // a'xx , b'xy , c'yy , d'x , e'y , f
-    SkDQuadImplicit i2(quad2);
-    return i1.match(i2);
-}
diff --git a/src/pathops/SkDQuadImplicit.h b/src/pathops/SkDQuadImplicit.h
deleted file mode 100644
index 24f1aac..0000000
--- a/src/pathops/SkDQuadImplicit.h
+++ /dev/null
@@ -1,39 +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 SkDQuadImplicit_DEFINED
-#define SkDQuadImplicit_DEFINED
-
-#include "SkPathOpsQuad.h"
-
-class SkDQuadImplicit {
-public:
-    explicit SkDQuadImplicit(const SkDQuad& q);
-
-    bool match(const SkDQuadImplicit& two) const;
-    static bool Match(const SkDQuad& quad1, const SkDQuad& quad2);
-
-    double x2() const { return fP[kXx_Coeff]; }
-    double xy() const { return fP[kXy_Coeff]; }
-    double y2() const { return fP[kYy_Coeff]; }
-    double x() const { return fP[kX_Coeff]; }
-    double y() const { return fP[kY_Coeff]; }
-    double c() const { return fP[kC_Coeff]; }
-
-private:
-    enum Coeffs {
-        kXx_Coeff,
-        kXy_Coeff,
-        kYy_Coeff,
-        kX_Coeff,
-        kY_Coeff,
-        kC_Coeff,
-    };
-
-    double fP[kC_Coeff + 1];
-};
-
-#endif
diff --git a/src/pathops/SkDQuadIntersection.cpp b/src/pathops/SkDQuadIntersection.cpp
deleted file mode 100644
index fcb9171..0000000
--- a/src/pathops/SkDQuadIntersection.cpp
+++ /dev/null
@@ -1,617 +0,0 @@
-// Another approach is to start with the implicit form of one curve and solve
-// (seek implicit coefficients in QuadraticParameter.cpp
-// by substituting in the parametric form of the other.
-// The downside of this approach is that early rejects are difficult to come by.
-// http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html#step
-
-#include "SkDQuadImplicit.h"
-#include "SkIntersections.h"
-#include "SkPathOpsLine.h"
-#include "SkQuarticRoot.h"
-#include "SkTArray.h"
-#include "SkTSort.h"
-
-/* given the implicit form 0 = Ax^2 + Bxy + Cy^2 + Dx + Ey + F
- * and given x = at^2 + bt + c  (the parameterized form)
- *           y = dt^2 + et + f
- * then
- * 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 SkDQuadImplicit& i, const SkDQuad& quad, double roots[4],
-        bool oneHint, bool flip, int firstCubicRoot) {
-    SkDQuad flipped;
-    const SkDQuad& q = flip ? (flipped = quad.flip()) : quad;
-    double a, b, c;
-    SkDQuad::SetABC(&q[0].fX, &a, &b, &c);
-    double d, e, f;
-    SkDQuad::SetABC(&q[0].fY, &d, &e, &f);
-    const double t4 =     i.x2() *  a * a
-                    +     i.xy() *  a * d
-                    +     i.y2() *  d * d;
-    const double t3 = 2 * i.x2() *  a * b
-                    +     i.xy() * (a * e +     b * d)
-                    + 2 * i.y2() *  d * e;
-    const double t2 =     i.x2() * (b * b + 2 * a * c)
-                    +     i.xy() * (c * d +     b * e + a * f)
-                    +     i.y2() * (e * e + 2 * d * f)
-                    +     i.x()  *  a
-                    +     i.y()  *  d;
-    const double t1 = 2 * i.x2() *  b * c
-                    +     i.xy() * (c * e + b * f)
-                    + 2 * i.y2() *  e * f
-                    +     i.x()  *  b
-                    +     i.y()  *  e;
-    const double t0 =     i.x2() *  c * c
-                    +     i.xy() *  c * f
-                    +     i.y2() *  f * f
-                    +     i.x()  *  c
-                    +     i.y()  *  f
-                    +     i.c();
-    int rootCount = SkReducedQuarticRoots(t4, t3, t2, t1, t0, oneHint, roots);
-    if (rootCount < 0) {
-        rootCount = SkQuarticRootsReal(firstCubicRoot, t4, t3, t2, t1, t0, roots);
-    }
-    if (flip) {
-        for (int index = 0; index < rootCount; ++index) {
-            roots[index] = 1 - roots[index];
-        }
-    }
-    return rootCount;
-}
-
-static int addValidRoots(const double roots[4], const int count, double valid[4]) {
-    int result = 0;
-    int index;
-    for (index = 0; index < count; ++index) {
-        if (!approximately_zero_or_more(roots[index]) || !approximately_one_or_less(roots[index])) {
-            continue;
-        }
-        double t = 1 - roots[index];
-        if (approximately_less_than_zero(t)) {
-            t = 0;
-        } else if (approximately_greater_than_one(t)) {
-            t = 1;
-        }
-        SkASSERT(t >= 0 && t <= 1);
-        valid[result++] = t;
-    }
-    return result;
-}
-
-static bool only_end_pts_in_common(const SkDQuad& q1, const SkDQuad& q2) {
-// the idea here is to see at minimum do a quick reject by rotating all points
-// to either side of the line formed by connecting the endpoints
-// if the opposite curves points are on the line or on the other side, the
-// curves at most intersect at the endpoints
-    for (int oddMan = 0; oddMan < 3; ++oddMan) {
-        const SkDPoint* endPt[2];
-        for (int opp = 1; opp < 3; ++opp) {
-            int end = oddMan ^ opp;  // choose a value not equal to oddMan
-            if (3 == end) {  // and correct so that largest value is 1 or 2
-                end = opp;
-            }
-            endPt[opp - 1] = &q1[end];
-        }
-        double origX = endPt[0]->fX;
-        double origY = endPt[0]->fY;
-        double adj = endPt[1]->fX - origX;
-        double opp = endPt[1]->fY - origY;
-        double sign = (q1[oddMan].fY - origY) * adj - (q1[oddMan].fX - origX) * opp;
-        if (approximately_zero(sign)) {
-            goto tryNextHalfPlane;
-        }
-        for (int n = 0; n < 3; ++n) {
-            double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
-            if (test * sign > 0 && !precisely_zero(test)) {
-                goto tryNextHalfPlane;
-            }
-        }
-        return true;
-tryNextHalfPlane:
-        ;
-    }
-    return false;
-}
-
-// 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 add_intercept(const SkDQuad& q1, const SkDQuad& q2, double tMin, double tMax,
-                          SkIntersections* i, bool* subDivide) {
-    double tMid = (tMin + tMax) / 2;
-    SkDPoint mid = q2.ptAtT(tMid);
-    SkDLine line;
-    line[0] = line[1] = mid;
-    SkDVector dxdy = q2.dxdyAtT(tMid);
-    line[0] -= dxdy;
-    line[1] += dxdy;
-    SkIntersections rootTs;
-    rootTs.allowNear(false);
-    int roots = rootTs.intersect(q1, line);
-    if (roots == 0) {
-        if (subDivide) {
-            *subDivide = true;
-        }
-        return true;
-    }
-    if (roots == 2) {
-        return false;
-    }
-    SkDPoint pt2 = q1.ptAtT(rootTs[0][0]);
-    if (!pt2.approximatelyEqual(mid)) {
-        return false;
-    }
-    i->insertSwap(rootTs[0][0], tMid, pt2);
-    return true;
-}
-
-static bool is_linear_inner(const SkDQuad& q1, double t1s, double t1e, const SkDQuad& q2,
-                            double t2s, double t2e, SkIntersections* i, bool* subDivide) {
-    SkDQuad hull = q1.subDivide(t1s, t1e);
-    SkDLine line = {{hull[2], hull[0]}};
-    const SkDLine* testLines[] = { &line, (const SkDLine*) &hull[0], (const SkDLine*) &hull[1] };
-    const size_t kTestCount = SK_ARRAY_COUNT(testLines);
-    SkSTArray<kTestCount * 2, double, true> tsFound;
-    for (size_t index = 0; index < kTestCount; ++index) {
-        SkIntersections rootTs;
-        rootTs.allowNear(false);
-        int roots = rootTs.intersect(q2, *testLines[index]);
-        for (int idx2 = 0; idx2 < roots; ++idx2) {
-            double t = rootTs[0][idx2];
-#if 0 // def SK_DEBUG   // FIXME : accurate for error = 16, error of 17.5 seen
-// {{{136.08723965397621, 1648.2814535211637}, {593.49031197259478, 1190.8784277439891}, {593.49031197259478, 544.0128173828125}}}
-// {{{-968.181396484375, 544.0128173828125}, {592.2825927734375, 870.552490234375}, {593.435302734375, 557.8828125}}}
-
-            SkDPoint qPt = q2.ptAtT(t);
-            SkDPoint lPt = testLines[index]->ptAtT(rootTs[1][idx2]);
-            SkASSERT(qPt.approximatelyDEqual(lPt));
-#endif
-            if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) {
-                continue;
-            }
-            tsFound.push_back(rootTs[0][idx2]);
-        }
-    }
-    int tCount = tsFound.count();
-    if (tCount <= 0) {
-        return true;
-    }
-    double tMin, tMax;
-    if (tCount == 1) {
-        tMin = tMax = tsFound[0];
-    } else {
-        SkASSERT(tCount > 1);
-        SkTQSort<double>(tsFound.begin(), tsFound.end() - 1);
-        tMin = tsFound[0];
-        tMax = tsFound[tsFound.count() - 1];
-    }
-    SkDPoint end = q2.ptAtT(t2s);
-    bool startInTriangle = hull.pointInHull(end);
-    if (startInTriangle) {
-        tMin = t2s;
-    }
-    end = q2.ptAtT(t2e);
-    bool endInTriangle = hull.pointInHull(end);
-    if (endInTriangle) {
-        tMax = t2e;
-    }
-    int split = 0;
-    SkDVector dxy1, dxy2;
-    if (tMin != tMax || tCount > 2) {
-        dxy2 = q2.dxdyAtT(tMin);
-        for (int index = 1; index < tCount; ++index) {
-            dxy1 = dxy2;
-            dxy2 = q2.dxdyAtT(tsFound[index]);
-            double dot = dxy1.dot(dxy2);
-            if (dot < 0) {
-                split = index - 1;
-                break;
-            }
-        }
-    }
-    if (split == 0) {  // there's one point
-        if (add_intercept(q1, q2, tMin, tMax, i, subDivide)) {
-            return true;
-        }
-        i->swap();
-        return is_linear_inner(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 (add_intercept(q1, q2, tMin, tsFound[split - 1], i, subDivide)) {
-        result = true;
-    } else {
-        i->swap();
-        result = is_linear_inner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i, subDivide);
-    }
-    if (add_intercept(q1, q2, tsFound[split], tMax, i, subDivide)) {
-        result = true;
-    } else {
-        i->swap();
-        result |= is_linear_inner(q2, tsFound[split], tMax, q1, t1s, t1e, i, subDivide);
-    }
-    return result;
-}
-
-static double flat_measure(const SkDQuad& q) {
-    SkDVector mid = q[1] - q[0];
-    SkDVector dxy = q[2] - 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 is_linear(const SkDQuad& q1, const SkDQuad& q2, SkIntersections* i) {
-    if (i->flatMeasure()) {
-        // for backward compatibility, use the old method when called from cubics
-        // FIXME: figure out how to fix cubics when it calls the new path
-        double measure = flat_measure(q1);
-        // OPTIMIZE: (get rid of sqrt) use approximately_zero
-        if (!approximately_zero_sqrt(measure)) {  // approximately_zero_sqrt
-            return false;
-        }
-     } else {
-        if (!q1.isLinear(0, 2)) {
-            return false;
-        }
-    }
-    return is_linear_inner(q1, 0, 1, q2, 0, 1, i, NULL);
-}
-
-// FIXME: if flat measure is sufficiently large, then probably the quartic solution failed
-// avoid imprecision incurred with chopAt
-static void relaxed_is_linear(const SkDQuad* q1, double s1, double e1, const SkDQuad* q2,
-        double s2, double e2, SkIntersections* i) {
-    double m1 = flat_measure(*q1);
-    double m2 = flat_measure(*q2);
-    i->reset();
-    const SkDQuad* rounder, *flatter;
-    double sf, midf, ef, sr, er;
-    if (m2 < m1) {
-        rounder = q1;
-        sr = s1;
-        er = e1;
-        flatter = q2;
-        sf = s2;
-        midf = (s2 + e2) / 2;
-        ef = e2;
-    } else {
-        rounder = q2;
-        sr = s2;
-        er = e2;
-        flatter = q1;
-        sf = s1;
-        midf = (s1 + e1) / 2;
-        ef = e1;
-    }
-    bool subDivide = false;
-    is_linear_inner(*flatter, sf, ef, *rounder, sr, er, i, &subDivide);
-    if (subDivide) {
-        relaxed_is_linear(flatter, sf, midf, rounder, sr, er, i);
-        relaxed_is_linear(flatter, midf, ef, rounder, sr, er, i);
-    }
-    if (m2 < m1) {
-        i->swapPts();
-    }
-}
-
-// each time through the loop, this computes values it had from the last loop
-// if i == j == 1, the center values are still good
-// otherwise, for i != 1 or j != 1, four of the values are still good
-// and if i == 1 ^ j == 1, an additional value is good
-static bool binary_search(const SkDQuad& quad1, const SkDQuad& quad2, double* t1Seed,
-                          double* t2Seed, SkDPoint* pt) {
-    double tStep = ROUGH_EPSILON;
-    SkDPoint t1[3], t2[3];
-    int calcMask = ~0;
-    do {
-        if (calcMask & (1 << 1)) t1[1] = quad1.ptAtT(*t1Seed);
-        if (calcMask & (1 << 4)) t2[1] = quad2.ptAtT(*t2Seed);
-        if (t1[1].approximatelyEqual(t2[1])) {
-            *pt = t1[1];
-    #if ONE_OFF_DEBUG
-            SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) == (%1.9g,%1.9g)\n", __FUNCTION__,
-                    t1Seed, t2Seed, t1[1].fX, t1[1].fY, t2[1].fX, t2[1].fY);
-    #endif
-            if (*t1Seed < 0) {
-                *t1Seed = 0;
-            } else if (*t1Seed > 1) {
-                *t1Seed = 1;
-            }
-            if (*t2Seed < 0) {
-                *t2Seed = 0;
-            } else if (*t2Seed > 1) {
-                *t2Seed = 1;
-            }
-            return true;
-        }
-        if (calcMask & (1 << 0)) t1[0] = quad1.ptAtT(SkTMax(0., *t1Seed - tStep));
-        if (calcMask & (1 << 2)) t1[2] = quad1.ptAtT(SkTMin(1., *t1Seed + tStep));
-        if (calcMask & (1 << 3)) t2[0] = quad2.ptAtT(SkTMax(0., *t2Seed - tStep));
-        if (calcMask & (1 << 5)) t2[2] = quad2.ptAtT(SkTMin(1., *t2Seed + tStep));
-        double dist[3][3];
-        // OPTIMIZE: using calcMask value permits skipping some distance calcuations
-        //   if prior loop's results are moved to correct slot for reuse
-        dist[1][1] = t1[1].distanceSquared(t2[1]);
-        int best_i = 1, best_j = 1;
-        for (int i = 0; i < 3; ++i) {
-            for (int j = 0; j < 3; ++j) {
-                if (i == 1 && j == 1) {
-                    continue;
-                }
-                dist[i][j] = t1[i].distanceSquared(t2[j]);
-                if (dist[best_i][best_j] > dist[i][j]) {
-                    best_i = i;
-                    best_j = j;
-                }
-            }
-        }
-        if (best_i == 1 && best_j == 1) {
-            tStep /= 2;
-            if (tStep < FLT_EPSILON_HALF) {
-                break;
-            }
-            calcMask = (1 << 0) | (1 << 2) | (1 << 3) | (1 << 5);
-            continue;
-        }
-        if (best_i == 0) {
-            *t1Seed -= tStep;
-            t1[2] = t1[1];
-            t1[1] = t1[0];
-            calcMask = 1 << 0;
-        } else if (best_i == 2) {
-            *t1Seed += tStep;
-            t1[0] = t1[1];
-            t1[1] = t1[2];
-            calcMask = 1 << 2;
-        } else {
-            calcMask = 0;
-        }
-        if (best_j == 0) {
-            *t2Seed -= tStep;
-            t2[2] = t2[1];
-            t2[1] = t2[0];
-            calcMask |= 1 << 3;
-        } else if (best_j == 2) {
-            *t2Seed += tStep;
-            t2[0] = t2[1];
-            t2[1] = t2[2];
-            calcMask |= 1 << 5;
-        }
-    } while (true);
-#if ONE_OFF_DEBUG
-    SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) != (%1.9g,%1.9g) %s\n", __FUNCTION__,
-        t1Seed, t2Seed, t1[1].fX, t1[1].fY, t1[2].fX, t1[2].fY);
-#endif
-    return false;
-}
-
-static void lookNearEnd(const SkDQuad& q1, const SkDQuad& q2, int testT,
-        const SkIntersections& orig, bool swap, SkIntersections* i) {
-    if (orig.used() == 1 && orig[!swap][0] == testT) {
-        return;
-    }
-    if (orig.used() == 2 && orig[!swap][1] == testT) {
-        return;
-    }
-    SkDLine tmpLine;
-    int testTIndex = testT << 1;
-    tmpLine[0] = tmpLine[1] = q2[testTIndex];
-    tmpLine[1].fX += q2[1].fY - q2[testTIndex].fY;
-    tmpLine[1].fY -= q2[1].fX - q2[testTIndex].fX;
-    SkIntersections impTs;
-    impTs.intersectRay(q1, tmpLine);
-    for (int index = 0; index < impTs.used(); ++index) {
-        SkDPoint realPt = impTs.pt(index);
-        if (!tmpLine[0].approximatelyPEqual(realPt)) {
-            continue;
-        }
-        if (swap) {
-            i->insert(testT, impTs[0][index], tmpLine[0]);
-        } else {
-            i->insert(impTs[0][index], testT, tmpLine[0]);
-        }
-    }
-}
-
-int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
-    fMax = 4;
-    bool exactMatch = false;
-    // if the quads share an end point, check to see if they overlap
-    for (int i1 = 0; i1 < 3; i1 += 2) {
-        for (int i2 = 0; i2 < 3; i2 += 2) {
-            if (q1[i1].asSkPoint() == q2[i2].asSkPoint()) {
-                insert(i1 >> 1, i2 >> 1, q1[i1]);
-                exactMatch = true;
-            }
-        }
-    }
-    SkASSERT(fUsed < 3);
-    if (only_end_pts_in_common(q1, q2)) {
-        return fUsed;
-    }
-    if (only_end_pts_in_common(q2, q1)) {
-        return fUsed;
-    }
-    // see if either quad is really a line
-    // FIXME: figure out why reduce step didn't find this earlier
-    if (is_linear(q1, q2, this)) {
-        return fUsed;
-    }
-    SkIntersections swapped;
-    swapped.setMax(fMax);
-    if (is_linear(q2, q1, &swapped)) {
-        swapped.swapPts();
-        *this = swapped;
-        return fUsed;
-    }
-    SkIntersections copyI(*this);
-    lookNearEnd(q1, q2, 0, *this, false, &copyI);
-    lookNearEnd(q1, q2, 1, *this, false, &copyI);
-    lookNearEnd(q2, q1, 0, *this, true, &copyI);
-    lookNearEnd(q2, q1, 1, *this, true, &copyI);
-    int innerEqual = 0;
-    if (copyI.fUsed >= 2) {
-        SkASSERT(copyI.fUsed <= 4);
-        double width = copyI[0][1] - copyI[0][0];
-        int midEnd = 1;
-        for (int index = 2; index < copyI.fUsed; ++index) {
-            double testWidth = copyI[0][index] - copyI[0][index - 1];
-            if (testWidth <= width) {
-                continue;
-            }
-            midEnd = index;
-        }
-        for (int index = 0; index < 2; ++index) {
-            double testT = (copyI[0][midEnd] * (index + 1)
-                    + copyI[0][midEnd - 1] * (2 - index)) / 3;
-            SkDPoint testPt1 = q1.ptAtT(testT);
-            testT = (copyI[1][midEnd] * (index + 1) + copyI[1][midEnd - 1] * (2 - index)) / 3;
-            SkDPoint testPt2 = q2.ptAtT(testT);
-            innerEqual += testPt1.approximatelyEqual(testPt2);
-        }
-    }
-    bool expectCoincident = copyI.fUsed >= 2 && innerEqual == 2;
-    if (expectCoincident) {
-        reset();
-        insertCoincident(copyI[0][0], copyI[1][0], copyI.fPt[0]);
-        int last = copyI.fUsed - 1;
-        insertCoincident(copyI[0][last], copyI[1][last], copyI.fPt[last]);
-        return fUsed;
-    }
-    SkDQuadImplicit i1(q1);
-    SkDQuadImplicit i2(q2);
-    int index;
-    bool flip1 = q1[2] == q2[0];
-    bool flip2 = q1[0] == q2[2];
-    bool useCubic = q1[0] == q2[0];
-    double roots1[4];
-    int rootCount = findRoots(i2, q1, roots1, useCubic, flip1, 0);
-    // OPTIMIZATION: could short circuit here if all roots are < 0 or > 1
-    double roots1Copy[4];
-    SkDEBUGCODE(sk_bzero(roots1Copy, sizeof(roots1Copy)));
-    int r1Count = addValidRoots(roots1, rootCount, roots1Copy);
-    SkDPoint pts1[4];
-    for (index = 0; index < r1Count; ++index) {
-        pts1[index] = q1.ptAtT(roots1Copy[index]);
-    }
-    double roots2[4];
-    int rootCount2 = findRoots(i1, q2, roots2, useCubic, flip2, 0);
-    double roots2Copy[4];
-    int r2Count = addValidRoots(roots2, rootCount2, roots2Copy);
-    SkDPoint pts2[4];
-    for (index = 0; index < r2Count; ++index) {
-        pts2[index] = q2.ptAtT(roots2Copy[index]);
-    }
-    bool triedBinary = false;
-    if (r1Count == r2Count && r1Count <= 1) {
-        if (r1Count == 1 && used() == 0) {
-            if (pts1[0].approximatelyEqual(pts2[0])) {
-                insert(roots1Copy[0], roots2Copy[0], pts1[0]);
-            } else {
-                // find intersection by chasing t
-                triedBinary = true;
-                if (binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) {
-                    insert(roots1Copy[0], roots2Copy[0], pts1[0]);
-                }
-            }
-        }
-        return fUsed;
-    }
-    int closest[4];
-    double dist[4];
-    bool foundSomething = false;
-    for (index = 0; index < r1Count; ++index) {
-        dist[index] = DBL_MAX;
-        closest[index] = -1;
-        for (int ndex2 = 0; ndex2 < r2Count; ++ndex2) {
-            if (!pts2[ndex2].approximatelyEqual(pts1[index])) {
-                continue;
-            }
-            double dx = pts2[ndex2].fX - pts1[index].fX;
-            double dy = pts2[ndex2].fY - pts1[index].fY;
-            double distance = dx * dx + dy * dy;
-            if (dist[index] <= distance) {
-                continue;
-            }
-            for (int outer = 0; outer < index; ++outer) {
-                if (closest[outer] != ndex2) {
-                    continue;
-                }
-                if (dist[outer] < distance) {
-                    goto next;
-                }
-                closest[outer] = -1;
-            }
-            dist[index] = distance;
-            closest[index] = ndex2;
-            foundSomething = true;
-        next:
-            ;
-        }
-    }
-    if (r1Count && r2Count && !foundSomething) {
-        if (exactMatch) {
-            SkASSERT(fUsed > 0);
-            return fUsed;
-        }
-        relaxed_is_linear(&q1, 0, 1, &q2, 0, 1, this);
-        if (fUsed) {
-            return fUsed;
-        }
-        // maybe the curves are nearly coincident
-        if (!triedBinary && binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) {
-            insert(roots1Copy[0], roots2Copy[0], pts1[0]);
-        }
-        return fUsed;
-    }
-    int used = 0;
-    do {
-        double lowest = DBL_MAX;
-        int lowestIndex = -1;
-        for (index = 0; index < r1Count; ++index) {
-            if (closest[index] < 0) {
-                continue;
-            }
-            if (roots1Copy[index] < lowest) {
-                lowestIndex = index;
-                lowest = roots1Copy[index];
-            }
-        }
-        if (lowestIndex < 0) {
-            break;
-        }
-        insert(roots1Copy[lowestIndex], roots2Copy[closest[lowestIndex]],
-                pts1[lowestIndex]);
-        closest[lowestIndex] = -1;
-    } while (++used < r1Count);
-    return fUsed;
-}
-
-void SkIntersections::alignQuadPts(const SkPoint q1[3], const SkPoint q2[3]) {
-    for (int index = 0; index < used(); ++index) {
-        const SkPoint result = pt(index).asSkPoint();
-        if (q1[0] == result || q1[2] == result || q2[0] == result || q2[2] == result) {
-            continue;
-        }
-        if (SkDPoint::ApproximatelyEqual(q1[0], result)) {
-            fPt[index].set(q1[0]);
-//            SkASSERT(way_roughly_zero(fT[0][index]));  // this value can be bigger than way rough
-            fT[0][index] = 0;
-        } else if (SkDPoint::ApproximatelyEqual(q1[2], result)) {
-            fPt[index].set(q1[2]);
-//            SkASSERT(way_roughly_equal(fT[0][index], 1));
-            fT[0][index] = 1;
-        }
-        if (SkDPoint::ApproximatelyEqual(q2[0], result)) {
-            fPt[index].set(q2[0]);
-//            SkASSERT(way_roughly_zero(fT[1][index]));
-            fT[1][index] = 0;
-        } else if (SkDPoint::ApproximatelyEqual(q2[2], result)) {
-            fPt[index].set(q2[2]);
-//            SkASSERT(way_roughly_equal(fT[1][index], 1));
-            fT[1][index] = 1;
-        }
-    }
-}
diff --git a/src/pathops/SkDQuadLineIntersection.cpp b/src/pathops/SkDQuadLineIntersection.cpp
index ef8edb0..5e3596e 100644
--- a/src/pathops/SkDQuadLineIntersection.cpp
+++ b/src/pathops/SkDQuadLineIntersection.cpp
@@ -95,16 +95,46 @@
 
     LineQuadraticIntersections(const SkDQuad& q, const SkDLine& l, SkIntersections* i)
         : fQuad(q)
-        , fLine(l)
+        , fLine(&l)
         , fIntersections(i)
         , fAllowNear(true) {
         i->setMax(3);  // allow short partial coincidence plus discrete intersection
     }
 
+    LineQuadraticIntersections(const SkDQuad& q)
+        : fQuad(q) 
+        SkDEBUGPARAMS(fLine(NULL))
+        SkDEBUGPARAMS(fIntersections(NULL))
+        SkDEBUGPARAMS(fAllowNear(false)) {
+    }
+
     void allowNear(bool allow) {
         fAllowNear = allow;
     }
 
+    void checkCoincident() {
+        int last = fIntersections->used() - 1;
+        for (int index = 0; index < last; ) {
+            double quadMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2;
+            SkDPoint quadMidPt = fQuad.ptAtT(quadMidT);
+            double t = fLine->nearPoint(quadMidPt, NULL);
+            if (t < 0) {
+                ++index;
+                continue;
+            }
+            if (fIntersections->isCoincident(index)) {
+                fIntersections->removeOne(index);
+                --last;
+            } else if (fIntersections->isCoincident(index + 1)) {
+                fIntersections->removeOne(index + 1);
+                --last;
+            } else {
+                fIntersections->setCoincident(index++);
+            }
+            fIntersections->setCoincident(index);
+        }
+    }
+
     int intersectRay(double roots[2]) {
     /*
         solve by rotating line+quad so line is horizontal, then finding the roots
@@ -121,11 +151,11 @@
         for each of the three points (e.g. n = 0 to 2)
         quad[n].fY' = (quad[n].fY - line[0].fY) * A - (quad[n].fX - line[0].fX) * O
     */
-        double adj = fLine[1].fX - fLine[0].fX;
-        double opp = fLine[1].fY - fLine[0].fY;
+        double adj = (*fLine)[1].fX - (*fLine)[0].fX;
+        double opp = (*fLine)[1].fY - (*fLine)[0].fY;
         double r[3];
         for (int n = 0; n < 3; ++n) {
-            r[n] = (fQuad[n].fY - fLine[0].fY) * adj - (fQuad[n].fX - fLine[0].fX) * opp;
+            r[n] = (fQuad[n].fY - (*fLine)[0].fY) * adj - (fQuad[n].fX - (*fLine)[0].fX) * opp;
         }
         double A = r[2];
         double B = r[1];
@@ -140,20 +170,17 @@
         if (fAllowNear) {
             addNearEndPoints();
         }
-        if (fIntersections->used() == 2) {
-            // FIXME : need sharable code that turns spans into coincident if middle point is on
-        } else {
-            double rootVals[2];
-            int roots = intersectRay(rootVals);
-            for (int index = 0; index < roots; ++index) {
-                double quadT = rootVals[index];
-                double lineT = findLineT(quadT);
-                SkDPoint pt;
-                if (pinTs(&quadT, &lineT, &pt, kPointUninitialized)) {
-                    fIntersections->insert(quadT, lineT, pt);
-                }
+        double rootVals[2];
+        int roots = intersectRay(rootVals);
+        for (int index = 0; index < roots; ++index) {
+            double quadT = rootVals[index];
+            double lineT = findLineT(quadT);
+            SkDPoint pt;
+            if (pinTs(&quadT, &lineT, &pt, kPointUninitialized) && uniqueAnswer(quadT, pt)) {
+                fIntersections->insert(quadT, lineT, pt);
             }
         }
+        checkCoincident();
         return fIntersections->used();
     }
 
@@ -178,16 +205,41 @@
             double quadT = rootVals[index];
             SkDPoint pt = fQuad.ptAtT(quadT);
             double lineT = (pt.fX - left) / (right - left);
-            if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
+            if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
                 fIntersections->insert(quadT, lineT, pt);
             }
         }
         if (flipped) {
             fIntersections->flip();
         }
+        checkCoincident();
         return fIntersections->used();
     }
 
+    bool uniqueAnswer(double quadT, const SkDPoint& pt) {
+        for (int inner = 0; inner < fIntersections->used(); ++inner) {
+            if (fIntersections->pt(inner) != pt) {
+                continue;
+            }
+            double existingQuadT = (*fIntersections)[0][inner];
+            if (quadT == existingQuadT) {
+                return false;
+            }
+            // check if midway on quad is also same point. If so, discard this
+            double quadMidT = (existingQuadT + quadT) / 2;
+            SkDPoint quadMidPt = fQuad.ptAtT(quadMidT);
+            if (quadMidPt.approximatelyEqual(pt)) {
+                return false;
+            }
+        }
+#if ONE_OFF_DEBUG
+        SkDPoint qPt = fQuad.ptAtT(quadT);
+        SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
+                qPt.fX, qPt.fY);
+#endif
+        return true;
+    }
+
     int verticalIntersect(double axisIntercept, double roots[2]) {
         double D = fQuad[2].fX;  // f
         double E = fQuad[1].fX;  // e
@@ -209,13 +261,14 @@
             double quadT = rootVals[index];
             SkDPoint pt = fQuad.ptAtT(quadT);
             double lineT = (pt.fY - top) / (bottom - top);
-            if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) {
+            if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
                 fIntersections->insert(quadT, lineT, pt);
             }
         }
         if (flipped) {
             fIntersections->flip();
         }
+        checkCoincident();
         return fIntersections->used();
     }
 
@@ -223,7 +276,7 @@
     // add endpoints first to get zero and one t values exactly
     void addExactEndPoints() {
         for (int qIndex = 0; qIndex < 3; qIndex += 2) {
-            double lineT = fLine.exactPoint(fQuad[qIndex]);
+            double lineT = fLine->exactPoint(fQuad[qIndex]);
             if (lineT < 0) {
                 continue;
             }
@@ -238,7 +291,7 @@
             if (fIntersections->hasT(quadT)) {
                 continue;
             }
-            double lineT = fLine.nearPoint(fQuad[qIndex], NULL);
+            double lineT = fLine->nearPoint(fQuad[qIndex], NULL);
             if (lineT < 0) {
                 continue;
             }
@@ -301,12 +354,12 @@
 
     double findLineT(double t) {
         SkDPoint xy = fQuad.ptAtT(t);
-        double dx = fLine[1].fX - fLine[0].fX;
-        double dy = fLine[1].fY - fLine[0].fY;
+        double dx = (*fLine)[1].fX - (*fLine)[0].fX;
+        double dy = (*fLine)[1].fY - (*fLine)[0].fY;
         if (fabs(dx) > fabs(dy)) {
-            return (xy.fX - fLine[0].fX) / dx;
+            return (xy.fX - (*fLine)[0].fX) / dx;
         }
-        return (xy.fY - fLine[0].fY) / dy;
+        return (xy.fY - (*fLine)[0].fY) / dy;
     }
 
     bool pinTs(double* quadT, double* lineT, SkDPoint* pt, PinTPoint ptSet) {
@@ -319,16 +372,16 @@
         double qT = *quadT = SkPinT(*quadT);
         double lT = *lineT = SkPinT(*lineT);
         if (lT == 0 || lT == 1 || (ptSet == kPointUninitialized && qT != 0 && qT != 1)) {
-            *pt = fLine.ptAtT(lT);
+            *pt = (*fLine).ptAtT(lT);
         } else if (ptSet == kPointUninitialized) {
             *pt = fQuad.ptAtT(qT);
         }
         SkPoint gridPt = pt->asSkPoint();
-        if (SkDPoint::ApproximatelyEqual(gridPt, fLine[0].asSkPoint())) {
-            *pt = fLine[0];
+        if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[0].asSkPoint())) {
+            *pt = (*fLine)[0];
             *lineT = 0;
-        } else if (SkDPoint::ApproximatelyEqual(gridPt, fLine[1].asSkPoint())) {
-            *pt = fLine[1];
+        } else if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[1].asSkPoint())) {
+            *pt = (*fLine)[1];
             *lineT = 1;
         }
         if (fIntersections->used() > 0 && approximately_equal((*fIntersections)[1][0], *lineT)) {
@@ -346,7 +399,7 @@
 
 private:
     const SkDQuad& fQuad;
-    const SkDLine& fLine;
+    const SkDLine* fLine;
     SkIntersections* fIntersections;
     bool fAllowNear;
 };
@@ -379,3 +432,13 @@
     }
     return fUsed;
 }
+
+int SkIntersections::HorizontalIntercept(const SkDQuad& quad, SkScalar y, double* roots) {
+    LineQuadraticIntersections q(quad);
+    return q.horizontalIntersect(y, roots);
+}
+
+int SkIntersections::VerticalIntercept(const SkDQuad& quad, SkScalar x, double* roots) {
+    LineQuadraticIntersections q(quad);
+    return q.verticalIntersect(x, roots);
+}
diff --git a/src/pathops/SkIntersectionHelper.h b/src/pathops/SkIntersectionHelper.h
index 3569c93..79de034 100644
--- a/src/pathops/SkIntersectionHelper.h
+++ b/src/pathops/SkIntersectionHelper.h
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 #include "SkOpContour.h"
+#include "SkOpSegment.h"
 #include "SkPath.h"
 
 #ifdef SK_DEBUG
@@ -18,45 +19,13 @@
         kVerticalLine_Segment = 0,
         kLine_Segment = SkPath::kLine_Verb,
         kQuad_Segment = SkPath::kQuad_Verb,
+        kConic_Segment = SkPath::kConic_Verb,
         kCubic_Segment = SkPath::kCubic_Verb,
     };
 
-    bool addCoincident(SkIntersectionHelper& other, const SkIntersections& ts, bool swap) {
-        return fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
-    }
-
-    // FIXME: does it make sense to write otherIndex now if we're going to
-    // fix it up later?
-    void addOtherT(int index, double otherT, int otherIndex) {
-        fContour->addOtherT(fIndex, index, otherT, otherIndex);
-    }
-
-    bool addPartialCoincident(SkIntersectionHelper& other, const SkIntersections& ts, int index,
-            bool swap) {
-        return fContour->addPartialCoincident(fIndex, other.fContour, other.fIndex, ts, index,
-                swap);
-    }
-
-    // Avoid collapsing t values that are close to the same since
-    // we walk ts to describe consecutive intersections. Since a pair of ts can
-    // be nearly equal, any problems caused by this should be taken care
-    // of later.
-    // On the edge or out of range values are negative; add 2 to get end
-    int addT(const SkIntersectionHelper& other, const SkPoint& pt, double newT) {
-        return fContour->addT(fIndex, other.fContour, other.fIndex, pt, newT);
-    }
-
-    int addSelfT(const SkPoint& pt, double newT) {
-        return fContour->addSelfT(fIndex, pt, newT);
-    }
-
     bool advance() {
-        return ++fIndex < fLast;
-    }
-
-    void alignTPt(SkIntersectionHelper& other, bool swap, int index,
-            SkIntersections* ts, SkPoint* point) {
-        fContour->alignTPt(fIndex, other.fContour, other.fIndex, swap, index, ts, point);
+        fSegment = fSegment->next();
+        return fSegment != NULL;
     }
 
     SkScalar bottom() const {
@@ -64,30 +33,15 @@
     }
 
     const SkPathOpsBounds& bounds() const {
-        return fContour->segments()[fIndex].bounds();
+        return fSegment->bounds();
+    }
+
+    SkOpContour* contour() const {
+        return fSegment->contour();
     }
 
     void init(SkOpContour* contour) {
-        fContour = contour;
-        fIndex = 0;
-        fLast = contour->segments().count();
-    }
-
-    bool isAdjacent(const SkIntersectionHelper& next) {
-        return fContour == next.fContour && fIndex + 1 == next.fIndex;
-    }
-
-    bool isFirstLast(const SkIntersectionHelper& next) {
-        return fContour == next.fContour && fIndex == 0
-                && next.fIndex == fLast - 1;
-    }
-
-    bool isPartial(double t1, double t2, const SkDPoint& pt1, const SkDPoint& pt2) const {
-        const SkOpSegment& segment = fContour->segments()[fIndex];
-        double mid = (t1 + t2) / 2;
-        SkDPoint midPtByT = segment.dPtAtT(mid);
-        SkDPoint midPtByAvg = SkDPoint::Mid(pt1, pt2);
-        return midPtByT.approximatelyPEqual(midPtByAvg);
+        fSegment = contour->first();
     }
 
     SkScalar left() const {
@@ -95,39 +49,42 @@
     }
 
     const SkPoint* pts() const {
-        return fContour->segments()[fIndex].pts();
+        return fSegment->pts();
     }
 
     SkScalar right() const {
         return bounds().fRight;
     }
 
+    SkOpSegment* segment() const {
+        return fSegment;
+    }
+
     SegmentType segmentType() const {
-        const SkOpSegment& segment = fContour->segments()[fIndex];
-        SegmentType type = (SegmentType) segment.verb();
+        SegmentType type = (SegmentType) fSegment->verb();
         if (type != kLine_Segment) {
             return type;
         }
-        if (segment.isHorizontal()) {
+        if (fSegment->isHorizontal()) {
             return kHorizontalLine_Segment;
         }
-        if (segment.isVertical()) {
+        if (fSegment->isVertical()) {
             return kVerticalLine_Segment;
         }
         return kLine_Segment;
     }
 
     bool startAfter(const SkIntersectionHelper& after) {
-        fIndex = after.fIndex;
-        return advance();
+        fSegment = after.fSegment->next();
+        return fSegment != NULL;
     }
 
     SkScalar top() const {
         return bounds().fTop;
     }
 
-    SkPath::Verb verb() const {
-        return fContour->segments()[fIndex].verb();
+    SkScalar weight() const {
+        return fSegment->weight();
     }
 
     SkScalar x() const {
@@ -147,10 +104,5 @@
     }
 
 private:
-    // utility callable by the user from the debugger when the implementation code is linked in
-    void dump() const;
-
-    SkOpContour* fContour;
-    int fIndex;
-    int fLast;
+    SkOpSegment* fSegment;
 };
diff --git a/src/pathops/SkIntersections.cpp b/src/pathops/SkIntersections.cpp
index e9875cf..7caf04b 100644
--- a/src/pathops/SkIntersections.cpp
+++ b/src/pathops/SkIntersections.cpp
@@ -7,52 +7,22 @@
 
 #include "SkIntersections.h"
 
-void SkIntersections::append(const SkIntersections& i) {
-    for (int index = 0; index < i.fUsed; ++index) {
-        insert(i[0][index], i[1][index], i.pt(index));
-    }
-}
-
-int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar, SkScalar, SkScalar, bool) = {
-    NULL,
-    &SkIntersections::verticalLine,
-    &SkIntersections::verticalQuad,
-    &SkIntersections::verticalCubic
-};
-
-int ( SkIntersections::* const CurveRay[])(const SkPoint[], const SkDLine&) = {
-    NULL,
-    &SkIntersections::lineRay,
-    &SkIntersections::quadRay,
-    &SkIntersections::cubicRay
-};
-
-int SkIntersections::coincidentUsed() const {
-    if (!fIsCoincident[0]) {
-        SkASSERT(!fIsCoincident[1]);
-        return 0;
-    }
-    int count = 0;
-    SkDEBUGCODE(int count2 = 0;)
+int SkIntersections::closestTo(double rangeStart, double rangeEnd, const SkDPoint& testPt,
+        double* closestDist) const {
+    int closest = -1;
+    *closestDist = SK_ScalarMax;
     for (int index = 0; index < fUsed; ++index) {
-        if (fIsCoincident[0] & (1 << index)) {
-            ++count;
+        if (!between(rangeStart, fT[0][index], rangeEnd)) {
+            continue;
         }
-#ifdef SK_DEBUG
-        if (fIsCoincident[1] & (1 << index)) {
-            ++count2;
+        const SkDPoint& iPt = fPt[index];
+        double dist = testPt.distanceSquared(iPt);
+        if (*closestDist > dist) {
+            *closestDist = dist;
+            closest = index;
         }
-#endif
     }
-    SkASSERT(count == count2);
-    return count;
-}
-
-int SkIntersections::cubicRay(const SkPoint pts[4], const SkDLine& line) {
-    SkDCubic cubic;
-    cubic.set(pts);
-    fMax = 3;
-    return intersectRay(cubic, line);
+    return closest;
 }
 
 void SkIntersections::flip() {
@@ -105,7 +75,6 @@
     int remaining = fUsed - index;
     if (remaining > 0) {
         memmove(&fPt[index + 1], &fPt[index], sizeof(fPt[0]) * remaining);
-        memmove(&fPt2[index + 1], &fPt2[index], sizeof(fPt2[0]) * remaining);
         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);
         int clearMask = ~((1 << index) - 1);
@@ -125,45 +94,53 @@
     SkASSERT(one == 0 || one == 1);
     SkASSERT(two == 0 || two == 1);
     SkASSERT(pt1 != pt2);
-    SkASSERT(fNearlySame[(int) one]);
+    fNearlySame[one ? 1 : 0] = true;
     (void) insert(one, two, pt1);
-    fPt2[one ? fUsed - 1 : 0] = pt2;
+    fPt2[one ? 1 : 0] = pt2;
 }
 
-void SkIntersections::insertCoincident(double one, double two, const SkDPoint& pt) {
+int SkIntersections::insertCoincident(double one, double two, const SkDPoint& pt) {
     int index = insertSwap(one, two, pt);
+    if (index >= 0) {
+        setCoincident(index);
+    }
+    return index;
+}
+
+void SkIntersections::setCoincident(int index) {
+    SkASSERT(index >= 0);
     int bit = 1 << index;
     fIsCoincident[0] |= bit;
     fIsCoincident[1] |= bit;
 }
 
-int SkIntersections::lineRay(const SkPoint pts[2], const SkDLine& line) {
-    SkDLine l;
-    l.set(pts);
-    fMax = 2;
-    return intersectRay(l, line);
+void SkIntersections::merge(const SkIntersections& a, int aIndex, const SkIntersections& b,
+        int bIndex) {
+    this->reset();
+    fT[0][0] = a.fT[0][aIndex];
+    fT[1][0] = b.fT[0][bIndex];
+    fPt[0] = a.fPt[aIndex];
+    fPt2[0] = b.fPt[bIndex];
+    fUsed = 1;
 }
 
-void SkIntersections::offset(int base, double start, double end) {
-    for (int index = base; index < fUsed; ++index) {
-        double val = fT[fSwap][index];
-        val *= end - start;
-        val += start;
-        fT[fSwap][index] = val;
+int SkIntersections::mostOutside(double rangeStart, double rangeEnd, const SkDPoint& origin) const {
+    int result = -1;
+    for (int index = 0; index < fUsed; ++index) {
+        if (!between(rangeStart, fT[0][index], rangeEnd)) {
+            continue;
+        }
+        if (result < 0) {
+            result = index;
+            continue;
+        }
+        SkDVector best = fPt[result] - origin;
+        SkDVector test = fPt[index] - origin;
+        if (test.crossCheck(best) < 0) {
+            result = index;
+        }
     }
-}
-
-int SkIntersections::quadRay(const SkPoint pts[3], const SkDLine& line) {
-    SkDQuad quad;
-    quad.set(pts);
-    fMax = 2;
-    return intersectRay(quad, line);
-}
-
-void SkIntersections::quickRemoveOne(int index, int replace) {
-    if (index < replace) {
-        fT[0][index] = fT[0][replace];
-    }
+    return result;
 }
 
 void SkIntersections::removeOne(int index) {
@@ -172,7 +149,6 @@
         return;
     }
     memmove(&fPt[index], &fPt[index + 1], sizeof(fPt[0]) * remaining);
-    memmove(&fPt2[index], &fPt2[index + 1], sizeof(fPt2[0]) * remaining);
     memmove(&fT[0][index], &fT[0][index + 1], sizeof(fT[0][0]) * remaining);
     memmove(&fT[1][index], &fT[1][index + 1], sizeof(fT[1][0]) * remaining);
 //    SkASSERT(fIsCoincident[0] == 0);
@@ -181,31 +157,3 @@
     SkASSERT(!(coBit ^ (fIsCoincident[1] & (1 << index))));
     fIsCoincident[1] -= ((fIsCoincident[1] >> 1) & ~((1 << index) - 1)) + coBit;
 }
-
-void SkIntersections::swapPts() {
-    int index;
-    for (index = 0; index < fUsed; ++index) {
-        SkTSwap(fT[0][index], fT[1][index]);
-    }
-}
-
-int SkIntersections::verticalLine(const SkPoint a[2], SkScalar top, SkScalar bottom,
-        SkScalar x, bool flipped) {
-    SkDLine line;
-    line.set(a);
-    return vertical(line, top, bottom, x, flipped);
-}
-
-int SkIntersections::verticalQuad(const SkPoint a[3], SkScalar top, SkScalar bottom,
-        SkScalar x, bool flipped) {
-    SkDQuad quad;
-    quad.set(a);
-    return vertical(quad, top, bottom, x, flipped);
-}
-
-int SkIntersections::verticalCubic(const SkPoint a[4], SkScalar top, SkScalar bottom,
-        SkScalar x, bool flipped) {
-    SkDCubic cubic;
-    cubic.set(a);
-    return vertical(cubic, top, bottom, x, flipped);
-}
diff --git a/src/pathops/SkIntersections.h b/src/pathops/SkIntersections.h
index a1bde51..c12db38 100644
--- a/src/pathops/SkIntersections.h
+++ b/src/pathops/SkIntersections.h
@@ -7,6 +7,7 @@
 #ifndef SkIntersections_DEFINE
 #define SkIntersections_DEFINE
 
+#include "SkPathOpsConic.h"
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsLine.h"
 #include "SkPathOpsPoint.h"
@@ -16,7 +17,6 @@
 public:
     SkIntersections()
         : fSwap(0)
-        , fFlatMeasure(false)
 #ifdef SK_DEBUG
         , fDepth(0)
 #endif
@@ -24,7 +24,6 @@
         sk_bzero(fPt, sizeof(fPt));
         sk_bzero(fPt2, sizeof(fPt2));
         sk_bzero(fT, sizeof(fT));
-        sk_bzero(fIsCoincident, sizeof(fIsCoincident));
         sk_bzero(fNearlySame, sizeof(fNearlySame));
         reset();
         fMax = 0;  // require that the caller set the max
@@ -32,7 +31,7 @@
 
     class TArray {
     public:
-        explicit TArray(const double ts[9]) : fTArray(ts) {}
+        explicit TArray(const double ts[10]) : fTArray(ts) {}
         double operator[](int n) const {
             return fTArray[n];
         }
@@ -40,28 +39,40 @@
     };
     TArray operator[](int n) const { return TArray(fT[n]); }
 
-    void allowFlatMeasure(bool flatAllowed) {
-        fFlatMeasure = flatAllowed;
-    }
-
     void allowNear(bool nearAllowed) {
         fAllowNear = nearAllowed;
     }
 
-    int cubic(const SkPoint a[4]) {
-        SkDCubic cubic;
-        cubic.set(a);
-        fMax = 1;  // self intersect
-        return intersect(cubic);
+    void clearCoincidence(int index) {
+        SkASSERT(index >= 0);
+        int bit = 1 << index;
+        fIsCoincident[0] &= ~bit;
+        fIsCoincident[1] &= ~bit;
     }
 
-    int cubicCubic(const SkPoint a[4], const SkPoint b[4]) {
-        SkDCubic aCubic;
-        aCubic.set(a);
-        SkDCubic bCubic;
-        bCubic.set(b);
-        fMax = 9;
-        return intersect(aCubic, bCubic);
+    int conicHorizontal(const SkPoint a[3], SkScalar weight, SkScalar left, SkScalar right,
+                SkScalar y, bool flipped) {
+        SkDConic conic;
+        conic.set(a, weight);
+        fMax = 2;
+        return horizontal(conic, left, right, y, flipped);
+    }
+
+    int conicVertical(const SkPoint a[3], SkScalar weight, SkScalar top, SkScalar bottom,
+            SkScalar x, bool flipped) {
+        SkDConic conic;
+        conic.set(a, weight);
+        fMax = 2;
+        return vertical(conic, top, bottom, x, flipped);
+    }
+
+    int conicLine(const SkPoint a[3], SkScalar weight, const SkPoint b[2]) {
+        SkDConic conic;
+        conic.set(a, weight);
+        SkDLine line;
+        line.set(b);
+        fMax = 3; // 2;  permit small coincident segment + non-coincident intersection
+        return intersect(conic, line);
     }
 
     int cubicHorizontal(const SkPoint a[4], SkScalar left, SkScalar right, SkScalar y,
@@ -88,19 +99,6 @@
         return intersect(cubic, line);
     }
 
-    int cubicQuad(const SkPoint a[4], const SkPoint b[3]) {
-        SkDCubic cubic;
-        cubic.set(a);
-        SkDQuad quad;
-        quad.set(b);
-        fMax = 7;
-        return intersect(cubic, quad);
-    }
-
-    bool flatMeasure() const {
-        return fFlatMeasure;
-    }
-
     bool hasT(double t) const {
         SkASSERT(t == 0 || t == 1);
         return fUsed > 0 && (t == 0 ? fT[0][0] == 0 : fT[0][fUsed - 1] == 1);
@@ -178,19 +176,11 @@
         return intersect(quad, line);
     }
 
-    int quadQuad(const SkPoint a[3], const SkPoint b[3]) {
-        SkDQuad aQuad;
-        aQuad.set(a);
-        SkDQuad bQuad;
-        bQuad.set(b);
-        fMax = 4;
-        return intersect(aQuad, bQuad);
-    }
-
     // leaves swap, max alone
     void reset() {
         fAllowNear = true;
         fUsed = 0;
+        sk_bzero(fIsCoincident, sizeof(fIsCoincident));
     }
 
     void set(bool swap, int tIndex, double t) {
@@ -205,8 +195,6 @@
         fSwap ^= true;
     }
 
-    void swapPts();
-
     bool swapped() const {
         return fSwap;
     }
@@ -219,54 +207,66 @@
         SkASSERT(--fDepth >= 0);
     }
 
+    bool unBumpT(int index) {
+        SkASSERT(fUsed == 1);
+        fT[0][index] = fT[0][index] * (1 + BUMP_EPSILON * 2) - BUMP_EPSILON;
+        if (!between(0, fT[0][index], 1)) {
+            fUsed = 0;
+            return false;
+        }
+        return true;
+    }
+
     void upDepth() {
         SkASSERT(++fDepth < 16);
     }
 
     void alignQuadPts(const SkPoint a[3], const SkPoint b[3]);
-    void append(const SkIntersections& );
     int cleanUpCoincidence();
-    int coincidentUsed() const;
+    int closestTo(double rangeStart, double rangeEnd, const SkDPoint& testPt, double* dist) const;
     void cubicInsert(double one, double two, const SkDPoint& pt, const SkDCubic& c1,
                      const SkDCubic& c2);
-    int cubicRay(const SkPoint pts[4], const SkDLine& line);
     void flip();
-    int horizontal(const SkDLine&, double y);
     int horizontal(const SkDLine&, double left, double right, double y, bool flipped);
     int horizontal(const SkDQuad&, double left, double right, double y, bool flipped);
     int horizontal(const SkDQuad&, double left, double right, double y, double tRange[2]);
     int horizontal(const SkDCubic&, double y, double tRange[3]);
+    int horizontal(const SkDConic&, double left, double right, double y, bool flipped);
     int horizontal(const SkDCubic&, double left, double right, double y, bool flipped);
     int horizontal(const SkDCubic&, double left, double right, double y, double tRange[3]);
+    static double HorizontalIntercept(const SkDLine& line, double y);
+    static int HorizontalIntercept(const SkDQuad& quad, SkScalar y, double* roots);
+    static int HorizontalIntercept(const SkDConic& conic, SkScalar y, double* roots);
     // FIXME : does not respect swap
     int insert(double one, double two, const SkDPoint& pt);
     void insertNear(double one, double two, const SkDPoint& pt1, const SkDPoint& pt2);
     // start if index == 0 : end if index == 1
-    void insertCoincident(double one, double two, const SkDPoint& pt);
+    int insertCoincident(double one, double two, const SkDPoint& pt);
     int intersect(const SkDLine&, const SkDLine&);
     int intersect(const SkDQuad&, const SkDLine&);
     int intersect(const SkDQuad&, const SkDQuad&);
-    int intersect(const SkDCubic&);  // return true if cubic self-intersects
+    int intersect(const SkDConic&, const SkDLine&);
+    int intersect(const SkDConic&, const SkDQuad&);
+    int intersect(const SkDConic&, const SkDConic&);
     int intersect(const SkDCubic&, const SkDLine&);
     int intersect(const SkDCubic&, const SkDQuad&);
+    int intersect(const SkDCubic&, const SkDConic&);
     int intersect(const SkDCubic&, const SkDCubic&);
     int intersectRay(const SkDLine&, const SkDLine&);
     int intersectRay(const SkDQuad&, const SkDLine&);
+    int intersectRay(const SkDConic&, const SkDLine&);
     int intersectRay(const SkDCubic&, const SkDLine&);
-    static SkDPoint Line(const SkDLine&, const SkDLine&);
-    int lineRay(const SkPoint pts[2], const SkDLine& line);
-    void offset(int base, double start, double end);
-    void quickRemoveOne(int index, int replace);
-    int quadRay(const SkPoint pts[3], const SkDLine& line);
+    void merge(const SkIntersections& , int , const SkIntersections& , int );
+    int mostOutside(double rangeStart, double rangeEnd, const SkDPoint& origin) const;
     void removeOne(int index);
-    static bool Test(const SkDLine& , const SkDLine&);
-    int vertical(const SkDLine&, double x);
+    void setCoincident(int index);
     int vertical(const SkDLine&, double top, double bottom, double x, bool flipped);
     int vertical(const SkDQuad&, double top, double bottom, double x, bool flipped);
+    int vertical(const SkDConic&, double top, double bottom, double x, bool flipped);
     int vertical(const SkDCubic&, double top, double bottom, double x, bool flipped);
-    int verticalCubic(const SkPoint a[4], SkScalar top, SkScalar bottom, SkScalar x, bool flipped);
-    int verticalLine(const SkPoint a[2], SkScalar top, SkScalar bottom, SkScalar x, bool flipped);
-    int verticalQuad(const SkPoint a[3], SkScalar top, SkScalar bottom, SkScalar x, bool flipped);
+    static double VerticalIntercept(const SkDLine& line, double x);
+    static int VerticalIntercept(const SkDQuad& quad, SkScalar x, double* roots);
+    static int VerticalIntercept(const SkDConic& conic, SkScalar x, double* roots);
 
     int depth() const {
 #ifdef SK_DEBUG
@@ -276,6 +276,9 @@
 #endif
     }
 
+    int debugCoincidentUsed() const;
+    void dump() const;  // implemented for testing only
+
 private:
     bool cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2);
     bool cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2);
@@ -283,23 +286,18 @@
     void cleanUpParallelLines(bool parallel);
     void computePoints(const SkDLine& line, int used);
 
-    SkDPoint fPt[9];  // FIXME: since scans store points as SkPoint, this should also
-    SkDPoint fPt2[9];  // used by nearly same to store alternate intersection point
-    double fT[2][9];
+    SkDPoint fPt[10];  // FIXME: since scans store points as SkPoint, this should also
+    SkDPoint fPt2[2];  // used by nearly same to store alternate intersection point
+    double fT[2][10];
     uint16_t fIsCoincident[2];  // bit set for each curve's coincident T
     bool fNearlySame[2];  // true if end points nearly match
     unsigned char fUsed;
     unsigned char fMax;
     bool fAllowNear;
     bool fSwap;
-    bool fFlatMeasure;  // backwards-compatibility when cubics uses quad intersection
 #ifdef SK_DEBUG
     int fDepth;
 #endif
 };
 
-extern int (SkIntersections::* const CurveRay[])(const SkPoint[], const SkDLine& );
-extern int (SkIntersections::* const CurveVertical[])(const SkPoint[], SkScalar top, SkScalar bottom,
-            SkScalar x, bool flipped);
-
 #endif
diff --git a/src/pathops/SkOpAngle.cpp b/src/pathops/SkOpAngle.cpp
index b3a188c..6e49c49 100644
--- a/src/pathops/SkOpAngle.cpp
+++ b/src/pathops/SkOpAngle.cpp
@@ -4,26 +4,26 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#include "SkIntersections.h"
 #include "SkOpAngle.h"
 #include "SkOpSegment.h"
 #include "SkPathOpsCurve.h"
 #include "SkTSort.h"
 
-#if DEBUG_ANGLE
-#include "SkString.h"
-#endif
-
 /* Angles are sorted counterclockwise. The smallest angle has a positive x and the smallest
    positive y. The largest angle has a positive x and a zero y. */
 
 #if DEBUG_ANGLE
-    static bool CompareResult(SkString* bugOut, int append, bool compare) {
+    static bool CompareResult(const char* func, SkString* bugOut, SkString* bugPart, int append,
+             bool compare) {
         SkDebugf("%s %c %d\n", bugOut->c_str(), compare ? 'T' : 'F', append);
+        SkDebugf("%sPart %s\n", func, bugPart[0].c_str());
+        SkDebugf("%sPart %s\n", func, bugPart[1].c_str());
+        SkDebugf("%sPart %s\n", func, bugPart[2].c_str());
         return compare;
     }
 
-    #define COMPARE_RESULT(append, compare) CompareResult(&bugOut, append, compare)
+    #define COMPARE_RESULT(append, compare) CompareResult(__FUNCTION__, &bugOut, bugPart, append, \
+            compare)
 #else
     #define COMPARE_RESULT(append, compare) compare
 #endif
@@ -58,51 +58,50 @@
 */
 
 // return true if lh < this < rh
-bool SkOpAngle::after(const SkOpAngle* test) const {
-    const SkOpAngle& lh = *test;
-    const SkOpAngle& rh = *lh.fNext;
-    SkASSERT(&lh != &rh);
+bool SkOpAngle::after(SkOpAngle* test) {
+    SkOpAngle* lh = test;
+    SkOpAngle* rh = lh->fNext;
+    SkASSERT(lh != rh);
 #if DEBUG_ANGLE
     SkString bugOut;
     bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
                   " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
                   " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__,
-            lh.fSegment->debugID(), lh.debugID(), lh.fSectorStart, lh.fSectorEnd,
-            lh.fSegment->t(lh.fStart), lh.fSegment->t(lh.fEnd),
-            fSegment->debugID(), debugID(), fSectorStart, fSectorEnd, fSegment->t(fStart),
-            fSegment->t(fEnd),
-            rh.fSegment->debugID(), rh.debugID(), rh.fSectorStart, rh.fSectorEnd,
-            rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd));
+            lh->segment()->debugID(), lh->debugID(), lh->fSectorStart, lh->fSectorEnd,
+            lh->fStart->t(), lh->fEnd->t(),
+            segment()->debugID(), debugID(), fSectorStart, fSectorEnd, fStart->t(), fEnd->t(),
+            rh->segment()->debugID(), rh->debugID(), rh->fSectorStart, rh->fSectorEnd,
+            rh->fStart->t(), rh->fEnd->t());
+    SkString bugPart[3] = { lh->debugPart(), this->debugPart(), rh->debugPart() };
 #endif
-    if (lh.fComputeSector && !const_cast<SkOpAngle&>(lh).computeSector()) {
+    if (lh->fComputeSector && !lh->computeSector()) {
         return COMPARE_RESULT(1, true);
     }
-    if (fComputeSector && !const_cast<SkOpAngle*>(this)->computeSector()) {
+    if (fComputeSector && !this->computeSector()) {
         return COMPARE_RESULT(2, true);
     }
-    if (rh.fComputeSector && !const_cast<SkOpAngle&>(rh).computeSector()) {
+    if (rh->fComputeSector && !rh->computeSector()) {
         return COMPARE_RESULT(3, true);
     }
 #if DEBUG_ANGLE  // reset bugOut with computed sectors
     bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
                   " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g"
                   " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__,
-            lh.fSegment->debugID(), lh.debugID(), lh.fSectorStart, lh.fSectorEnd,
-            lh.fSegment->t(lh.fStart), lh.fSegment->t(lh.fEnd),
-            fSegment->debugID(), debugID(), fSectorStart, fSectorEnd, fSegment->t(fStart),
-            fSegment->t(fEnd),
-            rh.fSegment->debugID(), rh.debugID(), rh.fSectorStart, rh.fSectorEnd,
-            rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd));
+            lh->segment()->debugID(), lh->debugID(), lh->fSectorStart, lh->fSectorEnd,
+            lh->fStart->t(), lh->fEnd->t(),
+            segment()->debugID(), debugID(), fSectorStart, fSectorEnd, fStart->t(), fEnd->t(),
+            rh->segment()->debugID(), rh->debugID(), rh->fSectorStart, rh->fSectorEnd,
+            rh->fStart->t(), rh->fEnd->t());
 #endif
-    bool ltrOverlap = (lh.fSectorMask | rh.fSectorMask) & fSectorMask;
-    bool lrOverlap = lh.fSectorMask & rh.fSectorMask;
+    bool ltrOverlap = (lh->fSectorMask | rh->fSectorMask) & fSectorMask;
+    bool lrOverlap = lh->fSectorMask & rh->fSectorMask;
     int lrOrder;  // set to -1 if either order works
     if (!lrOverlap) {  // no lh/rh sector overlap
         if (!ltrOverlap) {  // no lh/this/rh sector overlap
-            return COMPARE_RESULT(4,  (lh.fSectorEnd > rh.fSectorStart)
-                    ^ (fSectorStart > lh.fSectorEnd) ^ (fSectorStart > rh.fSectorStart));
+            return COMPARE_RESULT(4,  (lh->fSectorEnd > rh->fSectorStart)
+                    ^ (fSectorStart > lh->fSectorEnd) ^ (fSectorStart > rh->fSectorStart));
         }
-        int lrGap = (rh.fSectorStart - lh.fSectorStart + 32) & 0x1f;
+        int lrGap = (rh->fSectorStart - lh->fSectorStart + 32) & 0x1f;
         /* A tiny change can move the start +/- 4. The order can only be determined if
            lr gap is not 12 to 20 or -12 to -20.
                -31 ..-21      1
@@ -115,24 +114,24 @@
          */
         lrOrder = lrGap > 20 ? 0 : lrGap > 11 ? -1 : 1;
     } else {
-        lrOrder = (int) lh.orderable(rh);
+        lrOrder = (int) lh->orderable(rh);
         if (!ltrOverlap) {
             return COMPARE_RESULT(5, !lrOrder);
         }
     }
     int ltOrder;
-    SkASSERT((lh.fSectorMask & fSectorMask) || (rh.fSectorMask & fSectorMask));
-    if (lh.fSectorMask & fSectorMask) {
-        ltOrder = (int) lh.orderable(*this);
+    SkASSERT((lh->fSectorMask & fSectorMask) || (rh->fSectorMask & fSectorMask));
+    if (lh->fSectorMask & fSectorMask) {
+        ltOrder = (int) lh->orderable(this);
     } else {
-        int ltGap = (fSectorStart - lh.fSectorStart + 32) & 0x1f;
+        int ltGap = (fSectorStart - lh->fSectorStart + 32) & 0x1f;
         ltOrder = ltGap > 20 ? 0 : ltGap > 11 ? -1 : 1;
     }
     int trOrder;
-    if (rh.fSectorMask & fSectorMask) {
+    if (rh->fSectorMask & fSectorMask) {
         trOrder = (int) orderable(rh);
     } else {
-        int trGap = (rh.fSectorStart - fSectorStart + 32) & 0x1f;
+        int trGap = (rh->fSectorStart - fSectorStart + 32) & 0x1f;
         trOrder = trGap > 20 ? 0 : trGap > 11 ? -1 : 1;
     }
     if (lrOrder >= 0 && ltOrder >= 0 && trOrder >= 0) {
@@ -145,20 +144,20 @@
     if (ltOrder == 0 && lrOrder == 0) {
         SkASSERT(trOrder < 0);
         // FIXME : once this is verified to work, remove one opposite angle call
-        SkDEBUGCODE(bool lrOpposite = lh.oppositePlanes(rh));
-        bool ltOpposite = lh.oppositePlanes(*this);
+        SkDEBUGCODE(bool lrOpposite = lh->oppositePlanes(rh));
+        bool ltOpposite = lh->oppositePlanes(this);
         SkASSERT(lrOpposite != ltOpposite);
         return COMPARE_RESULT(8, ltOpposite);
     } else if (ltOrder == 1 && trOrder == 0) {
         SkASSERT(lrOrder < 0);
-        SkDEBUGCODE(bool ltOpposite = lh.oppositePlanes(*this));
+        SkDEBUGCODE(bool ltOpposite = lh->oppositePlanes(this));
         bool trOpposite = oppositePlanes(rh);
         SkASSERT(ltOpposite != trOpposite);
         return COMPARE_RESULT(9, trOpposite);
     } else if (lrOrder == 1 && trOrder == 1) {
         SkASSERT(ltOrder < 0);
         SkDEBUGCODE(bool trOpposite = oppositePlanes(rh));
-        bool lrOpposite = lh.oppositePlanes(rh);
+        bool lrOpposite = lh->oppositePlanes(rh);
         SkASSERT(lrOpposite != trOpposite);
         return COMPARE_RESULT(10, lrOpposite);
     }
@@ -173,77 +172,50 @@
 
 // given a line, see if the opposite curve's convex hull is all on one side
 // returns -1=not on one side    0=this CW of test   1=this CCW of test
-int SkOpAngle::allOnOneSide(const SkOpAngle& test) const {
+int SkOpAngle::allOnOneSide(const SkOpAngle* test) {
     SkASSERT(!fIsCurve);
-    SkASSERT(test.fIsCurve);
-    const SkDPoint& origin = test.fCurvePart[0];
+    SkASSERT(test->fIsCurve);
+    const SkDPoint& origin = test->fCurvePart[0];
     SkVector line;
-    if (fSegment->verb() == SkPath::kLine_Verb) {
-        const SkPoint* linePts = fSegment->pts();
-        int lineStart = fStart < fEnd ? 0 : 1;
+    if (segment()->verb() == SkPath::kLine_Verb) {
+        const SkPoint* linePts = segment()->pts();
+        int lineStart = fStart->t() < fEnd->t() ? 0 : 1;
         line = linePts[lineStart ^ 1] - linePts[lineStart];
     } else {
         SkPoint shortPts[2] = { fCurvePart[0].asSkPoint(), fCurvePart[1].asSkPoint() };
         line = shortPts[1] - shortPts[0];
     }
     float crosses[3];
-    SkPath::Verb testVerb = test.fSegment->verb();
+    SkPath::Verb testVerb = test->segment()->verb();
     int iMax = SkPathOpsVerbToPoints(testVerb);
 //    SkASSERT(origin == test.fCurveHalf[0]);
-    const SkDCubic& testCurve = test.fCurvePart;
-//    do {
-        for (int index = 1; index <= iMax; ++index) {
-            float xy1 = (float) (line.fX * (testCurve[index].fY - origin.fY));
-            float xy2 = (float) (line.fY * (testCurve[index].fX - origin.fX));
-            crosses[index - 1] = AlmostEqualUlps(xy1, xy2) ? 0 : xy1 - xy2;
-        }
-        if (crosses[0] * crosses[1] < 0) {
+    const SkDCurve& testCurve = test->fCurvePart;
+    for (int index = 1; index <= iMax; ++index) {
+        float xy1 = (float) (line.fX * (testCurve[index].fY - origin.fY));
+        float xy2 = (float) (line.fY * (testCurve[index].fX - origin.fX));
+        crosses[index - 1] = AlmostEqualUlps(xy1, xy2) ? 0 : xy1 - xy2;
+    }
+    if (crosses[0] * crosses[1] < 0) {
+        return -1;
+    }
+    if (SkPath::kCubic_Verb == testVerb) {
+        if (crosses[0] * crosses[2] < 0 || crosses[1] * crosses[2] < 0) {
             return -1;
         }
-        if (SkPath::kCubic_Verb == testVerb) {
-            if (crosses[0] * crosses[2] < 0 || crosses[1] * crosses[2] < 0) {
-                return -1;
-            }
-        }
-        if (crosses[0]) {
-            return crosses[0] < 0;
-        }
-        if (crosses[1]) {
-            return crosses[1] < 0;
-        }
-        if (SkPath::kCubic_Verb == testVerb && crosses[2]) {
-            return crosses[2] < 0;
-        }
+    }
+    if (crosses[0]) {
+        return crosses[0] < 0;
+    }
+    if (crosses[1]) {
+        return crosses[1] < 0;
+    }
+    if (SkPath::kCubic_Verb == testVerb && crosses[2]) {
+        return crosses[2] < 0;
+    }
     fUnorderable = true;
     return -1;
 }
 
-bool SkOpAngle::calcSlop(double x, double y, double rx, double ry, bool* result) const {
-    double absX = fabs(x);
-    double absY = fabs(y);
-    double length = absX < absY ? absX / 2 + absY : absX + absY / 2;
-    int exponent;
-    (void) frexp(length, &exponent);
-    double epsilon = ldexp(FLT_EPSILON, exponent);
-    SkPath::Verb verb = fSegment->verb();
-    SkASSERT(verb == SkPath::kQuad_Verb || verb == SkPath::kCubic_Verb);
-    // FIXME: the quad and cubic factors are made up ; determine actual values
-    double slop = verb == SkPath::kQuad_Verb ? 4 * epsilon : 512 * epsilon;
-    double xSlop = slop;
-    double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _copysign ?
-    double x1 = x - xSlop;
-    double y1 = y + ySlop;
-    double x_ry1 = x1 * ry;
-    double rx_y1 = rx * y1;
-    *result = x_ry1 < rx_y1;
-    double x2 = x + xSlop;
-    double y2 = y - ySlop;
-    double x_ry2 = x2 * ry;
-    double rx_y2 = rx * y2;
-    bool less2 = x_ry2 < rx_y2;
-    return *result == less2;
-}
-
 bool SkOpAngle::checkCrossesZero() const {
     int start = SkTMin(fSectorStart, fSectorEnd);
     int end = SkTMax(fSectorStart, fSectorEnd);
@@ -251,31 +223,97 @@
     return crossesZero;
 }
 
-bool SkOpAngle::checkParallel(const SkOpAngle& rh) const {
+// loop looking for a pair of angle parts that are too close to be sorted
+/* This is called after other more simple intersection and angle sorting tests have been exhausted.
+   This should be rarely called -- the test below is thorough and time consuming.
+   This checks the distance between start points; the distance between 
+*/
+void SkOpAngle::checkNearCoincidence() {
+    SkOpAngle* test = this;
+    do {
+        SkOpSegment* testSegment = test->segment();
+        double testStartT = test->start()->t();
+        SkDPoint testStartPt = testSegment->dPtAtT(testStartT);
+        double testEndT = test->end()->t();
+        SkDPoint testEndPt = testSegment->dPtAtT(testEndT);
+        double testLenSq = testStartPt.distanceSquared(testEndPt);
+        if (0) {
+            SkDebugf("%s testLenSq=%1.9g id=%d\n", __FUNCTION__, testLenSq, testSegment->debugID());
+        }
+        double testMidT = (testStartT + testEndT) / 2;
+        SkOpAngle* next = test;
+        while ((next = next->fNext) != this) {
+            SkOpSegment* nextSegment = next->segment();
+            double testMidDistSq = testSegment->distSq(testMidT, next);
+            double testEndDistSq = testSegment->distSq(testEndT, next);
+            double nextStartT = next->start()->t();
+            SkDPoint nextStartPt = nextSegment->dPtAtT(nextStartT);
+            double distSq = testStartPt.distanceSquared(nextStartPt);
+            double nextEndT = next->end()->t();
+            double nextMidT = (nextStartT + nextEndT) / 2;
+            double nextMidDistSq = nextSegment->distSq(nextMidT, test);
+            double nextEndDistSq = nextSegment->distSq(nextEndT, test);
+            if (0) {
+                SkDebugf("%s distSq=%1.9g testId=%d nextId=%d\n", __FUNCTION__, distSq,
+                        testSegment->debugID(), nextSegment->debugID());
+                SkDebugf("%s testMidDistSq=%1.9g\n", __FUNCTION__, testMidDistSq);
+                SkDebugf("%s testEndDistSq=%1.9g\n", __FUNCTION__, testEndDistSq);
+                SkDebugf("%s nextMidDistSq=%1.9g\n", __FUNCTION__, nextMidDistSq);
+                SkDebugf("%s nextEndDistSq=%1.9g\n", __FUNCTION__, nextEndDistSq);
+                SkDPoint nextEndPt = nextSegment->dPtAtT(nextEndT);
+                double nextLenSq = nextStartPt.distanceSquared(nextEndPt);
+                SkDebugf("%s nextLenSq=%1.9g\n", __FUNCTION__, nextLenSq);
+                SkDebugf("\n");
+            }
+        }
+        test = test->fNext;
+    } while (test->fNext != this); 
+}
+
+bool SkOpAngle::checkParallel(SkOpAngle* rh) {
     SkDVector scratch[2];
     const SkDVector* sweep, * tweep;
-    if (!fUnorderedSweep) {
-        sweep = fSweep;
+    if (!this->fUnorderedSweep) {
+        sweep = this->fSweep;
     } else {
-        scratch[0] = fCurvePart[1] - fCurvePart[0];
+        scratch[0] = this->fCurvePart[1] - this->fCurvePart[0];
         sweep = &scratch[0];
     }
-    if (!rh.fUnorderedSweep) {
-        tweep = rh.fSweep;
+    if (!rh->fUnorderedSweep) {
+        tweep = rh->fSweep;
     } else {
-        scratch[1] = rh.fCurvePart[1] - rh.fCurvePart[0];
+        scratch[1] = rh->fCurvePart[1] - rh->fCurvePart[0];
         tweep = &scratch[1];
     }
     double s0xt0 = sweep->crossCheck(*tweep);
     if (tangentsDiverge(rh, s0xt0)) {
         return s0xt0 < 0;
     }
-    SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0];
-    SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0];
+    // compute the perpendicular to the endpoints and see where it intersects the opposite curve
+    // if the intersections within the t range, do a cross check on those
+    bool inside;
+    if (!fCurvePart[SkPathOpsVerbToPoints(this->segment()->verb())].approximatelyEqual(
+            rh->fCurvePart[SkPathOpsVerbToPoints(rh->segment()->verb())])) {
+        if (this->endToSide(rh, &inside)) {
+            return inside;
+        }
+        if (rh->endToSide(this, &inside)) {
+            return !inside;
+        }
+    }
+    if (this->midToSide(rh, &inside)) {
+        return inside;
+    }
+    if (rh->midToSide(this, &inside)) {
+        return !inside;
+    }
+    // compute the cross check from the mid T values (last resort)
+    SkDVector m0 = segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
+    SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
     double m0xm1 = m0.crossCheck(m1);
     if (m0xm1 == 0) {
-        fUnorderable = true;
-        rh.fUnorderable = true;
+        this->fUnorderable = true;
+        rh->fUnorderable = true;
         return true;
     }
     return m0xm1 < 0;
@@ -288,48 +326,55 @@
     if (fComputedSector) {
         return !fUnorderable;
     }
-//    SkASSERT(fSegment->verb() != SkPath::kLine_Verb && small());
     fComputedSector = true;
-    int step = fStart < fEnd ? 1 : -1;
-    int limit = step > 0 ? fSegment->count() : -1;
-    int checkEnd = fEnd;
-    do {
-// advance end
-        const SkOpSpan& span = fSegment->span(checkEnd);
-        const SkOpSegment* other = span.fOther;
-        int oCount = other->count();
-        for (int oIndex = 0; oIndex < oCount; ++oIndex) {
-            const SkOpSpan& oSpan = other->span(oIndex);
-            if (oSpan.fOther != fSegment) {
-                continue;
-            }
-            if (oSpan.fOtherIndex == checkEnd) {
-                continue;
-            }
-            if (!approximately_equal(oSpan.fOtherT, span.fT)) {
-                continue;
-            }
-            goto recomputeSector;
-        }
-        checkEnd += step;
-    } while (checkEnd != limit);
-recomputeSector:
-    if (checkEnd == fEnd || checkEnd - step == fEnd) {
+    bool stepUp = fStart->t() < fEnd->t();
+    const SkOpSpanBase* checkEnd = fEnd;
+    if (checkEnd->final() && stepUp) {
         fUnorderable = true;
         return false;
     }
-    int saveEnd = fEnd;
-    fComputedEnd = fEnd = checkEnd - step;
+    do {
+// advance end
+        const SkOpSegment* other = checkEnd->segment();
+        const SkOpSpanBase* oSpan = other->head();
+        do {
+            if (oSpan->segment() != segment()) {
+                continue;
+            }
+            if (oSpan == checkEnd) {
+                continue;
+            }
+            if (!approximately_equal(oSpan->t(), checkEnd->t())) {
+                continue;
+            }
+            goto recomputeSector;
+        } while (!oSpan->final() && (oSpan = oSpan->upCast()->next()));
+        checkEnd = stepUp ? !checkEnd->final()
+                ? checkEnd->upCast()->next() : NULL
+                : checkEnd->prev();
+    } while (checkEnd);
+recomputeSector:
+    SkOpSpanBase* computedEnd = stepUp ? checkEnd ? checkEnd->prev() : fEnd->segment()->head() 
+            : checkEnd ? checkEnd->upCast()->next() : fEnd->segment()->tail();
+    if (checkEnd == fEnd || computedEnd == fEnd || computedEnd == fStart) {
+        fUnorderable = true;
+        return false;
+    }
+    if (stepUp != (fStart->t() < computedEnd->t())) {
+        fUnorderable = true;
+        return false;
+    }
+    SkOpSpanBase* saveEnd = fEnd;
+    fComputedEnd = fEnd = computedEnd;
     setSpans();
     setSector();
     fEnd = saveEnd;
     return !fUnorderable;
 }
 
-// returns -1 if overlaps   0 if no overlap cw    1 if no overlap ccw
-int SkOpAngle::convexHullOverlaps(const SkOpAngle& rh) const {
-    const SkDVector* sweep = fSweep;
-    const SkDVector* tweep = rh.fSweep;
+int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
+    const SkDVector* sweep = this->fSweep;
+    const SkDVector* tweep = rh->fSweep;
     double s0xs1 = sweep[0].crossCheck(sweep[1]);
     double s0xt0 = sweep[0].crossCheck(tweep[0]);
     double s1xt0 = sweep[1].crossCheck(tweep[0]);
@@ -359,8 +404,8 @@
     // if the outside sweeps are greater than 180 degress:
         // first assume the inital tangents are the ordering
         // if the midpoint direction matches the inital order, that is enough
-    SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0];
-    SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0];
+    SkDVector m0 = this->segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
+    SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
     double m0xm1 = m0.crossCheck(m1);
     if (s0xt0 > 0 && m0xm1 > 0) {
         return 0;
@@ -394,34 +439,30 @@
     return sqrt(longest) / dist;
 }
 
-bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const {
-    SkPath::Verb lVerb = fSegment->verb();
-    SkPath::Verb rVerb = rh.fSegment->verb();
+bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
+    SkPath::Verb lVerb = this->segment()->verb();
+    SkPath::Verb rVerb = rh->segment()->verb();
     int lPts = SkPathOpsVerbToPoints(lVerb);
     int rPts = SkPathOpsVerbToPoints(rVerb);
-    SkDLine rays[] = {{{fCurvePart[0], rh.fCurvePart[rPts]}},
-            {{fCurvePart[0], fCurvePart[lPts]}}};
+    SkDLine rays[] = {{{this->fCurvePart[0], rh->fCurvePart[rPts]}},
+            {{this->fCurvePart[0], this->fCurvePart[lPts]}}};
     if (rays[0][1] == rays[1][1]) {
         return checkParallel(rh);
     }
     double smallTs[2] = {-1, -1};
     bool limited[2] = {false, false};
     for (int index = 0; index < 2; ++index) {
-        const SkOpSegment& segment = index ? *rh.fSegment : *fSegment;
-        SkIntersections i;
-        int cPts = index ? rPts : lPts;
-        (*CurveIntersectRay[cPts])(segment.pts(), rays[index], &i);
+        SkPath::Verb cVerb = index ? rVerb : lVerb;
         // if the curve is a line, then the line and the ray intersect only at their crossing
-        if (cPts == 1) { // line
+        if (cVerb == SkPath::kLine_Verb) {
             continue;
         }
-//      SkASSERT(i.used() >= 1);
-//        if (i.used() <= 1) {
-//            continue;
-//        }
-        double tStart = segment.t(index ? rh.fStart : fStart);
-        double tEnd = segment.t(index ? rh.fComputedEnd : fComputedEnd);
-        bool testAscends = index ? rh.fStart < rh.fComputedEnd : fStart < fComputedEnd;
+        const SkOpSegment& segment = index ? *rh->segment() : *this->segment();
+        SkIntersections i;
+        (*CurveIntersectRay[cVerb])(segment.pts(), segment.weight(), rays[index], &i);
+        double tStart = index ? rh->fStart->t() : this->fStart->t();
+        double tEnd = index ? rh->fComputedEnd->t() : this->fComputedEnd->t();
+        bool testAscends = tStart < (index ? rh->fComputedEnd->t() : this->fComputedEnd->t());
         double t = testAscends ? 0 : 1;
         for (int idx2 = 0; idx2 < i.used(); ++idx2) {
             double testT = i[0][idx2];
@@ -435,29 +476,6 @@
             limited[index] = approximately_equal_orderable(t, tEnd);
         }
     }
-#if 0
-    if (smallTs[0] < 0 && smallTs[1] < 0) {  // if neither ray intersects, do endpoint sort
-        double m0xm1 = 0;
-        if (lVerb == SkPath::kLine_Verb) {
-            SkASSERT(rVerb != SkPath::kLine_Verb);
-            SkDVector m0 = rays[1][1] - fCurvePart[0];
-            SkDPoint endPt;
-            endPt.set(rh.fSegment->pts()[rh.fStart < rh.fEnd ? rPts : 0]);
-            SkDVector m1 = endPt - fCurvePart[0];
-            m0xm1 = m0.crossCheck(m1);
-        }
-        if (rVerb == SkPath::kLine_Verb) {
-            SkDPoint endPt;
-            endPt.set(fSegment->pts()[fStart < fEnd ? lPts : 0]);
-            SkDVector m0 = endPt - fCurvePart[0];
-            SkDVector m1 = rays[0][1] - fCurvePart[0];
-            m0xm1 = m0.crossCheck(m1);
-        }
-        if (m0xm1 != 0) {
-            return m0xm1 < 0;
-        }
-    }
-#endif
     bool sRayLonger = false;
     SkDVector sCept = {0, 0};
     double sCeptT = -1;
@@ -467,7 +485,7 @@
         if (smallTs[index] < 0) {
             continue;
         }
-        const SkOpSegment& segment = index ? *rh.fSegment : *fSegment;
+        const SkOpSegment& segment = index ? *rh->segment() : *this->segment();
         const SkDPoint& dPt = segment.dPtAtT(smallTs[index]);
         SkDVector cept = dPt - rays[index][0];
         // If this point is on the curve, it should have been detected earlier by ordinary
@@ -498,7 +516,7 @@
         double minX, minY, maxX, maxY;
         minX = minY = SK_ScalarInfinity;
         maxX = maxY = -SK_ScalarInfinity;
-        const SkDCubic& curve = index ? rh.fCurvePart : fCurvePart;
+        const SkDCurve& curve = index ? rh->fCurvePart : this->fCurvePart;
         int ptCount = index ? rPts : lPts;
         for (int idx2 = 0; idx2 <= ptCount; ++idx2) {
             minX = SkTMin(minX, curve[idx2].fX);
@@ -508,7 +526,7 @@
         }
         double maxWidth = SkTMax(maxX - minX, maxY - minY);
         delta /= maxWidth;
-        if (delta > 1e-4 && (useIntersect ^= true)) {  // FIXME: move this magic number
+        if (delta > 1e-3 && (useIntersect ^= true)) {  // FIXME: move this magic number
             sRayLonger = rayLonger;
             sCept = cept;
             sCeptT = smallTs[index];
@@ -516,9 +534,9 @@
         }
     }
     if (useIntersect) {
-        const SkDCubic& curve = sIndex ? rh.fCurvePart : fCurvePart;
-        const SkOpSegment& segment = sIndex ? *rh.fSegment : *fSegment;
-        double tStart = segment.t(sIndex ? rh.fStart : fStart);
+        const SkDCurve& curve = sIndex ? rh->fCurvePart : this->fCurvePart;
+        const SkOpSegment& segment = sIndex ? *rh->segment() : *this->segment();
+        double tStart = sIndex ? rh->fStart->t() : fStart->t();
         SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0];
         double septDir = mid.crossCheck(sCept);
         if (!septDir) {
@@ -530,76 +548,57 @@
     }
 }
 
-// Most of the time, the first one can be found trivially by detecting the smallest sector value.
-// If all angles have the same sector value, actual sorting is required.
-const SkOpAngle* SkOpAngle::findFirst() const {
-    const SkOpAngle* best = this;
-    int bestStart = SkTMin(fSectorStart, fSectorEnd);
-    const SkOpAngle* angle = this;
-    while ((angle = angle->fNext) != this) {
-        int angleEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd);
-        if (angleEnd < bestStart) {
-            return angle;    // we wrapped around
-        }
-        int angleStart = SkTMin(angle->fSectorStart, angle->fSectorEnd);
-        if (bestStart > angleStart) {
-            best = angle;
-            bestStart = angleStart;
-        }
+bool SkOpAngle::endToSide(const SkOpAngle* rh, bool* inside) const {
+    const SkOpSegment* segment = this->segment();
+    SkPath::Verb verb = segment->verb();
+    SkDLine rayEnd;
+    rayEnd[0].set(this->fEnd->pt());
+    rayEnd[1] = rayEnd[0];
+    SkDVector slopeAtEnd = (*CurveDSlopeAtT[verb])(segment->pts(), segment->weight(),
+            this->fEnd->t());
+    rayEnd[1].fX += slopeAtEnd.fY;
+    rayEnd[1].fY -= slopeAtEnd.fX;
+    SkIntersections iEnd;
+    const SkOpSegment* oppSegment = rh->segment();
+    SkPath::Verb oppVerb = oppSegment->verb();
+    (*CurveIntersectRay[oppVerb])(oppSegment->pts(), oppSegment->weight(), rayEnd, &iEnd);
+    double endDist;
+    int closestEnd = iEnd.closestTo(rh->fStart->t(), rh->fEnd->t(), rayEnd[0], &endDist);
+    if (closestEnd < 0) {
+        return false;
     }
-    // back up to the first possible angle
-    const SkOpAngle* firstBest = best;
-    angle = best;
-    int bestEnd = SkTMax(best->fSectorStart, best->fSectorEnd);
-    while ((angle = angle->previous()) != firstBest) {
-        if (angle->fStop) {
-            break;
-        }
-        int angleStart = SkTMin(angle->fSectorStart, angle->fSectorEnd);
-        // angles that are smaller by one aren't necessary better, since the larger may be a line
-        // and the smaller may be a curve that curls to the other side of the line.
-        if (bestEnd + 1 < angleStart) {
-            return best;
-        }
-        best = angle;
-        bestEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd);
+    if (!endDist) {
+        return false;
     }
-    // in the case where all angles are nearly in the same sector, check the order to find the best
-    firstBest = best;
-    angle = best;
-    do {
-        angle = angle->fNext;
-        if (angle->fStop) {
-            return firstBest;
-        }
-        bool orderable = best->orderable(*angle);  // note: may return an unorderable angle
-        if (orderable == 0) {
-            return angle;
-        }
-        best = angle;
-    } while (angle != firstBest);
-    // if the angles are equally ordered, fall back on the initial tangent
-    bool foundBelow = false;
-    while ((angle = angle->fNext)) {
-        SkDVector scratch[2];
-        const SkDVector* sweep;
-        if (!angle->fUnorderedSweep) {
-            sweep = angle->fSweep;
-        } else {
-            scratch[0] = angle->fCurvePart[1] - angle->fCurvePart[0];
-            sweep = &scratch[0];
-        }
-        bool isAbove = sweep->fY <= 0;
-        if (isAbove && foundBelow) {
-            return angle;
-        }
-        foundBelow |= !isAbove;
-        if (angle == firstBest) {
-            return NULL; // should not loop around
-        }
+    SkDPoint start;
+    start.set(this->fStart->pt());
+    // OPTIMIZATION: multiple times in the code we find the max scalar
+    double minX, minY, maxX, maxY;
+    minX = minY = SK_ScalarInfinity;
+    maxX = maxY = -SK_ScalarInfinity;
+    const SkDCurve& curve = rh->fCurvePart;
+    int oppPts = SkPathOpsVerbToPoints(oppVerb);
+    for (int idx2 = 0; idx2 <= oppPts; ++idx2) {
+        minX = SkTMin(minX, curve[idx2].fX);
+        minY = SkTMin(minY, curve[idx2].fY);
+        maxX = SkTMax(maxX, curve[idx2].fX);
+        maxY = SkTMax(maxY, curve[idx2].fY);
     }
-    SkASSERT(0);  // should never get here
-    return NULL;
+    double maxWidth = SkTMax(maxX - minX, maxY - minY);
+    endDist /= maxWidth;
+    if (endDist < 5e-11) {  // empirically found
+        return false;
+    }
+    const SkDPoint* endPt = &rayEnd[0];
+    SkDPoint oppPt = iEnd.pt(closestEnd);
+    SkDVector vLeft = *endPt - start;
+    SkDVector vRight = oppPt - start;
+    double dir = vLeft.crossCheck(vRight);
+    if (!dir) {
+        return false;
+    }
+    *inside = dir < 0;
+    return true;
 }
 
 /*      y<0 y==0 y>0  x<0 x==0 x>0 xy<0 xy==0 xy>0
@@ -639,6 +638,11 @@
     return sector;
 }
 
+SkOpGlobalState* SkOpAngle::globalState() const {
+    return this->segment()->globalState();
+}
+
+
 // OPTIMIZE: if this loops to only one other angle, after first compare fails, insert on other side
 // OPTIMIZE: return where insertion succeeded. Then, start next insertion on opposite side
 void SkOpAngle::insert(SkOpAngle* angle) {
@@ -662,9 +666,6 @@
     }
     SkOpAngle* next = fNext;
     if (next->fNext == this) {
-        if (angle->overlap(*this)) {  // angles are essentially coincident
-            return;
-        }
         if (singleton || angle->after(this)) {
             this->fNext = angle;
             angle->fNext = next;
@@ -678,9 +679,6 @@
     SkOpAngle* last = this;
     do {
         SkASSERT(last->fNext == next);
-        if (angle->overlap(*last) || angle->overlap(*next)) {
-            return;
-        }
         if (angle->after(last)) {
             last->fNext = angle;
             angle->fNext = next;
@@ -689,48 +687,49 @@
         }
         last = next;
         next = next->fNext;
-        if (last == this && next->fUnorderable) {
-            fUnorderable = true;
+        if (last == this) {
+            if (next->fUnorderable) {
+                fUnorderable = true;
+            } else {
+                globalState()->setAngleCoincidence();
+                this->fNext = angle;
+                angle->fNext = next;
+                angle->fCheckCoincidence = true;
+            }
             return;
         }
-        SkASSERT(last != this);
     } while (true);
 }
 
-bool SkOpAngle::isHorizontal() const {
-    return !fIsCurve && fSweep[0].fY == 0;
-}
-
-SkOpSpan* SkOpAngle::lastMarked() const {
+SkOpSpanBase* SkOpAngle::lastMarked() const {
     if (fLastMarked) {
-        if (fLastMarked->fChased) {
+        if (fLastMarked->chased()) {
             return NULL;
         }
-        fLastMarked->fChased = true;
+        fLastMarked->setChased(true);
     }
     return fLastMarked;
 }
 
-bool SkOpAngle::loopContains(const SkOpAngle& test) const {
+bool SkOpAngle::loopContains(const SkOpAngle* angle) const {
     if (!fNext) {
         return false;
     }
     const SkOpAngle* first = this;
     const SkOpAngle* loop = this;
-    const SkOpSegment* tSegment = test.fSegment;
-    double tStart = tSegment->span(test.fStart).fT;
-    double tEnd = tSegment->span(test.fEnd).fT;
+    const SkOpSegment* tSegment = angle->fStart->segment();
+    double tStart = angle->fStart->t();
+    double tEnd = angle->fEnd->t();
     do {
-        const SkOpSegment* lSegment = loop->fSegment;
-        // FIXME : use precisely_equal ? or compare points exactly ?
+        const SkOpSegment* lSegment = loop->fStart->segment();
         if (lSegment != tSegment) {
             continue;
         }
-        double lStart = lSegment->span(loop->fStart).fT;
+        double lStart = loop->fStart->t();
         if (lStart != tEnd) {
             continue;
         }
-        double lEnd = lSegment->span(loop->fEnd).fT;
+        double lEnd = loop->fEnd->t();
         if (lEnd == tStart) {
             return true;
         }
@@ -749,22 +748,6 @@
     return count;
 }
 
-// OPTIMIZATION: can this be done better in after when angles are sorted?
-void SkOpAngle::markStops() {
-    SkOpAngle* angle = this;
-    int lastEnd = SkTMax(fSectorStart, fSectorEnd);
-    do {
-        angle = angle->fNext;
-        int angleStart = SkTMin(angle->fSectorStart, angle->fSectorEnd);
-        // angles that are smaller by one aren't necessary better, since the larger may be a line
-        // and the smaller may be a curve that curls to the other side of the line.
-        if (lastEnd + 1 < angleStart) {
-            angle->fStop = true;
-        }
-        lastEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd);
-    } while (angle != this);
-}
-
 bool SkOpAngle::merge(SkOpAngle* angle) {
     SkASSERT(fNext);
     SkASSERT(angle->fNext);
@@ -782,39 +765,63 @@
         working = next;
     } while (working != angle);
     // it's likely that a pair of the angles are unorderable
-#if 0 && DEBUG_ANGLE
-    SkOpAngle* last = angle;
-    working = angle->fNext;
-    do {
-        SkASSERT(last->fNext == working);
-        last->fNext = working->fNext;
-        SkASSERT(working->after(last));
-        last->fNext = working;
-        last = working;
-        working = working->fNext;
-    } while (last != angle);
-#endif
     debugValidateNext();
     return true;
 }
 
 double SkOpAngle::midT() const {
-    return (fSegment->t(fStart) + fSegment->t(fEnd)) / 2;
+    return (fStart->t() + fEnd->t()) / 2;
 }
 
-bool SkOpAngle::oppositePlanes(const SkOpAngle& rh) const {
-    int startSpan = abs(rh.fSectorStart - fSectorStart);
+bool SkOpAngle::midToSide(const SkOpAngle* rh, bool* inside) const {
+    const SkOpSegment* segment = this->segment();
+    SkPath::Verb verb = segment->verb();
+    const SkPoint& startPt = this->fStart->pt();
+    const SkPoint& endPt = this->fEnd->pt();
+    SkDPoint dStartPt;
+    dStartPt.set(startPt);
+    SkDLine rayMid;
+    rayMid[0].fX = (startPt.fX + endPt.fX) / 2;
+    rayMid[0].fY = (startPt.fY + endPt.fY) / 2;
+    rayMid[1].fX = rayMid[0].fX + (endPt.fY - startPt.fY);
+    rayMid[1].fY = rayMid[0].fY - (endPt.fX - startPt.fX);
+    SkIntersections iMid;
+    (*CurveIntersectRay[verb])(segment->pts(), segment->weight(), rayMid, &iMid);
+    int iOutside = iMid.mostOutside(this->fStart->t(), this->fEnd->t(), dStartPt);
+    if (iOutside < 0) {
+        return false;
+    }
+    const SkOpSegment* oppSegment = rh->segment();
+    SkPath::Verb oppVerb = oppSegment->verb();
+    SkIntersections oppMid;
+    (*CurveIntersectRay[oppVerb])(oppSegment->pts(), oppSegment->weight(), rayMid, &oppMid);
+    int oppOutside = oppMid.mostOutside(rh->fStart->t(), rh->fEnd->t(), dStartPt);
+    if (oppOutside < 0) {
+        return false;
+    }
+    SkDVector iSide = iMid.pt(iOutside) - dStartPt;
+    SkDVector oppSide = oppMid.pt(oppOutside) - dStartPt;
+    double dir = iSide.crossCheck(oppSide);
+    if (!dir) {
+        return false;
+    }
+    *inside = dir < 0;
+    return true;
+}
+
+bool SkOpAngle::oppositePlanes(const SkOpAngle* rh) const {
+    int startSpan = abs(rh->fSectorStart - fSectorStart);
     return startSpan >= 8;
 }
 
-bool SkOpAngle::orderable(const SkOpAngle& rh) const {
+bool SkOpAngle::orderable(SkOpAngle* rh) {
     int result;
     if (!fIsCurve) {
-        if (!rh.fIsCurve) {
+        if (!rh->fIsCurve) {
             double leftX = fTangentHalf.dx();
             double leftY = fTangentHalf.dy();
-            double rightX = rh.fTangentHalf.dx();
-            double rightY = rh.fTangentHalf.dy();
+            double rightX = rh->fTangentHalf.dx();
+            double rightY = rh->fTangentHalf.dy();
             double x_ry = leftX * rightY;
             double rx_y = rightX * leftY;
             if (x_ry == rx_y) {
@@ -829,14 +836,14 @@
         if ((result = allOnOneSide(rh)) >= 0) {
             return result;
         }
-        if (fUnorderable || approximately_zero(rh.fSide)) {
+        if (fUnorderable || approximately_zero(rh->fSide)) {
             goto unorderable;
         }
-    } else if (!rh.fIsCurve) {
-        if ((result = rh.allOnOneSide(*this)) >= 0) {
+    } else if (!rh->fIsCurve) {
+        if ((result = rh->allOnOneSide(this)) >= 0) {
             return !result;
         }
-        if (rh.fUnorderable || approximately_zero(fSide)) {
+        if (rh->fUnorderable || approximately_zero(fSide)) {
             goto unorderable;
         }
     }
@@ -846,27 +853,10 @@
     return endsIntersect(rh);
 unorderable:
     fUnorderable = true;
-    rh.fUnorderable = true;
+    rh->fUnorderable = true;
     return true;
 }
 
-bool SkOpAngle::overlap(const SkOpAngle& other) const {
-    int min = SkTMin(fStart, fEnd);
-    const SkOpSpan& span = fSegment->span(min);
-    const SkOpSegment* oSeg = other.fSegment;
-    int oMin = SkTMin(other.fStart, other.fEnd);
-    const SkOpSpan& oSpan = oSeg->span(oMin);
-    if (!span.fSmall && !oSpan.fSmall) {
-        return false;
-    }
-    if (fSegment->span(fStart).fPt != oSeg->span(other.fStart).fPt) {
-        return false;
-    }
-    // see if small span is contained by opposite span
-    return span.fSmall ? oSeg->containsPt(fSegment->span(fEnd).fPt, other.fEnd, other.fStart)
-            : fSegment->containsPt(oSeg->span(other.fEnd).fPt, fEnd, fStart);
-}
-
 // OPTIMIZE: if this shows up in a profile, add a previous pointer
 // as is, this should be rarely called
 SkOpAngle* SkOpAngle::previous() const {
@@ -880,26 +870,31 @@
     } while (true);
 }
 
-void SkOpAngle::set(const SkOpSegment* segment, int start, int end) {
-    fSegment = segment;
+SkOpSegment* SkOpAngle::segment() const {
+    return fStart->segment();
+}
+
+void SkOpAngle::set(SkOpSpanBase* start, SkOpSpanBase* end) {
     fStart = start;
     fComputedEnd = fEnd = end;
+    SkASSERT(start != end);
     fNext = NULL;
-    fComputeSector = fComputedSector = false;
-    fStop = false;
+    fComputeSector = fComputedSector = fCheckCoincidence = false;
     setSpans();
     setSector();
+    SkDEBUGCODE(fID = start ? start->globalState()->nextAngleID() : -1);
 }
 
 void SkOpAngle::setCurveHullSweep() {
     fUnorderedSweep = false;
     fSweep[0] = fCurvePart[1] - fCurvePart[0];
-    if (SkPath::kLine_Verb == fSegment->verb()) {
+    const SkOpSegment* segment = fStart->segment();
+    if (SkPath::kLine_Verb == segment->verb()) {
         fSweep[1] = fSweep[0];
         return;
     }
     fSweep[1] = fCurvePart[2] - fCurvePart[0];
-    if (SkPath::kCubic_Verb != fSegment->verb()) {
+    if (SkPath::kCubic_Verb != segment->verb()) {
         if (!fSweep[0].fX && !fSweep[0].fY) {
             fSweep[0] = fSweep[1];
         }
@@ -933,64 +928,22 @@
     fSweep[1] = thirdSweep;
 }
 
-void SkOpAngle::setSector() {
-    SkPath::Verb verb = fSegment->verb();
-    if (SkPath::kLine_Verb != verb && small()) {
-        goto deferTilLater;
-    }
-    fSectorStart = findSector(verb, fSweep[0].fX, fSweep[0].fY);
-    if (fSectorStart < 0) {
-        goto deferTilLater;
-    }
-    if (!fIsCurve) {  // if it's a line or line-like, note that both sectors are the same
-        SkASSERT(fSectorStart >= 0);
-        fSectorEnd = fSectorStart;
-        fSectorMask = 1 << fSectorStart;
-        return;
-    }
-    SkASSERT(SkPath::kLine_Verb != verb);
-    fSectorEnd = findSector(verb, fSweep[1].fX, fSweep[1].fY);
-    if (fSectorEnd < 0) {
-deferTilLater:
-        fSectorStart = fSectorEnd = -1;
-        fSectorMask = 0;
-        fComputeSector = true;  // can't determine sector until segment length can be found
-        return;
-    }
-    if (fSectorEnd == fSectorStart) {
-        SkASSERT((fSectorStart & 3) != 3);  // if the sector has no span, it can't be an exact angle
-        fSectorMask = 1 << fSectorStart;
-        return;
-    }
-    bool crossesZero = checkCrossesZero();
-    int start = SkTMin(fSectorStart, fSectorEnd);
-    bool curveBendsCCW = (fSectorStart == start) ^ crossesZero;
-    // bump the start and end of the sector span if they are on exact compass points
-    if ((fSectorStart & 3) == 3) {
-        fSectorStart = (fSectorStart + (curveBendsCCW ? 1 : 31)) & 0x1f;
-    }
-    if ((fSectorEnd & 3) == 3) {
-        fSectorEnd = (fSectorEnd + (curveBendsCCW ? 31 : 1)) & 0x1f;
-    }
-    crossesZero = checkCrossesZero();
-    start = SkTMin(fSectorStart, fSectorEnd);
-    int end = SkTMax(fSectorStart, fSectorEnd);
-    if (!crossesZero) {
-        fSectorMask = (unsigned) -1 >> (31 - end + start) << start;
-    } else {
-        fSectorMask = (unsigned) -1 >> (31 - start) | (-1 << end);
-    }
-}
-
 void SkOpAngle::setSpans() {
-    fUnorderable = fSegment->isTiny(this);
+    fUnorderable = false;
     fLastMarked = NULL;
-    const SkPoint* pts = fSegment->pts();
+    if (!fStart) {
+        fUnorderable = true;
+        return;
+    }
+    const SkOpSegment* segment = fStart->segment();
+    const SkPoint* pts = segment->pts();
+    SkDEBUGCODE(fCurvePart.fVerb = SkPath::kCubic_Verb);
     SkDEBUGCODE(fCurvePart[2].fX = fCurvePart[2].fY = fCurvePart[3].fX = fCurvePart[3].fY
             = SK_ScalarNaN);
-    fSegment->subDivide(fStart, fEnd, &fCurvePart);
+    SkDEBUGCODE(fCurvePart.fVerb = segment->verb());
+    segment->subDivide(fStart, fEnd, &fCurvePart);
     setCurveHullSweep();
-    const SkPath::Verb verb = fSegment->verb();
+    const SkPath::Verb verb = segment->verb();
     if (verb != SkPath::kLine_Verb
             && !(fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0)) {
         SkDLine lineHalf;
@@ -1002,29 +955,29 @@
     switch (verb) {
     case SkPath::kLine_Verb: {
         SkASSERT(fStart != fEnd);
-        const SkPoint& cP1 = pts[fStart < fEnd];
+        const SkPoint& cP1 = pts[fStart->t() < fEnd->t()];
         SkDLine lineHalf;
-        lineHalf[0].set(fSegment->span(fStart).fPt);
+        lineHalf[0].set(fStart->pt());
         lineHalf[1].set(cP1);
         fTangentHalf.lineEndPoints(lineHalf);
         fSide = 0;
         fIsCurve = false;
         } return;
-    case SkPath::kQuad_Verb: {
+    case SkPath::kQuad_Verb:
+    case SkPath::kConic_Verb: {
         SkLineParameters tangentPart;
-        SkDQuad& quad2 = *SkTCast<SkDQuad*>(&fCurvePart);
-        (void) tangentPart.quadEndPoints(quad2);
+        (void) tangentPart.quadEndPoints(fCurvePart.fQuad);
         fSide = -tangentPart.pointDistance(fCurvePart[2]);  // not normalized -- compare sign only
         } break;
     case SkPath::kCubic_Verb: {
         SkLineParameters tangentPart;
-        (void) tangentPart.cubicPart(fCurvePart);
+        (void) tangentPart.cubicPart(fCurvePart.fCubic);
         fSide = -tangentPart.pointDistance(fCurvePart[3]);
         double testTs[4];
         // OPTIMIZATION: keep inflections precomputed with cubic segment?
         int testCount = SkDCubic::FindInflections(pts, testTs);
-        double startT = fSegment->t(fStart);
-        double endT = fSegment->t(fEnd);
+        double startT = fStart->t();
+        double endT = fEnd->t();
         double limitT = endT;
         int index;
         for (index = 0; index < testCount; ++index) {
@@ -1049,9 +1002,9 @@
                 testT = (testT + testTs[testIndex + 1]) / 2;
             }
             // OPTIMIZE: could avoid call for t == startT, endT
-            SkDPoint pt = dcubic_xy_at_t(pts, testT);
+            SkDPoint pt = dcubic_xy_at_t(pts, segment->weight(), testT);
             SkLineParameters tangentPart;
-            tangentPart.cubicEndPoints(fCurvePart);
+            tangentPart.cubicEndPoints(fCurvePart.fCubic);
             double testSide = tangentPart.pointDistance(pt);
             if (fabs(bestSide) < fabs(testSide)) {
                 bestSide = testSide;
@@ -1064,19 +1017,62 @@
     }
 }
 
-bool SkOpAngle::small() const {
-    int min = SkMin32(fStart, fEnd);
-    int max = SkMax32(fStart, fEnd);
-    for (int index = min; index < max; ++index) {
-        const SkOpSpan& mSpan = fSegment->span(index);
-        if (!mSpan.fSmall) {
-            return false;
-        }
+void SkOpAngle::setSector() {
+    if (!fStart) {
+        fUnorderable = true;
+        return;
     }
-    return true;
+    const SkOpSegment* segment = fStart->segment();
+    SkPath::Verb verb = segment->verb();
+    fSectorStart = this->findSector(verb, fSweep[0].fX, fSweep[0].fY);
+    if (fSectorStart < 0) {
+        goto deferTilLater;
+    }
+    if (!fIsCurve) {  // if it's a line or line-like, note that both sectors are the same
+        SkASSERT(fSectorStart >= 0);
+        fSectorEnd = fSectorStart;
+        fSectorMask = 1 << fSectorStart;
+        return;
+    }
+    SkASSERT(SkPath::kLine_Verb != verb);
+    fSectorEnd = this->findSector(verb, fSweep[1].fX, fSweep[1].fY);
+    if (fSectorEnd < 0) {
+deferTilLater:
+        fSectorStart = fSectorEnd = -1;
+        fSectorMask = 0;
+        fComputeSector = true;  // can't determine sector until segment length can be found
+        return;
+    }
+    if (fSectorEnd == fSectorStart
+            && (fSectorStart & 3) != 3) { // if the sector has no span, it can't be an exact angle
+        fSectorMask = 1 << fSectorStart;
+        return;
+    }
+    bool crossesZero = this->checkCrossesZero();
+    int start = SkTMin(fSectorStart, fSectorEnd);
+    bool curveBendsCCW = (fSectorStart == start) ^ crossesZero;
+    // bump the start and end of the sector span if they are on exact compass points
+    if ((fSectorStart & 3) == 3) {
+        fSectorStart = (fSectorStart + (curveBendsCCW ? 1 : 31)) & 0x1f;
+    }
+    if ((fSectorEnd & 3) == 3) {
+        fSectorEnd = (fSectorEnd + (curveBendsCCW ? 31 : 1)) & 0x1f;
+    }
+    crossesZero = this->checkCrossesZero();
+    start = SkTMin(fSectorStart, fSectorEnd);
+    int end = SkTMax(fSectorStart, fSectorEnd);
+    if (!crossesZero) {
+        fSectorMask = (unsigned) -1 >> (31 - end + start) << start;
+    } else {
+        fSectorMask = (unsigned) -1 >> (31 - start) | (-1 << end);
+    }
 }
 
-bool SkOpAngle::tangentsDiverge(const SkOpAngle& rh, double s0xt0) const {
+SkOpSpan* SkOpAngle::starter() {
+    return fStart->starter(fEnd);
+}
+
+bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
     if (s0xt0 == 0) {
         return false;
     }
@@ -1090,7 +1086,7 @@
     // m = (v2.y * v1.x - v2.x * v1.y) / (v2.x * v1.x + v2.y * v1.y)
     // m = v1.cross(v2) / v1.dot(v2)
     const SkDVector* sweep = fSweep;
-    const SkDVector* tweep = rh.fSweep;
+    const SkDVector* tweep = rh->fSweep;
     double s0dt0 = sweep[0].dot(tweep[0]);
     if (!s0dt0) {
         return true;
@@ -1100,36 +1096,6 @@
     double sDist = sweep[0].length() * m;
     double tDist = tweep[0].length() * m;
     bool useS = fabs(sDist) < fabs(tDist);
-    double mFactor = fabs(useS ? distEndRatio(sDist) : rh.distEndRatio(tDist));
-    return mFactor < 5000;  // empirically found limit
-}
-
-SkOpAngleSet::SkOpAngleSet() 
-    : fAngles(NULL)
-#if DEBUG_ANGLE
-    , fCount(0)
-#endif
-{
-}
-
-SkOpAngleSet::~SkOpAngleSet() {
-    SkDELETE(fAngles);
-}
-
-SkOpAngle& SkOpAngleSet::push_back() {
-    if (!fAngles) {
-        fAngles = SkNEW_ARGS(SkChunkAlloc, (2));
-    }
-    void* ptr = fAngles->allocThrow(sizeof(SkOpAngle));
-    SkOpAngle* angle = (SkOpAngle*) ptr;
-#if DEBUG_ANGLE
-    angle->setID(++fCount);
-#endif
-    return *angle;
-}
-
-void SkOpAngleSet::reset() {
-    if (fAngles) {
-        fAngles->reset();
-    }
+    double mFactor = fabs(useS ? this->distEndRatio(sDist) : rh->distEndRatio(tDist));
+    return mFactor < 2400;  // empirically found limit
 }
diff --git a/src/pathops/SkOpAngle.h b/src/pathops/SkOpAngle.h
index 1dc4250..dba3f3f 100644
--- a/src/pathops/SkOpAngle.h
+++ b/src/pathops/SkOpAngle.h
@@ -7,17 +7,19 @@
 #ifndef SkOpAngle_DEFINED
 #define SkOpAngle_DEFINED
 
-#include "SkChunkAlloc.h"
 #include "SkLineParameters.h"
+#include "SkPathOpsCurve.h"
+#if DEBUG_ANGLE
+#include "SkString.h"
+#endif
 
+class SkOpContour;
+class SkOpPtT;
 class SkOpSegment;
-struct SkOpSpan;
+class SkOpSpanBase;
+class SkOpSpan;
 
-// sorting angles
-// given angles of {dx dy ddx ddy dddx dddy} sort them
-class SkOpAngle {
-public:
-    enum { kStackBasedCount = 8 }; // FIXME: determine what this should be
+struct SkOpAngle {
     enum IncludeType {
         kUnaryWinding,
         kUnaryXor,
@@ -25,29 +27,65 @@
         kBinaryOpp,
     };
 
+    bool after(SkOpAngle* test);
+    int allOnOneSide(const SkOpAngle* test);
+    bool checkCrossesZero() const;
+    void checkNearCoincidence();
+    bool checkParallel(SkOpAngle* );
+    bool computeSector();
+    int convexHullOverlaps(const SkOpAngle* ) const;
 
-    int end() const {
+    const SkOpAngle* debugAngle(int id) const;
+    SkOpContour* debugContour(int id);
+
+    int debugID() const {
+        return SkDEBUGRELEASE(fID, -1);
+    }
+
+#if DEBUG_SORT
+    void debugLoop() const;
+#endif
+
+#if DEBUG_ANGLE
+    SkString debugPart() const;
+#endif
+    const SkOpPtT* debugPtT(int id) const;
+    const SkOpSegment* debugSegment(int id) const;
+    int debugSign() const;
+    const SkOpSpanBase* debugSpan(int id) const;
+    void debugValidate() const; 
+    void debugValidateNext() const;  // in debug builds, verify that angle loop is uncorrupted
+    double distEndRatio(double dist) const;
+    // available to testing only
+    void dump() const;
+    void dumpCurves() const;
+    void dumpLoop() const;
+    void dumpOne(bool functionHeader) const;
+    void dumpTo(const SkOpSegment* fromSeg, const SkOpAngle* ) const;
+    void dumpTest() const;
+
+    SkOpSpanBase* end() const {
         return fEnd;
     }
 
-    const SkOpAngle* findFirst() const;
-
-    bool inLoop() const {
-        return !!fNext;
-    }
-
+    bool endsIntersect(SkOpAngle* );
+    bool endToSide(const SkOpAngle* rh, bool* inside) const;
+    int findSector(SkPath::Verb verb, double x, double y) const;
+    SkOpGlobalState* globalState() const;
     void insert(SkOpAngle* );
-    bool isHorizontal() const;
-    SkOpSpan* lastMarked() const;
-    bool loopContains(const SkOpAngle& ) const;
+    SkOpSpanBase* lastMarked() const;
+    bool loopContains(const SkOpAngle* ) const;
     int loopCount() const;
-    void markStops();
     bool merge(SkOpAngle* );
+    double midT() const;
+    bool midToSide(const SkOpAngle* rh, bool* inside) const;
 
     SkOpAngle* next() const {
         return fNext;
     }
 
+    bool oppositePlanes(const SkOpAngle* rh) const;
+    bool orderable(SkOpAngle* rh);  // false == this < rh ; true == this > rh
     SkOpAngle* previous() const;
 
     int sectorEnd() const {
@@ -58,120 +96,55 @@
         return fSectorStart;
     }
 
-    void set(const SkOpSegment* segment, int start, int end);
+    SkOpSegment* segment() const;
 
-    void setLastMarked(SkOpSpan* marked) {
+    void set(SkOpSpanBase* start, SkOpSpanBase* end);
+    void setCurveHullSweep();
+
+    void setID(int id) {
+        SkDEBUGCODE(fID = id);
+    }
+
+    void setLastMarked(SkOpSpanBase* marked) {
         fLastMarked = marked;
     }
 
-    SkOpSegment* segment() const {
-        return const_cast<SkOpSegment*>(fSegment);
-    }
+    void setSector();
+    void setSpans();
 
-    int sign() const {
-        return SkSign32(fStart - fEnd);
-    }
-
-    bool small() const;
-
-    int start() const {
+    SkOpSpanBase* start() const {
         return fStart;
     }
 
+    SkOpSpan* starter();
+    bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
+
     bool unorderable() const {
         return fUnorderable;
     }
 
-    // available to testing only
-#if DEBUG_SORT
-    void debugLoop() const;  // called by code during run
-#endif
-#if DEBUG_ANGLE
-    void debugSameAs(const SkOpAngle* compare) const;
-#endif
-    void dump() const;
-    void dumpLoop() const;
-    void dumpTo(const SkOpSegment* fromSeg, const SkOpAngle* ) const;
-
-#if DEBUG_ANGLE
-    int debugID() const { return fID; }
-
-    void setID(int id) {
-        fID = id;
-    }
-#else
-    int debugID() const { return 0; }
-#endif
-
-#if DEBUG_VALIDATE
-    void debugValidateLoop() const;
-#endif
-
-private:
-    bool after(const SkOpAngle* test) const;
-    int allOnOneSide(const SkOpAngle& test) const;
-    bool calcSlop(double x, double y, double rx, double ry, bool* result) const;
-    bool checkCrossesZero() const;
-    bool checkParallel(const SkOpAngle& ) const;
-    bool computeSector();
-    int convexHullOverlaps(const SkOpAngle& ) const;
-    double distEndRatio(double dist) const;
-    int findSector(SkPath::Verb verb, double x, double y) const;
-    bool endsIntersect(const SkOpAngle& ) const;
-    double midT() const;
-    bool oppositePlanes(const SkOpAngle& rh) const;
-    bool orderable(const SkOpAngle& rh) const;  // false == this < rh ; true == this > rh
-    bool overlap(const SkOpAngle& test) const;
-    void setCurveHullSweep();
-    void setSector();
-    void setSpans();
-    bool tangentsDiverge(const SkOpAngle& rh, double s0xt0) const;
-
-    SkDCubic fCurvePart; // the curve from start to end
+    SkDCurve fCurvePart;  // the curve from start to end
     double fSide;
     SkLineParameters fTangentHalf;  // used only to sort a pair of lines or line-like sections
-    const SkOpSegment* fSegment;
     SkOpAngle* fNext;
-    SkOpSpan* fLastMarked;
+    SkOpSpanBase* fLastMarked;
     SkDVector fSweep[2];
-    int fStart;
-    int fEnd;
-    int fComputedEnd;
+    SkOpSpanBase* fStart;
+    SkOpSpanBase* fEnd;
+    SkOpSpanBase* fComputedEnd;
     int fSectorMask;
     int8_t fSectorStart;  // in 32nds of a circle
     int8_t fSectorEnd;
     bool fIsCurve;
-    bool fStop; // set if ordered angle is greater than the previous
-    mutable bool fUnorderable;  // this is editable by orderable()
+    bool fUnorderable;
     bool fUnorderedSweep;  // set when a cubic's first control point between the sweep vectors
     bool fComputeSector;
     bool fComputedSector;
+    bool fCheckCoincidence;
+    SkDEBUGCODE(int fID);
 
-#if DEBUG_ANGLE
-    int fID;
-#endif
-#if DEBUG_VALIDATE
-    void debugValidateNext() const;  // in debug builds, verify that angle loop is uncorrupted
-#else
-    void debugValidateNext() const {}
-#endif
-    void dumpOne(bool showFunc) const;  // available to testing only
-    void dumpPartials() const;  // utility to be called by user from debugger
-    friend class PathOpsAngleTester;
 };
 
-class SkOpAngleSet {
-public:
-    SkOpAngleSet();
-    ~SkOpAngleSet();
-    SkOpAngle& push_back();
-    void reset();
-private:
-    void dump() const;  // utility to be called by user from debugger
-    SkChunkAlloc* fAngles;
-#if DEBUG_ANGLE
-    int fCount;
-#endif
-};
+
 
 #endif
diff --git a/src/pathops/SkOpBuilder.cpp b/src/pathops/SkOpBuilder.cpp
index 489ad63..1a446f1 100644
--- a/src/pathops/SkOpBuilder.cpp
+++ b/src/pathops/SkOpBuilder.cpp
@@ -10,9 +10,9 @@
 #include "SkPathOps.h"
 
 void SkOpBuilder::add(const SkPath& path, SkPathOp op) {
-    if (0 == fOps.count() && op != kUnion_PathOp) {
+    if (0 == fOps.count() && op != kUnion_SkPathOp) {
         fPathRefs.push_back() = SkPath();
-        *fOps.append() = kUnion_PathOp;
+        *fOps.append() = kUnion_SkPathOp;
     }
     fPathRefs.push_back() = path;
     *fOps.append() = op;
@@ -27,12 +27,13 @@
    paths with union ops could be locally resolved and still improve over doing the
    ops one at a time. */
 bool SkOpBuilder::resolve(SkPath* result) {
+    SkPath original = *result;
     int count = fOps.count();
     bool allUnion = true;
     SkPath::Direction firstDir;
     for (int index = 0; index < count; ++index) {
         SkPath* test = &fPathRefs[index];
-        if (kUnion_PathOp != fOps[index] || test->isInverseFillType()) {
+        if (kUnion_SkPathOp != fOps[index] || test->isInverseFillType()) {
             allUnion = false;
             break;
         }
@@ -67,6 +68,7 @@
         for (int index = 1; index < count; ++index) {
             if (!Op(*result, fPathRefs[index], fOps[index], result)) {
                 reset();
+                *result = original;
                 return false;
             }
         }
@@ -77,10 +79,16 @@
     for (int index = 0; index < count; ++index) {
         if (!Simplify(fPathRefs[index], &fPathRefs[index])) {
             reset();
+            *result = original;
             return false;
         }
         sum.addPath(fPathRefs[index]);
     }
     reset();
-    return Simplify(sum, result);
+    sum.setFillType(SkPath::kEvenOdd_FillType);
+    bool success = Simplify(sum, result);
+    if (!success) {
+        *result = original;
+    }
+    return success;
 }
diff --git a/src/pathops/SkOpCoincidence.cpp b/src/pathops/SkOpCoincidence.cpp
new file mode 100755
index 0000000..eb0ccc1
--- /dev/null
+++ b/src/pathops/SkOpCoincidence.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkOpCoincidence.h"
+#include "SkOpSegment.h"
+#include "SkPathOpsTSect.h"
+
+bool SkOpCoincidence::extend(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
+        SkOpPtT* oppPtTEnd) {
+    // if there is an existing pair that overlaps the addition, extend it
+    SkCoincidentSpans* coinRec = fHead;
+    if (coinRec) {
+        do {
+            if (coinRec->fCoinPtTStart->segment() != coinPtTStart->segment()) {
+                continue;
+            }
+            if (coinRec->fOppPtTStart->segment() != oppPtTStart->segment()) {
+                continue;
+            }
+            if (coinRec->fCoinPtTStart->fT > coinPtTEnd->fT) {
+                continue;
+            }
+            if (coinRec->fCoinPtTEnd->fT < coinPtTStart->fT) {
+                continue;
+            }
+            if (coinRec->fCoinPtTStart->fT > coinPtTStart->fT) {
+                coinRec->fCoinPtTStart = coinPtTStart;
+                coinRec->fOppPtTStart = oppPtTStart;
+            }
+            if (coinRec->fCoinPtTEnd->fT < coinPtTEnd->fT) {
+                coinRec->fCoinPtTEnd = coinPtTEnd;
+                coinRec->fOppPtTEnd = oppPtTEnd;
+            }
+            return true;
+        } while ((coinRec = coinRec->fNext));
+    }
+    return false;
+}
+
+void SkOpCoincidence::add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
+        SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator) {
+    SkASSERT(coinPtTStart->fT < coinPtTEnd->fT);
+    bool flipped = oppPtTStart->fT > oppPtTEnd->fT;
+    SkCoincidentSpans* coinRec = SkOpTAllocator<SkCoincidentSpans>::Allocate(allocator);
+    coinRec->fNext = this->fHead;
+    coinRec->fCoinPtTStart = coinPtTStart;
+    coinRec->fCoinPtTEnd = coinPtTEnd;
+    coinRec->fOppPtTStart = oppPtTStart;
+    coinRec->fOppPtTEnd = oppPtTEnd;
+    coinRec->fFlipped = flipped;
+    this->fHead = coinRec;
+}
+
+static void t_range(const SkOpPtT* overS, const SkOpPtT* overE, double tStart, double tEnd,
+        const SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd, double* coinTs, double* coinTe) {
+    double denom = overE->fT - overS->fT;
+    double start = 0 < denom ? tStart : tEnd;
+    double end = 0 < denom ? tEnd : tStart;
+    double sRatio = (start - overS->fT) / denom;
+    double eRatio = (end - overS->fT) / denom;
+    *coinTs = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * sRatio;
+    *coinTe = coinPtTStart->fT + (coinPtTEnd->fT - coinPtTStart->fT) * eRatio;
+}
+
+bool SkOpCoincidence::addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
+                      const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
+        SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+        SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator) {
+    double coinTs, coinTe, oppTs, oppTe;
+    t_range(over1s, over1e, tStart, tEnd, coinPtTStart, coinPtTEnd, &coinTs, &coinTe);
+    t_range(over2s, over2e, tStart, tEnd, oppPtTStart, oppPtTEnd, &oppTs, &oppTe);
+    SkOpSegment* coinSeg = coinPtTStart->segment();
+    SkOpSegment* oppSeg = oppPtTStart->segment();
+    SkASSERT(coinSeg != oppSeg);
+    SkCoincidentSpans* check = this->fHead;
+    do {
+        const SkOpSegment* checkCoinSeg = check->fCoinPtTStart->segment();
+        if (checkCoinSeg != coinSeg && checkCoinSeg != oppSeg) {
+            continue;
+        }
+        const SkOpSegment* checkOppSeg = check->fOppPtTStart->segment();
+        if (checkOppSeg != coinSeg && checkOppSeg != oppSeg) {
+            continue;
+        }
+        int cTs = coinTs;
+        int cTe = coinTe;
+        int oTs = oppTs;
+        int oTe = oppTe;
+        if (checkCoinSeg != coinSeg) {
+            SkASSERT(checkOppSeg != oppSeg);
+            SkTSwap(cTs, oTs);
+            SkTSwap(cTe, oTe);
+        }
+        int tweenCount = (int) between(check->fCoinPtTStart->fT, cTs, check->fCoinPtTEnd->fT)
+                       + (int) between(check->fCoinPtTStart->fT, cTe, check->fCoinPtTEnd->fT)
+                       + (int) between(check->fOppPtTStart->fT, oTs, check->fOppPtTEnd->fT)
+                       + (int) between(check->fOppPtTStart->fT, oTe, check->fOppPtTEnd->fT);
+//        SkASSERT(tweenCount == 0 || tweenCount == 4);
+        if (tweenCount) {
+            return true;
+        }
+    } while ((check = check->fNext));
+    if ((over1s->fT < over1e->fT) != (over2s->fT < over2e->fT)) {
+        SkTSwap(oppTs, oppTe);
+    }
+    if (coinTs > coinTe) {
+        SkTSwap(coinTs, coinTe);
+        SkTSwap(oppTs, oppTe);
+    }
+    SkOpPtT* cs = coinSeg->addMissing(coinTs, oppSeg, allocator);
+    SkOpPtT* ce = coinSeg->addMissing(coinTe, oppSeg, allocator);
+    if (cs == ce) {
+        return false;
+    }
+    SkOpPtT* os = oppSeg->addMissing(oppTs, coinSeg, allocator);
+    SkOpPtT* oe = oppSeg->addMissing(oppTe, coinSeg, allocator);
+    SkASSERT(os != oe);
+    cs->addOpp(os);
+    ce->addOpp(oe);
+    this->add(cs, ce, os, oe, allocator);
+    return true;
+}
+
+bool SkOpCoincidence::addMissing(SkChunkAlloc* allocator) {
+    SkCoincidentSpans* outer = this->fHead;
+    if (!outer) {
+        return true;
+    }
+    do {
+        SkCoincidentSpans* inner = outer;
+        while ((inner = inner->fNext)) {
+            double overS, overE;
+            if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
+                    inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
+                if (!this->addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
+                        inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
+                        outer->fOppPtTStart, outer->fOppPtTEnd,
+                        inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) {
+                    return false;
+                }
+            } else if (this->overlap(outer->fCoinPtTStart, outer->fCoinPtTEnd,
+                    inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
+                if (!this->addIfMissing(outer->fCoinPtTStart, outer->fCoinPtTEnd,
+                        inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
+                        outer->fOppPtTStart, outer->fOppPtTEnd,
+                        inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) {
+                    return false;
+                }
+            } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
+                    inner->fCoinPtTStart, inner->fCoinPtTEnd, &overS, &overE)) {
+                if (!this->addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
+                        inner->fCoinPtTStart, inner->fCoinPtTEnd, overS, overE,
+                        outer->fCoinPtTStart, outer->fCoinPtTEnd,
+                        inner->fOppPtTStart, inner->fOppPtTEnd, allocator)) {
+                    return false;
+                }
+            } else if (this->overlap(outer->fOppPtTStart, outer->fOppPtTEnd,
+                    inner->fOppPtTStart, inner->fOppPtTEnd, &overS, &overE)) {
+                if (!this->addIfMissing(outer->fOppPtTStart, outer->fOppPtTEnd,
+                        inner->fOppPtTStart, inner->fOppPtTEnd, overS, overE,
+                        outer->fCoinPtTStart, outer->fCoinPtTEnd,
+                        inner->fCoinPtTStart, inner->fCoinPtTEnd, allocator)) {
+                    return false;
+                }
+            }
+        }
+
+    } while ((outer = outer->fNext));
+    return true;
+}
+
+bool SkOpCoincidence::contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
+        SkOpPtT* oppPtTEnd, bool flipped) {
+    SkCoincidentSpans* coin = fHead;
+    if (!coin) {
+        return false;
+    }
+    do {
+        if (coin->fCoinPtTStart == coinPtTStart &&  coin->fCoinPtTEnd == coinPtTEnd
+                && coin->fOppPtTStart == oppPtTStart && coin->fOppPtTEnd == oppPtTEnd
+                && coin->fFlipped == flipped) {
+            return true;
+        }
+    } while ((coin = coin->fNext));
+    return false;
+}
+
+// walk span sets in parallel, moving winding from one to the other
+bool SkOpCoincidence::apply() {
+    SkCoincidentSpans* coin = fHead;
+    if (!coin) {
+        return true;
+    }
+    do {
+        SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
+        if (start->deleted()) {
+            continue;
+        }
+        SkOpSpanBase* end = coin->fCoinPtTEnd->span();
+        SkASSERT(start == start->starter(end));
+        bool flipped = coin->fFlipped;
+        SkOpSpan* oStart = (flipped ? coin->fOppPtTEnd : coin->fOppPtTStart)->span()->upCast();
+        if (oStart->deleted()) {
+            continue;
+        }
+        SkOpSpanBase* oEnd = (flipped ? coin->fOppPtTStart : coin->fOppPtTEnd)->span();
+        SkASSERT(oStart == oStart->starter(oEnd));
+        SkOpSegment* segment = start->segment();
+        SkOpSegment* oSegment = oStart->segment();
+        bool operandSwap = segment->operand() != oSegment->operand();
+        if (flipped) {
+            do {
+                SkOpSpanBase* oNext = oStart->next();
+                if (oNext == oEnd) {
+                    break;
+                }
+                oStart = oNext->upCast();
+            } while (true);
+        }
+        do {
+            int windValue = start->windValue();
+            int oppValue = start->oppValue();
+            int oWindValue = oStart->windValue();
+            int oOppValue = oStart->oppValue();
+            // winding values are added or subtracted depending on direction and wind type
+            // same or opposite values are summed depending on the operand value
+            int windDiff = operandSwap ? oOppValue : oWindValue;
+            int oWindDiff = operandSwap ? oppValue : windValue;
+            if (!flipped) {
+                windDiff = -windDiff;
+                oWindDiff = -oWindDiff;
+            }
+            if (windValue && (windValue > windDiff || (windValue == windDiff
+                    && oWindValue <= oWindDiff))) {
+                if (operandSwap) {
+                    SkTSwap(oWindValue, oOppValue);
+                }
+                if (flipped) {
+                    windValue -= oWindValue;
+                    oppValue -= oOppValue;
+                } else {
+                    windValue += oWindValue;
+                    oppValue += oOppValue;
+                }
+                if (segment->isXor()) {
+                    windValue &= 1;
+                }
+                if (segment->oppXor()) {
+                    oppValue &= 1;
+                }
+                oWindValue = oOppValue = 0;
+            } else {
+                if (operandSwap) {
+                    SkTSwap(windValue, oppValue);
+                }
+                if (flipped) {
+                    oWindValue -= windValue;
+                    oOppValue -= oppValue;
+                } else {
+                    oWindValue += windValue;
+                    oOppValue += oppValue;
+                }
+                if (oSegment->isXor()) {
+                    oWindValue &= 1;
+                }
+                if (oSegment->oppXor()) {
+                    oOppValue &= 1;
+                }
+                windValue = oppValue = 0;
+            }
+            start->setWindValue(windValue);
+            start->setOppValue(oppValue);
+            oStart->setWindValue(oWindValue);
+            oStart->setOppValue(oOppValue);
+            if (!windValue && !oppValue) {
+                segment->markDone(start);
+            }
+            if (!oWindValue && !oOppValue) {
+                oSegment->markDone(oStart);
+            }
+            SkOpSpanBase* next = start->next();
+            SkOpSpanBase* oNext = flipped ? oStart->prev() : oStart->next();
+            if (next == end) {
+                break;
+            }
+            start = next->upCast();
+            // if the opposite ran out too soon, just reuse the last span
+            if (!oNext || !oNext->upCastable()) {
+               oNext = oStart;
+            }
+            oStart = oNext->upCast();
+        } while (true);
+    } while ((coin = coin->fNext));
+    return true;
+}
+
+void SkOpCoincidence::detach(SkCoincidentSpans* remove) {
+    SkCoincidentSpans* coin = fHead;
+    SkCoincidentSpans* prev = NULL;
+    SkCoincidentSpans* next;
+    do {
+        next = coin->fNext;
+        if (coin == remove) {
+            if (prev) {
+                prev->fNext = next;
+            } else {
+                fHead = next;
+            }
+            break;
+        }
+        prev = coin;
+    } while ((coin = next));
+    SkASSERT(coin);
+}
+
+void SkOpCoincidence::expand() {
+    SkCoincidentSpans* coin = fHead;
+    if (!coin) {
+        return;
+    }
+    do {
+        SkOpSpan* start = coin->fCoinPtTStart->span()->upCast();
+        SkOpSpanBase* end = coin->fCoinPtTEnd->span();
+        SkOpSegment* segment = coin->fCoinPtTStart->segment();
+        SkOpSegment* oppSegment = coin->fOppPtTStart->segment();
+        SkOpSpan* prev = start->prev();
+        SkOpPtT* oppPtT;
+        if (prev && (oppPtT = prev->contains(oppSegment))) {
+            double midT = (prev->t() + start->t()) / 2;
+            if (segment->isClose(midT, oppSegment)) {
+                coin->fCoinPtTStart = prev->ptT();
+                coin->fOppPtTStart = oppPtT;
+            }
+        }
+        SkOpSpanBase* next = end->final() ? NULL : end->upCast()->next();
+        if (next && (oppPtT = next->contains(oppSegment))) {
+            double midT = (end->t() + next->t()) / 2;
+            if (segment->isClose(midT, oppSegment)) {
+                coin->fCoinPtTEnd = next->ptT();
+                coin->fOppPtTEnd = oppPtT;
+            }
+        }
+    } while ((coin = coin->fNext));
+}
+
+void SkOpCoincidence::fixUp(SkOpPtT* deleted, SkOpPtT* kept) {
+    SkCoincidentSpans* coin = fHead;
+    if (!coin) {
+        return;
+    }
+    do {
+        if (coin->fCoinPtTStart == deleted) {
+            if (coin->fCoinPtTEnd->span() == kept->span()) {
+                return this->detach(coin);
+            }
+            coin->fCoinPtTStart = kept;
+        }
+        if (coin->fCoinPtTEnd == deleted) {
+            if (coin->fCoinPtTStart->span() == kept->span()) {
+                return this->detach(coin);
+            }
+            coin->fCoinPtTEnd = kept;
+        }
+        if (coin->fOppPtTStart == deleted) {
+            if (coin->fOppPtTEnd->span() == kept->span()) {
+                return this->detach(coin);
+            }
+            coin->fOppPtTStart = kept;
+        }
+        if (coin->fOppPtTEnd == deleted) {
+            if (coin->fOppPtTStart->span() == kept->span()) {
+                return this->detach(coin);
+            }
+            coin->fOppPtTEnd = kept;
+        }
+    } while ((coin = coin->fNext));
+}
+
+void SkOpCoincidence::mark() {
+    SkCoincidentSpans* coin = fHead;
+    if (!coin) {
+        return;
+    }
+    do {
+        SkOpSpanBase* end = coin->fCoinPtTEnd->span();
+        SkOpSpanBase* oldEnd = end;
+        SkOpSpan* start = coin->fCoinPtTStart->span()->starter(&end);
+        SkOpSpanBase* oEnd = coin->fOppPtTEnd->span();
+        SkOpSpanBase* oOldEnd = oEnd;
+        SkOpSpanBase* oStart = coin->fOppPtTStart->span()->starter(&oEnd);
+        bool flipped = (end == oldEnd) != (oEnd == oOldEnd);
+        if (flipped) {
+            SkTSwap(oStart, oEnd);
+        }
+        SkOpSpanBase* next = start;
+        SkOpSpanBase* oNext = oStart;
+        // check to see if coincident span could be bigger
+
+        do {
+            next = next->upCast()->next();
+            oNext = flipped ? oNext->prev() : oNext->upCast()->next();
+            if (next == end || oNext == oEnd) {
+                break;
+            }
+            if (!next->containsCoinEnd(oNext)) {
+                next->insertCoinEnd(oNext);
+            }
+            SkOpSpan* nextSpan = next->upCast();
+            SkOpSpan* oNextSpan = oNext->upCast();
+            if (!nextSpan->containsCoincidence(oNextSpan)) {
+                nextSpan->insertCoincidence(oNextSpan);
+            }
+        } while (true);
+    } while ((coin = coin->fNext));
+}
+
+bool SkOpCoincidence::overlap(const SkOpPtT* coin1s, const SkOpPtT* coin1e, 
+        const SkOpPtT* coin2s, const SkOpPtT* coin2e, double* overS, double* overE) const {
+    if (coin1s->segment() != coin2s->segment()) {
+        return false;
+    }
+    *overS = SkTMax(SkTMin(coin1s->fT, coin1e->fT), SkTMin(coin2s->fT, coin2e->fT));
+    *overE = SkTMin(SkTMax(coin1s->fT, coin1e->fT), SkTMax(coin2s->fT, coin2e->fT));
+    return *overS < *overE;
+}
diff --git a/src/pathops/SkOpCoincidence.h b/src/pathops/SkOpCoincidence.h
index 287bfd1..ce57999 100644
--- a/src/pathops/SkOpCoincidence.h
+++ b/src/pathops/SkOpCoincidence.h
@@ -19,6 +19,8 @@
     SkOpPtT* fOppPtTStart;
     SkOpPtT* fOppPtTEnd;
     bool fFlipped;
+
+    void dump() const;
 };
 
 class SkOpCoincidence {
@@ -28,13 +30,31 @@
     }
 
     void add(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
-             SkOpPtT* oppPtTEnd, bool flipped, SkChunkAlloc* allocator);
-    void apply();
+             SkOpPtT* oppPtTEnd, SkChunkAlloc* allocator);
+    bool addMissing(SkChunkAlloc* allocator);
+    void addMissing(SkCoincidentSpans* check, SkChunkAlloc* allocator);
+    bool apply();
     bool contains(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
                   SkOpPtT* oppPtTEnd, bool flipped);
+    void debugShowCoincidence() const;
+    void detach(SkCoincidentSpans* );
     void dump() const;
+    void expand();
+    bool extend(SkOpPtT* coinPtTStart, SkOpPtT* coinPtTEnd, SkOpPtT* oppPtTStart,
+        SkOpPtT* oppPtTEnd);
+    void fixUp(SkOpPtT* deleted, SkOpPtT* kept);
     void mark();
 
+private:
+    bool addIfMissing(const SkOpPtT* over1s, const SkOpPtT* over1e,
+                      const SkOpPtT* over2s, const SkOpPtT* over2e, double tStart, double tEnd,
+                      SkOpPtT* coinPtTStart, const SkOpPtT* coinPtTEnd,
+                      SkOpPtT* oppPtTStart, const SkOpPtT* oppPtTEnd,
+                      SkChunkAlloc* allocator);
+    bool overlap(const SkOpPtT* coinStart1, const SkOpPtT* coinEnd1,
+                 const SkOpPtT* coinStart2, const SkOpPtT* coinEnd2,
+                 double* overS, double* overE) const;
+
     SkCoincidentSpans* fHead;
 };
 
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp
index 28c072a..178ba3e 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -4,759 +4,57 @@
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
-#include "SkIntersections.h"
 #include "SkOpContour.h"
+#include "SkOpTAllocator.h"
 #include "SkPathWriter.h"
+#include "SkReduceOrder.h"
 #include "SkTSort.h"
 
-bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
-        const SkIntersections& ts, bool swap) {
-    SkPoint pt0 = ts.pt(0).asSkPoint();
-    SkPoint pt1 = ts.pt(1).asSkPoint();
-    if (pt0 == pt1 || ts[0][0] == ts[0][1] || ts[1][0] == ts[1][1]) {
-        // FIXME: one could imagine a case where it would be incorrect to ignore this
-        // suppose two self-intersecting cubics overlap to be coincident --
-        // this needs to check that by some measure the t values are far enough apart
-        // or needs to check to see if the self-intersection bit was set on the cubic segment
-        return false;
-    }
-    SkCoincidence& coincidence = fCoincidences.push_back();
-    coincidence.fOther = other;
-    coincidence.fSegments[0] = index;
-    coincidence.fSegments[1] = otherIndex;
-    coincidence.fTs[swap][0] = ts[0][0];
-    coincidence.fTs[swap][1] = ts[0][1];
-    coincidence.fTs[!swap][0] = ts[1][0];
-    coincidence.fTs[!swap][1] = ts[1][1];
-    coincidence.fPts[swap][0] = pt0;
-    coincidence.fPts[swap][1] = pt1;
-    bool nearStart = ts.nearlySame(0);
-    bool nearEnd = ts.nearlySame(1);
-    coincidence.fPts[!swap][0] = nearStart ? ts.pt2(0).asSkPoint() : pt0;
-    coincidence.fPts[!swap][1] = nearEnd ? ts.pt2(1).asSkPoint() : pt1;
-    coincidence.fNearly[0] = nearStart;
-    coincidence.fNearly[1] = nearEnd;
-    return true;
-}
-
-SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
-    int segmentCount = fSortedSegments.count();
-    SkASSERT(segmentCount > 0);
-    for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
-        SkOpSegment* testSegment = fSortedSegments[sortedIndex];
-        if (testSegment->done()) {
-            continue;
-        }
-        *start = *end = 0;
-        while (testSegment->nextCandidate(start, end)) {
-            if (!testSegment->isVertical(*start, *end)) {
-                return testSegment;
-            }
-        }
+SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4],
+        SkChunkAlloc* allocator) {
+    switch (verb) {
+        case SkPath::kLine_Verb: {
+            SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 2);
+            memcpy(ptStorage, pts, sizeof(SkPoint) * 2);
+            return appendSegment(allocator).addLine(ptStorage, this);
+        } break;
+        case SkPath::kQuad_Verb: {
+            SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3);
+            memcpy(ptStorage, pts, sizeof(SkPoint) * 3);
+            return appendSegment(allocator).addQuad(ptStorage, this);
+        } break;
+        case SkPath::kConic_Verb: {
+            SkASSERT(0);  // the original curve is a cubic, which will never reduce to a conic
+        } break;
+        case SkPath::kCubic_Verb: {
+            SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 4);
+            memcpy(ptStorage, pts, sizeof(SkPoint) * 4);
+            return appendSegment(allocator).addCubic(ptStorage, this);
+        } break;
+        default:
+            SkASSERT(0);
     }
     return NULL;
 }
 
-// if one is very large the smaller may have collapsed to nothing
-static void bump_out_close_span(double* startTPtr, double* endTPtr) {
-    double startT = *startTPtr;
-    double endT = *endTPtr;
-    if (approximately_negative(endT - startT)) {
-        if (endT <= 1 - FLT_EPSILON) {
-            *endTPtr += FLT_EPSILON;
-            SkASSERT(*endTPtr <= 1);
-        } else {
-            *startTPtr -= FLT_EPSILON;
-            SkASSERT(*startTPtr >= 0);
-        }
-    }
-}
-
-// first pass, add missing T values
-// second pass, determine winding values of overlaps
-void SkOpContour::addCoincidentPoints() {
-    int count = fCoincidences.count();
-    for (int index = 0; index < count; ++index) {
-        SkCoincidence& coincidence = fCoincidences[index];
-        int thisIndex = coincidence.fSegments[0];
-        SkOpSegment& thisOne = fSegments[thisIndex];
-        SkOpContour* otherContour = coincidence.fOther;
-        int otherIndex = coincidence.fSegments[1];
-        SkOpSegment& 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("o");
-    #endif
-        double startT = coincidence.fTs[0][0];
-        double endT = coincidence.fTs[0][1];
-        bool startSwapped, oStartSwapped, cancelers;
-        if ((cancelers = startSwapped = startT > endT)) {
-            SkTSwap(startT, endT);
-        }
-        bump_out_close_span(&startT, &endT);
-        SkASSERT(!approximately_negative(endT - startT));
-        double oStartT = coincidence.fTs[1][0];
-        double oEndT = coincidence.fTs[1][1];
-        if ((oStartSwapped = oStartT > oEndT)) {
-            SkTSwap(oStartT, oEndT);
-            cancelers ^= true;
-        }
-        bump_out_close_span(&oStartT, &oEndT);
-        SkASSERT(!approximately_negative(oEndT - oStartT));
-        const SkPoint& startPt = coincidence.fPts[0][startSwapped];
-        if (cancelers) {
-            // make sure startT and endT have t entries
-            if (startT > 0 || oEndT < 1
-                    || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) {
-                thisOne.addTPair(startT, &other, oEndT, true, startPt,
-                        coincidence.fPts[1][startSwapped]);
-            }
-            const SkPoint& oStartPt = coincidence.fPts[1][oStartSwapped];
-            if (oStartT > 0 || endT < 1
-                    || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) {
-                other.addTPair(oStartT, &thisOne, endT, true, oStartPt,
-                        coincidence.fPts[0][oStartSwapped]);
-            }
-        } else {
-            if (startT > 0 || oStartT > 0
-                    || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) {
-                thisOne.addTPair(startT, &other, oStartT, true, startPt,
-                        coincidence.fPts[1][startSwapped]);
-            }
-            const SkPoint& oEndPt = coincidence.fPts[1][!oStartSwapped];
-            if (endT < 1 || oEndT < 1
-                    || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) {
-                other.addTPair(oEndT, &thisOne, endT, true, oEndPt,
-                        coincidence.fPts[0][!oStartSwapped]);
-            }
-        }
-    #if DEBUG_CONCIDENT
-        thisOne.debugShowTs("+");
-        other.debugShowTs("o");
-    #endif
-    }
-    // if there are multiple pairs of coincidence that share an edge, see if the opposite
-    // are also coincident
-    for (int index = 0; index < count - 1; ++index) {
-        const SkCoincidence& coincidence = fCoincidences[index];
-        int thisIndex = coincidence.fSegments[0];
-        SkOpContour* otherContour = coincidence.fOther;
-        int otherIndex = coincidence.fSegments[1];
-        for (int idx2 = 1; idx2 < count; ++idx2) {
-            const SkCoincidence& innerCoin = fCoincidences[idx2];
-            int innerThisIndex = innerCoin.fSegments[0];
-            if (thisIndex == innerThisIndex) {
-                checkCoincidentPair(coincidence, 1, innerCoin, 1, false);
-            }
-            if (this == otherContour && otherIndex == innerThisIndex) {
-                checkCoincidentPair(coincidence, 0, innerCoin, 1, false);
-            }
-            SkOpContour* innerOtherContour = innerCoin.fOther;
-            innerThisIndex = innerCoin.fSegments[1];
-            if (this == innerOtherContour && thisIndex == innerThisIndex) {
-                checkCoincidentPair(coincidence, 1, innerCoin, 0, false);
-            }
-            if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
-                checkCoincidentPair(coincidence, 0, innerCoin, 0, false);
-            }
-        }
-    }
-}
-
-bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
-        const SkIntersections& ts, int ptIndex, bool swap) {
-    SkPoint pt0 = ts.pt(ptIndex).asSkPoint();
-    SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint();
-    if (SkDPoint::ApproximatelyEqual(pt0, pt1)) {
-        // FIXME: one could imagine a case where it would be incorrect to ignore this
-        // suppose two self-intersecting cubics overlap to form a partial coincidence --
-        // although it isn't clear why the regular coincidence could wouldn't pick this up
-        // this is exceptional enough to ignore for now
-        return false;
-    }
-    SkCoincidence& coincidence = fPartialCoincidences.push_back();
-    coincidence.fOther = other;
-    coincidence.fSegments[0] = index;
-    coincidence.fSegments[1] = otherIndex;
-    coincidence.fTs[swap][0] = ts[0][ptIndex];
-    coincidence.fTs[swap][1] = ts[0][ptIndex + 1];
-    coincidence.fTs[!swap][0] = ts[1][ptIndex];
-    coincidence.fTs[!swap][1] = ts[1][ptIndex + 1];
-    coincidence.fPts[0][0] = coincidence.fPts[1][0] = pt0;
-    coincidence.fPts[0][1] = coincidence.fPts[1][1] = pt1;
-    coincidence.fNearly[0] = 0;
-    coincidence.fNearly[1] = 0;
-    return true;
-}
-
-void SkOpContour::align(const SkOpSegment::AlignedSpan& aligned, bool swap,
-        SkCoincidence* coincidence) {
-    for (int idx2 = 0; idx2 < 2; ++idx2) {
-        if (coincidence->fPts[0][idx2] == aligned.fOldPt
-                && coincidence->fTs[swap][idx2] == aligned.fOldT) {
-            SkASSERT(SkDPoint::RoughlyEqual(coincidence->fPts[0][idx2], aligned.fPt));
-            coincidence->fPts[0][idx2] = aligned.fPt;
-            SkASSERT(way_roughly_equal(coincidence->fTs[swap][idx2], aligned.fT));
-            coincidence->fTs[swap][idx2] = aligned.fT;
-        }
-    }
-}
-
-void SkOpContour::alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
-        SkTArray<SkCoincidence, true>* coincidences) {
-    int count = coincidences->count();
-    for (int index = 0; index < count; ++index) {
-        SkCoincidence& coincidence = (*coincidences)[index];
-        int thisIndex = coincidence.fSegments[0];
-        const SkOpSegment* thisOne = &fSegments[thisIndex];
-        const SkOpContour* otherContour = coincidence.fOther;
-        int otherIndex = coincidence.fSegments[1];
-        const SkOpSegment* other = &otherContour->fSegments[otherIndex];
-        if (thisOne == aligned.fOther1 && other == aligned.fOther2) {
-            align(aligned, false, &coincidence);
-        } else if (thisOne == aligned.fOther2 && other == aligned.fOther1) {
-            align(aligned, true, &coincidence);
-        }
-    }
-}
-
-void SkOpContour::alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex, 
-        bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const {
-    int zeroPt;
-    if ((zeroPt = alignT(swap, tIndex, ts)) >= 0) {
-        alignPt(segmentIndex, point, zeroPt);
-    }
-    if ((zeroPt = other->alignT(!swap, tIndex, ts)) >= 0) {
-        other->alignPt(otherIndex, point, zeroPt);
-    }
-}
-
-void SkOpContour::alignPt(int index, SkPoint* point, int zeroPt) const {
-    const SkOpSegment& segment = fSegments[index];
-    if (0 == zeroPt) {     
-        *point = segment.pts()[0];
-    } else {
-        *point = segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
-    }
-}
-
-int SkOpContour::alignT(bool swap, int tIndex, SkIntersections* ts) const {
-    double tVal = (*ts)[swap][tIndex];
-    if (tVal != 0 && precisely_zero(tVal)) {
-        ts->set(swap, tIndex, 0);
-        return 0;
-    } 
-     if (tVal != 1 && precisely_equal(tVal, 1)) {
-        ts->set(swap, tIndex, 1);
-        return 1;
-    }
-    return -1;
-}
-
-bool SkOpContour::calcAngles() {
-    int segmentCount = fSegments.count();
-    for (int test = 0; test < segmentCount; ++test) {
-        if (!fSegments[test].calcAngles()) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool SkOpContour::calcCoincidentWinding() {
-    int count = fCoincidences.count();
-#if DEBUG_CONCIDENT
-    if (count > 0) {
-        SkDebugf("%s count=%d\n", __FUNCTION__, count);
-    }
-#endif
-    for (int index = 0; index < count; ++index) {
-        SkCoincidence& coincidence = fCoincidences[index];
-        if (!calcCommonCoincidentWinding(coincidence)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-void SkOpContour::calcPartialCoincidentWinding() {
-    int count = fPartialCoincidences.count();
-#if DEBUG_CONCIDENT
-    if (count > 0) {
-        SkDebugf("%s count=%d\n", __FUNCTION__, count);
-    }
-#endif
-    for (int index = 0; index < count; ++index) {
-        SkCoincidence& coincidence = fPartialCoincidences[index];
-        calcCommonCoincidentWinding(coincidence);
-    }
-    // if there are multiple pairs of partial coincidence that share an edge, see if the opposite
-    // are also coincident
-    for (int index = 0; index < count - 1; ++index) {
-        const SkCoincidence& coincidence = fPartialCoincidences[index];
-        int thisIndex = coincidence.fSegments[0];
-        SkOpContour* otherContour = coincidence.fOther;
-        int otherIndex = coincidence.fSegments[1];
-        for (int idx2 = 1; idx2 < count; ++idx2) {
-            const SkCoincidence& innerCoin = fPartialCoincidences[idx2];
-            int innerThisIndex = innerCoin.fSegments[0];
-            if (thisIndex == innerThisIndex) {
-                checkCoincidentPair(coincidence, 1, innerCoin, 1, true);
-            }
-            if (this == otherContour && otherIndex == innerThisIndex) {
-                checkCoincidentPair(coincidence, 0, innerCoin, 1, true);
-            }
-            SkOpContour* innerOtherContour = innerCoin.fOther;
-            innerThisIndex = innerCoin.fSegments[1];
-            if (this == innerOtherContour && thisIndex == innerThisIndex) {
-                checkCoincidentPair(coincidence, 1, innerCoin, 0, true);
-            }
-            if (otherContour == innerOtherContour && otherIndex == innerThisIndex) {
-                checkCoincidentPair(coincidence, 0, innerCoin, 0, true);
-            }
-        }
-    }
-}
-
-void SkOpContour::checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
-        const SkCoincidence& twoCoin, int twoIdx, bool partial) {
-    SkASSERT((oneIdx ? this : oneCoin.fOther) == (twoIdx ? this : twoCoin.fOther));
-    SkASSERT(oneCoin.fSegments[!oneIdx] == twoCoin.fSegments[!twoIdx]);
-    // look for common overlap
-    double min = SK_ScalarMax;
-    double max = SK_ScalarMin;
-    double min1 = oneCoin.fTs[!oneIdx][0];
-    double max1 = oneCoin.fTs[!oneIdx][1];
-    double min2 = twoCoin.fTs[!twoIdx][0];
-    double max2 = twoCoin.fTs[!twoIdx][1];
-    bool cancelers = (min1 < max1) != (min2 < max2);
-    if (min1 > max1) {
-        SkTSwap(min1, max1);
-    }
-    if (min2 > max2) {
-        SkTSwap(min2, max2);
-    }
-    if (between(min1, min2, max1)) {
-        min = min2;
-    }
-    if (between(min1, max2, max1)) {
-        max = max2;
-    }
-    if (between(min2, min1, max2)) {
-        min = SkTMin(min, min1);
-    }
-    if (between(min2, max1, max2)) {
-        max = SkTMax(max, max1);
-    }
-    if (min >= max) {
-        return;  // no overlap
-    }
-    // look to see if opposite are different segments
-    int seg1Index = oneCoin.fSegments[oneIdx];
-    int seg2Index = twoCoin.fSegments[twoIdx];
-    if (seg1Index == seg2Index) {
-        return;
-    }
-    SkOpContour* contour1 = oneIdx ? oneCoin.fOther : this;
-    SkOpContour* contour2 = twoIdx ? twoCoin.fOther : this;
-    SkOpSegment* segment1 = &contour1->fSegments[seg1Index];
-    SkOpSegment* segment2 = &contour2->fSegments[seg2Index];
-    // find opposite t value ranges corresponding to reference min/max range
-    const SkOpContour* refContour = oneIdx ? this : oneCoin.fOther;
-    const int refSegIndex = oneCoin.fSegments[!oneIdx];
-    const SkOpSegment* refSegment = &refContour->fSegments[refSegIndex];
-    int seg1Start = segment1->findOtherT(min, refSegment);
-    int seg1End = segment1->findOtherT(max, refSegment);
-    int seg2Start = segment2->findOtherT(min, refSegment);
-    int seg2End = segment2->findOtherT(max, refSegment);
-    // if the opposite pairs already contain min/max, we're done
-    if (seg1Start >= 0 && seg1End >= 0 && seg2Start >= 0 && seg2End >= 0) {
-        return;
-    }
-    double loEnd = SkTMin(min1, min2);
-    double hiEnd = SkTMax(max1, max2);
-    // insert the missing coincident point(s)
-    double missingT1 = -1;
-    double otherT1 = -1;
-    if (seg1Start < 0) {
-        if (seg2Start < 0) {
-            return;
-        }
-        missingT1 = segment1->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
-                segment2, seg1End);
-        if (missingT1 < 0) {
-            return;
-        }
-        const SkOpSpan* missingSpan = &segment2->span(seg2Start);
-        otherT1 = missingSpan->fT;
-    } else if (seg2Start < 0) {
-        SkASSERT(seg1Start >= 0);
-        missingT1 = segment2->calcMissingTStart(refSegment, loEnd, min, max, hiEnd,
-                segment1, seg2End);
-        if (missingT1 < 0) {
-            return;
-        }
-        const SkOpSpan* missingSpan = &segment1->span(seg1Start);
-        otherT1 = missingSpan->fT;
-    }
-    SkPoint missingPt1;
-    SkOpSegment* addTo1 = NULL;
-    SkOpSegment* addOther1 = seg1Start < 0 ? segment2 : segment1;
-    int minTIndex = refSegment->findExactT(min, addOther1);
-    SkASSERT(minTIndex >= 0);
-    if (missingT1 >= 0) {
-        missingPt1 = refSegment->span(minTIndex).fPt;
-        addTo1 = seg1Start < 0 ? segment1 : segment2;
-    }
-    double missingT2 = -1;
-    double otherT2 = -1;
-    if (seg1End < 0) {
-        if (seg2End < 0) {
-            return;
-        }
-        missingT2 = segment1->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
-                segment2, seg1Start);
-        if (missingT2 < 0) {
-            return;
-        }
-        const SkOpSpan* missingSpan = &segment2->span(seg2End);
-        otherT2 = missingSpan->fT;
-    } else if (seg2End < 0) {
-        SkASSERT(seg1End >= 0);
-        missingT2 = segment2->calcMissingTEnd(refSegment, loEnd, min, max, hiEnd,
-                segment1, seg2Start);
-        if (missingT2 < 0) {
-            return;
-        }
-        const SkOpSpan* missingSpan = &segment1->span(seg1End);
-        otherT2 = missingSpan->fT;
-    }
-    SkPoint missingPt2;
-    SkOpSegment* addTo2 = NULL;
-    SkOpSegment* addOther2 = seg1End < 0 ? segment2 : segment1;
-    int maxTIndex = refSegment->findExactT(max, addOther2);
-    SkASSERT(maxTIndex >= 0);
-    if (missingT2 >= 0) {
-        missingPt2 = refSegment->span(maxTIndex).fPt;
-        addTo2 = seg1End < 0 ? segment1 : segment2;
-    }
-    if (missingT1 >= 0) {
-        addTo1->pinT(missingPt1, &missingT1);
-        addTo1->addTPair(missingT1, addOther1, otherT1, false, missingPt1);
-    } else {
-        SkASSERT(minTIndex >= 0);
-        missingPt1 = refSegment->span(minTIndex).fPt;
-    }
-    if (missingT2 >= 0) {
-        addTo2->pinT(missingPt2, &missingT2);
-        addTo2->addTPair(missingT2, addOther2, otherT2, false, missingPt2);
-    } else {
-        SkASSERT(minTIndex >= 0);
-        missingPt2 = refSegment->span(maxTIndex).fPt;
-    }
-    if (!partial) {
-        return;
-    }
-    if (cancelers) {
-        if (missingT1 >= 0) {
-            if (addTo1->reversePoints(missingPt1, missingPt2)) {
-                SkTSwap(missingPt1, missingPt2);
-            }
-            addTo1->addTCancel(missingPt1, missingPt2, addOther1);
-        } else {
-            if (addTo2->reversePoints(missingPt1, missingPt2)) {
-                SkTSwap(missingPt1, missingPt2);
-            }
-            addTo2->addTCancel(missingPt1, missingPt2, addOther2);
-        }
-    } else if (missingT1 >= 0) {
-        SkAssertResult(addTo1->addTCoincident(missingPt1, missingPt2,
-                addTo1 == addTo2 ? missingT2 : otherT2, addOther1));
-    } else {
-        SkAssertResult(addTo2->addTCoincident(missingPt2, missingPt1,
-                addTo2 == addTo1 ? missingT1 : otherT1, addOther2));
-    }
-}
-
-void SkOpContour::joinCoincidence(const SkTArray<SkCoincidence, true>& coincidences, bool partial) {
-    int count = coincidences.count();
-#if DEBUG_CONCIDENT
-    if (count > 0) {
-        SkDebugf("%s count=%d\n", __FUNCTION__, count);
-    }
-#endif
-    // look for a lineup where the partial implies another adjoining coincidence
-    for (int index = 0; index < count; ++index) {
-        const SkCoincidence& coincidence = coincidences[index];
-        int thisIndex = coincidence.fSegments[0];
-        SkOpSegment& thisOne = fSegments[thisIndex];
-        if (thisOne.done()) {
-            continue;
-        }
-        SkOpContour* otherContour = coincidence.fOther;
-        int otherIndex = coincidence.fSegments[1];
-        SkOpSegment& other = otherContour->fSegments[otherIndex];
-        if (other.done()) {
-            continue;
-        }
-        double startT = coincidence.fTs[0][0];
-        double endT = coincidence.fTs[0][1];
-        if (startT == endT) {  // this can happen in very large compares
-            continue;
-        }
-        double oStartT = coincidence.fTs[1][0];
-        double oEndT = coincidence.fTs[1][1];
-        if (oStartT == oEndT) {
-            continue;
-        }
-        bool swapStart = startT > endT;
-        bool swapOther = oStartT > oEndT;
-        const SkPoint* startPt = &coincidence.fPts[0][0];
-        const SkPoint* endPt = &coincidence.fPts[0][1];
-        if (swapStart) {
-            SkTSwap(startT, endT);
-            SkTSwap(oStartT, oEndT);
-            SkTSwap(startPt, endPt);
-        }
-        bool cancel = swapOther != swapStart;
-        int step = swapStart ? -1 : 1;
-        int oStep = swapOther ? -1 : 1;
-        double oMatchStart = cancel ? oEndT : oStartT;
-        if (partial ? startT != 0 || oMatchStart != 0 : (startT == 0) != (oMatchStart == 0)) {
-            bool added = false;
-            if (oMatchStart != 0) {
-                const SkPoint& oMatchStartPt = cancel ? *endPt : *startPt;
-                added = thisOne.joinCoincidence(&other, oMatchStart, oMatchStartPt, oStep, cancel);
-            }
-            if (!cancel && startT != 0 && !added) {
-                (void) other.joinCoincidence(&thisOne, startT, *startPt, step, cancel);
-            }
-        }
-        double oMatchEnd = cancel ? oStartT : oEndT;
-        if (partial ? endT != 1 || oMatchEnd != 1 : (endT == 1) != (oMatchEnd == 1)) {
-            bool added = false;
-            if (cancel && endT != 1 && !added) {
-                (void) other.joinCoincidence(&thisOne, endT, *endPt, -step, cancel);
-            }
-        }
-    }
-}
-
-bool SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
-    if (coincidence.fNearly[0] && coincidence.fNearly[1]) {
-        return true;
-    }
-    int thisIndex = coincidence.fSegments[0];
-    SkOpSegment& thisOne = fSegments[thisIndex];
-    if (thisOne.done()) {
-        return true;
-    }
-    SkOpContour* otherContour = coincidence.fOther;
-    int otherIndex = coincidence.fSegments[1];
-    SkOpSegment& other = otherContour->fSegments[otherIndex];
-    if (other.done()) {
-        return true;
-    }
-    double startT = coincidence.fTs[0][0];
-    double endT = coincidence.fTs[0][1];
-    const SkPoint* startPt = &coincidence.fPts[0][0];
-    const SkPoint* endPt = &coincidence.fPts[0][1];
-    bool cancelers;
-    if ((cancelers = startT > endT)) {
-        SkTSwap<double>(startT, endT);
-        SkTSwap<const SkPoint*>(startPt, endPt);
-    }
-    bump_out_close_span(&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;
-    }
-    bump_out_close_span(&oStartT, &oEndT);
-    SkASSERT(!approximately_negative(oEndT - oStartT));
-    bool success = true;
-    if (cancelers) {
-        thisOne.addTCancel(*startPt, *endPt, &other);
-    } else {
-        success = thisOne.addTCoincident(*startPt, *endPt, endT, &other);
-    }
-#if DEBUG_CONCIDENT
-    thisOne.debugShowTs("p");
-    other.debugShowTs("o");
-#endif
-    return success;
-}
-
-void SkOpContour::resolveNearCoincidence() {
-    int count = fCoincidences.count();
-    for (int index = 0; index < count; ++index) {
-        SkCoincidence& coincidence = fCoincidences[index];
-        if (!coincidence.fNearly[0] || !coincidence.fNearly[1]) {
-            continue;
-        }
-        int thisIndex = coincidence.fSegments[0];
-        SkOpSegment& thisOne = fSegments[thisIndex];
-        SkOpContour* otherContour = coincidence.fOther;
-        int otherIndex = coincidence.fSegments[1];
-        SkOpSegment& other = otherContour->fSegments[otherIndex];
-        if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
-            // OPTIMIZATION: remove from coincidence array
-            continue;
-        }
-    #if DEBUG_CONCIDENT
-        thisOne.debugShowTs("-");
-        other.debugShowTs("o");
-    #endif
-        double startT = coincidence.fTs[0][0];
-        double endT = coincidence.fTs[0][1];
-        bool cancelers;
-        if ((cancelers = startT > endT)) {
-            SkTSwap<double>(startT, endT);
-        }
-        if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
-            if (endT <= 1 - FLT_EPSILON) {
-                endT += FLT_EPSILON;
-                SkASSERT(endT <= 1);
-            } else {
-                startT -= FLT_EPSILON;
-                SkASSERT(startT >= 0);
-            }
-        }
-        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));
-        if (cancelers) {
-            thisOne.blindCancel(coincidence, &other);
-        } else {
-            thisOne.blindCoincident(coincidence, &other);
-        }
-    }
-}
-
-void SkOpContour::sortAngles() {
-    int segmentCount = fSegments.count();
-    for (int test = 0; test < segmentCount; ++test) {
-        fSegments[test].sortAngles();
-    }
-}
-
-void SkOpContour::sortSegments() {
-    int segmentCount = fSegments.count();
-    fSortedSegments.push_back_n(segmentCount);
-    for (int test = 0; test < segmentCount; ++test) {
-        fSortedSegments[test] = &fSegments[test];
-    }
-    SkTQSort<SkOpSegment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
-    fFirstSorted = 0;
-}
-
 void SkOpContour::toPath(SkPathWriter* path) const {
-    int segmentCount = fSegments.count();
-    const SkPoint& pt = fSegments.front().pts()[0];
+    const SkPoint& pt = fHead.pts()[0];
     path->deferredMove(pt);
-    for (int test = 0; test < segmentCount; ++test) {
-        fSegments[test].addCurveTo(0, 1, path, true);
-    }
+    const SkOpSegment* segment = &fHead;
+    do {
+        segment->addCurveTo(segment->head(), segment->tail(), path, true);
+    } while ((segment = segment->next()));
     path->close();
 }
 
-void SkOpContour::topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY,
-        SkOpSegment** topStart) {
-    int segmentCount = fSortedSegments.count();
-    SkASSERT(segmentCount > 0);
-    int sortedIndex = fFirstSorted;
-    fDone = true;  // may be cleared below
-    for ( ; sortedIndex < segmentCount; ++sortedIndex) {
-        SkOpSegment* testSegment = fSortedSegments[sortedIndex];
-        if (testSegment->done()) {
-            if (sortedIndex == fFirstSorted) {
-                ++fFirstSorted;
-            }
+SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) {
+    SkOpSegment* segment = &fHead;
+    do {
+        if (segment->done()) {
             continue;
         }
-        fDone = false;
-        SkPoint testXY = testSegment->activeLeftTop(NULL);
-        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;
-            }
-        }
-        *topStart = testSegment;
-        *bestXY = testXY;
-    }
-}
-
-SkOpSegment* SkOpContour::undoneSegment(int* start, int* end) {
-    int segmentCount = fSegments.count();
-    for (int test = 0; test < segmentCount; ++test) {
-        SkOpSegment* testSegment = &fSegments[test];
-        if (testSegment->done()) {
-            continue;
-        }
-        testSegment->undoneSpan(start, end);
-        return testSegment;
-    }
+        segment->undoneSpan(startPtr, endPtr);
+        return segment;
+    } while ((segment = segment->next()));
     return NULL;
 }
-
-#if DEBUG_SHOW_WINDING
-int SkOpContour::debugShowWindingValues(int totalSegments, int ofInterest) {
-    int count = fSegments.count();
-    int sum = 0;
-    for (int index = 0; index < count; ++index) {
-        sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
-    }
-//      SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
-    return sum;
-}
-
-void SkOpContour::debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList) {
-//     int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
-//    int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
-    int ofInterest = 1 << 5 | 1 << 8;
-    int total = 0;
-    int index;
-    for (index = 0; index < contourList.count(); ++index) {
-        total += contourList[index]->segments().count();
-    }
-    int sum = 0;
-    for (index = 0; index < contourList.count(); ++index) {
-        sum += contourList[index]->debugShowWindingValues(total, ofInterest);
-    }
-//       SkDebugf("%s total=%d\n", __FUNCTION__, sum);
-}
-#endif
-
-void SkOpContour::setBounds() {
-    int count = fSegments.count();
-    if (count == 0) {
-        SkDebugf("%s empty contour\n", __FUNCTION__);
-        SkASSERT(0);
-        // FIXME: delete empty contour?
-        return;
-    }
-    fBounds = fSegments.front().bounds();
-    for (int index = 1; index < count; ++index) {
-        fBounds.add(fSegments[index].bounds());
-    }
-}
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index 7a1cc09..dd5dbb4 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -8,31 +8,24 @@
 #define SkOpContour_DEFINED
 
 #include "SkOpSegment.h"
-#include "SkTArray.h"
+#include "SkTDArray.h"
+#include "SkTSort.h"
 
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-#include "SkThread.h"
-#endif
-
-class SkIntersections;
-class SkOpContour;
+class SkChunkAlloc;
+enum class SkOpRayDir;
+struct SkOpRayHit;
 class SkPathWriter;
 
-struct SkCoincidence {
-    SkOpContour* fOther;
-    int fSegments[2];
-    double fTs[2][2];
-    SkPoint fPts[2][2];
-    int fNearly[2];
-};
-
 class SkOpContour {
 public:
     SkOpContour() {
         reset();
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-        fID = sk_atomic_inc(&SkPathOpsDebug::gContourID);
-#endif
+    }
+
+    ~SkOpContour() {
+        if (fNext) {
+            fNext->~SkOpContour();
+        }
     }
 
     bool operator<(const SkOpContour& rh) const {
@@ -41,211 +34,277 @@
                 : fBounds.fTop < rh.fBounds.fTop;
     }
 
-    bool addCoincident(int index, SkOpContour* other, int otherIndex,
-                       const SkIntersections& ts, bool swap);
-    void addCoincidentPoints();
+    void addConic(SkPoint pts[3], SkScalar weight, SkChunkAlloc* allocator) {
+        appendSegment(allocator).addConic(pts, weight, this);
+    }
 
-    void addCross(const SkOpContour* crosser) {
-#ifdef DEBUG_CROSS
-        for (int index = 0; index < fCrosses.count(); ++index) {
-            SkASSERT(fCrosses[index] != crosser);
+    void addCubic(SkPoint pts[4], SkChunkAlloc* allocator) {
+        appendSegment(allocator).addCubic(pts, this);
+    }
+
+    SkOpSegment* addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator);
+
+    void addLine(SkPoint pts[2], SkChunkAlloc* allocator) {
+        appendSegment(allocator).addLine(pts, this);
+    }
+
+    void addQuad(SkPoint pts[3], SkChunkAlloc* allocator) {
+        appendSegment(allocator).addQuad(pts, this);
+    }
+
+    void align() {
+        SkASSERT(fCount > 0);
+        SkOpSegment* segment = &fHead;
+        do {
+            segment->align();
+        } while ((segment = segment->next()));
+    }
+
+    SkOpSegment& appendSegment(SkChunkAlloc* allocator) {
+        SkOpSegment* result = fCount++
+                ? SkOpTAllocator<SkOpSegment>::Allocate(allocator) : &fHead;
+        result->setPrev(fTail);
+        if (fTail) {
+            fTail->setNext(result);
         }
-#endif
-        fCrosses.push_back(crosser);
+        fTail = result;
+        return *result;
     }
 
-    void addCubic(const SkPoint pts[4]) {
-        fSegments.push_back().addCubic(pts, fOperand, fXor);
-        fContainsCurves = fContainsCubics = true;
-    }
-
-    int addLine(const SkPoint pts[2]) {
-        fSegments.push_back().addLine(pts, fOperand, fXor);
-        return fSegments.count();
-    }
-
-    void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
-        fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
-    }
-
-    bool addPartialCoincident(int index, SkOpContour* other, int otherIndex,
-                       const SkIntersections& ts, int ptIndex, bool swap);
-
-    int addQuad(const SkPoint pts[3]) {
-        fSegments.push_back().addQuad(pts, fOperand, fXor);
-        fContainsCurves = true;
-        return fSegments.count();
-    }
-
-    int addT(int segIndex, SkOpContour* other, int otherIndex, const SkPoint& pt, double newT) {
-        setContainsIntercepts();
-        return fSegments[segIndex].addT(&other->fSegments[otherIndex], pt, newT);
-    }
-
-    int addSelfT(int segIndex, const SkPoint& pt, double newT) {
-        setContainsIntercepts();
-        return fSegments[segIndex].addSelfT(pt, newT);
-    }
-
-    void align(const SkOpSegment::AlignedSpan& aligned, bool swap, SkCoincidence* coincidence);
-    void alignCoincidence(const SkOpSegment::AlignedSpan& aligned,
-            SkTArray<SkCoincidence, true>* coincidences);
-
-    void alignCoincidence(const SkOpSegment::AlignedSpan& aligned) {
-        alignCoincidence(aligned, &fCoincidences);
-        alignCoincidence(aligned, &fPartialCoincidences);
-    }
-
-    void alignMultiples(SkTDArray<SkOpSegment::AlignedSpan>* aligned) {
-        int segmentCount = fSegments.count();
-        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
-            SkOpSegment& segment = fSegments[sIndex];
-            if (segment.hasMultiples()) {
-                segment.alignMultiples(aligned);
-            }
+    SkOpContour* appendContour(SkChunkAlloc* allocator) {
+        SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(allocator);
+        contour->setNext(NULL);
+        SkOpContour* prev = this;
+        SkOpContour* next;
+        while ((next = prev->next())) {
+            prev = next;
         }
+        prev->setNext(contour);
+        return contour;
     }
-
-    void alignTPt(int segmentIndex, const SkOpContour* other, int otherIndex,
-                  bool swap, int tIndex, SkIntersections* ts, SkPoint* point) const;
-
+    
     const SkPathOpsBounds& bounds() const {
         return fBounds;
     }
 
-    bool calcAngles();
-    bool calcCoincidentWinding();
-    void calcPartialCoincidentWinding();
-
-    void checkDuplicates() {
-        int segmentCount = fSegments.count();
-        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
-            SkOpSegment& segment = fSegments[sIndex];
-            if (segment.count() > 2) {
-                segment.checkDuplicates();
-            }
-        }
-    }
-
-    bool checkEnds() {
-        if (!fContainsCurves) {
-            return true;
-        }
-        int segmentCount = fSegments.count();
-        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
-            SkOpSegment* segment = &fSegments[sIndex];
-            if (segment->verb() == SkPath::kLine_Verb) {
-                continue;
-            }
-            if (segment->done()) {
-                continue;   // likely coincident, nothing to do
-            }
-            if (!segment->checkEnds()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    void checkMultiples() {
-        int segmentCount = fSegments.count();
-        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
-            SkOpSegment& segment = fSegments[sIndex];
-            if (segment.count() > 2) {
-                segment.checkMultiples();
-                fMultiples |= segment.hasMultiples();
-            }
-        }
-    }
-
-    void checkSmall() {
-        int segmentCount = fSegments.count();
-        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
-            SkOpSegment& segment = fSegments[sIndex];
-            // OPTIMIZATION : skip segments that are done?
-            if (segment.hasSmall()) {
-                segment.checkSmall();
-            }
-        }
-    }
-
-    // if same point has different T values, choose a common T
-    void checkTiny() {
-        int segmentCount = fSegments.count();
-        if (segmentCount <= 2) {
-            return;
-        }
-        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
-            SkOpSegment& segment = fSegments[sIndex];
-            if (segment.hasTiny()) {
-                segment.checkTiny();
-            }
-        }
+    void calcAngles(SkChunkAlloc* allocator) {
+        SkASSERT(fCount > 0);
+        SkOpSegment* segment = &fHead;
+        do {
+            segment->calcAngles(allocator);
+        } while ((segment = segment->next()));
     }
 
     void complete() {
         setBounds();
-        fContainsIntercepts = false;
     }
 
-    bool containsCubics() const {
-        return fContainsCubics;
+    int count() const {
+        return fCount;
     }
 
-    bool crosses(const SkOpContour* crosser) const {
-        for (int index = 0; index < fCrosses.count(); ++index) {
-            if (fCrosses[index] == crosser) {
-                return true;
-            }
-        }
-        return false;
+    int debugID() const {
+        return SkDEBUGRELEASE(fID, -1);
+    }
+
+    int debugIndent() const {
+        return SkDEBUGRELEASE(fDebugIndent, 0);
+    }
+
+#if DEBUG_ACTIVE_SPANS
+    void debugShowActiveSpans() {
+        SkOpSegment* segment = &fHead;
+        do {
+            segment->debugShowActiveSpans();
+        } while ((segment = segment->next()));
+    }
+#endif
+
+    const SkOpAngle* debugAngle(int id) const {
+        return SkDEBUGRELEASE(this->globalState()->debugAngle(id), NULL);
+    }
+
+    SkOpContour* debugContour(int id) {
+        return SkDEBUGRELEASE(this->globalState()->debugContour(id), NULL);
+    }
+
+    const SkOpPtT* debugPtT(int id) const {
+        return SkDEBUGRELEASE(this->globalState()->debugPtT(id), NULL);
+    }
+
+    const SkOpSegment* debugSegment(int id) const {
+        return SkDEBUGRELEASE(this->globalState()->debugSegment(id), NULL);
+    }
+
+    const SkOpSpanBase* debugSpan(int id) const {
+        return SkDEBUGRELEASE(this->globalState()->debugSpan(id), NULL);
+    }
+
+    SkOpGlobalState* globalState() const {
+        return fState; 
+    }
+
+    void debugValidate() const {
+#if DEBUG_VALIDATE
+        const SkOpSegment* segment = &fHead;
+        const SkOpSegment* prior = NULL;
+        do {
+            segment->debugValidate();
+            SkASSERT(segment->prev() == prior);
+            prior = segment;
+        } while ((segment = segment->next()));
+        SkASSERT(prior == fTail);
+#endif
     }
 
     bool done() const {
         return fDone;
     }
 
+    void dump() const;
+    void dumpAll() const;
+    void dumpAngles() const;
+    void dumpContours() const;
+    void dumpContoursAll() const;
+    void dumpContoursAngles() const;
+    void dumpContoursPts() const;
+    void dumpContoursPt(int segmentID) const;
+    void dumpContoursSegment(int segmentID) const;
+    void dumpContoursSpan(int segmentID) const;
+    void dumpContoursSpans() const;
+    void dumpPt(int ) const;
+    void dumpPts() const;
+    void dumpPtsX() const;
+    void dumpSegment(int ) const;
+    void dumpSegments(SkPathOp op) const;
+    void dumpSpan(int ) const;
+    void dumpSpans() const;
+
     const SkPoint& end() const {
-        const SkOpSegment& segment = fSegments.back();
-        return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
+        return fTail->pts()[SkPathOpsVerbToPoints(fTail->verb())];
     }
 
-    void fixOtherTIndex() {
-        int segmentCount = fSegments.count();
-        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
-            fSegments[sIndex].fixOtherTIndex();
-        }
+    SkOpSpan* findSortableTop(SkOpContour* );
+
+    SkOpSegment* first() {
+        SkASSERT(fCount > 0);
+        return &fHead;
     }
 
-    bool hasMultiples() const {
-        return fMultiples;
+    const SkOpSegment* first() const {
+        SkASSERT(fCount > 0);
+        return &fHead;
     }
 
-    void joinCoincidence() {
-        joinCoincidence(fCoincidences, false);
-        joinCoincidence(fPartialCoincidences, true);
+    void indentDump() const {
+        SkDEBUGCODE(fDebugIndent += 2);
     }
 
-    SkOpSegment* nonVerticalSegment(int* start, int* end);
+    void init(SkOpGlobalState* globalState, bool operand, bool isXor) {
+        fState = globalState;
+        fOperand = operand;
+        fXor = isXor;
+        SkDEBUGCODE(fID = globalState->nextContourID());
+    }
+
+    bool isXor() const {
+        return fXor;
+    }
+
+    void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
+        SkASSERT(fCount > 0);
+        SkOpSegment* segment = &fHead;
+        do {
+            if (fState->angleCoincidence()) {
+                segment->checkAngleCoin(coincidences, allocator);
+            } else {
+                segment->missingCoincidence(coincidences, allocator);
+            }
+        } while ((segment = segment->next()));
+    }
+
+    bool moveMultiples() {
+        SkASSERT(fCount > 0);
+        SkOpSegment* segment = &fHead;
+        do {
+            segment->moveMultiples();
+        } while ((segment = segment->next()));
+        return true;
+    }
+
+    void moveNearby() {
+        SkASSERT(fCount > 0);
+        SkOpSegment* segment = &fHead;
+        do {
+            segment->moveNearby();
+        } while ((segment = segment->next()));
+    }
+
+    SkOpContour* next() {
+        return fNext;
+    }
+
+    const SkOpContour* next() const {
+        return fNext;
+    }
 
     bool operand() const {
         return fOperand;
     }
 
+    bool oppXor() const {
+        return fOppXor;
+    }
+
+    void outdentDump() const {
+        SkDEBUGCODE(fDebugIndent -= 2);
+    }
+
+    void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits, SkChunkAlloc* );
+
+    void remove(SkOpContour* contour) {
+        if (contour == this) {
+            SkASSERT(fCount == 0);
+            return;
+        }
+        SkASSERT(contour->fNext == NULL);
+        SkOpContour* prev = this;
+        SkOpContour* next;
+        while ((next = prev->next()) != contour) {
+            SkASSERT(next);
+            prev = next;
+        }
+        SkASSERT(prev);
+        prev->setNext(NULL);
+    }
+
     void reset() {
-        fSegments.reset();
-        fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
-        fContainsCurves = fContainsCubics = fContainsIntercepts = fDone = fMultiples = false;
+        fTail = NULL;
+        fNext = NULL;
+        fCount = 0;
+        fDone = false;
+        fTopsFound = false;
+        SkDEBUGCODE(fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin));
+        SkDEBUGCODE(fFirstSorted = -1);
+        SkDEBUGCODE(fDebugIndent = 0);
     }
 
-    void resolveNearCoincidence();
-
-    SkTArray<SkOpSegment>& segments() {
-        return fSegments;
+    void setBounds() {
+        SkASSERT(fCount > 0);
+        const SkOpSegment* segment = &fHead;
+        fBounds = segment->bounds();
+        while ((segment = segment->next())) {
+            fBounds.add(segment->bounds());
+        }
     }
 
-    void setContainsIntercepts() {
-        fContainsIntercepts = true;
+    void setGlobalState(SkOpGlobalState* state) {
+        fState = state;
+    }
+
+    void setNext(SkOpContour* contour) {
+//        SkASSERT(!fNext == !!contour);
+        fNext = contour;
     }
 
     void setOperand(bool isOp) {
@@ -254,107 +313,61 @@
 
     void setOppXor(bool isOppXor) {
         fOppXor = isOppXor;
-        int segmentCount = fSegments.count();
-        for (int test = 0; test < segmentCount; ++test) {
-            fSegments[test].setOppXor(isOppXor);
-        }
     }
 
     void setXor(bool isXor) {
         fXor = isXor;
     }
 
-    void sortAngles();
-    void sortSegments();
+    SkPath::Verb simplifyCubic(SkPoint pts[4]);
 
-    const SkPoint& start() const {
-        return fSegments.front().pts()[0];
+    void sortAngles() {
+        SkASSERT(fCount > 0);
+        SkOpSegment* segment = &fHead;
+        do {
+            segment->sortAngles();
+        } while ((segment = segment->next()));
     }
 
-    void toPath(SkPathWriter* path) const;
+    const SkPoint& start() const {
+        return fHead.pts()[0];
+    }
 
     void toPartialBackward(SkPathWriter* path) const {
-        int segmentCount = fSegments.count();
-        for (int test = segmentCount - 1; test >= 0; --test) {
-            fSegments[test].addCurveTo(1, 0, path, true);
-        }
+        const SkOpSegment* segment = fTail;
+        do {
+            segment->addCurveTo(segment->tail(), segment->head(), path, true);
+        } while ((segment = segment->prev()));
     }
 
     void toPartialForward(SkPathWriter* path) const {
-        int segmentCount = fSegments.count();
-        for (int test = 0; test < segmentCount; ++test) {
-            fSegments[test].addCurveTo(0, 1, path, true);
-        }
+        const SkOpSegment* segment = &fHead;
+        do {
+            segment->addCurveTo(segment->head(), segment->tail(), path, true);
+        } while ((segment = segment->next()));
     }
 
-    void topSortableSegment(const SkPoint& topLeft, SkPoint* bestXY, SkOpSegment** topStart);
-    SkOpSegment* undoneSegment(int* start, int* end);
-
-    int updateSegment(int index, const SkPoint* pts) {
-        SkOpSegment& segment = fSegments[index];
-        segment.updatePts(pts);
-        return SkPathOpsVerbToPoints(segment.verb()) + 1;
-    }
-
-#if DEBUG_TEST
-    SkTArray<SkOpSegment>& debugSegments() {
-        return fSegments;
-    }
-#endif
-
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
-    void debugShowActiveSpans() {
-        for (int index = 0; index < fSegments.count(); ++index) {
-            fSegments[index].debugShowActiveSpans();
-        }
-    }
-#endif
-
-#if DEBUG_SHOW_WINDING
-    int debugShowWindingValues(int totalSegments, int ofInterest);
-    static void debugShowWindingValues(const SkTArray<SkOpContour*, true>& contourList);
-#endif
-
-    // available to test routines only
-    void dump() const;
-    void dumpAngles() const;
-    void dumpCoincidence(const SkCoincidence& ) const;
-    void dumpCoincidences() const;
-    void dumpPt(int ) const;
-    void dumpPts() const;
-    void dumpSpan(int ) const;
-    void dumpSpans() const;
+    void toPath(SkPathWriter* path) const;
+    SkOpSegment* undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr);
 
 private:
-    void alignPt(int index, SkPoint* point, int zeroPt) const;
-    int alignT(bool swap, int tIndex, SkIntersections* ts) const;
-    bool calcCommonCoincidentWinding(const SkCoincidence& );
-    void checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
-                             const SkCoincidence& twoCoin, int twoIdx, bool partial);
-    void joinCoincidence(const SkTArray<SkCoincidence, true>& , bool partial);
-    void setBounds();
-
-    SkTArray<SkOpSegment> fSegments;
-    SkTArray<SkOpSegment*, true> fSortedSegments;
-    int fFirstSorted;
-    SkTArray<SkCoincidence, true> fCoincidences;
-    SkTArray<SkCoincidence, true> fPartialCoincidences;
-    SkTArray<const SkOpContour*, true> fCrosses;
+    SkOpGlobalState* fState;
+    SkOpSegment fHead;
+    SkOpSegment* fTail;
+    SkOpContour* fNext;
     SkPathOpsBounds fBounds;
-    bool fContainsIntercepts;  // FIXME: is this used by anybody?
-    bool fContainsCubics;
-    bool fContainsCurves;
-    bool fDone;
-    bool fMultiples;  // set if some segment has multiple identical intersections with other curves
+    int fCount;
+    int fFirstSorted;
+    bool fDone;  // set by find top segment
+    bool fTopsFound;
     bool fOperand;  // true for the second argument to a binary operator
-    bool fXor;
-    bool fOppXor;
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-    int debugID() const { return fID; }
-    int fID;
-#else
-    int debugID() const { return -1; }
-#endif
+    bool fXor;  // set if original path had even-odd fill
+    bool fOppXor;  // set if opposite path had even-odd fill
+    SkDEBUGCODE(int fID);
+    SkDEBUGCODE(mutable int fDebugIndent);
+};
+
+class SkOpContourHead : public SkOpContour {
 };
 
 #endif
diff --git a/src/pathops/SkOpCubicHull.cpp b/src/pathops/SkOpCubicHull.cpp
index 11eaa1f..6b17608 100644
--- a/src/pathops/SkOpCubicHull.cpp
+++ b/src/pathops/SkOpCubicHull.cpp
@@ -14,6 +14,18 @@
             return false;
         }
         rotPath = cubic;
+        if (dy) {
+            rotPath[index].fY = cubic[zero].fY;
+            int mask = other_two(index, zero);
+            int side1 = index ^ mask;
+            int side2 = zero ^ mask;
+            if (approximately_equal(cubic[side1].fY, cubic[zero].fY)) {
+                rotPath[side1].fY = cubic[zero].fY;
+            }
+            if (approximately_equal(cubic[side2].fY, cubic[zero].fY)) {
+                rotPath[side2].fY = cubic[zero].fY;
+            }
+        }
         return true;
     }
     for (int index = 0; index < 4; ++index) {
@@ -81,9 +93,21 @@
                         order[2] = 2;
                         return 3;
                     }
-                    SkASSERT(fPts[2] == fPts[0] || fPts[2] == fPts[3]);
-                    order[2] = 1;
-                    return 3;
+                    if (fPts[2] == fPts[0] || fPts[2] == fPts[3]) {
+                        order[2] = 1;
+                        return 3;
+                    }
+                    // one of the control points may be very nearly but not exactly equal -- 
+                    double dist1_0 = fPts[1].distanceSquared(fPts[0]);
+                    double dist1_3 = fPts[1].distanceSquared(fPts[3]);
+                    double dist2_0 = fPts[2].distanceSquared(fPts[0]);
+                    double dist2_3 = fPts[2].distanceSquared(fPts[3]);
+                    double smallest1distSq = SkTMin(dist1_0, dist1_3);
+                    double smallest2distSq = SkTMin(dist2_0, dist2_3);
+                    if (approximately_zero(SkTMin(smallest1distSq, smallest2distSq))) {
+                        order[2] = smallest1distSq < smallest2distSq ? 2 : 1;
+                        return 3;
+                    }
                 }
                 midX = index;
             } else if (sides == 0) { // '0' means both to one side or the other
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp
index 803a5f4..7216830 100644
--- a/src/pathops/SkOpEdgeBuilder.cpp
+++ b/src/pathops/SkOpEdgeBuilder.cpp
@@ -9,7 +9,7 @@
 #include "SkReduceOrder.h"
 
 void SkOpEdgeBuilder::init() {
-    fCurrentContour = NULL;
+    fCurrentContour = fContoursHead;
     fOperand = false;
     fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
             : kWinding_PathOpsMask;
@@ -19,32 +19,43 @@
 
 void SkOpEdgeBuilder::addOperand(const SkPath& path) {
     SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
-    fPathVerbs.pop_back();
+    fPathVerbs.pop();
     fPath = &path;
     fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
             : kWinding_PathOpsMask;
     preFetch();
 }
 
-bool SkOpEdgeBuilder::finish() {
-    if (fUnparseable || !walk()) {
+int SkOpEdgeBuilder::count() const {
+    SkOpContour* contour = fContoursHead;
+    int count = 0;
+    while (contour) {
+        count += contour->count() > 0;
+        contour = contour->next();
+    }
+    return count;
+}
+
+bool SkOpEdgeBuilder::finish(SkChunkAlloc* allocator) {
+    fOperand = false;
+    if (fUnparseable || !walk(allocator)) {
         return false;
     }
     complete();
-    if (fCurrentContour && !fCurrentContour->segments().count()) {
-        fContours.pop_back();
+    if (fCurrentContour && !fCurrentContour->count()) {
+        fContoursHead->remove(fCurrentContour);
     }
     return true;
 }
 
 void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) {
     if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) {
-        fPathVerbs.push_back(SkPath::kLine_Verb);
-        fPathPts.push_back_n(1, &curveStart);
+        *fPathVerbs.append() = SkPath::kLine_Verb;
+        *fPathPts.append() = curveStart;
     } else {
         fPathPts[fPathPts.count() - 1] = curveStart;
     }
-    fPathVerbs.push_back(SkPath::kClose_Verb);
+    *fPathVerbs.append() = SkPath::kClose_Verb;
 }
 
 // very tiny points cause numerical instability : don't allow them
@@ -57,14 +68,11 @@
     }
 }
 
-
 int SkOpEdgeBuilder::preFetch() {
     if (!fPath->isFinite()) {
         fUnparseable = true;
         return 0;
     }
-    SkAutoConicToQuads quadder;
-    const SkScalar quadderTol = SK_Scalar1 / 16;
     SkPath::RawIter iter(*fPath);
     SkPoint curveStart;
     SkPoint curve[4];
@@ -78,18 +86,18 @@
                 if (!fAllowOpenContours && lastCurve) {
                     closeContour(curve[0], curveStart);
                 }
-                fPathVerbs.push_back(verb);
+                *fPathVerbs.append() = verb;
                 force_small_to_zero(&pts[0]);
-                fPathPts.push_back(pts[0]);
+                *fPathPts.append() = pts[0];
                 curveStart = curve[0] = pts[0];
                 lastCurve = false;
                 continue;
             case SkPath::kLine_Verb:
                 force_small_to_zero(&pts[1]);
                 if (SkDPoint::ApproximatelyEqual(curve[0], pts[1])) {
-                    uint8_t lastVerb = fPathVerbs.back();
+                    uint8_t lastVerb = fPathVerbs.top();
                     if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) {
-                        fPathPts.back() = pts[1];
+                        fPathPts.top() = pts[1];
                     }
                     continue;  // skip degenerate points
                 }
@@ -104,18 +112,16 @@
                     continue;  // skip degenerate points
                 }
                 break;
-            case SkPath::kConic_Verb: {
-                    const SkPoint* quadPts = quadder.computeQuads(pts, iter.conicWeight(),
-                            quadderTol);
-                    const int nQuads = quadder.countQuads();
-                    for (int i = 0; i < nQuads; ++i) {
-                       fPathVerbs.push_back(SkPath::kQuad_Verb);
-                    }
-                    fPathPts.push_back_n(nQuads * 2, &quadPts[1]);
-                    curve[0] = pts[2];
-                    lastCurve = true;
+            case SkPath::kConic_Verb:
+                force_small_to_zero(&pts[1]);
+                force_small_to_zero(&pts[2]);
+                curve[1] = pts[1];
+                curve[2] = pts[2];
+                verb = SkReduceOrder::Conic(curve, iter.conicWeight(), pts);
+                if (verb == SkPath::kMove_Verb) {
+                    continue;  // skip degenerate points
                 }
-                continue;
+                break;
             case SkPath::kCubic_Verb:
                 force_small_to_zero(&pts[1]);
                 force_small_to_zero(&pts[2]);
@@ -135,16 +141,19 @@
             case SkPath::kDone_Verb:
                 continue;
         }
-        fPathVerbs.push_back(verb);
+        *fPathVerbs.append() = verb;
         int ptCount = SkPathOpsVerbToPoints(verb);
-        fPathPts.push_back_n(ptCount, &pts[1]);
+        fPathPts.append(ptCount, &pts[1]);
+        if (verb == SkPath::kConic_Verb) {
+            *fWeights.append() = iter.conicWeight();
+        }
         curve[0] = pts[ptCount];
         lastCurve = true;
     } while (verb != SkPath::kDone_Verb);
     if (!fAllowOpenContours && lastCurve) {
         closeContour(curve[0], curveStart);
     }
-    fPathVerbs.push_back(SkPath::kDone_Verb);
+    *fPathVerbs.append() = SkPath::kDone_Verb;
     return fPathVerbs.count() - 1;
 }
 
@@ -153,10 +162,11 @@
     return true;
 }
 
-bool SkOpEdgeBuilder::walk() {
+bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) {
     uint8_t* verbPtr = fPathVerbs.begin();
     uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
-    const SkPoint* pointsPtr = fPathPts.begin() - 1;
+    SkPoint* pointsPtr = fPathPts.begin() - 1;
+    SkScalar* weightPtr = fWeights.begin();
     SkPath::Verb verb;
     while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
         if (verbPtr == endOfFirstHalf) {
@@ -165,7 +175,7 @@
         verbPtr++;
         switch (verb) {
             case SkPath::kMove_Verb:
-                if (fCurrentContour) {
+                if (fCurrentContour && fCurrentContour->count()) {
                     if (fAllowOpenContours) {
                         complete();
                     } else if (!close()) {
@@ -173,21 +183,53 @@
                     }
                 }
                 if (!fCurrentContour) {
-                    fCurrentContour = fContours.push_back_n(1);
-                    fCurrentContour->setOperand(fOperand);
-                    fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_PathOpsMask);
+                    fCurrentContour = fContoursHead->appendContour(allocator);
                 }
+                fCurrentContour->init(fGlobalState, fOperand,
+                    fXorMask[fOperand] == kEvenOdd_PathOpsMask);
                 pointsPtr += 1;
                 continue;
             case SkPath::kLine_Verb:
-                fCurrentContour->addLine(pointsPtr);
+                fCurrentContour->addLine(pointsPtr, fAllocator);
                 break;
             case SkPath::kQuad_Verb:
-                fCurrentContour->addQuad(pointsPtr);
+                fCurrentContour->addQuad(pointsPtr, fAllocator);
                 break;
-            case SkPath::kCubic_Verb:
-                fCurrentContour->addCubic(pointsPtr);
+            case SkPath::kConic_Verb:
+                fCurrentContour->addConic(pointsPtr, *weightPtr++, fAllocator);
                 break;
+            case SkPath::kCubic_Verb: {
+                // split self-intersecting cubics in two before proceeding
+                // if the cubic is convex, it doesn't self intersect.
+                SkScalar loopT;
+                SkDCubic::CubicType cubicType;
+                if (SkDCubic::ComplexBreak(pointsPtr, &loopT, &cubicType)) {
+                    SkPoint cubicPair[7]; 
+                    SkChopCubicAt(pointsPtr, cubicPair, loopT);
+                    if (!SkScalarsAreFinite(&cubicPair[0].fX, SK_ARRAY_COUNT(cubicPair) * 2)) {
+                        return false;
+                    }
+                    SkPoint cStorage[2][4];
+                    SkPath::Verb v1 = SkReduceOrder::Cubic(&cubicPair[0], cStorage[0]);
+                    SkPath::Verb v2 = SkReduceOrder::Cubic(&cubicPair[3], cStorage[1]);
+                    if (v1 != SkPath::kMove_Verb && v2 != SkPath::kMove_Verb) {
+                        SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &cubicPair[0] : cStorage[0];
+                        SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &cubicPair[3] : cStorage[1];
+                        for (int index = 0; index < SkPathOpsVerbToPoints(v1); ++index) {
+                            force_small_to_zero(&curve1[index]);
+                        }
+                        for (int index = 0; index < SkPathOpsVerbToPoints(v2); ++index) {
+                            force_small_to_zero(&curve2[index]);
+                        }
+                        fCurrentContour->addCurve(v1, curve1, fAllocator)->setCubicType(cubicType);
+                        fCurrentContour->addCurve(v2, curve2, fAllocator)->setCubicType(cubicType);
+                    } else {
+                        fCurrentContour->addCubic(pointsPtr, fAllocator);
+                    }
+                } else {
+                    fCurrentContour->addCubic(pointsPtr, fAllocator);
+                }
+                } break;
             case SkPath::kClose_Verb:
                 SkASSERT(fCurrentContour);
                 if (!close()) {
@@ -198,10 +240,11 @@
                 SkDEBUGFAIL("bad verb");
                 return false;
         }
-        pointsPtr += SkPathOpsVerbToPoints(verb);
         SkASSERT(fCurrentContour);
+        fCurrentContour->debugValidate();
+        pointsPtr += SkPathOpsVerbToPoints(verb);
     }
-   if (fCurrentContour && !fAllowOpenContours && !close()) {
+   if (fCurrentContour && fCurrentContour->count() &&!fAllowOpenContours && !close()) {
        return false;
    }
    return true;
diff --git a/src/pathops/SkOpEdgeBuilder.h b/src/pathops/SkOpEdgeBuilder.h
index fd07445..f81f727 100644
--- a/src/pathops/SkOpEdgeBuilder.h
+++ b/src/pathops/SkOpEdgeBuilder.h
@@ -9,20 +9,25 @@
 
 #include "SkOpContour.h"
 #include "SkPathWriter.h"
-#include "SkTArray.h"
 
 class SkOpEdgeBuilder {
 public:
-    SkOpEdgeBuilder(const SkPathWriter& path, SkTArray<SkOpContour>& contours)
-        : fPath(path.nativePath())
-        , fContours(contours)
+    SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkChunkAlloc* allocator,
+            SkOpGlobalState* globalState)
+        : fAllocator(allocator)  // FIXME: replace with const, tune this
+        , fGlobalState(globalState)
+        , fPath(path.nativePath())
+        , fContoursHead(contours2)
         , fAllowOpenContours(true) {
         init();
     }
 
-    SkOpEdgeBuilder(const SkPath& path, SkTArray<SkOpContour>& contours)
-        : fPath(&path)
-        , fContours(contours)
+    SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkChunkAlloc* allocator,
+            SkOpGlobalState* globalState)
+        : fAllocator(allocator)
+        , fGlobalState(globalState)
+        , fPath(&path)
+        , fContoursHead(contours2)
         , fAllowOpenContours(false) {
         init();
     }
@@ -30,13 +35,19 @@
     void addOperand(const SkPath& path);
 
     void complete() {
-        if (fCurrentContour && fCurrentContour->segments().count()) {
+        if (fCurrentContour && fCurrentContour->count()) {
             fCurrentContour->complete();
             fCurrentContour = NULL;
         }
     }
 
-    bool finish();
+    int count() const;
+    bool finish(SkChunkAlloc* );
+
+    const SkOpContour* head() const {
+        return fContoursHead;
+    }
+
     void init();
     bool unparseable() const { return fUnparseable; }
     SkPathOpsMask xorMask() const { return fXorMask[fOperand]; }
@@ -45,13 +56,16 @@
     void closeContour(const SkPoint& curveEnd, const SkPoint& curveStart);
     bool close();
     int preFetch();
-    bool walk();
+    bool walk(SkChunkAlloc* );
 
+    SkChunkAlloc* fAllocator;
+    SkOpGlobalState* fGlobalState;
     const SkPath* fPath;
-    SkTArray<SkPoint, true> fPathPts;
-    SkTArray<uint8_t, true> fPathVerbs;
+    SkTDArray<SkPoint> fPathPts;
+    SkTDArray<SkScalar> fWeights;
+    SkTDArray<uint8_t> fPathVerbs;
     SkOpContour* fCurrentContour;
-    SkTArray<SkOpContour>& fContours;
+    SkOpContour* fContoursHead;
     SkPathOpsMask fXorMask[2];
     int fSecondHalf;
     bool fOperand;
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 1fb5afa..8bd0f03 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -4,11 +4,20 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-#include "SkIntersections.h"
+#include "SkOpCoincidence.h"
 #include "SkOpContour.h"
 #include "SkOpSegment.h"
 #include "SkPathWriter.h"
-#include "SkTSort.h"
+
+/*
+After computing raw intersections, post process all segments to:
+- find small collections of points that can be collapsed to a single point
+- find missing intersections to resolve differences caused by different algorithms
+
+Consider segments containing tiny or small intervals. Consider coincident segments
+because coincidence finds intersections through distance measurement that non-coincident
+intersection tests cannot.
+ */
 
 #define F (false)      // discard the edge
 #define T (true)       // keep the edge
@@ -19,7 +28,7 @@
     {F, T}, {T, F},
 };
 
-static const bool gActiveEdge[kXOR_PathOp + 1][2][2][2][2] = {
+static const bool gActiveEdge[kXOR_SkPathOp + 1][2][2][2][2] = {
 //                 miFrom=0                              miFrom=1
 //         miTo=0             miTo=1             miTo=0             miTo=1
 //     suFrom=0    1      suFrom=0    1      suFrom=0    1      suFrom=0    1
@@ -33,147 +42,84 @@
 #undef F
 #undef T
 
-enum {
-    kOutsideTrackedTCount = 16,  // FIXME: determine what this should be
-    kMissingSpanCount = 4,  // FIXME: determine what this should be
-};
-
-const SkOpAngle* SkOpSegment::activeAngle(int index, int* start, int* end, bool* done,
-        bool* sortable) const {
-    if (const SkOpAngle* result = activeAngleInner(index, start, end, done, sortable)) {
+SkOpAngle* SkOpSegment::activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr,
+        SkOpSpanBase** endPtr, bool* done) {
+    if (SkOpAngle* result = activeAngleInner(start, startPtr, endPtr, done)) {
         return result;
     }
-    double referenceT = fTs[index].fT;
-    int lesser = index;
-    while (--lesser >= 0
-            && (precisely_negative(referenceT - fTs[lesser].fT) || fTs[lesser].fTiny)) {
-        if (const SkOpAngle* result = activeAngleOther(lesser, start, end, done, sortable)) {
-            return result;
-        }
+    if (SkOpAngle* result = activeAngleOther(start, startPtr, endPtr, done)) {
+        return result;
     }
-    do {
-        if (const SkOpAngle* result = activeAngleOther(index, start, end, done, sortable)) {
-            return result;
-        }
-        if (++index == fTs.count()) {
-            break;
-        }
-        if (fTs[index - 1].fTiny) {
-            referenceT = fTs[index].fT;
-            continue;
-        }
-    } while (precisely_negative(fTs[index].fT - referenceT));
     return NULL;
 }
 
-const SkOpAngle* SkOpSegment::activeAngleInner(int index, int* start, int* end, bool* done,
-        bool* sortable) const {
-    int next = nextExactSpan(index, 1);
-    if (next > 0) {
-        const SkOpSpan& upSpan = fTs[index];
-        if (upSpan.fWindValue || upSpan.fOppValue) {
-            if (*end < 0) {
-                *start = index;
-                *end = next;
+SkOpAngle* SkOpSegment::activeAngleInner(SkOpSpanBase* start, SkOpSpanBase** startPtr,
+        SkOpSpanBase** endPtr, bool* done) {
+    SkOpSpan* upSpan = start->upCastable();
+    if (upSpan) {
+        if (upSpan->windValue() || upSpan->oppValue()) {
+            SkOpSpanBase* next = upSpan->next();
+            if (!*endPtr) {
+                *startPtr = start;
+                *endPtr = next;
             }
-            if (!upSpan.fDone) {
-                if (upSpan.fWindSum != SK_MinS32) {
-                    return spanToAngle(index, next);
+            if (!upSpan->done()) {
+                if (upSpan->windSum() != SK_MinS32) {
+                    return spanToAngle(start, next);
                 }
                 *done = false;
             }
         } else {
-            SkASSERT(upSpan.fDone);
+            SkASSERT(upSpan->done());
         }
     }
-    int prev = nextExactSpan(index, -1);
+    SkOpSpan* downSpan = start->prev();
     // edge leading into junction
-    if (prev >= 0) {
-        const SkOpSpan& downSpan = fTs[prev];
-        if (downSpan.fWindValue || downSpan.fOppValue) {
-            if (*end < 0) {
-                *start = index;
-                *end = prev;
+    if (downSpan) {
+        if (downSpan->windValue() || downSpan->oppValue()) {
+            if (!*endPtr) {
+                *startPtr = start;
+                *endPtr = downSpan;
             }
-            if (!downSpan.fDone) {
-                if (downSpan.fWindSum != SK_MinS32) {
-                    return spanToAngle(index, prev);
+            if (!downSpan->done()) {
+                if (downSpan->windSum() != SK_MinS32) {
+                    return spanToAngle(start, downSpan);
                 }
                 *done = false;
             }
         } else {
-            SkASSERT(downSpan.fDone);
+            SkASSERT(downSpan->done());
         }
     }
     return NULL;
 }
 
-const SkOpAngle* SkOpSegment::activeAngleOther(int index, int* start, int* end, bool* done,
-        bool* sortable) const {
-    const SkOpSpan* span = &fTs[index];
-    SkOpSegment* other = span->fOther;
-    int oIndex = span->fOtherIndex;
-    return other->activeAngleInner(oIndex, start, end, done, sortable);
+SkOpAngle* SkOpSegment::activeAngleOther(SkOpSpanBase* start, SkOpSpanBase** startPtr,
+        SkOpSpanBase** endPtr, bool* done) {
+    SkOpPtT* oPtT = start->ptT()->next();
+    SkOpSegment* other = oPtT->segment();
+    SkOpSpanBase* oSpan = oPtT->span();
+    return other->activeAngleInner(oSpan, startPtr, endPtr, done);
 }
 
-SkPoint SkOpSegment::activeLeftTop(int* firstT) const {
-    SkASSERT(!done());
-    SkPoint topPt = {SK_ScalarMax, SK_ScalarMax};
-    int count = fTs.count();
-    // see if either end is not done since we want smaller Y of the pair
-    bool lastDone = true;
-    double lastT = -1;
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = fTs[index];
-        if (span.fDone && lastDone) {
-            goto next;
-        }
-        if (approximately_negative(span.fT - lastT)) {
-            goto next;
-        }
-        {
-            const SkPoint& xy = xyAtT(&span);
-            if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) {
-                topPt = xy;
-                if (firstT) {
-                    *firstT = index;
-                }
-            }
-            if (fVerb != SkPath::kLine_Verb && !lastDone) {
-                SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
-                if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
-                        && topPt.fX > curveTop.fX)) {
-                    topPt = curveTop;
-                    if (firstT) {
-                        *firstT = index;
-                    }
-                }
-            }
-            lastT = span.fT;
-        }
-next:
-        lastDone = span.fDone;
-    }
-    return topPt;
-}
-
-bool SkOpSegment::activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op) {
-    int sumMiWinding = updateWinding(endIndex, index);
-    int sumSuWinding = updateOppWinding(endIndex, index);
+bool SkOpSegment::activeOp(SkOpSpanBase* start, SkOpSpanBase* end, int xorMiMask, int xorSuMask,
+        SkPathOp op) {
+    int sumMiWinding = this->updateWinding(end, start);
+    int sumSuWinding = this->updateOppWinding(end, start);
 #if DEBUG_LIMIT_WIND_SUM
     SkASSERT(abs(sumMiWinding) <= DEBUG_LIMIT_WIND_SUM);
     SkASSERT(abs(sumSuWinding) <= DEBUG_LIMIT_WIND_SUM);
 #endif
-    if (fOperand) {
+    if (this->operand()) {
         SkTSwap<int>(sumMiWinding, sumSuWinding);
     }
-    return activeOp(xorMiMask, xorSuMask, index, endIndex, op, &sumMiWinding, &sumSuWinding);
+    return this->activeOp(xorMiMask, xorSuMask, start, end, op, &sumMiWinding, &sumSuWinding);
 }
 
-bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
-        int* sumMiWinding, int* sumSuWinding) {
+bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end,
+        SkPathOp op, int* sumMiWinding, int* sumSuWinding) {
     int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
-    setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
+    this->setUpWindings(start, end, sumMiWinding, sumSuWinding,
             &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
     bool miFrom;
     bool miTo;
@@ -193,186 +139,42 @@
     bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
 #if DEBUG_ACTIVE_OP
     SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
-            __FUNCTION__, debugID(), span(index).fT, span(endIndex).fT,
+            __FUNCTION__, debugID(), start->t(), end->t(),
             SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
 #endif
     return result;
 }
 
-bool SkOpSegment::activeWinding(int index, int endIndex) {
-    int sumWinding = updateWinding(endIndex, index);
-    return activeWinding(index, endIndex, &sumWinding);
+bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end) {
+    int sumWinding = updateWinding(end, start);
+    return activeWinding(start, end, &sumWinding);
 }
 
-bool SkOpSegment::activeWinding(int index, int endIndex, int* sumWinding) {
+bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding) {
     int maxWinding;
-    setUpWinding(index, endIndex, &maxWinding, sumWinding);
+    setUpWinding(start, end, &maxWinding, sumWinding);
     bool from = maxWinding != 0;
     bool to = *sumWinding  != 0;
     bool result = gUnaryActiveEdge[from][to];
     return result;
 }
 
-void SkOpSegment::addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt,
-        SkOpSegment* other) {
-    int tIndex = -1;
-    int tCount = fTs.count();
-    int oIndex = -1;
-    int oCount = other->fTs.count();
-    do {
-        ++tIndex;
-    } while (startPt != fTs[tIndex].fPt && tIndex < tCount);
-    int tIndexStart = tIndex;
-    do {
-        ++oIndex;
-    } while (endPt != other->fTs[oIndex].fPt && oIndex < oCount);
-    int oIndexStart = oIndex;
-    const SkPoint* nextPt;
-    do {
-        nextPt = &fTs[++tIndex].fPt;
-        SkASSERT(fTs[tIndex].fT < 1 || startPt != *nextPt);
-    } while (startPt == *nextPt);
-    double nextT = fTs[tIndex].fT;
-    const SkPoint* oNextPt;
-    do {
-        oNextPt = &other->fTs[++oIndex].fPt;
-        SkASSERT(other->fTs[oIndex].fT < 1 || endPt != *oNextPt);
-    } while (endPt == *oNextPt);
-    double oNextT = other->fTs[oIndex].fT;
-    // at this point, spans before and after are at:
-    //  fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
-    // if tIndexStart == 0, no prior span
-    // if nextT == 1, no following span
-
-    // advance the span with zero winding
-    // if the following span exists (not past the end, non-zero winding)
-    // connect the two edges
-    if (!fTs[tIndexStart].fWindValue) {
-        if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
-#if DEBUG_CONCIDENT
-            SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
-                    __FUNCTION__, fID, other->fID, tIndexStart - 1,
-                    fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
-                    xyAtT(tIndexStart).fY);
-#endif
-            SkPoint copy = fTs[tIndexStart].fPt;  // add t pair may move the point array
-            addTPair(fTs[tIndexStart].fT, other, other->fTs[oIndex].fT, false, copy);
-        }
-        if (nextT < 1 && fTs[tIndex].fWindValue) {
-#if DEBUG_CONCIDENT
-            SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
-                    __FUNCTION__, fID, other->fID, tIndex,
-                    fTs[tIndex].fT, xyAtT(tIndex).fX,
-                    xyAtT(tIndex).fY);
-#endif
-            SkPoint copy = fTs[tIndex].fPt;  // add t pair may move the point array
-            addTPair(fTs[tIndex].fT, other, other->fTs[oIndexStart].fT, false, copy);
-        }
-    } else {
-        SkASSERT(!other->fTs[oIndexStart].fWindValue);
-        if (oIndexStart > 0 && other->fTs[oIndexStart - 1].fWindValue) {
-#if DEBUG_CONCIDENT
-            SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
-                    __FUNCTION__, fID, other->fID, oIndexStart - 1,
-                    other->fTs[oIndexStart].fT, other->xyAtT(oIndexStart).fX,
-                    other->xyAtT(oIndexStart).fY);
-            other->debugAddTPair(other->fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
-#endif
-        }
-        if (oNextT < 1 && other->fTs[oIndex].fWindValue) {
-#if DEBUG_CONCIDENT
-            SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
-                    __FUNCTION__, fID, other->fID, oIndex,
-                    other->fTs[oIndex].fT, other->xyAtT(oIndex).fX,
-                    other->xyAtT(oIndex).fY);
-            other->debugAddTPair(other->fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
-#endif
-        }
-    }
-}
-
-void SkOpSegment::addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt,
-        SkOpSegment* other) {
-    // walk this to startPt
-    // walk other to startPt
-    // if either is > 0, add a pointer to the other, copying adjacent winding
-    int tIndex = -1;
-    int oIndex = -1;
-    do {
-        ++tIndex;
-    } while (startPt != fTs[tIndex].fPt);
-    int ttIndex = tIndex;
-    bool checkOtherTMatch = false;
-    do {
-        const SkOpSpan& span = fTs[ttIndex];
-        if (startPt != span.fPt) {
-            break;
-        }
-        if (span.fOther == other && span.fPt == startPt) {
-            checkOtherTMatch = true;
-            break;
-        }
-    } while (++ttIndex < count());
-    do {
-        ++oIndex;
-    } while (startPt != other->fTs[oIndex].fPt);
-    bool skipAdd = false;
-    if (checkOtherTMatch) {
-        int ooIndex = oIndex;
-        do {
-            const SkOpSpan& oSpan = other->fTs[ooIndex];
-            if (startPt != oSpan.fPt) {
-                break;
-            }
-            if (oSpan.fT == fTs[ttIndex].fOtherT) {
-                skipAdd = true;
-                break;
-            }
-        } while (++ooIndex < other->count());
-    }
-    if ((tIndex > 0 || oIndex > 0 || fOperand != other->fOperand) && !skipAdd) {
-        addTPair(fTs[tIndex].fT, other, other->fTs[oIndex].fT, false, startPt);
-    }
-    SkPoint nextPt = startPt;
-    do {
-        const SkPoint* workPt;
-        do {
-            workPt = &fTs[++tIndex].fPt;
-        } while (nextPt == *workPt);
-        const SkPoint* oWorkPt;
-        do {
-            oWorkPt = &other->fTs[++oIndex].fPt;
-        } while (nextPt == *oWorkPt);
-        nextPt = *workPt;
-        double tStart = fTs[tIndex].fT;
-        double oStart = other->fTs[oIndex].fT;
-        if (tStart == 1 && oStart == 1 && fOperand == other->fOperand) {
-            break;
-        }
-        if (*workPt == *oWorkPt) {
-            addTPair(tStart, other, oStart, false, nextPt);
-        }
-    } while (endPt != nextPt);
-}
-
-void SkOpSegment::addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
-    init(pts, SkPath::kCubic_Verb, operand, evenOdd);
-    fBounds.setCubicBounds(pts);
-}
-
-void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active) const {
-    SkPoint edge[4];
+void SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
+        SkPathWriter* path, bool active) const {
+    SkOpCurve edge;
     const SkPoint* ePtr;
-    int lastT = fTs.count() - 1;
-    if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
+    SkScalar eWeight;
+    if ((start == &fHead && end == &fTail) || (start == &fTail && end == &fHead)) {
         ePtr = fPts;
+        eWeight = fWeight;
     } else {
     // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
-        subDivide(start, end, edge);
-        ePtr = edge;
+        subDivide(start, end, &edge);
+        ePtr = edge.fPts;
+        eWeight = edge.fWeight;
     }
     if (active) {
-        bool reverse = ePtr == fPts && start != 0;
+        bool reverse = ePtr == fPts && start != &fHead;
         if (reverse) {
             path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
             switch (fVerb) {
@@ -382,13 +184,15 @@
                 case SkPath::kQuad_Verb:
                     path->quadTo(ePtr[1], ePtr[0]);
                     break;
+                case SkPath::kConic_Verb:
+                    path->conicTo(ePtr[1], ePtr[0], eWeight);
+                    break;
                 case SkPath::kCubic_Verb:
                     path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
                     break;
                 default:
                     SkASSERT(0);
             }
-   //         return ePtr[0];
        } else {
             path->deferredMoveLine(ePtr[0]);
             switch (fVerb) {
@@ -398,6 +202,9 @@
                 case SkPath::kQuad_Verb:
                     path->quadTo(ePtr[1], ePtr[2]);
                     break;
+                case SkPath::kConic_Verb:
+                    path->conicTo(ePtr[1], ePtr[2], eWeight);
+                    break;
                 case SkPath::kCubic_Verb:
                     path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
                     break;
@@ -406,1473 +213,231 @@
             }
         }
     }
-  //  return ePtr[SkPathOpsVerbToPoints(fVerb)];
 }
 
-void SkOpSegment::addEndSpan(int endIndex) {
-    SkASSERT(span(endIndex).fT == 1 || (span(endIndex).fTiny
-//            && approximately_greater_than_one(span(endIndex).fT)
-    ));
-    int spanCount = fTs.count();
-    int startIndex = endIndex - 1;
-    while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
-        --startIndex;
-        SkASSERT(startIndex > 0);
-        --endIndex;
-    }
-    SkOpAngle& angle = fAngles.push_back();
-    angle.set(this, spanCount - 1, startIndex);
-#if DEBUG_ANGLE
-    debugCheckPointsEqualish(endIndex, spanCount);
-#endif
-    setFromAngle(endIndex, &angle);
-}
-
-void SkOpSegment::setFromAngle(int endIndex, SkOpAngle* angle) {
-    int spanCount = fTs.count();
+SkOpPtT* SkOpSegment::addMissing(double t, SkOpSegment* opp, SkChunkAlloc* allocator) {
+    SkOpSpanBase* existing = NULL;
+    SkOpSpanBase* test = &fHead;
+    double testT;
     do {
-        fTs[endIndex].fFromAngle = angle;
-    } while (++endIndex < spanCount);
-}
-
-void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
-    init(pts, SkPath::kLine_Verb, operand, evenOdd);
-    fBounds.set(pts, 2);
-}
-
-// add 2 to edge or out of range values to get T extremes
-void SkOpSegment::addOtherT(int index, double otherT, int otherIndex) {
-    SkOpSpan& span = fTs[index];
-    if (precisely_zero(otherT)) {
-        otherT = 0;
-    } else if (precisely_equal(otherT, 1)) {
-        otherT = 1;
-    }
-    span.fOtherT = otherT;
-    span.fOtherIndex = otherIndex;
-}
-
-void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
-    init(pts, SkPath::kQuad_Verb, operand, evenOdd);
-    fBounds.setQuadBounds(pts);
-}
-
-SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
-    int spanIndex = count() - 1;
-    int startIndex = nextExactSpan(spanIndex, -1);
-    SkASSERT(startIndex >= 0);
-    SkOpAngle& angle = fAngles.push_back();
-    *anglePtr = &angle;
-    angle.set(this, spanIndex, startIndex);
-    setFromAngle(spanIndex, &angle);
-    SkOpSegment* other;
-    int oStartIndex, oEndIndex;
-    do {
-        const SkOpSpan& span = fTs[spanIndex];
-        SkASSERT(span.fT > 0);
-        other = span.fOther;
-        oStartIndex = span.fOtherIndex;
-        oEndIndex = other->nextExactSpan(oStartIndex, 1);
-        if (oEndIndex > 0 && other->span(oStartIndex).fWindValue) {
+        if ((testT = test->ptT()->fT) >= t) {
+            if (testT == t) {
+                existing = test;
+            }
             break;
         }
-        oEndIndex = oStartIndex;
-        oStartIndex = other->nextExactSpan(oEndIndex, -1);
-        --spanIndex;
-    } while (oStartIndex < 0 || !other->span(oStartIndex).fWindSum);
-    SkOpAngle& oAngle = other->fAngles.push_back();
-    oAngle.set(other, oStartIndex, oEndIndex);
-    other->setToAngle(oEndIndex, &oAngle);
-    *otherPtr = other;
-    return &oAngle;
-}
-
-SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
-    int endIndex = nextExactSpan(0, 1);
-    SkASSERT(endIndex > 0);
-    SkOpAngle& angle = fAngles.push_back();
-    *anglePtr = &angle;
-    angle.set(this, 0, endIndex);
-    setToAngle(endIndex, &angle);
-    int spanIndex = 0;
-    SkOpSegment* other;
-    int oStartIndex, oEndIndex;
-    do {
-        const SkOpSpan& span = fTs[spanIndex];
-        SkASSERT(span.fT < 1);
-        other = span.fOther;
-        oEndIndex = span.fOtherIndex;
-        oStartIndex = other->nextExactSpan(oEndIndex, -1);
-        if (oStartIndex >= 0 && other->span(oStartIndex).fWindValue) {
-            break;
-        }
-        oStartIndex = oEndIndex;
-        oEndIndex = other->nextExactSpan(oStartIndex, 1);
-        ++spanIndex;
-    } while (oEndIndex < 0 || !other->span(oStartIndex).fWindValue);
-    SkOpAngle& oAngle = other->fAngles.push_back();
-    oAngle.set(other, oEndIndex, oStartIndex);
-    other->setFromAngle(oEndIndex, &oAngle);
-    *otherPtr = other;
-    return &oAngle;
-}
-
-SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
-    SkOpSegment* other;
-    SkOpAngle* angle, * otherAngle;
-    if (step > 0) {
-        otherAngle = addSingletonAngleUp(&other, &angle);
+    } while ((test = test->upCast()->next()));
+    SkOpPtT* result;
+    if (existing && existing->contains(opp)) {
+        result = existing->ptT();
     } else {
-        otherAngle = addSingletonAngleDown(&other, &angle);
+        result = this->addT(t, SkOpSegment::kNoAlias, allocator);
     }
-    angle->insert(otherAngle);
-    return angle;
-}
-
-void SkOpSegment::addStartSpan(int endIndex) {
-    int index = 0;
-    SkOpAngle& angle = fAngles.push_back();
-    angle.set(this, index, endIndex);
-#if DEBUG_ANGLE
-    debugCheckPointsEqualish(index, endIndex);
-#endif
-    setToAngle(endIndex, &angle);
-}
-
-void SkOpSegment::setToAngle(int endIndex, SkOpAngle* angle) {
-    int index = 0;
-    do {
-        fTs[index].fToAngle = angle;
-    } while (++index < endIndex);
-}
-
-    // Defer all coincident edge processing until
-    // after normal intersections have been computed
-
-// no need to be tricky; insert in normal T order
-// resolve overlapping ts when considering coincidence later
-
-    // add non-coincident intersection. Resulting edges are sorted in T.
-int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
-    SkASSERT(this != other || fVerb == SkPath::kCubic_Verb);
- #if 0  // this needs an even rougher association to be useful
-    SkASSERT(SkDPoint::RoughlyEqual(ptAtT(newT), pt));
- #endif
-    const SkPoint& firstPt = fPts[0];
-    const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
-    SkASSERT(newT == 0 || !precisely_zero(newT));
-    SkASSERT(newT == 1 || !precisely_equal(newT, 1));
-    // FIXME: in the pathological case where there is a ton of intercepts,
-    //  binary search?
-    int insertedAt = -1;
-    int tCount = fTs.count();
-    for (int index = 0; index < tCount; ++index) {
-        // OPTIMIZATION: if there are three or more identical Ts, then
-        // the fourth and following could be further insertion-sorted so
-        // that all the edges are clockwise or counterclockwise.
-        // This could later limit segment tests to the two adjacent
-        // neighbors, although it doesn't help with determining which
-        // circular direction to go in.
-        const SkOpSpan& span = fTs[index];
-        if (newT < span.fT) {
-            insertedAt = index;
-            break;
-        }
-        if (newT == span.fT) {
-            if (pt == span.fPt) {
-                insertedAt = index;
-                break;
-            }
-            if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
-                insertedAt = index;
-                break;
-            }
-        }
-    }
-    SkOpSpan* span;
-    if (insertedAt >= 0) {
-        span = fTs.insert(insertedAt);
-    } else {
-        insertedAt = tCount;
-        span = fTs.append();
-    }
-    span->fT = newT;
-    span->fOtherT = -1;
-    span->fOther = other;
-    span->fPt = pt;
-#if 0
-    // cubics, for instance, may not be exact enough to satisfy this check (e.g., cubicOp69d)
-    SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
-            && approximately_equal(xyAtT(newT).fY, pt.fY));
-#endif
-    span->fFromAngle = NULL;
-    span->fToAngle = NULL;
-    span->fWindSum = SK_MinS32;
-    span->fOppSum = SK_MinS32;
-    span->fWindValue = 1;
-    span->fOppValue = 0;
-    span->fChased = false;
-    span->fCoincident = false;
-    span->fLoop = false;
-    span->fNear = false;
-    span->fMultiple = false;
-    span->fSmall = false;
-    span->fTiny = false;
-    if ((span->fDone = newT == 1)) {
-        ++fDoneSpans;
-    }
-    setSpanFlags(pt, newT, span);
-    return insertedAt;
-}
-
-void SkOpSegment::setSpanFlags(const SkPoint& pt, double newT, SkOpSpan* span) {
-    int less = -1;
-// FIXME: note that this relies on spans being a continguous array
-// find range of spans with nearly the same point as this one
-    // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
-    while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, pt)) {
-        if (fVerb == SkPath::kCubic_Verb) {
-            double tInterval = newT - span[less].fT;
-            double tMid = newT - tInterval / 2;
-            SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
-            if (!midPt.approximatelyEqual(xyAtT(span))) {
-                break;
-            }
-        }
-        --less;
-    }
-    int more = 1;
-    // FIXME: SkDPoint::ApproximatelyEqual is better but breaks tests at the moment
-    while (fTs.end() - &span[more - 1] > 1 && AlmostEqualUlps(span[more].fPt, pt)) {
-        if (fVerb == SkPath::kCubic_Verb) {
-            double tEndInterval = span[more].fT - newT;
-            double tMid = newT - tEndInterval / 2;
-            SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
-            if (!midEndPt.approximatelyEqual(xyAtT(span))) {
-                break;
-            }
-        }
-        ++more;
-    }
-    ++less;
-    --more;
-    while (more - 1 > less && span[more].fPt == span[more - 1].fPt
-            && span[more].fT == span[more - 1].fT) {
-        --more;
-    }
-    if (less == more) {
-        return;
-    }
-    if (precisely_negative(span[more].fT - span[less].fT)) {
-        return;
-    }
-// if the total range of t values is big enough, mark all tiny
-    bool tiny = span[less].fPt == span[more].fPt;
-    int index = less;
-    do {
-        fSmall = span[index].fSmall = true;
-        fTiny |= span[index].fTiny = tiny;
-        if (!span[index].fDone) {
-            span[index].fDone = true;
-            ++fDoneSpans;
-        }
-    } while (++index < more);
-    return;
-}
-
-void SkOpSegment::resetSpanFlags() {
-    fSmall = fTiny = false;
-    fDoneSpans = 0;
-    int start = 0;
-    int last = this->count() - 1;
-    do {
-        SkOpSpan* startSpan = &this->fTs[start];
-        double startT = startSpan->fT;
-        startSpan->fSmall = startSpan->fTiny = false;  // sets range initial
-        bool terminus = startT == 1;
-        if ((startSpan->fDone = !startSpan->fWindValue | terminus)) {
-            ++fDoneSpans;
-        }
-        ++start;  // range initial + 1
-        if (terminus) {
-            continue;
-        }
-        const SkPoint& pt = startSpan->fPt;
-        int end = start;  // range initial + 1
-        while (end <= last) {
-            const SkOpSpan& endSpan = this->span(end);
-            if (!AlmostEqualUlps(endSpan.fPt, pt)) {
-                break;
-            }
-            if (fVerb == SkPath::kCubic_Verb) {
-                double tMid = (startSpan->fT + endSpan.fT) / 2;
-                SkDPoint midEndPt = dcubic_xy_at_t(fPts, tMid);
-                if (!midEndPt.approximatelyEqual(xyAtT(startSpan))) {
-                    break;
-                }
-            }
-            ++end;
-        }
-        if (start == end) {  // end == range final + 1
-            continue;
-        }
-        while (--end >= start) {  // end == range final
-            const SkOpSpan& endSpan = this->span(end);
-            const SkOpSpan& priorSpan = this->span(end - 1);
-            if (endSpan.fPt != priorSpan.fPt || endSpan.fT != priorSpan.fT) {
-                break;  // end == range final + 1
-            }
-        }
-        if (end < start) {  // end == range final + 1
-            continue;
-        }
-        int index = start - 1;  // index == range initial
-        start = end;  // start = range final + 1
-        const SkOpSpan& nextSpan = this->span(end);
-        if (precisely_negative(nextSpan.fT - startSpan->fT)) {
-            while (++index < end) {
-                startSpan = &this->fTs[index];
-                startSpan->fSmall = startSpan->fTiny = false;  // sets range initial + 1
-                if ((startSpan->fDone = !startSpan->fWindValue)) {
-                    ++fDoneSpans;
-                }
-            }
-            continue;
-        }
-        if (!startSpan->fWindValue) {
-            --fDoneSpans;  // added back below
-        }
-        bool tiny = nextSpan.fPt == startSpan->fPt;
-        do {
-            fSmall = startSpan->fSmall = true;  // sets range initial
-            fTiny |= startSpan->fTiny = tiny;
-            startSpan->fDone = true;
-            ++fDoneSpans;
-            startSpan = &this->fTs[++index];
-        } while (index < end);  // loop through tiny small range end (last)
-    } while (start <= last);
-}
-
-// set spans from start to end to decrement by one
-// note this walks other backwards
-// FIXME: there's probably an edge case that can be constructed where
-// two span in one segment are separated by float epsilon on one span but
-// not the other, if one segment is very small. For this
-// case the counts asserted below may or may not be enough to separate the
-// spans. Even if the counts work out, what if the spans aren't correctly
-// sorted? It feels better in such a case to match the span's other span
-// pointer since both coincident segments must contain the same spans.
-// FIXME? It seems that decrementing by one will fail for complex paths that
-// have three or more coincident edges. Shouldn't this subtract the difference
-// between the winding values?
-/*                                      |-->                           |-->
-this     0>>>>1>>>>2>>>>3>>>4      0>>>>1>>>>2>>>>3>>>4      0>>>>1>>>>2>>>>3>>>4
-other         2<<<<1<<<<0               2<<<<1<<<<0               2<<<<1<<<<0
-              ^         ^                 <--|                           <--|
-           startPt    endPt        test/oTest first pos      test/oTest final pos
-*/
-void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other) {
-    bool binary = fOperand != other->fOperand;
-    int index = 0;
-    while (startPt != fTs[index].fPt) {
-        SkASSERT(index < fTs.count());
-        ++index;
-    }
-    while (index > 0 && precisely_equal(fTs[index].fT, fTs[index - 1].fT)) {
-        --index;
-    }
-    bool oFoundEnd = false;
-    int oIndex = other->fTs.count();
-    while (startPt != other->fTs[--oIndex].fPt) {  // look for startPt match
-        SkASSERT(oIndex > 0);
-    }
-    double oStartT = other->fTs[oIndex].fT;
-    // look for first point beyond match
-    while (startPt == other->fTs[--oIndex].fPt || precisely_equal(oStartT, other->fTs[oIndex].fT)) {
-        if (!oIndex) {
-            return;  // tiny spans may move in the wrong direction
-        }
-    }
-    SkOpSpan* test = &fTs[index];
-    SkOpSpan* oTest = &other->fTs[oIndex];
-    SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
-    SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
-    bool decrement, track, bigger;
-    int originalWindValue;
-    const SkPoint* testPt;
-    const SkPoint* oTestPt;
-    do {
-        SkASSERT(test->fT < 1);
-        SkASSERT(oTest->fT < 1);
-        decrement = test->fWindValue && oTest->fWindValue;
-        track = test->fWindValue || oTest->fWindValue;
-        bigger = test->fWindValue >= oTest->fWindValue;
-        testPt = &test->fPt;
-        double testT = test->fT;
-        oTestPt = &oTest->fPt;
-        double oTestT = oTest->fT;
-        do {
-            if (decrement) {
-                if (binary && bigger) {
-                    test->fOppValue--;
-                } else {
-                    decrementSpan(test);
-                }
-            } else if (track) {
-                TrackOutsidePair(&outsidePts, *testPt, *oTestPt);
-            }
-            SkASSERT(index < fTs.count() - 1);
-            test = &fTs[++index];
-        } while (*testPt == test->fPt || precisely_equal(testT, test->fT));
-        originalWindValue = oTest->fWindValue;
-        do {
-            SkASSERT(oTest->fT < 1);
-            SkASSERT(originalWindValue == oTest->fWindValue);
-            if (decrement) {
-                if (binary && !bigger) {
-                    oTest->fOppValue--;
-                } else {
-                    other->decrementSpan(oTest);
-                }
-            } else if (track) {
-                TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
-            }
-            if (!oIndex) {
-                break;
-            }
-            oFoundEnd |= endPt == oTest->fPt;
-            oTest = &other->fTs[--oIndex];
-        } while (*oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT));
-    } while (endPt != test->fPt && test->fT < 1);
-    // FIXME: determine if canceled edges need outside ts added
-    if (!oFoundEnd) {
-        for (int oIdx2 = oIndex; oIdx2 >= 0; --oIdx2) {
-            SkOpSpan* oTst2 = &other->fTs[oIdx2];            
-            if (originalWindValue != oTst2->fWindValue) {
-                goto skipAdvanceOtherCancel;
-            }
-            if (!oTst2->fWindValue) {
-                goto skipAdvanceOtherCancel;
-            }
-            if (endPt == other->fTs[oIdx2].fPt) {
-                break;
-            }
-        }
-        oFoundEnd = endPt == oTest->fPt;
-        do {
-            SkASSERT(originalWindValue == oTest->fWindValue);
-            if (decrement) {
-                if (binary && !bigger) {
-                    oTest->fOppValue--;
-                } else {
-                    other->decrementSpan(oTest);
-                }
-            } else if (track) {
-                TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
-            }
-            if (!oIndex) {
-                break;
-            }
-            oTest = &other->fTs[--oIndex];
-            oFoundEnd |= endPt == oTest->fPt;
-        } while (!oFoundEnd || endPt == oTest->fPt);
-    }
-skipAdvanceOtherCancel:
-    int outCount = outsidePts.count();
-    if (!done() && outCount) {
-        addCancelOutsides(outsidePts[0], outsidePts[1], other);
-        if (outCount > 2) {
-            addCancelOutsides(outsidePts[outCount - 2], outsidePts[outCount - 1], other);
-        }
-    }
-    if (!other->done() && oOutsidePts.count()) {
-        other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
-    }
-    setCoincidentRange(startPt, endPt, other);
-    other->setCoincidentRange(startPt, endPt, this);
-}
-
-int SkOpSegment::addSelfT(const SkPoint& pt, double newT) {
-    // if the tail nearly intersects itself but not quite, the caller records this separately
-    int result = addT(this, pt, newT);
-    SkOpSpan* span = &fTs[result];
-    fLoop = span->fLoop = true;
+    SkASSERT(result);
     return result;
 }
 
-// find the starting or ending span with an existing loop of angles
-// FIXME? replicate for all identical starting/ending spans?
-// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
-// FIXME? assert that only one other span has a valid windValue or oppValue
-void SkOpSegment::addSimpleAngle(int index) {
-    SkOpSpan* span = &fTs[index];
-    int idx;
-    int start, end;
-    if (span->fT == 0) {
-        idx = 0;
-        span = &fTs[0];
-        do {
-            if (span->fToAngle) {
-                SkASSERT(span->fToAngle->loopCount() == 2);
-                SkASSERT(!span->fFromAngle);
-                span->fFromAngle = span->fToAngle->next();
-                return;
-            }
-            span = &fTs[++idx];
-        } while (span->fT == 0);
-        SkASSERT(!fTs[0].fTiny && fTs[idx].fT > 0);
-        addStartSpan(idx);
-        start = 0;
-        end = idx;
-    } else {
-        idx = count() - 1;
-        span = &fTs[idx];
-        do {
-            if (span->fFromAngle) {
-                SkASSERT(span->fFromAngle->loopCount() == 2);
-                SkASSERT(!span->fToAngle);
-                span->fToAngle = span->fFromAngle->next();
-                return;
-            }
-            span = &fTs[--idx];
-        } while (span->fT == 1);
-        SkASSERT(!fTs[idx].fTiny && fTs[idx].fT < 1);
-        addEndSpan(++idx);
-        start = idx;
-        end = count();
-    }
-    SkOpSegment* other;
-    SkOpSpan* oSpan;
-    index = start;
+SkOpPtT* SkOpSegment::addT(double t, AllowAlias allowAlias, SkChunkAlloc* allocator) {
+    debugValidate();
+    SkPoint pt = this->ptAtT(t);
+    SkOpSpanBase* span = &fHead;
     do {
-        span = &fTs[index];
-        other = span->fOther;
-        int oFrom = span->fOtherIndex;
-        oSpan = &other->fTs[oFrom];
-        if (oSpan->fT < 1 && oSpan->fWindValue) {
-            break;
+        SkOpPtT* result = span->ptT();
+        SkOpPtT* loop;
+        bool duplicatePt;
+        if (t == result->fT) {
+            goto bumpSpan;
         }
-        if (oSpan->fT == 0) {
-            continue;
-        }
-        oFrom = other->nextExactSpan(oFrom, -1);
-        SkOpSpan* oFromSpan = &other->fTs[oFrom];
-        SkASSERT(oFromSpan->fT < 1);
-        if (oFromSpan->fWindValue) {
-            break;
-        }
-    } while (++index < end);
-    SkOpAngle* angle, * oAngle;
-    if (span->fT == 0) {
-        SkASSERT(span->fOtherIndex - 1 >= 0);
-        SkASSERT(span->fOtherT == 1);
-        SkDEBUGCODE(int oPriorIndex = other->nextExactSpan(span->fOtherIndex, -1));
-        SkDEBUGCODE(const SkOpSpan& oPrior = other->span(oPriorIndex));
-        SkASSERT(!oPrior.fTiny && oPrior.fT < 1);
-        other->addEndSpan(span->fOtherIndex);
-        angle = span->fToAngle;
-        oAngle = oSpan->fFromAngle;
-    } else {
-        SkASSERT(span->fOtherIndex + 1 < other->count());
-        SkASSERT(span->fOtherT == 0);
-        SkASSERT(!oSpan->fTiny && (other->fTs[span->fOtherIndex + 1].fT > 0
-                || (other->fTs[span->fOtherIndex + 1].fFromAngle == NULL
-                && other->fTs[span->fOtherIndex + 1].fToAngle == NULL)));
-        int oIndex = 1;
-        do {
-            const SkOpSpan& osSpan = other->span(oIndex);
-            if (osSpan.fFromAngle || osSpan.fT > 0) {
-                break;
+        if (this->match(result, this, t, pt)) {
+            // see if any existing alias matches segment, pt, and t
+           loop = result->next();
+            duplicatePt = false;
+            while (loop != result) {
+                bool ptMatch = loop->fPt == pt;
+                if (loop->segment() == this && loop->fT == t && ptMatch) {
+                    goto bumpSpan;
+                }
+                duplicatePt |= ptMatch;
+                loop = loop->next();
             }
-            ++oIndex;
-            SkASSERT(oIndex < other->count());
-        } while (true);
-        other->addStartSpan(oIndex);
-        angle = span->fFromAngle;
-        oAngle = oSpan->fToAngle;
-    }
-    angle->insert(oAngle);
+            if (kNoAlias == allowAlias) {
+    bumpSpan:
+                span->bumpSpanAdds();
+                return result;
+            }
+            SkOpPtT* alias = SkOpTAllocator<SkOpPtT>::Allocate(allocator);
+            alias->init(result->span(), t, pt, duplicatePt);
+            result->insert(alias);
+            result->span()->unaligned();
+            this->debugValidate();
+#if DEBUG_ADD_T
+            SkDebugf("%s alias t=%1.9g segID=%d spanID=%d\n",  __FUNCTION__, t,
+                    alias->segment()->debugID(), alias->span()->debugID());
+#endif
+            span->bumpSpanAdds();
+            return alias;
+        }
+        if (t < result->fT) {
+            SkOpSpan* prev = result->span()->prev();
+            SkOpSpan* span = insert(prev, allocator);
+            span->init(this, prev, t, pt);
+            this->debugValidate();
+#if DEBUG_ADD_T
+            SkDebugf("%s insert t=%1.9g segID=%d spanID=%d\n", __FUNCTION__, t,
+                    span->segment()->debugID(), span->debugID());
+#endif
+            span->bumpSpanAdds();
+            return span->ptT();
+        }
+        SkASSERT(span != &fTail);
+    } while ((span = span->upCast()->next()));
+    SkASSERT(0);
+    return NULL;
 }
 
-void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
+// choose a solitary t and pt value; remove aliases; align the opposite ends
+void SkOpSegment::align() {
     debugValidate();
-    int count = this->count();
-    for (int index = 0; index < count; ++index) {
-        SkOpSpan& span = fTs[index];
-        if (!span.fMultiple) {
-            continue;
+    SkOpSpanBase* span = &fHead;
+    if (!span->aligned()) {
+        span->alignEnd(0, fPts[0]);
+    }
+    while ((span = span->upCast()->next())) {
+        if (span == &fTail) {
+            break;
         }
-        int end = nextExactSpan(index, 1);
-        SkASSERT(end > index + 1);
-        const SkPoint& thisPt = span.fPt;
-        while (index < end - 1) {
-            SkOpSegment* other1 = span.fOther;
-            int oCnt = other1->count();
-            for (int idx2 = index + 1; idx2 < end; ++idx2) {
-                SkOpSpan& span2 = fTs[idx2];
-                SkOpSegment* other2 = span2.fOther;
-                for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
-                    SkOpSpan& oSpan = other1->fTs[oIdx];
-                    if (oSpan.fOther != other2) {
-                        continue;
-                    }
-                    if (oSpan.fPt == thisPt) {
-                        goto skipExactMatches;
-                    }
-                }
-                for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
-                    SkOpSpan& oSpan = other1->fTs[oIdx];
-                    if (oSpan.fOther != other2) {
-                        continue;
-                    }
-                    if (SkDPoint::RoughlyEqual(oSpan.fPt, thisPt)) {
-                        SkOpSpan& oSpan2 = other2->fTs[oSpan.fOtherIndex];
-                        if (zero_or_one(span.fOtherT) || zero_or_one(oSpan.fT)
-                                || zero_or_one(span2.fOtherT) || zero_or_one(oSpan2.fT)) {
-                            return;
-                        }
-                        if (!way_roughly_equal(span.fOtherT, oSpan.fT)
-                                || !way_roughly_equal(span2.fOtherT, oSpan2.fT)
-                                || !way_roughly_equal(span2.fOtherT, oSpan.fOtherT)
-                                || !way_roughly_equal(span.fOtherT, oSpan2.fOtherT)) {
-                            return;
-                        }
-                        alignSpan(thisPt, span.fOtherT, other1, span2.fOtherT,
-                                other2, &oSpan, alignedArray);
-                        alignSpan(thisPt, span2.fOtherT, other2, span.fOtherT, 
-                                other1, &oSpan2, alignedArray);
-                        break;
-                    }
-                }
-        skipExactMatches:
-                ;
-            }
-            ++index;
-        }
+        span->align();
+    }
+    if (!span->aligned()) {
+        span->alignEnd(1, fPts[SkPathOpsVerbToPoints(fVerb)]);
+    }
+    if (this->collapsed()) {
+        SkOpSpan* span = &fHead;
+        do {
+            span->setWindValue(0);
+            span->setOppValue(0);
+            this->markDone(span);
+        } while ((span = span->next()->upCastable()));
     }
     debugValidate();
 }
 
-void SkOpSegment::alignRange(int lower, int upper,
-        const SkOpSegment* other, int oLower, int oUpper) {
-    for (int oIndex = oLower; oIndex <= oUpper; ++oIndex) {
-        const SkOpSpan& oSpan = other->span(oIndex);
-        const SkOpSegment* oOther = oSpan.fOther;
-        if (oOther == this) {
-            continue;
-        }
-        SkOpSpan* matchSpan;
-        int matchIndex;
-        const SkOpSpan* refSpan;
-        for (int iIndex = lower; iIndex <= upper; ++iIndex) {
-            const SkOpSpan& iSpan = this->span(iIndex);
-            const SkOpSegment* iOther = iSpan.fOther;
-            if (iOther == other) {
-                continue;
-            }
-            if (iOther == oOther) {
-                goto nextI;
-            }
-        }
-        {
-            // oSpan does not have a match in this
-            int iCount = this->count();
-            const SkOpSpan* iMatch = NULL;
-            double iMatchTDiff;
-            matchIndex = -1;
-            for (int iIndex = 0; iIndex < iCount; ++iIndex) {
-                const SkOpSpan& iSpan = this->span(iIndex);
-                const SkOpSegment* iOther = iSpan.fOther;
-                if (iOther != oOther) {
-                    continue;
-                }
-                double testTDiff = fabs(iSpan.fOtherT - oSpan.fOtherT);
-                if (!iMatch || testTDiff < iMatchTDiff) {
-                    matchIndex = iIndex;
-                    iMatch = &iSpan;
-                    iMatchTDiff = testTDiff;
-                }
-            }
-            if (matchIndex < 0) {
-                continue;  // the entry is missing, & will be picked up later (FIXME: fix it here?)
-            }
-            matchSpan = &this->fTs[matchIndex];
-            refSpan = &this->span(lower);
-            if (!SkDPoint::ApproximatelyEqual(matchSpan->fPt, refSpan->fPt)) {
-                goto nextI;
-            }
-            if (matchIndex != lower - 1 && matchIndex != upper + 1) {
-                // the consecutive spans need to be rearranged to get the missing one close 
-                continue;  // FIXME: more work to do
-            }
-        }
-        {
-            this->fixOtherTIndex();
-            SkScalar newT;
-            if (matchSpan->fT != 0 && matchSpan->fT != 1) {
-                newT = matchSpan->fT = refSpan->fT;
-                matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = refSpan->fT;
-            } else {  // leave span at the start or end there and adjust the neighbors
-                newT = matchSpan->fT;
-                for (int iIndex = lower; iIndex <= upper; ++iIndex) {
-                    matchSpan = &this->fTs[iIndex];
-                    matchSpan->fT = newT;
-                    matchSpan->fOther->fTs[matchSpan->fOtherIndex].fOtherT = newT;
-                }
-            }
-            this->resetSpanFlags();  // fix up small / tiny / done
-            // align ts of other ranges with adjacent spans that match the aligned points
-            lower = SkTMin(lower, matchIndex);
-            while (lower > 0) {
-                const SkOpSpan& span = this->span(lower - 1);
-                if (span.fT != newT) {
-                    break;
-                }
-                --lower;
-            }
-            upper = SkTMax(upper, matchIndex);
-            int last = this->count() - 1;
-            while (upper < last) {
-                const SkOpSpan& span = this->span(upper + 1);
-                if (span.fT != newT) {
-                    break;
-                }
-                ++upper;
-            }
-            for (int iIndex = lower; iIndex <= upper; ++iIndex) {
-                const SkOpSpan& span = this->span(iIndex);
-                SkOpSegment* aOther = span.fOther;
-                int aLower = span.fOtherIndex;
-                SkScalar aT = span.fOtherT;
-                bool aResetFlags = false;
-                while (aLower > 0) {
-                    SkOpSpan* aSpan = &aOther->fTs[aLower - 1];
-                    for (int iIndex = lower; iIndex <= upper; ++iIndex) {
-                        if (aSpan->fPt == this->fTs[iIndex].fPt) {
-                            goto matchFound;
-                        }
-                    }
-                    break;
-            matchFound:
-                    --aLower;
-                }
-                int aUpper = span.fOtherIndex;
-                int aLast = aOther->count() - 1;
-                while (aUpper < aLast) {
-                    SkOpSpan* aSpan = &aOther->fTs[aUpper + 1];
-                    for (int iIndex = lower; iIndex <= upper; ++iIndex) {
-                        if (aSpan->fPt == this->fTs[iIndex].fPt) {
-                            goto matchFound2;
-                        }
-                    }
-                    break;
-            matchFound2:
-                    ++aUpper;
-                }
-                if (aOther->fTs[aLower].fT == 0) {
-                    aT = 0;
-                } else if (aOther->fTs[aUpper].fT == 1) {
-                    aT = 1;
-                }
-                bool aFixed = false;
-                for (int aIndex = aLower; aIndex <= aUpper; ++aIndex) {
-                    SkOpSpan* aSpan = &aOther->fTs[aIndex];
-                    if (aSpan->fT == aT) {
-                        continue;
-                    }
-                    SkASSERT(way_roughly_equal(aSpan->fT, aT));
-                    if (!aFixed) {
-                        aOther->fixOtherTIndex();
-                        aFixed = true;
-                    }
-                    aSpan->fT = aT;
-                    aSpan->fOther->fTs[aSpan->fOtherIndex].fOtherT = aT;
-                    aResetFlags = true;
-                }
-                if (aResetFlags) {
-                    aOther->resetSpanFlags();
-                }
-            }
-        }
-nextI: ;
+void SkOpSegment::calcAngles(SkChunkAlloc* allocator) {
+    bool activePrior = !fHead.isCanceled();
+    if (activePrior && !fHead.simple()) {
+        addStartSpan(allocator);
     }
-}
-
-void SkOpSegment::alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other,
-        double otherT, const SkOpSegment* other2, SkOpSpan* oSpan,
-        SkTDArray<AlignedSpan>* alignedArray) {
-    AlignedSpan* aligned = alignedArray->append();
-    aligned->fOldPt = oSpan->fPt;
-    aligned->fPt = newPt;
-    aligned->fOldT = oSpan->fT;
-    aligned->fT = newT;
-    aligned->fSegment = this;  // OPTIMIZE: may be unused, can remove
-    aligned->fOther1 = other;
-    aligned->fOther2 = other2;
-    SkASSERT(SkDPoint::RoughlyEqual(oSpan->fPt, newPt));
-    oSpan->fPt = newPt;
-//    SkASSERT(way_roughly_equal(oSpan->fT, newT));
-    oSpan->fT = newT;
-//    SkASSERT(way_roughly_equal(oSpan->fOtherT, otherT));
-    oSpan->fOtherT = otherT;
-}
-
-bool SkOpSegment::alignSpan(int index, double thisT, const SkPoint& thisPt) {
-    bool aligned = false;
-    SkOpSpan* span = &fTs[index];
-    SkOpSegment* other = span->fOther;
-    int oIndex = span->fOtherIndex;
-    SkOpSpan* oSpan = &other->fTs[oIndex];
-    if (span->fT != thisT) {
-        span->fT = thisT;
-        oSpan->fOtherT = thisT;
-        aligned = true;
-    }
-    if (span->fPt != thisPt) {
-        span->fPt = thisPt;
-        oSpan->fPt = thisPt;
-        aligned = true;
-    }
-    double oT = oSpan->fT;
-    if (oT == 0) {
-        return aligned;
-    }
-    int oStart = other->nextSpan(oIndex, -1) + 1;
-    oSpan = &other->fTs[oStart];
-    int otherIndex = oStart;
-    if (oT == 1) {
-        if (aligned) {
-            while (oSpan->fPt == thisPt && oSpan->fT != 1) {
-                oSpan->fTiny = true;
-                ++oSpan;
-            }
+    SkOpSpan* prior = &fHead;
+    SkOpSpanBase* spanBase = fHead.next();
+    while (spanBase != &fTail) {
+        if (activePrior) {
+            SkOpAngle* priorAngle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+            priorAngle->set(spanBase, prior);
+            spanBase->setFromAngle(priorAngle);
         }
-        return aligned;
-    }
-    oT = oSpan->fT;
-    int oEnd = other->nextSpan(oIndex, 1);
-    bool oAligned = false;
-    if (oSpan->fPt != thisPt) {
-        oAligned |= other->alignSpan(oStart, oT, thisPt);
-    }
-    while (++otherIndex < oEnd) {
-        SkOpSpan* oNextSpan = &other->fTs[otherIndex];
-        if (oNextSpan->fT != oT || oNextSpan->fPt != thisPt) {
-            oAligned |= other->alignSpan(otherIndex, oT, thisPt);
+        SkOpSpan* span = spanBase->upCast();
+        bool active = !span->isCanceled();
+        SkOpSpanBase* next = span->next();
+        if (active) {
+            SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+            angle->set(span, next);
+            span->setToAngle(angle);
         }
-    }
-    if (oAligned) {
-        other->alignSpanState(oStart, oEnd);
-    }
-    return aligned;
-}
-
-void SkOpSegment::alignSpanState(int start, int end) {
-    SkOpSpan* lastSpan = &fTs[--end];
-    bool allSmall = lastSpan->fSmall;
-    bool allTiny = lastSpan->fTiny;
-    bool allDone = lastSpan->fDone;
-    SkDEBUGCODE(int winding = lastSpan->fWindValue);
-    SkDEBUGCODE(int oppWinding = lastSpan->fOppValue);
-    int index = start;
-    while (index < end) {
-        SkOpSpan* span = &fTs[index];
-        span->fSmall = allSmall;
-        span->fTiny = allTiny;
-        if (span->fDone != allDone) {
-            span->fDone = allDone;
-            fDoneSpans += allDone ? 1 : -1;
-        }
-        SkASSERT(span->fWindValue == winding);
-        SkASSERT(span->fOppValue == oppWinding);
-        ++index;
-    }
-}
-
-void SkOpSegment::blindCancel(const SkCoincidence& coincidence, SkOpSegment* other) {
-    bool binary = fOperand != other->fOperand;
-    int index = 0;
-    int last = this->count();
-    do {
-        SkOpSpan& span = this->fTs[--last];
-        if (span.fT != 1 && !span.fSmall) {
-            break;
-        }
-        span.fCoincident = true;
-    } while (true);
-    int oIndex = other->count();
-    do {
-        SkOpSpan& oSpan = other->fTs[--oIndex];
-        if (oSpan.fT != 1 && !oSpan.fSmall) {
-            break;
-        }
-        oSpan.fCoincident = true;
-    } while (true);
-    do {
-        SkOpSpan* test = &this->fTs[index];
-        int baseWind = test->fWindValue;
-        int baseOpp = test->fOppValue;
-        int endIndex = index;
-        while (++endIndex <= last) {
-            SkOpSpan* endSpan = &this->fTs[endIndex];
-            SkASSERT(endSpan->fT < 1);
-            if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
-                break;
-            }
-            endSpan->fCoincident = true;
-        }
-        SkOpSpan* oTest = &other->fTs[oIndex];
-        int oBaseWind = oTest->fWindValue;
-        int oBaseOpp = oTest->fOppValue;
-        int oStartIndex = oIndex;
-        while (--oStartIndex >= 0) {
-            SkOpSpan* oStartSpan = &other->fTs[oStartIndex];
-            if (oStartSpan->fWindValue != oBaseWind || oStartSpan->fOppValue != oBaseOpp) {
-                break;
-            }
-            oStartSpan->fCoincident = true;
-        }
-        bool decrement = baseWind && oBaseWind;
-        bool bigger = baseWind >= oBaseWind;
-        do {
-            SkASSERT(test->fT < 1);
-            if (decrement) {
-                if (binary && bigger) {
-                    test->fOppValue--;
-                } else {
-                    decrementSpan(test);
-                }
-            }
-            test->fCoincident = true;
-            test = &fTs[++index];
-        } while (index < endIndex);
-        do {
-            SkASSERT(oTest->fT < 1);
-            if (decrement) {
-                if (binary && !bigger) {
-                    oTest->fOppValue--;
-                } else {
-                    other->decrementSpan(oTest);
-                }
-            }
-            oTest->fCoincident = true;
-            oTest = &other->fTs[--oIndex];
-        } while (oIndex > oStartIndex);
-    } while (index <= last && oIndex >= 0);
-    SkASSERT(index > last);
-    SkASSERT(oIndex < 0);
-}
-
-void SkOpSegment::blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other) {
-    bool binary = fOperand != other->fOperand;
-    int index = 0;
-    int last = this->count();
-    do {
-        SkOpSpan& span = this->fTs[--last];
-        if (span.fT != 1 && !span.fSmall) {
-            break;
-        }
-        span.fCoincident = true;
-    } while (true);
-    int oIndex = 0;
-    int oLast = other->count();
-    do {
-        SkOpSpan& oSpan = other->fTs[--oLast];
-        if (oSpan.fT != 1 && !oSpan.fSmall) {
-            break;
-        }
-        oSpan.fCoincident = true;
-    } while (true);
-    do {
-        SkOpSpan* test = &this->fTs[index];
-        int baseWind = test->fWindValue;
-        int baseOpp = test->fOppValue;
-        int endIndex = index;
-        SkOpSpan* endSpan;
-        while (++endIndex <= last) {
-            endSpan = &this->fTs[endIndex];
-            SkASSERT(endSpan->fT < 1);
-            if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
-                break;
-            }
-            endSpan->fCoincident = true;
-        }
-        SkOpSpan* oTest = &other->fTs[oIndex];
-        int oBaseWind = oTest->fWindValue;
-        int oBaseOpp = oTest->fOppValue;
-        int oEndIndex = oIndex;
-        SkOpSpan* oEndSpan;
-        while (++oEndIndex <= oLast) {
-            oEndSpan = &this->fTs[oEndIndex];
-            SkASSERT(oEndSpan->fT < 1);
-            if (oEndSpan->fWindValue != oBaseWind || oEndSpan->fOppValue != oBaseOpp) {
-                break;
-            }
-            oEndSpan->fCoincident = true;
-        }
-        // consolidate the winding count even if done
-        if ((test->fWindValue || test->fOppValue) && (oTest->fWindValue || oTest->fOppValue)) {
-            if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
-                bumpCoincidentBlind(binary, index, endIndex);
-                other->bumpCoincidentOBlind(oIndex, oEndIndex);
-            } else {
-                other->bumpCoincidentBlind(binary, oIndex, oEndIndex);
-                bumpCoincidentOBlind(index, endIndex);
-            }
-        }
-        index = endIndex;
-        oIndex = oEndIndex;
-    } while (index <= last && oIndex <= oLast);
-    SkASSERT(index > last);
-    SkASSERT(oIndex > oLast);
-}
-
-void SkOpSegment::bumpCoincidentBlind(bool binary, int index, int endIndex) {
-    const SkOpSpan& oTest = fTs[index];
-    int oWindValue = oTest.fWindValue;
-    int oOppValue = oTest.fOppValue;
-    if (binary) {
-        SkTSwap<int>(oWindValue, oOppValue);
-    }
-    do {
-        (void) bumpSpan(&fTs[index], oWindValue, oOppValue);
-    } while (++index < endIndex);
-}
-
-bool SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
-        SkTArray<SkPoint, true>* outsideTs) {
-    int index = *indexPtr;
-    int oWindValue = oTest.fWindValue;
-    int oOppValue = oTest.fOppValue;
-    if (binary) {
-        SkTSwap<int>(oWindValue, oOppValue);
-    }
-    SkOpSpan* const test = &fTs[index];
-    SkOpSpan* end = test;
-    const SkPoint& oStartPt = oTest.fPt;
-    do {
-        if (end->fDone && !end->fTiny && !end->fSmall) {  // extremely large paths trigger this
-            return false;
-        }
-        if (bumpSpan(end, oWindValue, oOppValue)) {
-            TrackOutside(outsideTs, oStartPt);
-        }
-        end = &fTs[++index];
-    } while ((end->fPt == test->fPt || precisely_equal(end->fT, test->fT)) && end->fT < 1);
-    *indexPtr = index;
-    return true;
-}
-
-void SkOpSegment::bumpCoincidentOBlind(int index, int endIndex) {
-    do {
-        zeroSpan(&fTs[index]);
-    } while (++index < endIndex);
-}
-
-// because of the order in which coincidences are resolved, this and other
-// may not have the same intermediate points. Compute the corresponding
-// intermediate T values (using this as the master, other as the follower)
-// and walk other conditionally -- hoping that it catches up in the end
-bool SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
-        SkTArray<SkPoint, true>* oOutsidePts, const SkPoint& oEndPt) {
-    int oIndex = *oIndexPtr;
-    SkOpSpan* const oTest = &fTs[oIndex];
-    SkOpSpan* oEnd = oTest;
-    const SkPoint& oStartPt = oTest->fPt;
-    double oStartT = oTest->fT;
-#if 0  // FIXME : figure out what disabling this breaks
-    const SkPoint& startPt = test.fPt;
-    // this is always true since oEnd == oTest && oStartPt == oTest->fPt -- find proper condition
-    if (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
-        TrackOutside(oOutsidePts, startPt);
-    }
-#endif
-    bool foundEnd = false;
-    while (oStartPt == oEnd->fPt || precisely_equal(oStartT, oEnd->fT)) {
-        foundEnd |= oEndPt == oEnd->fPt;
-        zeroSpan(oEnd);
-        oEnd = &fTs[++oIndex];
-    }
-    *oIndexPtr = oIndex;
-    return foundEnd;
-}
-
-// FIXME: need to test this case:
-// contourA has two segments that are coincident
-// contourB has two segments that are coincident in the same place
-// each ends up with +2/0 pairs for winding count
-// since logic below doesn't transfer count (only increments/decrements) can this be
-// resolved to +4/0 ?
-
-// set spans from start to end to increment the greater by one and decrement
-// the lesser
-bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
-        SkOpSegment* other) {
-    bool binary = fOperand != other->fOperand;
-    int index = 0;
-    while (startPt != fTs[index].fPt) {
-        SkASSERT(index < fTs.count());
-        ++index;
-    }
-    double startT = fTs[index].fT;
-    while (index > 0 && precisely_equal(fTs[index - 1].fT, startT)) {
-        --index;
-    }
-    int oIndex = 0;
-    while (startPt != other->fTs[oIndex].fPt) {
-        SkASSERT(oIndex < other->fTs.count());
-        ++oIndex;
-    }
-    double oStartT = other->fTs[oIndex].fT;
-    while (oIndex > 0 && precisely_equal(other->fTs[oIndex - 1].fT, oStartT)) {
-        --oIndex;
-    }
-    SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
-    SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
-    SkOpSpan* test = &fTs[index];
-    const SkPoint* testPt = &test->fPt;
-    double testT = test->fT;
-    SkOpSpan* oTest = &other->fTs[oIndex];
-    const SkPoint* oTestPt = &oTest->fPt;
-    // paths with extreme data will fail this test and eject out of pathops altogether later on
-    // SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
-    do {
-        SkASSERT(test->fT < 1);
-        if (oTest->fT == 1) {
-            // paths with extreme data may be so mismatched that we fail here
-            return false;
-        }
-
-        // consolidate the winding count even if done
-        bool foundEnd = false;
-        if ((test->fWindValue == 0 && test->fOppValue == 0)
-                || (oTest->fWindValue == 0 && oTest->fOppValue == 0)) {
-            SkDEBUGCODE(int firstWind = test->fWindValue);
-            SkDEBUGCODE(int firstOpp = test->fOppValue);
-            do {
-                SkASSERT(firstWind == fTs[index].fWindValue);
-                SkASSERT(firstOpp == fTs[index].fOppValue);
-                ++index;
-                SkASSERT(index < fTs.count());
-            } while (*testPt == fTs[index].fPt);
-            SkDEBUGCODE(firstWind = oTest->fWindValue);
-            SkDEBUGCODE(firstOpp = oTest->fOppValue);
-            do {
-                SkASSERT(firstWind == other->fTs[oIndex].fWindValue);
-                SkASSERT(firstOpp == other->fTs[oIndex].fOppValue);
-                ++oIndex;
-                SkASSERT(oIndex < other->fTs.count());
-            } while (*oTestPt == other->fTs[oIndex].fPt);
-        } else {
-            if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
-                if (!bumpCoincidentThis(*oTest, binary, &index, &outsidePts)) {
-                    return false;
-                }
-                foundEnd = other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt);
-            } else {
-                if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
-                    return false;
-                }
-                foundEnd = bumpCoincidentOther(*oTest, &index, &outsidePts, endPt);
-            }
-        }
-        test = &fTs[index];
-        testPt = &test->fPt;
-        testT = test->fT;
-        oTest = &other->fTs[oIndex];
-        oTestPt = &oTest->fPt;
-        if (endPt == *testPt || precisely_equal(endT, testT)) {
-            break;
-        }
-        if (0 && foundEnd) {  // FIXME: this is likely needed but wait until a test case triggers it
-            break;
-        }
-//        SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
-    } while (endPt != *oTestPt);
-    // in rare cases, one may have ended before the other
-    if (endPt != *testPt && !precisely_equal(endT, testT)) {
-        int lastWind = test[-1].fWindValue;
-        int lastOpp = test[-1].fOppValue;
-        bool zero = lastWind == 0 && lastOpp == 0;
-        do {
-            if (test->fWindValue || test->fOppValue) {
-                test->fWindValue = lastWind;
-                test->fOppValue = lastOpp;
-                if (zero) {
-                    SkASSERT(!test->fDone);
-                    test->fDone = true;
-                    ++fDoneSpans;
-                }
-            }
-            test = &fTs[++index];
-            testPt = &test->fPt;
-        } while (endPt != *testPt);
-    }
-    if (endPt != *oTestPt) {
-        // look ahead to see if zeroing more spans will allows us to catch up
-        int oPeekIndex = oIndex;
-        bool success = true;
-        SkOpSpan* oPeek;
-        int oCount = other->count();
-        do {
-            oPeek = &other->fTs[oPeekIndex];
-            if (++oPeekIndex == oCount) {
-                success = false;
-                break;
-            }
-        } while (endPt != oPeek->fPt);
-        if (success) {
-            // make sure the matching point completes the coincidence span
-            success = false;
-            do {
-                if (oPeek->fOther == this) {
-                    success = true;
-                    break;
-                }
-                if (++oPeekIndex == oCount) {
-                    break;
-                }
-                oPeek = &other->fTs[oPeekIndex];
-            } while (endPt == oPeek->fPt);
-        }
-        if (success) {
-            do {
-                if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
-                    if (other->bumpCoincidentOther(*test, &oIndex, &oOutsidePts, endPt)) {
-                        break;
-                    }
-                } else {
-                    if (!other->bumpCoincidentThis(*test, binary, &oIndex, &oOutsidePts)) {
-                        return false;
-                    }
-                }
-                oTest = &other->fTs[oIndex];
-                oTestPt = &oTest->fPt;
-            } while (endPt != *oTestPt);
-        }
-    }
-    int outCount = outsidePts.count();
-    if (!done() && outCount) {
-        addCoinOutsides(outsidePts[0], endPt, other);
-    }
-    if (!other->done() && oOutsidePts.count()) {
-        other->addCoinOutsides(oOutsidePts[0], endPt, this);
-    }
-    setCoincidentRange(startPt, endPt, other);
-    other->setCoincidentRange(startPt, endPt, this);
-    return true;
-}
-
-// FIXME: this doesn't prevent the same span from being added twice
-// fix in caller, SkASSERT here?
-// FIXME: this may erroneously reject adds for cubic loops
-const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
-        const SkPoint& pt, const SkPoint& pt2) {
-    int tCount = fTs.count();
-    for (int tIndex = 0; tIndex < tCount; ++tIndex) {
-        const SkOpSpan& span = fTs[tIndex];
-        if (!approximately_negative(span.fT - t)) {
-            break;
-        }
-        if (span.fOther == other) {
-            bool tsMatch = approximately_equal(span.fT, t);
-            bool otherTsMatch = approximately_equal(span.fOtherT, otherT);
-            // FIXME: add cubic loop detecting logic here
-            // if fLoop bit is set on span, that could be enough if addOtherT copies the bit
-            // or if a new bit is added ala fOtherLoop
-            if (tsMatch || otherTsMatch) {
-#if DEBUG_ADD_T_PAIR
-                SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
-                        __FUNCTION__, fID, t, other->fID, otherT);
-#endif
-                return NULL;
-            }
-        }
-    }
-    int oCount = other->count();
-    for (int oIndex = 0; oIndex < oCount; ++oIndex) {
-        const SkOpSpan& oSpan = other->span(oIndex);
-        if (!approximately_negative(oSpan.fT - otherT)) {
-            break;
-        }
-        if (oSpan.fOther == this) {
-            bool otherTsMatch = approximately_equal(oSpan.fT, otherT);
-            bool tsMatch = approximately_equal(oSpan.fOtherT, t);
-            if (otherTsMatch || tsMatch) {
-#if DEBUG_ADD_T_PAIR
-                SkDebugf("%s addTPair other duplicate this=%d %1.9g other=%d %1.9g\n",
-                        __FUNCTION__, fID, t, other->fID, otherT);
-#endif
-                return NULL;
-            }
-        }
-    }
-#if DEBUG_ADD_T_PAIR
-    SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
-            __FUNCTION__, fID, t, other->fID, otherT);
-#endif
-    SkASSERT(other != this);
-    int insertedAt = addT(other, pt, t);
-    int otherInsertedAt = other->addT(this, pt2, otherT);
-    this->addOtherT(insertedAt, otherT, otherInsertedAt);
-    other->addOtherT(otherInsertedAt, t, insertedAt);
-    this->matchWindingValue(insertedAt, t, borrowWind);
-    other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
-    SkOpSpan& span = this->fTs[insertedAt];
-    if (pt != pt2) {
-        span.fNear = true;
-        SkOpSpan& oSpan = other->fTs[otherInsertedAt];
-        oSpan.fNear = true;
-    }
-    // if the newly inserted spans match a neighbor on one but not the other, make them agree
-    int lower = this->nextExactSpan(insertedAt, -1) + 1;
-    int upper = this->nextExactSpan(insertedAt, 1) - 1;
-    if (upper < 0) {
-        upper = this->count() - 1;
-    }
-    int oLower = other->nextExactSpan(otherInsertedAt, -1) + 1;
-    int oUpper = other->nextExactSpan(otherInsertedAt, 1) - 1;
-    if (oUpper < 0) {
-        oUpper = other->count() - 1;
-    }
-    if (lower == upper && oLower == oUpper) {
-        return &span;
-    }
-#if DEBUG_CONCIDENT
-    SkDebugf("%s id=%d lower=%d upper=%d other=%d oLower=%d oUpper=%d\n", __FUNCTION__,
-            debugID(), lower, upper, other->debugID(), oLower, oUpper);
-#endif
-    // find the nearby spans in one range missing in the other
-    this->alignRange(lower, upper, other, oLower, oUpper);
-    other->alignRange(oLower, oUpper, this, lower, upper);
-    return &span;
-}
-
-const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
-                           const SkPoint& pt) {
-    return addTPair(t, other, otherT, borrowWind, pt, pt);
-}
-
-bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
-    const SkPoint midPt = ptAtT(midT);
-    SkPathOpsBounds bounds;
-    bounds.set(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
-    bounds.sort();
-    return bounds.almostContains(midPt);
-}
-
-bool SkOpSegment::betweenTs(int lesser, double testT, int greater) const {
-    if (lesser > greater) {
-        SkTSwap<int>(lesser, greater);
-    }
-    return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
-}
-
-// in extreme cases (like the buffer overflow test) return false to abort
-// for now, if one t value represents two different points, then the values are too extreme
-// to generate meaningful results
-bool SkOpSegment::calcAngles() {
-    int spanCount = fTs.count();
-    if (spanCount <= 2) {
-        return spanCount == 2;
-    }
-    int index = 1;
-    const SkOpSpan* firstSpan = &fTs[index];
-    int activePrior = checkSetAngle(0);
-    const SkOpSpan* span = &fTs[0];
-    if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
-        index = findStartSpan(0);  // curve start intersects
-        if (fTs[index].fT == 0) {
-            return false;
-        }
-        SkASSERT(index > 0);
-        if (activePrior >= 0) {
-            addStartSpan(index);
-        }
-    }
-    bool addEnd;
-    int endIndex = spanCount - 1;
-    span = &fTs[endIndex - 1];
-    if ((addEnd = span->fT == 1 || span->fTiny)) {  // if curve end intersects
-        endIndex = findEndSpan(endIndex);
-        SkASSERT(endIndex > 0);
-    } else {
-        addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts();
-    }
-    SkASSERT(endIndex >= index);
-    int prior = 0;
-    while (index < endIndex) {
-        const SkOpSpan& fromSpan = fTs[index];  // for each intermediate intersection
-        const SkOpSpan* lastSpan;
-        span = &fromSpan;
-        int start = index;
-        do {
-            lastSpan = span;
-            span = &fTs[++index];
-            SkASSERT(index < spanCount);
-            if (!precisely_negative(span->fT - lastSpan->fT) && !lastSpan->fTiny) {
-                break;
-            }
-            if (!SkDPoint::ApproximatelyEqual(lastSpan->fPt, span->fPt)) {
-                return false;
-            }
-        } while (true);
-        SkOpAngle* angle = NULL;
-        SkOpAngle* priorAngle;
-        if (activePrior >= 0) {
-            int pActive = firstActive(prior);
-            SkASSERT(pActive < start);
-            priorAngle = &fAngles.push_back();
-            priorAngle->set(this, start, pActive);
-        }
-        int active = checkSetAngle(start);
-        if (active >= 0) {
-            SkASSERT(active < index);
-            angle = &fAngles.push_back();
-            angle->set(this, active, index);
-        }
-    #if DEBUG_ANGLE
-        debugCheckPointsEqualish(start, index);
-    #endif
-        prior = start;
-        do {
-            const SkOpSpan* startSpan = &fTs[start - 1];
-            if (!startSpan->fSmall || isCanceled(start - 1) || startSpan->fFromAngle
-                    || startSpan->fToAngle) {
-                break;
-            }
-            --start;
-        } while (start > 0);
-        do {
-            if (activePrior >= 0) {
-                SkASSERT(fTs[start].fFromAngle == NULL);
-                fTs[start].fFromAngle = priorAngle;
-            }
-            if (active >= 0) {
-                SkASSERT(fTs[start].fToAngle == NULL);
-                fTs[start].fToAngle = angle;
-            }
-        } while (++start < index);
         activePrior = active;
+        prior = span;
+        spanBase = next;
     }
-    if (addEnd && activePrior >= 0) {
-        addEndSpan(endIndex);
+    if (activePrior && !fTail.simple()) {
+        addEndSpan(allocator);
     }
-    return true;
 }
 
-int SkOpSegment::checkSetAngle(int tIndex) const {
-    const SkOpSpan* span = &fTs[tIndex];
-    while (span->fTiny /* || span->fSmall */) {
-        span = &fTs[++tIndex];
+void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
+    SkOpSpanBase* base = &fHead;
+    SkOpSpan* span;
+    do {
+        SkOpAngle* angle = base->fromAngle();
+        if (angle && angle->fCheckCoincidence) {
+            angle->checkNearCoincidence();
+        }
+        if (base->final()) {
+             break;
+        }
+        span = base->upCast();
+        angle = span->toAngle();
+        if (angle && angle->fCheckCoincidence) {
+            angle->checkNearCoincidence();
+        }
+    } while ((base = span->next()));
+}
+
+bool SkOpSegment::collapsed() const {
+    return fVerb == SkPath::kLine_Verb && fHead.pt() == fTail.pt();
+}
+
+void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+        SkOpAngle::IncludeType includeType) {
+    SkOpSegment* baseSegment = baseAngle->segment();
+    int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
+    int sumSuWinding;
+    bool binary = includeType >= SkOpAngle::kBinarySingle;
+    if (binary) {
+        sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
+        if (baseSegment->operand()) {
+            SkTSwap<int>(sumMiWinding, sumSuWinding);
+        }
     }
-    return isCanceled(tIndex) ? -1 : tIndex;
+    SkOpSegment* nextSegment = nextAngle->segment();
+    int maxWinding, sumWinding;
+    SkOpSpanBase* last;
+    if (binary) {
+        int oppMaxWinding, oppSumWinding;
+        nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
+                &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
+        last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
+                nextAngle);
+    } else {
+        nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
+                &maxWinding, &sumWinding);
+        last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+    }
+    nextAngle->setLastMarked(last);
+}
+
+void SkOpSegment::ComputeOneSumReverse(SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+        SkOpAngle::IncludeType includeType) {
+    SkOpSegment* baseSegment = baseAngle->segment();
+    int sumMiWinding = baseSegment->updateWinding(baseAngle);
+    int sumSuWinding;
+    bool binary = includeType >= SkOpAngle::kBinarySingle;
+    if (binary) {
+        sumSuWinding = baseSegment->updateOppWinding(baseAngle);
+        if (baseSegment->operand()) {
+            SkTSwap<int>(sumMiWinding, sumSuWinding);
+        }
+    }
+    SkOpSegment* nextSegment = nextAngle->segment();
+    int maxWinding, sumWinding;
+    SkOpSpanBase* last;
+    if (binary) {
+        int oppMaxWinding, oppSumWinding;
+        nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
+                &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
+        last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
+                nextAngle);
+    } else {
+        nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
+                &maxWinding, &sumWinding);
+        last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
+    }
+    nextAngle->setLastMarked(last);
 }
 
 // at this point, the span is already ordered, or unorderable
-int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
+int SkOpSegment::computeSum(SkOpSpanBase* start, SkOpSpanBase* end,
+        SkOpAngle::IncludeType includeType) {
     SkASSERT(includeType != SkOpAngle::kUnaryXor);
-    SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
+    SkOpAngle* firstAngle = this->spanToAngle(end, start);
     if (NULL == firstAngle || NULL == firstAngle->next()) {
         return SK_NaN32;
     }
@@ -1898,7 +463,7 @@
             baseAngle = NULL;
             continue;
         }
-        int testWinding = angle->segment()->windSum(angle);
+        int testWinding = angle->starter()->windSum();
         if (SK_MinS32 != testWinding) {
             baseAngle = angle;
             tryReverse = true;
@@ -1906,10 +471,10 @@
         }
         if (baseAngle) {
             ComputeOneSum(baseAngle, angle, includeType);
-            baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
+            baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
         }
     } while (next != firstAngle);
-    if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
+    if (baseAngle && SK_MinS32 == firstAngle->starter()->windSum()) {
         firstAngle = baseAngle;
         tryReverse = true;
     }
@@ -1925,1101 +490,48 @@
                 baseAngle = NULL;
                 continue;
             }
-            int testWinding = angle->segment()->windSum(angle);
+            int testWinding = angle->starter()->windSum();
             if (SK_MinS32 != testWinding) {
                 baseAngle = angle;
                 continue;
             }
             if (baseAngle) {
                 ComputeOneSumReverse(baseAngle, angle, includeType);
-                baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
+                baseAngle = SK_MinS32 != angle->starter()->windSum() ? angle : NULL;
             }
         } while (prior != firstAngle);
     }
-    int minIndex = SkMin32(startIndex, endIndex);
-    return windSum(minIndex);
+    return start->starter(end)->windSum();
 }
 
-void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
-        SkOpAngle::IncludeType includeType) {
-    const SkOpSegment* baseSegment = baseAngle->segment();
-    int sumMiWinding = baseSegment->updateWindingReverse(baseAngle);
-    int sumSuWinding;
-    bool binary = includeType >= SkOpAngle::kBinarySingle;
-    if (binary) {
-        sumSuWinding = baseSegment->updateOppWindingReverse(baseAngle);
-        if (baseSegment->operand()) {
-            SkTSwap<int>(sumMiWinding, sumSuWinding);
-        }
+void SkOpSegment::detach(const SkOpSpan* span) {
+    if (span->done()) {
+        --fDoneCount;
     }
-    SkOpSegment* nextSegment = nextAngle->segment();
-    int maxWinding, sumWinding;
-    SkOpSpan* last;
-    if (binary) {
-        int oppMaxWinding, oppSumWinding;
-        nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
-                &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
-        last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
-                nextAngle);
-    } else {
-        nextSegment->setUpWindings(nextAngle->start(), nextAngle->end(), &sumMiWinding,
-                &maxWinding, &sumWinding);
-        last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
-    }
-    nextAngle->setLastMarked(last);
+    --fCount;
+    SkASSERT(fCount >= fDoneCount);
 }
 
-void SkOpSegment::ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
-        SkOpAngle::IncludeType includeType) {
-    const SkOpSegment* baseSegment = baseAngle->segment();
-    int sumMiWinding = baseSegment->updateWinding(baseAngle);
-    int sumSuWinding;
-    bool binary = includeType >= SkOpAngle::kBinarySingle;
-    if (binary) {
-        sumSuWinding = baseSegment->updateOppWinding(baseAngle);
-        if (baseSegment->operand()) {
-            SkTSwap<int>(sumMiWinding, sumSuWinding);
-        }
-    }
-    SkOpSegment* nextSegment = nextAngle->segment();
-    int maxWinding, sumWinding;
-    SkOpSpan* last;
-    if (binary) {
-        int oppMaxWinding, oppSumWinding;
-        nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
-                &sumSuWinding, &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
-        last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding, oppSumWinding,
-                nextAngle);
-    } else {
-        nextSegment->setUpWindings(nextAngle->end(), nextAngle->start(), &sumMiWinding,
-                &maxWinding, &sumWinding);
-        last = nextSegment->markAngle(maxWinding, sumWinding, nextAngle);
-    }
-    nextAngle->setLastMarked(last);
-}
-
-bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
-    int step = index < endIndex ? 1 : -1;
-    do {
-        const SkOpSpan& span = this->span(index);
-        if (span.fPt == pt) {
-            const SkOpSpan& endSpan = this->span(endIndex);
-            return span.fT == endSpan.fT && pt != endSpan.fPt;
-        }
-        index += step;
-    } while (index != endIndex);
-    return false;
-}
-
-bool SkOpSegment::containsT(double t, const SkOpSegment* other, double otherT) const {
-    int count = this->count();
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = fTs[index];
-        if (t < span.fT) {
-            return false;
-        }
-        if (t == span.fT) {
-            if (other != span.fOther) {
-                continue;
-            }
-            if (other->fVerb != SkPath::kCubic_Verb) {
-                return true;
-            }
-            if (!other->fLoop) {
-                return true;
-            }
-            double otherMidT = (otherT + span.fOtherT) / 2;
-            SkPoint otherPt = other->ptAtT(otherMidT);
-            return SkDPoint::ApproximatelyEqual(span.fPt, otherPt);
-        }
-    }
-    return false;
-}
-
-int SkOpSegment::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
-    SkIntersections intersections;
-    // OPTIMIZE: use specialty function that intersects ray with curve,
-    // returning t values only for curve (we don't care about t on ray)
-    intersections.allowNear(false);
-    int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])
-            (fPts, top, bottom, basePt.fX, false);
-    if (pts == 0 || (current && pts == 1)) {
-        return bestTIndex;
-    }
-    if (current) {
-        SkASSERT(pts > 1);
-        int closestIdx = 0;
-        double closest = fabs(intersections[0][0] - mid);
-        for (int idx = 1; idx < pts; ++idx) {
-            double test = fabs(intersections[0][idx] - mid);
-            if (closest > test) {
-                closestIdx = idx;
-                closest = test;
-            }
-        }
-        intersections.quickRemoveOne(closestIdx, --pts);
-    }
-    double bestT = -1;
-    for (int index = 0; index < pts; ++index) {
-        double foundT = intersections[0][index];
-        if (approximately_less_than_zero(foundT)
-                    || approximately_greater_than_one(foundT)) {
+double SkOpSegment::distSq(double t, SkOpAngle* oppAngle) {
+    SkDPoint testPt = this->dPtAtT(t);
+    SkDLine testPerp = {{ testPt, testPt }};
+    SkDVector slope = this->dSlopeAtT(t);
+    testPerp[1].fX += slope.fY;
+    testPerp[1].fY -= slope.fX;
+    SkIntersections i;
+    SkOpSegment* oppSegment = oppAngle->segment();
+    (*CurveIntersectRay[oppSegment->verb()])(oppSegment->pts(), oppSegment->weight(), testPerp, &i);
+    double closestDistSq = SK_ScalarInfinity;
+    for (int index = 0; index < i.used(); ++index) {
+        if (!between(oppAngle->start()->t(), i[0][index], oppAngle->end()->t())) {
             continue;
         }
-        SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
-        if (approximately_negative(testY - *bestY)
-                || approximately_negative(basePt.fY - testY)) {
-            continue;
-        }
-        if (pts > 1 && fVerb == SkPath::kLine_Verb) {
-            return SK_MinS32;  // if the intersection is edge on, wait for another one
-        }
-        if (fVerb > SkPath::kLine_Verb) {
-            SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
-            if (approximately_zero(dx)) {
-                return SK_MinS32;  // hit vertical, wait for another one
-            }
-        }
-        *bestY = testY;
-        bestT = foundT;
-    }
-    if (bestT < 0) {
-        return bestTIndex;
-    }
-    SkASSERT(bestT >= 0);
-    SkASSERT(bestT <= 1);
-    int start;
-    int end = 0;
-    do {
-        start = end;
-        end = nextSpan(start, 1);
-    } 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;
-}
-
-bool SkOpSegment::decrementSpan(SkOpSpan* span) {
-    SkASSERT(span->fWindValue > 0);
-    if (--(span->fWindValue) == 0) {
-        if (!span->fOppValue && !span->fDone) {
-            span->fDone = true;
-            ++fDoneSpans;
-            return true;
+        double testDistSq = testPt.distanceSquared(i.pt(index));
+        if (closestDistSq > testDistSq) {
+            closestDistSq = testDistSq;
         }
     }
-    return false;
-}
-
-bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
-    SkASSERT(!span->fDone || span->fTiny || span->fSmall);
-    span->fWindValue += windDelta;
-    SkASSERT(span->fWindValue >= 0);
-    span->fOppValue += oppDelta;
-    SkASSERT(span->fOppValue >= 0);
-    if (fXor) {
-        span->fWindValue &= 1;
-    }
-    if (fOppXor) {
-        span->fOppValue &= 1;
-    }
-    if (!span->fWindValue && !span->fOppValue) {
-        if (!span->fDone) {
-            span->fDone = true;
-            ++fDoneSpans;
-        }
-        return true;
-    }
-    return false;
-}
-
-const SkOpSpan& SkOpSegment::firstSpan(const SkOpSpan& thisSpan) const {
-    const SkOpSpan* firstSpan = &thisSpan; // rewind to the start
-    const SkOpSpan* beginSpan = fTs.begin();
-    const SkPoint& testPt = thisSpan.fPt;
-    while (firstSpan > beginSpan && firstSpan[-1].fPt == testPt) {
-        --firstSpan;
-    }
-    return *firstSpan;
-}
-
-const SkOpSpan& SkOpSegment::lastSpan(const SkOpSpan& thisSpan) const {
-    const SkOpSpan* endSpan = fTs.end() - 1;  // last can't be small
-    const SkOpSpan* lastSpan = &thisSpan;  // find the end
-    const SkPoint& testPt = thisSpan.fPt;
-    while (lastSpan < endSpan && lastSpan[1].fPt == testPt) {
-        ++lastSpan;
-    }
-    return *lastSpan;
-}
-
-// with a loop, the comparison is move involved
-// scan backwards and forwards to count all matching points
-// (verify that there are twp scans marked as loops)
-// compare that against 2 matching scans for loop plus other results
-bool SkOpSegment::calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts) {
-    const SkOpSpan& firstSpan = this->firstSpan(thisSpan); // rewind to the start
-    const SkOpSpan& lastSpan = this->lastSpan(thisSpan);  // find the end
-    double firstLoopT = -1, lastLoopT = -1;
-    const SkOpSpan* testSpan = &firstSpan - 1;
-    while (++testSpan <= &lastSpan) {
-        if (testSpan->fLoop) {
-            firstLoopT = testSpan->fT;
-            break;
-        }
-    }
-    testSpan = &lastSpan + 1;
-    while (--testSpan >= &firstSpan) {
-        if (testSpan->fLoop) {
-            lastLoopT = testSpan->fT;
-            break;
-        }
-    }
-    SkASSERT((firstLoopT == -1) == (lastLoopT == -1));
-    if (firstLoopT == -1) {
-        return false;
-    }
-    SkASSERT(firstLoopT < lastLoopT);
-    testSpan = &firstSpan - 1;
-    smallCounts[0] = smallCounts[1] = 0;
-    while (++testSpan <= &lastSpan) {
-        SkASSERT(approximately_equal(testSpan->fT, firstLoopT) +
-                approximately_equal(testSpan->fT, lastLoopT) == 1);
-        smallCounts[approximately_equal(testSpan->fT, lastLoopT)]++;
-    }
-    return true;
-}
-
-double SkOpSegment::calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
-        double hiEnd, const SkOpSegment* other, int thisStart) {
-    if (max >= hiEnd) {
-        return -1;
-    }
-    int end = findOtherT(hiEnd, ref);
-    if (end < 0) {
-        return -1;
-    }
-    double tHi = span(end).fT;
-    double tLo, refLo;
-    if (thisStart >= 0) {
-        tLo = span(thisStart).fT;
-        refLo = min;
-    } else {
-        int start1 = findOtherT(loEnd, ref);
-        SkASSERT(start1 >= 0);
-        tLo = span(start1).fT;
-        refLo = loEnd;
-    }
-    double missingT = (max - refLo) / (hiEnd - refLo);
-    missingT = tLo + missingT * (tHi - tLo);
-    return missingT;
-}
-
-double SkOpSegment::calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
-        double hiEnd, const SkOpSegment* other, int thisEnd) {
-    if (min <= loEnd) {
-        return -1;
-    }
-    int start = findOtherT(loEnd, ref);
-    if (start < 0) {
-        return -1;
-    }
-    double tLo = span(start).fT;
-    double tHi, refHi;
-    if (thisEnd >= 0) {
-        tHi = span(thisEnd).fT;
-        refHi = max;
-    } else {
-        int end1 = findOtherT(hiEnd, ref);
-        if (end1 < 0) {
-            return -1;
-        }
-        tHi = span(end1).fT;
-        refHi = hiEnd;
-    }
-    double missingT = (min - loEnd) / (refHi - loEnd);
-    missingT = tLo + missingT * (tHi - tLo);
-    return missingT;
-}
-
-// see if spans with two or more intersections have the same number on the other end
-void SkOpSegment::checkDuplicates() {
-    debugValidate();
-    SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
-    int index;
-    int endIndex = 0;
-    bool endFound;
-    do {
-        index = endIndex;
-        endIndex = nextExactSpan(index, 1);
-        if ((endFound = endIndex < 0)) {
-            endIndex = count();
-        }
-        int dupCount = endIndex - index;
-        if (dupCount < 2) {
-            continue;
-        }
-        do {
-            const SkOpSpan* thisSpan = &fTs[index];
-            if (thisSpan->fNear) {
-                continue;
-            }
-            SkOpSegment* other = thisSpan->fOther;
-            int oIndex = thisSpan->fOtherIndex;
-            int oStart = other->nextExactSpan(oIndex, -1) + 1;
-            int oEnd = other->nextExactSpan(oIndex, 1);
-            if (oEnd < 0) {
-                oEnd = other->count();
-            }
-            int oCount = oEnd - oStart;
-            // force the other to match its t and this pt if not on an end point
-            if (oCount != dupCount) {
-                MissingSpan& missing = missingSpans.push_back();
-                missing.fOther = NULL;
-                SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
-                missing.fPt = thisSpan->fPt;
-                const SkOpSpan& oSpan = other->span(oIndex);
-                if (oCount > dupCount) {
-                    missing.fSegment = this;
-                    missing.fT = thisSpan->fT;
-                    other->checkLinks(&oSpan, &missingSpans);
-                } else {
-                    missing.fSegment = other;
-                    missing.fT = oSpan.fT;
-                    checkLinks(thisSpan, &missingSpans);
-                }
-                if (!missingSpans.back().fOther) {
-                    missingSpans.pop_back();
-                }
-            }
-        } while (++index < endIndex);
-    } while (!endFound);
-    int missingCount = missingSpans.count();
-    if (missingCount == 0) {
-        return;
-    }
-    SkSTArray<kMissingSpanCount, MissingSpan, true> missingCoincidence;
-    for (index = 0; index < missingCount; ++index)  {
-        MissingSpan& missing = missingSpans[index];
-        SkOpSegment* missingOther = missing.fOther;
-        if (missing.fSegment == missing.fOther) {
-            continue;
-        }
-#if 0  // FIXME: this eliminates spurious data from skpwww_argus_presse_fr_41 but breaks
-       // skpwww_fashionscandal_com_94 -- calcAngles complains, but I don't understand why
-        if (missing.fSegment->containsT(missing.fT, missing.fOther, missing.fOtherT)) {
-#if DEBUG_DUPLICATES
-            SkDebugf("skip 1 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fSegment->fID,
-                    missing.fT, missing.fOther->fID, missing.fOtherT);
-#endif
-            continue;
-        }
-        if (missing.fOther->containsT(missing.fOtherT, missing.fSegment, missing.fT)) {
-#if DEBUG_DUPLICATES
-            SkDebugf("skip 2 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fOther->fID,
-                    missing.fOtherT, missing.fSegment->fID, missing.fT);
-#endif
-            continue;
-        }
-#endif
-        // skip if adding would insert point into an existing coincindent span
-        if (missing.fSegment->inCoincidentSpan(missing.fT, missingOther)
-                && missingOther->inCoincidentSpan(missing.fOtherT, this)) {
-            continue;
-        }
-        // skip if the created coincident spans are small
-        if (missing.fSegment->coincidentSmall(missing.fPt, missing.fT, missingOther)
-                && missingOther->coincidentSmall(missing.fPt, missing.fOtherT, missing.fSegment)) {
-            continue;
-        }
-        const SkOpSpan* added = missing.fSegment->addTPair(missing.fT, missingOther,
-                missing.fOtherT, false, missing.fPt);
-        if (added && added->fSmall) {
-            missing.fSegment->checkSmallCoincidence(*added, &missingCoincidence);
-        }
-    }
-    for (index = 0; index < missingCount; ++index)  {
-        MissingSpan& missing = missingSpans[index];
-        missing.fSegment->fixOtherTIndex();
-        missing.fOther->fixOtherTIndex();
-    }
-    for (index = 0; index < missingCoincidence.count(); ++index) {
-        MissingSpan& missing = missingCoincidence[index];
-        missing.fSegment->fixOtherTIndex();
-    }
-    debugValidate();
-}
-
-// look to see if the curve end intersects an intermediary that intersects the other
-bool SkOpSegment::checkEnds() {
-    debugValidate();
-    SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
-    int count = fTs.count();
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = fTs[index];
-        double otherT = span.fOtherT;
-        if (otherT != 0 && otherT != 1) { // only check ends
-            continue;
-        }
-        const SkOpSegment* other = span.fOther;
-        // peek start/last describe the range of spans that match the other t of this span
-        int peekStart = span.fOtherIndex;
-        while (--peekStart >= 0 && other->fTs[peekStart].fT == otherT)
-            ;
-        int otherCount = other->fTs.count();
-        int peekLast = span.fOtherIndex;
-        while (++peekLast < otherCount && other->fTs[peekLast].fT == otherT)
-            ;
-        if (++peekStart == --peekLast) { // if there isn't a range, there's nothing to do
-            continue;
-        }
-        // t start/last describe the range of spans that match the t of this span
-        double t = span.fT;
-        double tBottom = -1;
-        int tStart = -1;
-        int tLast = count;
-        bool lastSmall = false;
-        double afterT = t;
-        for (int inner = 0; inner < count; ++inner) {
-            double innerT = fTs[inner].fT;
-            if (innerT <= t && innerT > tBottom) {
-                if (innerT < t || !lastSmall) {
-                    tStart = inner - 1;
-                }
-                tBottom = innerT;
-            }
-            if (innerT > afterT) {
-                if (t == afterT && lastSmall) {
-                    afterT = innerT;
-                } else {
-                    tLast = inner;
-                    break;
-                }
-            }
-            lastSmall = innerT <= t ? fTs[inner].fSmall : false;
-        }
-        for (int peekIndex = peekStart; peekIndex <= peekLast; ++peekIndex) {
-            if (peekIndex == span.fOtherIndex) {  // skip the other span pointed to by this span
-                continue;
-            }
-            const SkOpSpan& peekSpan = other->fTs[peekIndex];
-            SkOpSegment* match = peekSpan.fOther;
-            if (match->done()) {
-                continue;  // if the edge has already been eaten (likely coincidence), ignore it
-            }
-            const double matchT = peekSpan.fOtherT;
-            // see if any of the spans match the other spans
-            for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
-                const SkOpSpan& tSpan = fTs[tIndex];
-                if (tSpan.fOther == match) {
-                    if (tSpan.fOtherT == matchT) {
-                        goto nextPeekIndex;
-                    }
-                    double midT = (tSpan.fOtherT + matchT) / 2;
-                    if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
-                        goto nextPeekIndex;
-                    }
-                }
-            }
-            if (missingSpans.count() > 0) {
-                const MissingSpan& lastMissing = missingSpans.back();
-                if (lastMissing.fT == t
-                        && lastMissing.fOther == match
-                        && lastMissing.fOtherT == matchT) {
-                    SkASSERT(SkDPoint::ApproximatelyEqual(lastMissing.fPt, peekSpan.fPt));
-                    continue;
-                }
-            }
-            if (this == match) {
-                return false; // extremely large paths can trigger this
-            }
-#if DEBUG_CHECK_ALIGN
-            SkDebugf("%s id=%d missing t=%1.9g other=%d otherT=%1.9g pt=(%1.9g,%1.9g)\n",
-                    __FUNCTION__, fID, t, match->fID, matchT, peekSpan.fPt.fX, peekSpan.fPt.fY);
-#endif
-            // this segment is missing a entry that the other contains
-            // remember so we can add the missing one and recompute the indices
-            {
-                MissingSpan& missing = missingSpans.push_back();
-                SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
-                missing.fT = t;
-                SkASSERT(this != match);
-                missing.fOther = match;
-                missing.fOtherT = matchT;
-                missing.fPt = peekSpan.fPt;
-            }
-            break;
-nextPeekIndex:
-            ;
-        }
-    }
-    if (missingSpans.count() == 0) {
-        debugValidate();
-        return true;
-    }
-    debugValidate();
-    int missingCount = missingSpans.count();
-    for (int index = 0; index < missingCount; ++index)  {
-        MissingSpan& missing = missingSpans[index];
-        if (this != missing.fOther) {
-            addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt);
-        }
-    }
-    fixOtherTIndex();
-    // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
-    // avoid this
-    for (int index = 0; index < missingCount; ++index)  {
-        missingSpans[index].fOther->fixOtherTIndex();
-    }
-    debugValidate();
-    return true;
-}
-
-void SkOpSegment::checkLinks(const SkOpSpan* base,
-        SkTArray<MissingSpan, true>* missingSpans) const {
-    const SkOpSpan* first = fTs.begin();
-    const SkOpSpan* last = fTs.end() - 1;
-    SkASSERT(base >= first && last >= base);
-    const SkOpSegment* other = base->fOther;
-    const SkOpSpan* oFirst = other->fTs.begin();
-    const SkOpSpan* oLast = other->fTs.end() - 1;
-    const SkOpSpan* oSpan = &other->fTs[base->fOtherIndex];
-    const SkOpSpan* test = base;
-    const SkOpSpan* missing = NULL;
-    while (test > first && (--test)->fPt == base->fPt) {
-        if (this == test->fOther) {
-            continue;
-        }
-        CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
-    }
-    test = base;
-    while (test < last && (++test)->fPt == base->fPt) {
-        SkASSERT(this != test->fOther || test->fLoop);
-        CheckOneLink(test, oSpan, oFirst, oLast, &missing, missingSpans);
-    }
-}
-
-// see if spans with two or more intersections all agree on common t and point values
-void SkOpSegment::checkMultiples() {
-    debugValidate();
-    int index;
-    int end = 0;
-    while (fTs[++end].fT == 0)
-        ;
-    while (fTs[end].fT < 1) {
-        int start = index = end;
-        end = nextExactSpan(index, 1);
-        if (end <= index) {
-            return;  // buffer overflow example triggers this
-        }
-        if (index + 1 == end) {
-            continue;
-        }
-        // force the duplicates to agree on t and pt if not on the end
-        SkOpSpan& span = fTs[index];
-        double thisT = span.fT;
-        const SkPoint& thisPt = span.fPt;
-        span.fMultiple = true;
-        bool aligned = false;
-        while (++index < end) {
-            aligned |= alignSpan(index, thisT, thisPt);
-        }
-        if (aligned) {
-            alignSpanState(start, end);
-        }
-        fMultiples = true;
-    }
-    debugValidate();
-}
-
-void SkOpSegment::CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
-        const SkOpSpan* oFirst, const SkOpSpan* oLast, const SkOpSpan** missingPtr,
-        SkTArray<MissingSpan, true>* missingSpans) {
-    SkASSERT(oSpan->fPt == test->fPt);
-    const SkOpSpan* oTest = oSpan;
-    while (oTest > oFirst && (--oTest)->fPt == test->fPt) {
-        if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
-            return;
-        }
-    }
-    oTest = oSpan;
-    while (oTest < oLast && (++oTest)->fPt == test->fPt) {
-        if (oTest->fOther == test->fOther && oTest->fOtherT == test->fOtherT) {
-            return;
-        }
-    }
-    if (*missingPtr) {
-        missingSpans->push_back();
-    }
-    MissingSpan& lastMissing = missingSpans->back();
-    if (*missingPtr) {
-        lastMissing = missingSpans->end()[-2];
-    }
-    *missingPtr = test;
-    lastMissing.fOther = test->fOther;
-    lastMissing.fOtherT = test->fOtherT;
-}
-
-bool SkOpSegment::checkSmall(int index) const {
-    if (fTs[index].fSmall) {
-        return true;
-    }
-    double tBase = fTs[index].fT;
-    while (index > 0 && precisely_negative(tBase - fTs[--index].fT))
-        ;
-    return fTs[index].fSmall;
-}
-
-// a pair of curves may turn into coincident lines -- small may be a hint that that happened
-// if a cubic contains a loop, the counts must be adjusted
-void SkOpSegment::checkSmall() {
-    SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
-    const SkOpSpan* beginSpan = fTs.begin();
-    const SkOpSpan* thisSpan = beginSpan - 1;
-    const SkOpSpan* endSpan = fTs.end() - 1;  // last can't be small
-    while (++thisSpan < endSpan) {
-        if (!thisSpan->fSmall) {
-            continue;
-        }
-        if (!thisSpan->fWindValue) {
-            continue;
-        }
-        const SkOpSpan& firstSpan = this->firstSpan(*thisSpan);
-        const SkOpSpan& lastSpan = this->lastSpan(*thisSpan);
-        const SkOpSpan* nextSpan = &firstSpan + 1;
-        ptrdiff_t smallCount = &lastSpan - &firstSpan + 1;
-        SkASSERT(1 <= smallCount && smallCount < count());
-        if (smallCount <= 1 && !nextSpan->fSmall) {
-            SkASSERT(1 == smallCount);
-            checkSmallCoincidence(firstSpan, NULL);
-            continue;
-        }
-        // at this point, check for missing computed intersections
-        const SkPoint& testPt = firstSpan.fPt;
-        thisSpan = &firstSpan - 1;
-        SkOpSegment* other = NULL;
-        while (++thisSpan <= &lastSpan) {
-            other = thisSpan->fOther;
-            if (other != this) {
-                break;
-            }
-        }
-        SkASSERT(other != this);
-        int oIndex = thisSpan->fOtherIndex;
-        const SkOpSpan& oSpan = other->span(oIndex);
-        const SkOpSpan& oFirstSpan = other->firstSpan(oSpan);
-        const SkOpSpan& oLastSpan = other->lastSpan(oSpan);
-        ptrdiff_t oCount = &oLastSpan - &oFirstSpan + 1;
-        if (fLoop) {
-            int smallCounts[2];
-            SkASSERT(!other->fLoop);  // FIXME: we need more complicated logic for pair of loops
-            if (calcLoopSpanCount(*thisSpan, smallCounts)) {
-                if (smallCounts[0] && oCount != smallCounts[0]) {
-                    SkASSERT(0);  // FIXME: need a working test case to properly code & debug
-                }
-                if (smallCounts[1] && oCount != smallCounts[1]) {
-                    SkASSERT(0);  // FIXME: need a working test case to properly code & debug
-                }
-                goto nextSmallCheck;
-            }
-        }
-        if (other->fLoop) {
-            int otherCounts[2];
-            if (other->calcLoopSpanCount(other->span(oIndex), otherCounts)) {
-                if (otherCounts[0] && otherCounts[0] != smallCount) {
-                    SkASSERT(0);  // FIXME: need a working test case to properly code & debug
-                }
-                if (otherCounts[1] && otherCounts[1] != smallCount) {
-                    SkASSERT(0);  // FIXME: need a working test case to properly code & debug
-                }
-                goto nextSmallCheck;
-            }
-        }
-        if (oCount != smallCount) {  // check if number of pts in this match other
-            MissingSpan& missing = missingSpans.push_back();
-            missing.fOther = NULL;
-            SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
-            missing.fPt = testPt;
-            const SkOpSpan& oSpan = other->span(oIndex);
-            if (oCount > smallCount) {
-                missing.fSegment = this;
-                missing.fT = thisSpan->fT;
-                other->checkLinks(&oSpan, &missingSpans);
-            } else {
-                missing.fSegment = other;
-                missing.fT = oSpan.fT;
-                checkLinks(thisSpan, &missingSpans);
-            }
-            if (!missingSpans.back().fOther || missing.fSegment->done()) {
-                missingSpans.pop_back();
-            }
-        }
-nextSmallCheck:
-        thisSpan = &lastSpan;
-    }
-    int missingCount = missingSpans.count();
-    for (int index = 0; index < missingCount; ++index)  {
-        MissingSpan& missing = missingSpans[index];
-        SkOpSegment* missingOther = missing.fOther;
-        // note that add t pair may edit span arrays, so prior pointers to spans are no longer valid
-        if (!missing.fSegment->addTPair(missing.fT, missingOther, missing.fOtherT, false,
-                missing.fPt)) {
-            continue;
-        }
-        int otherTIndex = missingOther->findT(missing.fOtherT, missing.fPt, missing.fSegment);
-        const SkOpSpan& otherSpan = missingOther->span(otherTIndex);
-        if (otherSpan.fSmall) {
-            const SkOpSpan* nextSpan = &otherSpan;
-            if (nextSpan->fPt == missing.fPt) {
-                continue;
-            }
-            do {
-                ++nextSpan;
-            } while (nextSpan->fSmall);
-            if (nextSpan->fT == 1) {
-                continue;
-            }
-            SkAssertResult(missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt,
-                    nextSpan->fT, missingOther));
-        } else if (otherSpan.fT > 0) {
-            const SkOpSpan* priorSpan = &otherSpan;
-            do {
-                --priorSpan;
-            } while (priorSpan->fT == otherSpan.fT);
-            if (priorSpan->fSmall) {
-                missing.fSegment->addTCancel(missing.fPt, priorSpan->fPt, missingOther);
-            }
-        }
-    }
-    // OPTIMIZATION: this may fix indices more than once. Build an array of unique segments to
-    // avoid this
-    for (int index = 0; index < missingCount; ++index)  {
-        MissingSpan& missing = missingSpans[index];
-        missing.fSegment->fixOtherTIndex();
-        missing.fOther->fixOtherTIndex();
-    }
-    debugValidate();
-}
-
-void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
-        SkTArray<MissingSpan, true>* checkMultiple) {
-    SkASSERT(span.fSmall);
-    if (0 && !span.fWindValue) {
-        return;
-    }
-    SkASSERT(&span < fTs.end() - 1);
-    const SkOpSpan* next = &span + 1;
-    SkASSERT(!next->fSmall || checkMultiple);
-    if (checkMultiple) {
-        while (next->fSmall) {
-            ++next;
-            SkASSERT(next < fTs.end());
-        }
-    }
-    SkOpSegment* other = span.fOther;
-    while (other != next->fOther) {
-        if (!checkMultiple) {
-            return;
-        }
-        const SkOpSpan* test = next + 1;
-        if (test == fTs.end()) {
-            return;
-        }
-        if (test->fPt != next->fPt || !precisely_equal(test->fT, next->fT)) {
-            return;
-        }
-        next = test;
-    }
-    SkASSERT(span.fT < next->fT);
-    int oStartIndex = other->findExactT(span.fOtherT, this);
-    int oEndIndex = other->findExactT(next->fOtherT, this);
-    // FIXME: be overly conservative by limiting this to the caller that allows multiple smalls
-    if (!checkMultiple || fVerb != SkPath::kLine_Verb || other->fVerb != SkPath::kLine_Verb) {
-        SkPoint mid = ptAtT((span.fT + next->fT) / 2);
-        const SkOpSpan& oSpanStart = other->fTs[oStartIndex];
-        const SkOpSpan& oSpanEnd = other->fTs[oEndIndex];
-        SkPoint oMid = other->ptAtT((oSpanStart.fT + oSpanEnd.fT) / 2);
-        if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
-            return;
-        }
-    }
-    // FIXME: again, be overly conservative to avoid breaking existing tests
-    const SkOpSpan& oSpan = oStartIndex < oEndIndex ? other->fTs[oStartIndex]
-            : other->fTs[oEndIndex];
-    if (checkMultiple && !oSpan.fSmall) {
-        return;
-    }
-//    SkASSERT(oSpan.fSmall);
-    if (oStartIndex < oEndIndex) {
-        SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, other));
-    } else {
-        addTCancel(span.fPt, next->fPt, other);
-    }
-    if (!checkMultiple) {
-        return;
-    }
-    // check to see if either segment is coincident with a third segment -- if it is, and if
-    // the opposite segment is not already coincident with the third, make it so
-    // OPTIMIZE: to make this check easier, add coincident and cancel could set a coincident bit
-    if (span.fWindValue != 1 || span.fOppValue != 0) {
-//        start here;
-        // iterate through the spans, looking for the third coincident case
-        // if we find one, we need to return state to the caller so that the indices can be fixed
-        // this also suggests that all of this function is fragile since it relies on a valid index
-    }
-    // probably should make this a common function rather than copy/paste code
-    if (oSpan.fWindValue != 1 || oSpan.fOppValue != 0) {
-        const SkOpSpan* oTest = &oSpan;
-        while (--oTest >= other->fTs.begin()) {
-            if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
-                break;
-            }
-            SkOpSegment* testOther = oTest->fOther;
-            SkASSERT(testOther != this);
-            // look in both directions to see if there is a coincident span
-            const SkOpSpan* tTest = testOther->fTs.begin();
-            for (int testIndex = 0; testIndex < testOther->count(); ++testIndex) {
-                if (tTest->fPt != span.fPt) {
-                    ++tTest;
-                    continue;
-                }
-                if (testOther->verb() != SkPath::kLine_Verb
-                        || other->verb() != SkPath::kLine_Verb) {
-                    SkPoint mid = ptAtT((span.fT + next->fT) / 2);
-                    SkPoint oMid = other->ptAtT((oTest->fOtherT + tTest->fT) / 2);
-                    if (!SkDPoint::ApproximatelyEqual(mid, oMid)) {
-                        continue;
-                    }
-                }
-#if DEBUG_CONCIDENT
-                SkDebugf("%s coincident found=%d %1.9g %1.9g\n", __FUNCTION__, testOther->fID,
-                        oTest->fOtherT, tTest->fT);
-#endif
-                if (tTest->fT < oTest->fOtherT) {
-                    SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, testOther));
-                } else {
-                    addTCancel(span.fPt, next->fPt, testOther);
-                }
-                MissingSpan missing;
-                missing.fSegment = testOther;
-                checkMultiple->push_back(missing);
-                break;
-            }
-        }
-        oTest = &oSpan;
-        while (++oTest < other->fTs.end()) {
-            if (oTest->fPt != oSpan.fPt || !precisely_equal(oTest->fT, oSpan.fT)) {
-                break;
-            }
-
-        }
-    }
-}
-
-// if pair of spans on either side of tiny have the same end point and mid point, mark
-// them as parallel
-void SkOpSegment::checkTiny() {
-    SkSTArray<kMissingSpanCount, MissingSpan, true> missingSpans;
-    SkOpSpan* thisSpan = fTs.begin() - 1;
-    const SkOpSpan* endSpan = fTs.end() - 1;  // last can't be tiny
-    while (++thisSpan < endSpan) {
-        if (!thisSpan->fTiny) {
-            continue;
-        }
-        SkOpSpan* nextSpan = thisSpan + 1;
-        double thisT = thisSpan->fT;
-        double nextT = nextSpan->fT;
-        if (thisT == nextT) {
-            continue;
-        }
-        SkASSERT(thisT < nextT);
-        SkASSERT(thisSpan->fPt == nextSpan->fPt);
-        SkOpSegment* thisOther = thisSpan->fOther;
-        SkOpSegment* nextOther = nextSpan->fOther;
-        int oIndex = thisSpan->fOtherIndex;
-        for (int oStep = -1; oStep <= 1; oStep += 2) {
-            int oEnd = thisOther->nextExactSpan(oIndex, oStep);
-            if (oEnd < 0) {
-                continue;
-            }
-            const SkOpSpan& oSpan = thisOther->span(oEnd);
-            int nIndex = nextSpan->fOtherIndex;
-            for (int nStep = -1; nStep <= 1; nStep += 2) {
-                int nEnd = nextOther->nextExactSpan(nIndex, nStep);
-                if (nEnd < 0) {
-                    continue;
-                }
-                const SkOpSpan& nSpan = nextOther->span(nEnd);
-                if (oSpan.fPt != nSpan.fPt) {
-                    continue;
-                }
-                double oMidT = (thisSpan->fOtherT + oSpan.fT) / 2;
-                const SkPoint& oPt = thisOther->ptAtT(oMidT);
-                double nMidT = (nextSpan->fOtherT + nSpan.fT) / 2;
-                const SkPoint& nPt = nextOther->ptAtT(nMidT);
-                if (!AlmostEqualUlps(oPt, nPt)) {
-                    continue;
-                }
-#if DEBUG_CHECK_TINY
-                SkDebugf("%s [%d] add coincidence [%d] [%d]\n", __FUNCTION__, fID,
-                    thisOther->fID, nextOther->fID);
-#endif
-                // this segment is missing a entry that the other contains
-                // remember so we can add the missing one and recompute the indices
-                MissingSpan& missing = missingSpans.push_back();
-                SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
-                missing.fSegment = thisOther;
-                missing.fT = thisSpan->fOtherT;
-                SkASSERT(this != nextOther);
-                missing.fOther = nextOther;
-                missing.fOtherT = nextSpan->fOtherT;
-                missing.fPt = thisSpan->fPt;
-            }
-        }
-    }
-    int missingCount = missingSpans.count();
-    if (!missingCount) {
-        return;
-    }
-    for (int index = 0; index < missingCount; ++index)  {
-        MissingSpan& missing = missingSpans[index];
-        if (missing.fSegment != missing.fOther) {
-            missing.fSegment->addTPair(missing.fT, missing.fOther, missing.fOtherT, false,
-                    missing.fPt);
-        }
-    }
-    // OPTIMIZE: consolidate to avoid multiple calls to fix index
-    for (int index = 0; index < missingCount; ++index)  {
-        MissingSpan& missing = missingSpans[index];
-        missing.fSegment->fixOtherTIndex();
-        missing.fOther->fixOtherTIndex();
-    }
-}
-
-bool SkOpSegment::coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const {
-    int count = this->count();
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = this->span(index);
-        if (span.fOther != other) {
-            continue;
-        }
-        if (span.fPt == pt) {
-            continue;
-        }
-        if (!AlmostEqualUlps(span.fPt, pt)) {
-            continue;
-        }
-        if (fVerb != SkPath::kCubic_Verb) {
-            return true;
-        }
-        double tInterval = t - span.fT;
-        double tMid = t - tInterval / 2;
-        SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
-        return midPt.approximatelyEqual(xyAtT(t));
-    }
-    return false;
-}
-
-bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
-        int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
-    SkASSERT(span->fT == 0 || span->fT == 1);
-    SkASSERT(span->fOtherT == 0 || span->fOtherT == 1);
-    const SkOpSpan* otherSpan = &other->span(oEnd);
-    double refT = otherSpan->fT;
-    const SkPoint& refPt = otherSpan->fPt;
-    const SkOpSpan* lastSpan = &other->span(step > 0 ? other->count() - 1 : 0);
-    do {
-        const SkOpSegment* match = span->fOther;
-        if (match == otherSpan->fOther) {
-            // find start of respective spans and see if both have winding
-            int startIndex, endIndex;
-            if (span->fOtherT == 1) {
-                endIndex = span->fOtherIndex;
-                startIndex = match->nextExactSpan(endIndex, -1);
-            } else {
-                startIndex = span->fOtherIndex;
-                endIndex = match->nextExactSpan(startIndex, 1);
-            }
-            const SkOpSpan& startSpan = match->span(startIndex);
-            if (startSpan.fWindValue != 0) {
-                // draw ray from endSpan.fPt perpendicular to end tangent and measure distance
-                // to other segment.
-                const SkOpSpan& endSpan = match->span(endIndex);
-                SkDLine ray;
-                SkVector dxdy;
-                if (span->fOtherT == 1) {
-                    ray.fPts[0].set(startSpan.fPt);
-                    dxdy = match->dxdy(startIndex);
-                } else {
-                    ray.fPts[0].set(endSpan.fPt);
-                    dxdy = match->dxdy(endIndex);
-                }
-                ray.fPts[1].fX = ray.fPts[0].fX + dxdy.fY;
-                ray.fPts[1].fY = ray.fPts[0].fY - dxdy.fX;
-                SkIntersections i;
-                int roots = (i.*CurveRay[SkPathOpsVerbToPoints(other->verb())])(other->pts(), ray);
-                for (int index = 0; index < roots; ++index) {
-                    if (ray.fPts[0].approximatelyEqual(i.pt(index))) {
-                        double matchMidT = (match->span(startIndex).fT
-                                + match->span(endIndex).fT) / 2;
-                        SkPoint matchMidPt = match->ptAtT(matchMidT);
-                        double otherMidT = (i[0][index] + other->span(oStart).fT) / 2;
-                        SkPoint otherMidPt = other->ptAtT(otherMidT);
-                        if (SkDPoint::ApproximatelyEqual(matchMidPt, otherMidPt)) {
-                            *startPt = startSpan.fPt;
-                            *endPt = endSpan.fPt;
-                            *endT = endSpan.fT;
-                            return true;
-                        }
-                    }
-                }
-            }
-            return false;
-        }
-        if (otherSpan == lastSpan) {
-            break;
-        }
-        otherSpan += step;
-    } while (otherSpan->fT == refT || otherSpan->fPt == refPt);
-    return false;
-}
-
-int SkOpSegment::findEndSpan(int endIndex) const {
-    const SkOpSpan* span = &fTs[--endIndex];
-    const SkPoint& lastPt = span->fPt;
-    double endT = span->fT;
-    do {
-        span = &fTs[--endIndex];
-    } while (SkDPoint::ApproximatelyEqual(span->fPt, lastPt) && (span->fT == endT || span->fTiny));
-    return endIndex + 1;
+    return closestDistSq;
 }
 
 /*
@@ -3029,71 +541,57 @@
  The Opp variable name part designates that the value is for the Opposite operator.
  Opposite values result from combining coincident spans.
  */
-SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
-                                     bool* unsortable, SkPathOp op, const int xorMiMask,
-                                     const int xorSuMask) {
-    const int startIndex = *nextStart;
-    const int endIndex = *nextEnd;
-    SkASSERT(startIndex != endIndex);
-    SkDEBUGCODE(const int count = fTs.count());
-    SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
-    int step = SkSign32(endIndex - startIndex);
-    *nextStart = startIndex;
-    SkOpSegment* other = isSimple(nextStart, &step);
-    if (other) 
-    {
+SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
+        SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op, int xorMiMask, int xorSuMask) {
+    SkOpSpanBase* start = *nextStart;
+    SkOpSpanBase* end = *nextEnd;
+    SkASSERT(start != end);
+    int step = start->step(end);
+    SkOpSegment* other = this->isSimple(nextStart, &step);  // advances nextStart
+    if (other) {
     // 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) {
+        SkOpSpan* startSpan = start->starter(end);
+        if (startSpan->done()) {
             return NULL;
         }
-        markDoneBinary(min);
-        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());
-        if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
-            *unsortable = true;
-            return NULL;
-        }
+        markDone(startSpan);
+        *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
         return other;
     }
-    const int end = nextExactSpan(startIndex, step);
-    SkASSERT(end >= 0);
-    SkASSERT(startIndex - endIndex != 0);
-    SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+    SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
+    SkASSERT(endNear == end);  // is this ever not end?
+    SkASSERT(endNear);
+    SkASSERT(start != endNear);
+    SkASSERT((start->t() < endNear->t()) ^ (step < 0));
     // more than one viable candidate -- measure angles to find best
-
-    int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp);
+    int calcWinding = computeSum(start, endNear, SkOpAngle::kBinaryOpp);
     bool sortable = calcWinding != SK_NaN32;
     if (!sortable) {
         *unsortable = true;
-        markDoneBinary(SkMin32(startIndex, endIndex));
+        markDone(start->starter(end));
         return NULL;
     }
-    SkOpAngle* angle = spanToAngle(end, startIndex);
+    SkOpAngle* angle = this->spanToAngle(end, start);
     if (angle->unorderable()) {
         *unsortable = true;
-        markDoneBinary(SkMin32(startIndex, endIndex));
+        markDone(start->starter(end));
         return NULL;
     }
 #if DEBUG_SORT
     SkDebugf("%s\n", __FUNCTION__);
     angle->debugLoop();
 #endif
-    int sumMiWinding = updateWinding(endIndex, startIndex);
+    int sumMiWinding = updateWinding(end, start);
     if (sumMiWinding == SK_MinS32) {
         *unsortable = true;
-        markDoneBinary(SkMin32(startIndex, endIndex));
+        markDone(start->starter(end));
         return NULL;
     }
-    int sumSuWinding = updateOppWinding(endIndex, startIndex);
+    int sumSuWinding = updateOppWinding(end, start);
     if (operand()) {
         SkTSwap<int>(sumMiWinding, sumSuWinding);
     }
@@ -3110,11 +608,6 @@
         if (activeAngle) {
             ++activeCount;
             if (!foundAngle || (foundDone && activeCount & 1)) {
-                if (nextSegment->isTiny(nextAngle)) {
-                    *unsortable = true;
-                    markDoneBinary(SkMin32(startIndex, endIndex));
-                    return NULL;
-                }
                 foundAngle = nextAngle;
                 foundDone = nextSegment->done(nextAngle);
             }
@@ -3122,30 +615,24 @@
         if (nextSegment->done()) {
             continue;
         }
-        if (nextSegment->isTiny(nextAngle)) {
-            continue;
-        }
         if (!activeAngle) {
-            (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
+            (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
         }
-        SkOpSpan* last = nextAngle->lastMarked();
+        SkOpSpanBase* last = nextAngle->lastMarked();
         if (last) {
             SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
             *chase->append() = last;
 #if DEBUG_WINDING
-            SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
-                    last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
-                    last->fSmall);
+            SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
+                    last->segment()->debugID(), last->debugID());
+            if (!last->final()) {
+                SkDebugf(" windSum=%d", last->upCast()->windSum());
+            }
+            SkDebugf("\n");
 #endif
         }
     } while ((nextAngle = nextAngle->next()) != angle);
-#if DEBUG_ANGLE
-    if (foundAngle) {
-        foundAngle->debugSameAs(foundAngle);
-    }
-#endif
-
-    markDoneBinary(SkMin32(startIndex, endIndex));
+    start->segment()->markDone(start->starter(end));
     if (!foundAngle) {
         return NULL;
     }
@@ -3159,62 +646,55 @@
     return nextSegment;
 }
 
-SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart,
-                                          int* nextEnd, bool* unsortable) {
-    const int startIndex = *nextStart;
-    const int endIndex = *nextEnd;
-    SkASSERT(startIndex != endIndex);
-    SkDEBUGCODE(const int count = fTs.count());
-    SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
-    int step = SkSign32(endIndex - startIndex);
-    *nextStart = startIndex;
-    SkOpSegment* other = isSimple(nextStart, &step);
-    if (other) 
-    {
+SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpanBase*>* chase,
+        SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable) {
+    SkOpSpanBase* start = *nextStart;
+    SkOpSpanBase* end = *nextEnd;
+    SkASSERT(start != end);
+    int step = start->step(end);
+    SkOpSegment* other = this->isSimple(nextStart, &step);  // advances nextStart
+    if (other) {
     // 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) {
+        SkOpSpan* startSpan = start->starter(end);
+        if (startSpan->done()) {
             return NULL;
         }
-        markDoneUnary(min);
-        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());
-        if (other->isTiny(SkMin32(*nextStart, *nextEnd))) {
-            *unsortable = true;
-            return NULL;
-        }
+        markDone(startSpan);
+        *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
         return other;
     }
-    const int end = nextExactSpan(startIndex, step);
-    SkASSERT(end >= 0);
-    SkASSERT(startIndex - endIndex != 0);
-    SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+    SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
+    SkASSERT(endNear == end);  // is this ever not end?
+    SkASSERT(endNear);
+    SkASSERT(start != endNear);
+    SkASSERT((start->t() < endNear->t()) ^ (step < 0));
     // more than one viable candidate -- measure angles to find best
-
-    int calcWinding = computeSum(startIndex, end, SkOpAngle::kUnaryWinding);
+    int calcWinding = computeSum(start, endNear, SkOpAngle::kUnaryWinding);
     bool sortable = calcWinding != SK_NaN32;
     if (!sortable) {
         *unsortable = true;
-        markDoneUnary(SkMin32(startIndex, endIndex));
+        markDone(start->starter(end));
         return NULL;
     }
-    SkOpAngle* angle = spanToAngle(end, startIndex);
+    SkOpAngle* angle = this->spanToAngle(end, start);
+    if (angle->unorderable()) {
+        *unsortable = true;
+        markDone(start->starter(end));
+        return NULL;
+    }
 #if DEBUG_SORT
     SkDebugf("%s\n", __FUNCTION__);
     angle->debugLoop();
 #endif
-    int sumWinding = updateWinding(endIndex, startIndex);
+    int sumWinding = updateWinding(end, start);
     SkOpAngle* nextAngle = angle->next();
     const SkOpAngle* foundAngle = NULL;
     bool foundDone = false;
+    // iterate through the angle, and compute everyone's winding
     SkOpSegment* nextSegment;
     int activeCount = 0;
     do {
@@ -3224,11 +704,6 @@
         if (activeAngle) {
             ++activeCount;
             if (!foundAngle || (foundDone && activeCount & 1)) {
-                if (nextSegment->isTiny(nextAngle)) {
-                    *unsortable = true;
-                    markDoneUnary(SkMin32(startIndex, endIndex));
-                    return NULL;
-                }
                 foundAngle = nextAngle;
                 foundDone = nextSegment->done(nextAngle);
             }
@@ -3236,24 +711,24 @@
         if (nextSegment->done()) {
             continue;
         }
-        if (nextSegment->isTiny(nextAngle)) {
-            continue;
-        }
         if (!activeAngle) {
-            nextSegment->markAndChaseDoneUnary(nextAngle->start(), nextAngle->end());
+            (void) nextSegment->markAndChaseDone(nextAngle->start(), nextAngle->end());
         }
-        SkOpSpan* last = nextAngle->lastMarked();
+        SkOpSpanBase* last = nextAngle->lastMarked();
         if (last) {
             SkASSERT(!SkPathOpsDebug::ChaseContains(*chase, last));
             *chase->append() = last;
 #if DEBUG_WINDING
-            SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
-                    last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
-                    last->fSmall);
+            SkDebugf("%s chase.append segment=%d span=%d", __FUNCTION__,
+                    last->segment()->debugID(), last->debugID());
+            if (!last->final()) {
+                SkDebugf(" windSum=%d", last->upCast()->windSum());
+            }
+            SkDebugf("\n");
 #endif
         }
     } while ((nextAngle = nextAngle->next()) != angle);
-    markDoneUnary(SkMin32(startIndex, endIndex));
+    start->segment()->markDone(start->starter(end));
     if (!foundAngle) {
         return NULL;
     }
@@ -3267,57 +742,39 @@
     return nextSegment;
 }
 
-SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsortable) {
-    const int startIndex = *nextStart;
-    const int endIndex = *nextEnd;
-    SkASSERT(startIndex != endIndex);
-    SkDEBUGCODE(int count = fTs.count());
-    SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
-    int step = SkSign32(endIndex - startIndex);
-// Detect cases where all the ends canceled out (e.g.,
-// there is no angle) and therefore there's only one valid connection 
-    *nextStart = startIndex;
-    SkOpSegment* other = isSimple(nextStart, &step);
-    if (other)
-    {
+SkOpSegment* SkOpSegment::findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd,
+        bool* unsortable) {
+    SkOpSpanBase* start = *nextStart;
+    SkOpSpanBase* end = *nextEnd;
+    SkASSERT(start != end);
+    int step = start->step(end);
+    SkOpSegment* other = this->isSimple(nextStart, &step);  // advances nextStart
+    if (other) {
+    // 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) {
+        SkOpSpan* startSpan = start->starter(end);
+        if (startSpan->done()) {
             return NULL;
         }
-        markDone(min, 1);
-        double startT = other->fTs[*nextStart].fT;
-        // 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)) {
-            step = -step;
-            SkDEBUGCODE(firstLoop = false;)
-        }
-        do {
-            *nextEnd = *nextStart;
-            do {
-                *nextEnd += step;
-            } while (precisely_zero(startT - other->fTs[*nextEnd].fT));
-            if (other->fTs[SkMin32(*nextStart, *nextEnd)].fWindValue) {
-                break;
-            }
-            SkASSERT(firstLoop);
-            SkDEBUGCODE(firstLoop = false;)
-            step = -step;
-        } while (true);
-        SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count());
+        markDone(startSpan);
+        *nextEnd = step > 0 ? (*nextStart)->upCast()->next() : (*nextStart)->prev();
         return other;
     }
-    SkASSERT(startIndex - endIndex != 0);
-    SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
-    // parallel block above with presorted version
-    int end = nextExactSpan(startIndex, step);
-    SkASSERT(end >= 0);
-    SkOpAngle* angle = spanToAngle(end, startIndex);
-    SkASSERT(angle);
+    SkDEBUGCODE(SkOpSpanBase* endNear = step > 0 ? (*nextStart)->upCast()->next() \
+            : (*nextStart)->prev());
+    SkASSERT(endNear == end);  // is this ever not end?
+    SkASSERT(endNear);
+    SkASSERT(start != endNear);
+    SkASSERT((start->t() < endNear->t()) ^ (step < 0));
+    SkOpAngle* angle = this->spanToAngle(end, start);
+    if (angle->unorderable()) {
+        *unsortable = true;
+        markDone(start->starter(end));
+        return NULL;
+    }
 #if DEBUG_SORT
     SkDebugf("%s\n", __FUNCTION__);
     angle->debugLoop();
@@ -3325,16 +782,13 @@
     SkOpAngle* nextAngle = angle->next();
     const SkOpAngle* foundAngle = NULL;
     bool foundDone = false;
+    // iterate through the angle, and compute everyone's winding
     SkOpSegment* nextSegment;
     int activeCount = 0;
     do {
         nextSegment = nextAngle->segment();
         ++activeCount;
         if (!foundAngle || (foundDone && activeCount & 1)) {
-            if (nextSegment->isTiny(nextAngle)) {
-                *unsortable = true;
-                return NULL;
-            }
             foundAngle = nextAngle;
             if (!(foundDone = nextSegment->done(nextAngle))) {
                 break;
@@ -3342,7 +796,7 @@
         }
         nextAngle = nextAngle->next();
     } while (nextAngle != angle);
-    markDone(SkMin32(startIndex, endIndex), 1);
+    start->segment()->markDone(start->starter(end));
     if (!foundAngle) {
         return NULL;
     }
@@ -3356,545 +810,110 @@
     return nextSegment;
 }
 
-int SkOpSegment::findStartSpan(int startIndex) const {
-    int index = startIndex;
-    const SkOpSpan* span = &fTs[index];
-    const SkPoint& firstPt = span->fPt;
-    double firstT = span->fT;
-    const SkOpSpan* prior;
-    do {
-        prior = span;
-        span = &fTs[++index];
-    } while (SkDPoint::ApproximatelyEqual(span->fPt, firstPt)
-            && (span->fT == firstT || prior->fTiny));
-    return index;
+SkOpGlobalState* SkOpSegment::globalState() const {
+    return contour()->globalState(); 
 }
 
-int SkOpSegment::findExactT(double t, const SkOpSegment* match) const {
-    int count = this->count();
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = fTs[index];
-        if (span.fT == t && span.fOther == match) {
-            return index;
-        }
-    }
-    SkASSERT(0);
-    return -1;
+void SkOpSegment::init(SkPoint pts[], SkScalar weight, SkOpContour* contour, SkPath::Verb verb) {
+    fContour = contour;
+    fNext = NULL;
+    fPts = pts;
+    fWeight = weight;
+    fVerb = verb;
+    fCubicType = SkDCubic::kUnsplit_SkDCubicType;
+    fCount = 0;
+    fDoneCount = 0;
+    fTopsFound = false;
+    fVisited = false;
+    SkOpSpan* zeroSpan = &fHead;
+    zeroSpan->init(this, NULL, 0, fPts[0]);
+    SkOpSpanBase* oneSpan = &fTail;
+    zeroSpan->setNext(oneSpan);
+    oneSpan->initBase(this, zeroSpan, 1, fPts[SkPathOpsVerbToPoints(fVerb)]);
+    SkDEBUGCODE(fID = globalState()->nextSegmentID());
 }
 
-
-
-int SkOpSegment::findOtherT(double t, const SkOpSegment* match) const {
-    int count = this->count();
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = fTs[index];
-        if (span.fOtherT == t && span.fOther == match) {
-            return index;
+bool SkOpSegment::isClose(double t, const SkOpSegment* opp) const {
+    SkDPoint cPt = this->dPtAtT(t);
+    SkDVector dxdy = (*CurveDSlopeAtT[this->verb()])(this->pts(), this->weight(), t);
+    SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }};
+    SkIntersections i;
+    (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), perp, &i);
+    int used = i.used();
+    for (int index = 0; index < used; ++index) {
+        if (cPt.roughlyEqual(i.pt(index))) {
+            return true;
         }
     }
-    return -1;
+    return false;
 }
 
-int SkOpSegment::findT(double t, const SkPoint& pt, const SkOpSegment* match) const {
-    int count = this->count();
-    // prefer exact matches over approximate matches
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = fTs[index];
-        if (span.fT == t && span.fOther == match) {
-            return index;
-        }
-    }
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = fTs[index];
-        if (approximately_equal_orderable(span.fT, t) && span.fOther == match) {
-            return index;
-        }
-    }
-    // Usually, the pair of ts are an exact match. It's possible that the t values have
-    // been adjusted to make multiple intersections align. In this rare case, look for a
-    // matching point / match pair instead.
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = fTs[index];
-        if (span.fPt == pt && span.fOther == match) {
-            return index;
-        }
-    }
-    SkASSERT(0);
-    return -1;
+bool SkOpSegment::isXor() const {
+    return fContour->isXor();
 }
 
-SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsortable,
-        bool firstPass) {
-    // iterate through T intersections and return topmost
-    // topmost tangent from y-min to first pt is closer to horizontal
-    SkASSERT(!done());
-    int firstT = -1;
-    /* SkPoint topPt = */ activeLeftTop(&firstT);
-    if (firstT < 0) {
-        *unsortable = !firstPass;
-        firstT = 0;
-        while (fTs[firstT].fDone) {
-            SkASSERT(firstT < fTs.count());
-            ++firstT;
-        }
-        *tIndexPtr = firstT;
-        *endIndexPtr = nextExactSpan(firstT, 1);
-        return this;
-    }
-    // sort the edges to find the leftmost
-    int step = 1;
-    int end;
-    if (span(firstT).fDone || (end = nextSpan(firstT, step)) == -1) {
-        step = -1;
-        end = nextSpan(firstT, step);
-        SkASSERT(end != -1);
-    }
-    // if the topmost T is not on end, or is three-way or more, find left
-    // look for left-ness from tLeft to firstT (matching y of other)
-    SkASSERT(firstT - end != 0);
-    SkOpAngle* markAngle = spanToAngle(firstT, end);
-    if (!markAngle) {
-        markAngle = addSingletonAngles(step);
-    }
-    markAngle->markStops();
-    const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
-            : markAngle->findFirst();
-    if (!baseAngle) {
-        return NULL;  // nothing to do
-    }
-    SkScalar top = SK_ScalarMax;
-    const SkOpAngle* firstAngle = NULL;
-    const SkOpAngle* angle = baseAngle;
-    do {
-        if (!angle->unorderable()) {
-            SkOpSegment* next = angle->segment();
-            SkPathOpsBounds bounds;
-            next->subDivideBounds(angle->end(), angle->start(), &bounds);
-            bool nearSame = AlmostEqualUlps(top, bounds.top());
-            bool lowerSector = !firstAngle || angle->sectorEnd() < firstAngle->sectorStart();
-            bool lesserSector = top > bounds.fTop;
-            if (lesserSector && (!nearSame || lowerSector)) {
-                top = bounds.fTop;
-                firstAngle = angle;
-            }
-        }
-        angle = angle->next();
-    } while (angle != baseAngle);
-    if (!firstAngle) {
-        return NULL;  // if all are unorderable, give up
-    }
-#if DEBUG_SORT
-    SkDebugf("%s\n", __FUNCTION__);
-    firstAngle->debugLoop();
-#endif
-    // skip edges that have already been processed
-    angle = firstAngle;
-    SkOpSegment* leftSegment = NULL;
-    bool looped = false;
-    do {
-        *unsortable = angle->unorderable();
-        if (firstPass || !*unsortable) {
-            leftSegment = angle->segment();
-            *tIndexPtr = angle->end();
-            *endIndexPtr = angle->start();
-            if (!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fDone) {
-                break;
-            }
-        }
-        angle = angle->next();
-        looped = true;
-    } while (angle != firstAngle);
-    if (angle == firstAngle && looped) {
-        return NULL;
-    }
-    if (leftSegment->verb() >= SkPath::kQuad_Verb) {
-        const int tIndex = *tIndexPtr;
-        const int endIndex = *endIndexPtr;
-        bool swap;
-        if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
-    #if DEBUG_SWAP_TOP
-            SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
-                    __FUNCTION__,
-                    swap, leftSegment->debugInflections(tIndex, endIndex),
-                    leftSegment->serpentine(tIndex, endIndex),
-                    leftSegment->controlsContainedByEnds(tIndex, endIndex),
-                    leftSegment->monotonicInY(tIndex, endIndex));
-    #endif
-            if (swap) {
-    // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first
-    // sorted but merely the first not already processed (i.e., not done)
-                SkTSwap(*tIndexPtr, *endIndexPtr);
-            }
-        }
-    }
-    SkASSERT(!leftSegment->fTs[SkMin32(*tIndexPtr, *endIndexPtr)].fTiny);
-    return leftSegment;
-}
-
-int SkOpSegment::firstActive(int tIndex) const {
-    while (fTs[tIndex].fTiny) {
-        SkASSERT(!isCanceled(tIndex));
-        ++tIndex;
-    }
-    return tIndex;
-}
-
-// FIXME: not crazy about this
-// when the intersections are performed, the other index is into an
-// incomplete array. As the array grows, the indices become incorrect
-// while the following fixes the indices up again, it isn't smart about
-// skipping segments whose indices are already correct
-// assuming we leave the code that wrote the index in the first place
-// FIXME: if called after remove, this needs to correct tiny
-void SkOpSegment::fixOtherTIndex() {
-    int iCount = fTs.count();
-    for (int i = 0; i < iCount; ++i) {
-        SkOpSpan& iSpan = fTs[i];
-        double oT = iSpan.fOtherT;
-        SkOpSegment* other = iSpan.fOther;
-        int oCount = other->fTs.count();
-        SkDEBUGCODE(iSpan.fOtherIndex = -1);
-        for (int o = 0; o < oCount; ++o) {
-            SkOpSpan& oSpan = other->fTs[o];
-            if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) {
-                iSpan.fOtherIndex = o;
-                oSpan.fOtherIndex = i;
-                break;
-            }
-        }
-        SkASSERT(iSpan.fOtherIndex >= 0);
-    }
-}
-
-bool SkOpSegment::inCoincidentSpan(double t, const SkOpSegment* other) const {
-    int foundEnds = 0;
-    int count = this->count();
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = this->span(index);
-        if (span.fCoincident) {
-            foundEnds |= (span.fOther == other) << ((t > span.fT) + (t >= span.fT));
-        }
-    }
-    SkASSERT(foundEnds != 7);
-    return foundEnds == 0x3 || foundEnds == 0x5 || foundEnds == 0x6;  // two bits set
-}
-
-bool SkOpSegment::inconsistentAngle(int maxWinding, int sumWinding, int oppMaxWinding,
-        int oppSumWinding, const SkOpAngle* angle) const {
-    SkASSERT(angle->segment() == this);
-    if (UseInnerWinding(maxWinding, sumWinding)) {
-        maxWinding = sumWinding;
-    }
-    if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
-        oppMaxWinding = oppSumWinding;
-    }
-    return inconsistentWinding(angle, maxWinding, oppMaxWinding);
-}
-
-bool SkOpSegment::inconsistentWinding(const SkOpAngle* angle, int winding,
-        int oppWinding) const {
-    int index = angle->start();
-    int endIndex = angle->end();
-    int min = SkMin32(index, endIndex);
-    int step = SkSign32(endIndex - index);
-    if (inconsistentWinding(min, winding, oppWinding)) {
-        return true;
-    }
-    const SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, &step, &min, NULL))) {
-        if (other->fTs[min].fWindSum != SK_MinS32) {
+SkOpSpanBase* SkOpSegment::markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end) {
+    int step = start->step(end);
+    SkOpSpan* minSpan = start->starter(end);
+    markDone(minSpan);
+    SkOpSpanBase* last = NULL;
+    SkOpSegment* other = this;
+    while ((other = other->nextChase(&start, &step, &minSpan, &last))) {
+        if (other->done()) {
+            SkASSERT(!last);
             break;
         }
-        if (fOperand == other->fOperand) {
-            if (other->inconsistentWinding(min, winding, oppWinding)) {
-                return true;
-            }
-        } else {
-            if (other->inconsistentWinding(min, oppWinding, winding)) {
-                return true;
-            }
+        other->markDone(minSpan);
+    }
+    return last;
+}
+
+bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
+        SkOpSpanBase** lastPtr) {
+    SkOpSpan* spanStart = start->starter(end);
+    int step = start->step(end);
+    bool success = markWinding(spanStart, winding);
+    SkOpSpanBase* last = NULL;
+    SkOpSegment* other = this;
+    while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
+        if (spanStart->windSum() != SK_MinS32) {
+            SkASSERT(spanStart->windSum() == winding);
+            SkASSERT(!last);
+            break;
         }
+        (void) other->markWinding(spanStart, winding);
     }
-    return false;
-}
-
-bool SkOpSegment::inconsistentWinding(int index, int winding, int oppWinding) const {
-    SkASSERT(winding || oppWinding);
-    double referenceT = this->span(index).fT;
-    int lesser = index;
-    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
-        if (inconsistentWinding(__FUNCTION__, lesser, winding, oppWinding)) {
-            return true;
-        }
+    if (lastPtr) {
+        *lastPtr = last;
     }
-    do {
-        if (inconsistentWinding(__FUNCTION__, index, winding, oppWinding)) {
-            return true;
-        }
-   } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
-    return false;
-}
-
-bool SkOpSegment::inconsistentWinding(const char* funName, int tIndex, int winding,
-        int oppWinding) const {
-    const SkOpSpan& span = this->span(tIndex);
-    if (span.fDone && !span.fSmall) {
-        return false;
-    }
-    return (span.fWindSum != SK_MinS32 && span.fWindSum != winding)
-            || (span.fOppSum != SK_MinS32 && span.fOppSum != oppWinding);
-}
-
-void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
-    fDoneSpans = 0;
-    fOperand = operand;
-    fXor = evenOdd;
-    fPts = pts;
-    fVerb = verb;
-    fLoop = fMultiples = fSmall = fTiny = false;
-}
-
-void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
-    int local = spanSign(start, end);
-    SkDEBUGCODE(bool success);
-    if (angleIncludeType == SkOpAngle::kBinarySingle) {
-        int oppLocal = oppSign(start, end);
-        SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, oppLocal, NULL);
-    // OPTIMIZATION: the reverse mark and chase could skip the first marking
-        SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, oppLocal, NULL);
-    } else {
-        SkDEBUGCODE(success =) markAndChaseWinding(start, end, local, NULL);
-    // OPTIMIZATION: the reverse mark and chase could skip the first marking
-        SkDEBUGCODE(success |=) markAndChaseWinding(end, start, local, NULL);
-    }
-    SkASSERT(success);
-}
-
-/*
-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.
-*/
-bool SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
-                              int oppWind, SkScalar hitOppDx) {
-    SkASSERT(hitDx || !winding);
-    SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
-    SkASSERT(dx);
-    int windVal = windValue(SkMin32(start, end));
-#if DEBUG_WINDING_AT_T
-    SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
-            hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
-#endif
-    int sideWind = winding + (dx < 0 ? windVal : -windVal);
-    if (abs(winding) < abs(sideWind)) {
-        winding = sideWind;
-    }
-    SkDEBUGCODE(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;
-        }
-    }
-#if DEBUG_WINDING_AT_T
-    SkDebugf(" winding=%d oppWind=%d\n", winding, oppWind);
-#endif
-    // if this fails to mark (because the edges are too small) inform caller to try again
-    bool success = markAndChaseWinding(start, end, winding, oppWind, NULL);
-    // OPTIMIZATION: the reverse mark and chase could skip the first marking
-    success |= markAndChaseWinding(end, start, winding, oppWind, NULL);
     return success;
 }
 
-bool SkOpSegment::inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const {
-    if (!baseAngle->inLoop()) {
-        return false;
-    }
-    int index = *indexPtr;
-    SkOpAngle* from = fTs[index].fFromAngle;
-    SkOpAngle* to = fTs[index].fToAngle;
-    while (++index < spanCount) {
-        SkOpAngle* nextFrom = fTs[index].fFromAngle;
-        SkOpAngle* nextTo = fTs[index].fToAngle;
-        if (from != nextFrom || to != nextTo) {
-            break;
-        }
-    }
-    *indexPtr = index;
-    return true;
-}
-
-// OPTIMIZE: successive calls could start were the last leaves off
-// or calls could specialize to walk forwards or backwards
-bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
-    int tCount = fTs.count();
-    for (int index = 0; index < tCount; ++index) {
-        const SkOpSpan& span = fTs[index];
-        if (approximately_zero(startT - span.fT) && pt == span.fPt) {
-            return false;
-        }
-    }
-    return true;
-}
-
-
-SkOpSegment* SkOpSegment::isSimple(int* end, int* step) {
-    return nextChase(end, step, NULL, NULL);
-}
-
-bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
-    int start = angle->start();
-    int end = angle->end();
-    const SkOpSpan& mSpan = fTs[SkMin32(start, end)];
-    return mSpan.fTiny;
-}
-
-bool SkOpSegment::isTiny(int index) const {
-    return fTs[index].fTiny;
-}
-
-// look pair of active edges going away from coincident edge
-// one of them should be the continuation of other
-// if both are active, look to see if they both the connect to another coincident pair
-// if at least one is a line, then make the pair coincident
-// if neither is a line, test for coincidence
-bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt,
-        int step, bool cancel) {
-    int otherTIndex = other->findT(otherT, otherPt, this);
-    int next = other->nextExactSpan(otherTIndex, step);
-    int otherMin = SkMin32(otherTIndex, next);
-    int otherWind = other->span(otherMin).fWindValue;
-    if (otherWind == 0) {
-        return false;
-    }
-    if (next < 0) {
-        return false;  // can happen if t values were adjusted but coincident ts were not
-    }
-    int tIndex = 0;
-    do {
-        SkOpSpan* test = &fTs[tIndex];
-        SkASSERT(test->fT == 0);
-        if (test->fOther == other || test->fOtherT != 1) {
-            continue;
-        }
-        SkPoint startPt, endPt;
-        double endT;
-        if (findCoincidentMatch(test, other, otherTIndex, next, step, &startPt, &endPt, &endT)) {
-            SkOpSegment* match = test->fOther;
-            if (cancel) {
-                match->addTCancel(startPt, endPt, other);
-            } else {
-                if (!match->addTCoincident(startPt, endPt, endT, other)) {
+bool SkOpSegment::markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end,
+        int winding, int oppWinding, SkOpSpanBase** lastPtr) {
+    SkOpSpan* spanStart = start->starter(end);
+    int step = start->step(end);
+    bool success = markWinding(spanStart, winding, oppWinding);
+    SkOpSpanBase* last = NULL;
+    SkOpSegment* other = this;
+    while ((other = other->nextChase(&start, &step, &spanStart, &last))) {
+        if (spanStart->windSum() != SK_MinS32) {
+            if (this->operand() == other->operand()) {
+                if (spanStart->windSum() != winding || spanStart->oppSum() != oppWinding) {
+                    this->globalState()->setWindingFailed();
                     return false;
                 }
-            }
-            return true;
-        }
-    } while (fTs[++tIndex].fT == 0);
-    return false;
-}
-
-// this span is excluded by the winding rule -- chase the ends
-// as long as they are unambiguous to mark connections as done
-// and give them the same winding value
-
-SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
-    int step = SkSign32(endIndex - index);
-    int min = SkMin32(index, endIndex);
-    markDoneBinary(min);
-    SkOpSpan* last = NULL;
-    SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, &step, &min, &last))) {
-        if (other->done()) {
-            SkASSERT(!last);
-            break;
-        }
-        other->markDoneBinary(min);
-    }
-    return last;
-}
-
-SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
-    int step = SkSign32(endIndex - index);
-    int min = SkMin32(index, endIndex);
-    markDoneUnary(min);
-    SkOpSpan* last = NULL;
-    SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, &step, &min, &last))) {
-        if (other->done()) {
-            SkASSERT(!last);
-            break;
-        }
-        other->markDoneUnary(min);
-    }
-    return last;
-}
-
-bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, SkOpSpan** lastPtr) {
-    int index = angle->start();
-    int endIndex = angle->end();
-    return markAndChaseWinding(index, endIndex, winding, lastPtr);
-}
-
-bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, SkOpSpan** lastPtr) {
-    int min = SkMin32(index, endIndex);
-    int step = SkSign32(endIndex - index);
-    bool success = markWinding(min, winding);
-    SkOpSpan* last = NULL;
-    SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, &step, &min, &last))) {
-        if (other->fTs[min].fWindSum != SK_MinS32) {
-            SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
-            SkASSERT(!last);
-            break;
-        }
-        (void) other->markWinding(min, winding);
-    }
-    if (lastPtr) {
-        *lastPtr = last;
-    }
-    return success;
-}
-
-bool SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding, int oppWinding,
-        SkOpSpan** lastPtr) {
-    int min = SkMin32(index, endIndex);
-    int step = SkSign32(endIndex - index);
-    bool success = markWinding(min, winding, oppWinding);
-    SkOpSpan* last = NULL;
-    SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, &step, &min, &last))) {
-        if (other->fTs[min].fWindSum != SK_MinS32) {
-#ifdef SK_DEBUG
-            if (!other->fTs[min].fLoop) {
-                if (fOperand == other->fOperand) {
-// FIXME: this is probably a bug -- rects4 asserts here
-//                    SkASSERT(other->fTs[min].fWindSum == winding);
-// FIXME: this is probably a bug -- rects3 asserts here
-//                    SkASSERT(other->fTs[min].fOppSum == oppWinding);
-                } else {
-// FIXME: this is probably a bug -- issue414409b asserts here
-//                    SkASSERT(other->fTs[min].fWindSum == oppWinding);
-// FIXME: this is probably a bug -- skpwww_joomla_org_23 asserts here
-//                    SkASSERT(other->fTs[min].fOppSum == winding);
-                }
+            } else {
+                SkASSERT(spanStart->windSum() == oppWinding);
+                SkASSERT(spanStart->oppSum() == winding);
             }
             SkASSERT(!last);
-#endif
             break;
         }
-        if (fOperand == other->fOperand) {
-            (void) other->markWinding(min, winding, oppWinding);
+        if (this->operand() == other->operand()) {
+            (void) other->markWinding(spanStart, winding, oppWinding);
         } else {
-            (void) other->markWinding(min, oppWinding, winding);
+            (void) other->markWinding(spanStart, oppWinding, winding);
         }
     }
     if (lastPtr) {
@@ -3903,33 +922,29 @@
     return success;
 }
 
-bool SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding,
-        SkOpSpan** lastPtr) {
-    int start = angle->start();
-    int end = angle->end();
-    return markAndChaseWinding(start, end, winding, oppWinding, lastPtr);
-}
-
-SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
+SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle) {
     SkASSERT(angle->segment() == this);
     if (UseInnerWinding(maxWinding, sumWinding)) {
         maxWinding = sumWinding;
     }
-    SkOpSpan* last;
-    SkAssertResult(markAndChaseWinding(angle, maxWinding, &last));
+    SkOpSpanBase* last;
+    (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, &last);
 #if DEBUG_WINDING
     if (last) {
-        SkDebugf("%s last id=%d windSum=", __FUNCTION__,
-                last->fOther->fTs[last->fOtherIndex].fOther->debugID());
-        SkPathOpsDebug::WindingPrintf(last->fWindSum);
-        SkDebugf(" small=%d\n", last->fSmall);
+        SkDebugf("%s last seg=%d span=%d", __FUNCTION__,
+                last->segment()->debugID(), last->debugID());
+        if (!last->final()) {
+            SkDebugf(" windSum=");
+            SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
+        }
+        SkDebugf("\n");
     }
 #endif
     return last;
 }
 
-SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
-                                 int oppSumWinding, const SkOpAngle* angle) {
+SkOpSpanBase* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWinding,
+                                   int oppSumWinding, const SkOpAngle* angle) {
     SkASSERT(angle->segment() == this);
     if (UseInnerWinding(maxWinding, sumWinding)) {
         maxWinding = sumWinding;
@@ -3937,440 +952,137 @@
     if (oppMaxWinding != oppSumWinding && UseInnerWinding(oppMaxWinding, oppSumWinding)) {
         oppMaxWinding = oppSumWinding;
     }
-    SkOpSpan* last;
+    SkOpSpanBase* last = NULL;
     // caller doesn't require that this marks anything
-    (void) markAndChaseWinding(angle, maxWinding, oppMaxWinding, &last);
+    (void) markAndChaseWinding(angle->start(), angle->end(), maxWinding, oppMaxWinding, &last);
 #if DEBUG_WINDING
     if (last) {
-        SkDebugf("%s last id=%d windSum=", __FUNCTION__,
-                last->fOther->fTs[last->fOtherIndex].fOther->debugID());
-        SkPathOpsDebug::WindingPrintf(last->fWindSum);
-        SkDebugf(" small=%d\n", last->fSmall);
+        SkDebugf("%s last segment=%d span=%d", __FUNCTION__,
+                last->segment()->debugID(), last->debugID());
+        if (!last->final()) {
+            SkDebugf(" windSum=");
+            SkPathOpsDebug::WindingPrintf(last->upCast()->windSum());
+        }
+        SkDebugf(" \n");
     }
 #endif
     return last;
 }
 
-// FIXME: this should also mark spans with equal (x,y)
-// This may be called when the segment is already marked done. While this
-// wastes time, it shouldn't do any more than spin through the T spans.
-// OPTIMIZATION: abort on first done found (assuming that this code is
-// always called to mark segments done).
-void SkOpSegment::markDone(int index, int winding) {
-  //  SkASSERT(!done());
+void SkOpSegment::markDone(SkOpSpan* span) {
+    SkASSERT(this == span->segment());
+    if (span->done()) {
+        return;
+    }
+#if DEBUG_MARK_DONE
+    debugShowNewWinding(__FUNCTION__, span, span->windSum(), span->oppSum());
+#endif
+    span->setDone(true);
+    ++fDoneCount;
+    debugValidate();
+}
+
+bool SkOpSegment::markWinding(SkOpSpan* span, int winding) {
+    SkASSERT(this == span->segment());
     SkASSERT(winding);
-    double referenceT = fTs[index].fT;
-    int lesser = index;
-    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
-        markOneDone(__FUNCTION__, lesser, winding);
-    }
-    do {
-        markOneDone(__FUNCTION__, index, winding);
-    } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
-    debugValidate();
-}
-
-void SkOpSegment::markDoneBinary(int index) {
-    double referenceT = fTs[index].fT;
-    int lesser = index;
-    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
-        markOneDoneBinary(__FUNCTION__, lesser);
-    }
-    do {
-        markOneDoneBinary(__FUNCTION__, index);
-    } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
-    debugValidate();
-}
-
-void SkOpSegment::markDoneFinal(int index) {
-    double referenceT = fTs[index].fT;
-    int lesser = index;
-    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
-        markOneDoneFinal(__FUNCTION__, lesser);
-    }
-    do {
-        markOneDoneFinal(__FUNCTION__, index);
-    } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
-    debugValidate();
-}
-
-void SkOpSegment::markDoneUnary(int index) {
-    double referenceT = fTs[index].fT;
-    int lesser = index;
-    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
-        markOneDoneUnary(__FUNCTION__, lesser);
-    }
-    do {
-        markOneDoneUnary(__FUNCTION__, index);
-    } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
-    debugValidate();
-}
-
-void SkOpSegment::markOneDone(const char* funName, int tIndex, int winding) {
-    SkOpSpan* span;
-    (void) markOneWinding(funName, tIndex, winding, &span);  // allowed to do nothing
-    if (span->fDone) {
-        return;
-    }
-    span->fDone = true;
-    ++fDoneSpans;
-}
-
-void SkOpSegment::markOneDoneFinal(const char* funName, int tIndex) {
-    SkOpSpan* span = &fTs[tIndex];
-    if (span->fDone) {
-        return;
-    }
-    span->fDone = true;
-    ++fDoneSpans;
-}
-
-void SkOpSegment::markOneDoneBinary(const char* funName, int tIndex) {
-    SkOpSpan* span = verifyOneWinding(funName, tIndex);
-    if (!span) {
-        return;
-    }
-    SkASSERT(!span->fDone);
-    span->fDone = true;
-    ++fDoneSpans;
-}
-
-void SkOpSegment::markOneDoneUnary(const char* funName, int tIndex) {
-    SkOpSpan* span = verifyOneWindingU(funName, tIndex);
-    if (!span) {
-        return;
-    }
-    if (span->fWindSum == SK_MinS32) {
-        SkDebugf("%s uncomputed\n", __FUNCTION__);
-    }
-    SkASSERT(!span->fDone);
-    span->fDone = true;
-    ++fDoneSpans;
-}
-
-bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding, SkOpSpan** lastPtr) {
-    SkOpSpan* span = &fTs[tIndex];
-    if (lastPtr) {
-        *lastPtr = span;
-    }
-    if (span->fDone && !span->fSmall) {
+    if (span->done()) {
         return false;
     }
 #if DEBUG_MARK_DONE
-    debugShowNewWinding(funName, *span, winding);
+    debugShowNewWinding(__FUNCTION__, span, winding);
 #endif
-    SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
-#if DEBUG_LIMIT_WIND_SUM
-    SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
-#endif
-    span->fWindSum = winding;
+    span->setWindSum(winding);
+    debugValidate();
     return true;
 }
 
-bool SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
-        int oppWinding, SkOpSpan** lastPtr) {
-    SkOpSpan* span = &fTs[tIndex];
-    if (span->fDone && !span->fSmall) {
-        return false;
-    }
-#if DEBUG_MARK_DONE
-    debugShowNewWinding(funName, *span, winding, oppWinding);
-#endif
-    SkASSERT(span->fWindSum == SK_MinS32 || span->fWindSum == winding);
-#if DEBUG_LIMIT_WIND_SUM
-    SkASSERT(abs(winding) <= DEBUG_LIMIT_WIND_SUM);
-#endif
-    span->fWindSum = winding;
-    SkASSERT(span->fOppSum == SK_MinS32 || span->fOppSum == oppWinding);
-#if DEBUG_LIMIT_WIND_SUM
-    SkASSERT(abs(oppWinding) <= DEBUG_LIMIT_WIND_SUM);
-#endif
-    span->fOppSum = oppWinding;
-    debugValidate();
-    if (lastPtr) {
-        *lastPtr = span;
-    }
-    return true;
-}
-
-// from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
-bool SkOpSegment::clockwise(int tStart, int tEnd, bool* swap) const {
-    SkASSERT(fVerb != SkPath::kLine_Verb);
-    SkPoint edge[4];
-    subDivide(tStart, tEnd, edge);
-    int points = SkPathOpsVerbToPoints(fVerb);
-    double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
-    bool sumSet = false;
-    if (fVerb == SkPath::kCubic_Verb) {
-        SkDCubic cubic;
-        cubic.set(edge);
-        double inflectionTs[2];
-        int inflections = cubic.findInflections(inflectionTs);
-        // FIXME: this fixes cubicOp114 and breaks cubicOp58d
-        // the trouble is that cubics with inflections confuse whether the curve breaks towards
-        // or away, which in turn is used to determine if it is on the far right or left.
-        // Probably a totally different approach is in order. At one time I tried to project a
-        // horizontal ray to determine winding, but was confused by how to map the vertically
-        // oriented winding computation over. 
-        if (0 && inflections) {
-            double tLo = this->span(tStart).fT;
-            double tHi = this->span(tEnd).fT;
-            double tLoStart = tLo;
-            for (int index = 0; index < inflections; ++index) {
-                if (between(tLo, inflectionTs[index], tHi)) {
-                    tLo = inflectionTs[index];
-                }
-            }
-            if (tLo != tLoStart && tLo != tHi) {
-                SkDPoint sub[2];
-                sub[0] = cubic.ptAtT(tLo);
-                sub[1].set(edge[3]);
-                SkDPoint ctrl[2];
-                SkDCubic::SubDivide(fPts, sub[0], sub[1], tLo, tHi, ctrl);
-                edge[0] = sub[0].asSkPoint();
-                edge[1] = ctrl[0].asSkPoint();
-                edge[2] = ctrl[1].asSkPoint();
-                sum = (edge[0].fX - edge[3].fX) * (edge[0].fY + edge[3].fY);
-            }
-        }
-        SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
-        if (edge[1].fY < lesser && edge[2].fY < lesser) {
-            SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
-            SkDLine tangent2 = {{ {edge[2].fX, edge[2].fY}, {edge[3].fX, edge[3].fY} }};
-            if (SkIntersections::Test(tangent1, tangent2)) {
-                SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
-                sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
-                sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
-                sumSet = true;
-            }
-        }
-    }
-    if (!sumSet) {
-        for (int idx = 0; idx < points; ++idx){
-            sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
-        }
-    }
-    if (fVerb == SkPath::kCubic_Verb) {
-        SkDCubic cubic;
-        cubic.set(edge);
-         *swap = sum > 0 && !cubic.monotonicInY() && !cubic.serpentine();
-    } else {
-        SkDQuad quad;
-        quad.set(edge);
-        *swap = sum > 0 && !quad.monotonicInY();
-    }
-    return sum <= 0;
-}
-
-bool SkOpSegment::monotonicInY(int tStart, int tEnd) const {
-    SkASSERT(fVerb != SkPath::kLine_Verb);
-    if (fVerb == SkPath::kQuad_Verb) {
-        SkDQuad dst = SkDQuad::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
-        return dst.monotonicInY();
-    }
-    SkASSERT(fVerb == SkPath::kCubic_Verb);
-    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
-    return dst.monotonicInY();
-}
-
-bool SkOpSegment::serpentine(int tStart, int tEnd) const {
-    if (fVerb != SkPath::kCubic_Verb) {
-        return false;
-    }
-    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
-    return dst.serpentine();
-}
-
-SkOpSpan* SkOpSegment::verifyOneWinding(const char* funName, int tIndex) {
-    SkOpSpan& span = fTs[tIndex];
-    if (span.fDone) {
-        return NULL;
-    }
-#if DEBUG_MARK_DONE
-    debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
-#endif
-// If the prior angle in the sort is unorderable, the winding sum may not be computable.
-// To enable the assert, the 'prior is unorderable' state could be
-// piped down to this test, but not sure it's worth it.
-// (Once the sort order is stored in the span, this test may be feasible.)
-//    SkASSERT(span.fWindSum != SK_MinS32);
-//    SkASSERT(span.fOppSum != SK_MinS32);
-    return &span;
-}
-
-SkOpSpan* SkOpSegment::verifyOneWindingU(const char* funName, int tIndex) {
-    SkOpSpan& span = fTs[tIndex];
-    if (span.fDone) {
-        return NULL;
-    }
-#if DEBUG_MARK_DONE
-    debugShowNewWinding(funName, span, span.fWindSum);
-#endif
-// If the prior angle in the sort is unorderable, the winding sum may not be computable.
-// To enable the assert, the 'prior is unorderable' state could be
-// piped down to this test, but not sure it's worth it.
-// (Once the sort order is stored in the span, this test may be feasible.)
-//    SkASSERT(span.fWindSum != SK_MinS32);
-    return &span;
-}
-
-bool SkOpSegment::markWinding(int index, int winding) {
-//    SkASSERT(!done());
-    SkASSERT(winding);
-    double referenceT = fTs[index].fT;
-    int lesser = index;
-    bool success = false;
-    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
-        success |= markOneWinding(__FUNCTION__, lesser, winding, NULL);
-    }
-    do {
-        success |= markOneWinding(__FUNCTION__, index, winding, NULL);
-   } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
-    debugValidate();
-    return success;
-}
-
-bool SkOpSegment::markWinding(int index, int winding, int oppWinding) {
-//    SkASSERT(!done());
+bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) {
+    SkASSERT(this == span->segment());
     SkASSERT(winding || oppWinding);
-    double referenceT = fTs[index].fT;
-    int lesser = index;
-    bool success = false;
-    while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
-        success |= markOneWinding(__FUNCTION__, lesser, winding, oppWinding, NULL);
+    if (span->done()) {
+        return false;
     }
-    do {
-        success |= markOneWinding(__FUNCTION__, index, winding, oppWinding, NULL);
-   } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+#if DEBUG_MARK_DONE
+    debugShowNewWinding(__FUNCTION__, span, winding, oppWinding);
+#endif
+    span->setWindSum(winding);
+    span->setOppSum(oppWinding);
     debugValidate();
-    return success;
-}
-
-void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
-    int nextDoorWind = SK_MaxS32;
-    int nextOppWind = SK_MaxS32;
-    // prefer exact matches
-    if (tIndex > 0) {
-        const SkOpSpan& below = fTs[tIndex - 1];
-        if (below.fT == t) {
-            nextDoorWind = below.fWindValue;
-            nextOppWind = below.fOppValue;
-        }
-    }
-    if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
-        const SkOpSpan& above = fTs[tIndex + 1];
-        if (above.fT == t) {
-            nextDoorWind = above.fWindValue;
-            nextOppWind = above.fOppValue;
-        }
-    }
-    if (nextDoorWind == SK_MaxS32 && tIndex > 0) {
-        const SkOpSpan& below = fTs[tIndex - 1];
-        if (approximately_negative(t - below.fT)) {
-            nextDoorWind = below.fWindValue;
-            nextOppWind = below.fOppValue;
-        }
-    }
-    if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
-        const SkOpSpan& above = fTs[tIndex + 1];
-        if (approximately_negative(above.fT - t)) {
-            nextDoorWind = above.fWindValue;
-            nextOppWind = above.fOppValue;
-        }
-    }
-    if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
-        const SkOpSpan& below = fTs[tIndex - 1];
-        nextDoorWind = below.fWindValue;
-        nextOppWind = below.fOppValue;
-    }
-    if (nextDoorWind != SK_MaxS32) {
-        SkOpSpan& newSpan = fTs[tIndex];
-        newSpan.fWindValue = nextDoorWind;
-        newSpan.fOppValue = nextOppWind;
-        if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
-            newSpan.fDone = true;
-            ++fDoneSpans;
-        }
-    }
-}
-
-bool SkOpSegment::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;
 }
 
-static SkOpSegment* set_last(SkOpSpan** last, const SkOpSpan* endSpan) {
-    if (last && !endSpan->fSmall) {
-        *last = const_cast<SkOpSpan*>(endSpan);  // FIXME: get rid of cast
+bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT,
+        const SkPoint& testPt) const {
+    const SkOpSegment* baseParent = base->segment();
+    if (this == baseParent && this == testParent && precisely_equal(base->fT, testT)) {
+        return true;
+    }
+    if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) {
+        return false;
+    }
+    return !this->ptsDisjoint(base->fT, base->fPt, testT, testPt);
+}
+
+static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) {
+    if (last) {
+        *last = endSpan;
     }
     return NULL;
 }
 
-SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr,
-        SkOpSpan** last) const {
-    int origIndex = *indexPtr;
+SkOpSegment* SkOpSegment::nextChase(SkOpSpanBase** startPtr, int* stepPtr, SkOpSpan** minPtr,
+        SkOpSpanBase** last) const {
+    SkOpSpanBase* origStart = *startPtr;
     int step = *stepPtr;
-    int end = nextExactSpan(origIndex, step);
-    SkASSERT(end >= 0);
-    const SkOpSpan& endSpan = this->span(end);
-    SkOpAngle* angle = step > 0 ? endSpan.fFromAngle : endSpan.fToAngle;
-    int foundIndex;
-    int otherEnd;
+    SkOpSpanBase* endSpan = step > 0 ? origStart->upCast()->next() : origStart->prev();
+    SkASSERT(endSpan);
+    SkOpAngle* angle = step > 0 ? endSpan->fromAngle() : endSpan->upCast()->toAngle();
+    SkOpSpanBase* foundSpan;
+    SkOpSpanBase* otherEnd;
     SkOpSegment* other;
     if (angle == NULL) {
-        if (endSpan.fT != 0 && endSpan.fT != 1) {
+        if (endSpan->t() != 0 && endSpan->t() != 1) {
             return NULL;
         }
-        other = endSpan.fOther;
-        foundIndex = endSpan.fOtherIndex;
-        otherEnd = other->nextExactSpan(foundIndex, step);
+        SkOpPtT* otherPtT = endSpan->ptT()->next();
+        other = otherPtT->segment();
+        foundSpan = otherPtT->span();
+        otherEnd = step > 0 ? foundSpan->upCast()->next() : foundSpan->prev();
     } else {
         int loopCount = angle->loopCount();
         if (loopCount > 2) {
-            return set_last(last, &endSpan);
+            return set_last(last, endSpan);
         }
         const SkOpAngle* next = angle->next();
         if (NULL == next) {
             return NULL;
         }
-        if (angle->sign() != next->sign()) {
 #if DEBUG_WINDING
+        if (angle->debugSign() != next->debugSign() && !angle->segment()->contour()->isXor()
+                && !next->segment()->contour()->isXor()) {
             SkDebugf("%s mismatched signs\n", __FUNCTION__);
-#endif
-        //    return set_last(last, &endSpan);
         }
+#endif
         other = next->segment();
-        foundIndex = end = next->start();
+        foundSpan = endSpan = next->start();
         otherEnd = next->end();
     }
-    int foundStep = foundIndex < otherEnd ? 1 : -1;
+    int foundStep = foundSpan->step(otherEnd);
     if (*stepPtr != foundStep) {
-        return set_last(last, &endSpan);
+        return set_last(last, endSpan);
     }
-    SkASSERT(*indexPtr >= 0);
-    if (otherEnd < 0) {
+    SkASSERT(*startPtr);
+    if (!otherEnd) {
         return NULL;
     }
 //    SkASSERT(otherEnd >= 0);
-#if 1
-    int origMin = origIndex + (step < 0 ? step : 0);
-    const SkOpSpan& orig = this->span(origMin);
-#endif
-    int foundMin = SkMin32(foundIndex, otherEnd);
-#if 1
-    const SkOpSpan& found = other->span(foundMin);
-    if (found.fWindValue != orig.fWindValue || found.fOppValue != orig.fOppValue) {
-          return set_last(last, &endSpan);
+    SkOpSpan* origMin = step < 0 ? origStart->prev() : origStart->upCast();
+    SkOpSpan* foundMin = foundSpan->starter(otherEnd);
+    if (foundMin->windValue() != origMin->windValue()
+            || foundMin->oppValue() != origMin->oppValue()) {
+          return set_last(last, endSpan);
     }
-#endif
-    *indexPtr = foundIndex;
+    *startPtr = foundSpan;
     *stepPtr = foundStep;
     if (minPtr) {
         *minPtr = foundMin;
@@ -4378,101 +1090,330 @@
     return other;
 }
 
-// This has callers for two different situations: one establishes the end
-// of the current span, and one establishes the beginning of the next span
-// (thus the name). When this is looking for the end of the current span,
-// coincidence is found when the beginning Ts contain -step and the end
-// contains step. When it is looking for the beginning of the next, the
-// first Ts found can be ignored and the last Ts should contain -step.
-// OPTIMIZATION: probably should split into two functions
-int SkOpSegment::nextSpan(int from, int step) const {
-    const SkOpSpan& fromSpan = fTs[from];
-    int count = fTs.count();
-    int to = from;
-    while (step > 0 ? ++to < count : --to >= 0) {
-        const SkOpSpan& span = fTs[to];
-        if (approximately_zero(span.fT - fromSpan.fT)) {
+static void clear_visited(SkOpSpanBase* span) {
+    // reset visited flag back to false
+    do {
+        SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
+        while ((ptT = ptT->next()) != stopPtT) {
+            SkOpSegment* opp = ptT->segment();
+            opp->resetVisited();
+        }
+    } while (!span->final() && (span = span->upCast()->next()));
+}
+
+// look for pairs of undetected coincident curves
+// assumes that segments going in have visited flag clear
+// curve/curve intersection should now do a pretty good job of finding coincident runs so 
+// this may be only be necessary for line/curve pairs -- so skip unless this is a line and the
+// the opp is not a line
+void SkOpSegment::missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
+    if (this->verb() != SkPath::kLine_Verb) {
+        return;
+    }
+    if (this->done()) {
+        return;
+    }
+    SkOpSpan* prior = NULL;
+    SkOpSpanBase* spanBase = &fHead;
+    do {
+        SkOpPtT* ptT = spanBase->ptT(), * spanStopPtT = ptT;
+        SkASSERT(ptT->span() == spanBase);
+        while ((ptT = ptT->next()) != spanStopPtT) {
+            if (ptT->deleted()) {
+                continue;
+            }
+            SkOpSegment* opp = ptT->span()->segment();
+            if (opp->verb() == SkPath::kLine_Verb) {
+                continue;
+            }
+            if (opp->done()) {
+                continue;
+            }
+            // when opp is encounted the 1st time, continue; on 2nd encounter, look for coincidence
+            if (!opp->visited()) {
+                continue;
+            }
+            if (spanBase == &fHead) {
+                continue;
+            }
+            SkOpSpan* span = spanBase->upCastable();
+            // FIXME?: this assumes that if the opposite segment is coincident then no more
+            // coincidence needs to be detected. This may not be true.
+            if (span && span->containsCoincidence(opp)) { 
+                continue;
+            }
+            if (spanBase->containsCoinEnd(opp)) {
+                continue;
+            } 
+            SkOpPtT* priorPtT = NULL, * priorStopPtT;
+            // find prior span containing opp segment
+            SkOpSegment* priorOpp = NULL;
+            SkOpSpan* priorTest = spanBase->prev();
+            while (!priorOpp && priorTest) {
+                priorStopPtT = priorPtT = priorTest->ptT();
+                while ((priorPtT = priorPtT->next()) != priorStopPtT) {
+                    if (priorPtT->deleted()) {
+                        continue;
+                    }
+                    SkOpSegment* segment = priorPtT->span()->segment();
+                    if (segment == opp) {
+                        prior = priorTest;
+                        priorOpp = opp;
+                        break;
+                    }
+                }
+                priorTest = priorTest->prev();
+            }
+            if (!priorOpp) {
+                continue;
+            }
+            SkOpPtT* oppStart = prior->ptT();
+            SkOpPtT* oppEnd = spanBase->ptT();
+            bool swapped = priorPtT->fT > ptT->fT;
+            if (swapped) {
+                SkTSwap(priorPtT, ptT);
+                SkTSwap(oppStart, oppEnd);
+            }
+            bool flipped = oppStart->fT > oppEnd->fT;
+            bool coincident;
+            if (coincidences->contains(priorPtT, ptT, oppStart, oppEnd, flipped)) {
+                goto swapBack;
+            }
+            {
+                // average t, find mid pt
+                double midT = (prior->t() + spanBase->t()) / 2;
+                SkPoint midPt = this->ptAtT(midT);
+                coincident = true;
+                // if the mid pt is not near either end pt, project perpendicular through opp seg
+                if (!SkDPoint::ApproximatelyEqual(priorPtT->fPt, midPt)
+                        && !SkDPoint::ApproximatelyEqual(ptT->fPt, midPt)) {
+                    coincident = false;
+                    SkIntersections i;
+                    SkVector dxdy = (*CurveSlopeAtT[fVerb])(this->pts(), this->weight(), midT);
+                    SkDLine ray = {{{midPt.fX, midPt.fY},
+                            {midPt.fX + dxdy.fY, midPt.fY - dxdy.fX}}};
+                    (*CurveIntersectRay[opp->verb()])(opp->pts(), opp->weight(), ray, &i);
+                    // measure distance and see if it's small enough to denote coincidence
+                    for (int index = 0; index < i.used(); ++index) {
+                        SkDPoint oppPt = i.pt(index);
+                        if (oppPt.approximatelyEqual(midPt)) {
+                            SkVector oppDxdy = (*CurveSlopeAtT[opp->verb()])(opp->pts(),
+                                    opp->weight(), i[index][0]);
+                            oppDxdy.normalize();
+                            dxdy.normalize();
+                            SkScalar flatness = SkScalarAbs(dxdy.cross(oppDxdy) / FLT_EPSILON);
+                            coincident |= flatness < 5000;  // FIXME: replace with tuned value
+                        }
+                    }
+                }
+            }
+            if (coincident) {
+            // mark coincidence
+                if (!coincidences->extend(priorPtT, ptT, oppStart, oppEnd)
+                        && !coincidences->extend(oppStart, oppEnd, priorPtT, ptT)) {
+                    coincidences->add(priorPtT, ptT, oppStart, oppEnd, allocator);
+                }
+                clear_visited(&fHead);
+                return;
+            }
+    swapBack:
+            if (swapped) {
+                SkTSwap(priorPtT, ptT);
+            }
+        }
+    } while ((spanBase = spanBase->final() ? NULL : spanBase->upCast()->next()));
+    clear_visited(&fHead);
+}
+
+// if a span has more than one intersection, merge the other segments' span as needed
+void SkOpSegment::moveMultiples() {
+    debugValidate();
+    SkOpSpanBase* test = &fHead;
+    do {
+        int addCount = test->spanAddsCount();
+        SkASSERT(addCount >= 1);
+        if (addCount == 1) {
             continue;
         }
-        return to;
-    }
-    return -1;
-}
-
-// FIXME
-// this returns at any difference in T, vs. a preset minimum. It may be
-// that all callers to nextSpan should use this instead.
-int SkOpSegment::nextExactSpan(int from, int step) const {
-    int to = from;
-    if (step < 0) {
-        const SkOpSpan& fromSpan = fTs[from];
-        while (--to >= 0) {
-            const SkOpSpan& span = fTs[to];
-            if (precisely_negative(fromSpan.fT - span.fT) || span.fTiny) {
+        SkOpPtT* startPtT = test->ptT();
+        SkOpPtT* testPtT = startPtT;
+        do {  // iterate through all spans associated with start
+            SkOpSpanBase* oppSpan = testPtT->span();
+            if (oppSpan->spanAddsCount() == addCount) {
                 continue;
             }
-            return to;
-        }
-    } else {
-        while (fTs[from].fTiny) {
-            from++;
-        }
-        const SkOpSpan& fromSpan = fTs[from];
-        int count = fTs.count();
-        while (++to < count) {
-            const SkOpSpan& span = fTs[to];
-            if (precisely_negative(span.fT - fromSpan.fT)) {
+            if (oppSpan->deleted()) {
                 continue;
             }
-            return to;
-        }
-    }
-    return -1;
-}
-
-void SkOpSegment::pinT(const SkPoint& pt, double* t) {
-    if (pt == fPts[0]) {
-        *t = 0;
-    }
-    int count = SkPathOpsVerbToPoints(fVerb);
-    if (pt == fPts[count]) {
-        *t = 1;
-    }
-}
-
-bool SkOpSegment::reversePoints(const SkPoint& p1, const SkPoint& p2) const {
-    SkASSERT(p1 != p2);
-    int spanCount = count();
-    int p1IndexMin = -1;
-    int p2IndexMax = spanCount;
-    for (int index = 0; index < spanCount; ++index) {
-        const SkOpSpan& span = fTs[index];
-        if (span.fPt == p1) {
-            if (p1IndexMin < 0) {
-                p1IndexMin = index;
+            SkOpSegment* oppSegment = oppSpan->segment();
+            if (oppSegment == this) {
+                continue;
             }
-        } else if (span.fPt == p2) {
-            p2IndexMax = index;
-        }
-    }
-    return p1IndexMin > p2IndexMax;
+            // find range of spans to consider merging
+            SkOpSpanBase* oppPrev = oppSpan;
+            SkOpSpanBase* oppFirst = oppSpan;
+            while ((oppPrev = oppPrev->prev())) {
+                if (!roughly_equal(oppPrev->t(), oppSpan->t())) {
+                    break;
+                }
+                if (oppPrev->spanAddsCount() == addCount) {
+                    continue;
+                }
+                if (oppPrev->deleted()) {
+                    continue;
+                }
+                oppFirst = oppPrev;
+            }
+            SkOpSpanBase* oppNext = oppSpan;
+            SkOpSpanBase* oppLast = oppSpan;
+            while ((oppNext = oppNext->final() ? NULL : oppNext->upCast()->next())) {
+                if (!roughly_equal(oppNext->t(), oppSpan->t())) {
+                    break;
+                }
+                if (oppNext->spanAddsCount() == addCount) {
+                    continue;
+                }
+                if (oppNext->deleted()) {
+                    continue;
+                }
+                oppLast = oppNext;
+            }
+            if (oppFirst == oppLast) {
+                continue;
+            }
+            SkOpSpanBase* oppTest = oppFirst;
+            do {
+                if (oppTest == oppSpan) {
+                    continue;
+                }
+                // check to see if the candidate meets specific criteria:
+                // it contains spans of segments in test's loop but not including 'this'
+                SkOpPtT* oppStartPtT = oppTest->ptT();
+                SkOpPtT* oppPtT = oppStartPtT;
+                while ((oppPtT = oppPtT->next()) != oppStartPtT) {
+                    SkOpSegment* oppPtTSegment = oppPtT->segment();
+                    if (oppPtTSegment == this) {
+                        goto tryNextSpan;
+                    }
+                    SkOpPtT* matchPtT = startPtT;
+                    do {
+                        if (matchPtT->segment() == oppPtTSegment) {
+                            goto foundMatch;
+                        }
+                    } while ((matchPtT = matchPtT->next()) != startPtT);
+                    goto tryNextSpan;
+            foundMatch:  // merge oppTest and oppSpan
+                    oppSegment->debugValidate();
+                    if (oppTest == &oppSegment->fTail || oppTest == &oppSegment->fHead) {
+                        SkASSERT(oppSpan != &oppSegment->fHead); // don't expect collapse
+                        SkASSERT(oppSpan != &oppSegment->fTail);
+                        oppTest->merge(oppSpan->upCast());
+                    } else {
+                        oppSpan->merge(oppTest->upCast());
+                    }
+                    oppSegment->debugValidate();
+                    goto checkNextSpan;
+                }
+        tryNextSpan: 
+                ;
+            } while (oppTest != oppLast && (oppTest = oppTest->upCast()->next()));
+        } while ((testPtT = testPtT->next()) != startPtT);
+checkNextSpan: 
+        ;
+    } while ((test = test->final() ? NULL : test->upCast()->next()));
+    debugValidate();
 }
 
-void SkOpSegment::setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt, 
-        SkOpSegment* other) {
-    int count = this->count();
-    for (int index = 0; index < count; ++index) {
-        SkOpSpan &span = fTs[index];
-        if ((startPt == span.fPt || endPt == span.fPt) && other == span.fOther) {
-            span.fCoincident = true;
+// Move nearby t values and pts so they all hang off the same span. Alignment happens later.
+void SkOpSegment::moveNearby() {
+    debugValidate();
+    SkOpSpanBase* spanS = &fHead;
+    do {
+        SkOpSpanBase* test = spanS->upCast()->next();
+        SkOpSpanBase* next;
+        if (spanS->contains(test)) {
+            if (!test->final()) {
+                test->upCast()->detach(spanS->ptT());
+                continue;
+            } else if (spanS != &fHead) {
+                spanS->upCast()->detach(test->ptT());
+                spanS = test;
+                continue;
+            }
         }
-    }
+        do {  // iterate through all spans associated with start
+            SkOpPtT* startBase = spanS->ptT();
+            next = test->final() ? NULL : test->upCast()->next();
+            do {
+                SkOpPtT* testBase = test->ptT();
+                do {
+                    if (startBase == testBase) {
+                        goto checkNextSpan;
+                    }
+                    if (testBase->duplicate()) {
+                        continue;
+                    }
+                    if (this->match(startBase, testBase->segment(), testBase->fT, testBase->fPt)) {
+                        if (test == &this->fTail) {
+                            if (spanS == &fHead) {
+                                debugValidate();
+                                return;  // if this span has collapsed, remove it from parent
+                            }
+                            this->fTail.merge(spanS->upCast());
+                            debugValidate();
+                            return;
+                        }
+                        spanS->merge(test->upCast());
+                        spanS->upCast()->setNext(next);
+                        goto checkNextSpan;
+                    }
+                } while ((testBase = testBase->next()) != test->ptT());
+            } while ((startBase = startBase->next()) != spanS->ptT());
+    checkNextSpan:
+            ;
+        } while ((test = next));
+        spanS = spanS->upCast()->next();
+    } while (!spanS->final());
+    debugValidate();
 }
 
-void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
-        int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
-    int deltaSum = spanSign(index, endIndex);
-    int oppDeltaSum = oppSign(index, endIndex);
+bool SkOpSegment::operand() const {
+    return fContour->operand();
+}
+
+bool SkOpSegment::oppXor() const {
+    return fContour->oppXor();
+}
+
+bool SkOpSegment::ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const {
+    if (fVerb == SkPath::kLine_Verb) {
+        return false;
+    }
+    // quads (and cubics) can loop back to nearly a line so that an opposite curve
+    // hits in two places with very different t values.
+    // OPTIMIZATION: curves could be preflighted so that, for example, something like
+    // 'controls contained by ends' could avoid this check for common curves
+    // 'ends are extremes in x or y' is cheaper to compute and real-world common
+    // on the other hand, the below check is relatively inexpensive
+    double midT = (t1 + t2) / 2;
+    SkPoint midPt = this->ptAtT(midT);
+    double seDistSq = SkTMax(pt1.distanceToSqd(pt2) * 2, FLT_EPSILON * 2);
+    return midPt.distanceToSqd(pt1) > seDistSq || midPt.distanceToSqd(pt2) > seDistSq;
+}
+
+void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
+        int* maxWinding, int* sumWinding) {
+    int deltaSum = SpanSign(start, end);
+    *maxWinding = *sumMiWinding;
+    *sumWinding = *sumMiWinding -= deltaSum;
+    SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+}
+
+void SkOpSegment::setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
+        int* sumSuWinding, int* maxWinding, int* sumWinding, int* oppMaxWinding,
+        int* oppSumWinding) {
+    int deltaSum = SpanSign(start, end);
+    int oppDeltaSum = OppSign(start, end);
     if (operand()) {
         *maxWinding = *sumSuWinding;
         *sumWinding = *sumSuWinding -= deltaSum;
@@ -4484,238 +1425,197 @@
         *oppMaxWinding = *sumSuWinding;
         *oppSumWinding = *sumSuWinding -= oppDeltaSum;
     }
-#if DEBUG_LIMIT_WIND_SUM
-    SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
-    SkASSERT(abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
-#endif
-}
-
-void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding,
-        int* maxWinding, int* sumWinding) {
-    int deltaSum = spanSign(index, endIndex);
-    *maxWinding = *sumMiWinding;
-    *sumWinding = *sumMiWinding -= deltaSum;
-#if DEBUG_LIMIT_WIND_SUM
-    SkASSERT(abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
-#endif
+    SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*sumWinding) <= DEBUG_LIMIT_WIND_SUM);
+    SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(*oppSumWinding) <= DEBUG_LIMIT_WIND_SUM);
 }
 
 void SkOpSegment::sortAngles() {
-    int spanCount = fTs.count();
-    if (spanCount <= 2) {
-        return;
-    }
-    int index = 0;
+    SkOpSpanBase* span = &this->fHead;
     do {
-        SkOpAngle* fromAngle = fTs[index].fFromAngle;
-        SkOpAngle* toAngle = fTs[index].fToAngle;
+        SkOpAngle* fromAngle = span->fromAngle();
+        SkOpAngle* toAngle = span->final() ? NULL : span->upCast()->toAngle();
         if (!fromAngle && !toAngle) {
-            index += 1;
             continue;
         }
-        SkOpAngle* baseAngle = NULL;
-        if (fromAngle) {
-            baseAngle = fromAngle;
-            if (inLoop(baseAngle, spanCount, &index)) {
-                continue;
-            }
-        }
 #if DEBUG_ANGLE
         bool wroteAfterHeader = false;
 #endif
-        if (toAngle) {
-            if (!baseAngle) {
-                baseAngle = toAngle;
-                if (inLoop(baseAngle, spanCount, &index)) {
-                    continue;
-                }
-            } else {
-                SkDEBUGCODE(int newIndex = index);
-                SkASSERT(!inLoop(baseAngle, spanCount, &newIndex) && newIndex == index);
+        SkOpAngle* baseAngle = fromAngle;
+        if (fromAngle && toAngle) {
 #if DEBUG_ANGLE
-                SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
-                        index);
-                wroteAfterHeader = true;
+            SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), span->t(),
+                    span->debugID());
+            wroteAfterHeader = true;
 #endif
-                baseAngle->insert(toAngle);
-            }
+            fromAngle->insert(toAngle);
+        } else if (!fromAngle) {
+            baseAngle = toAngle;
         }
-        SkOpAngle* nextFrom, * nextTo;
-        int firstIndex = index;
+        SkOpPtT* ptT = span->ptT(), * stopPtT = ptT;
         do {
-            SkOpSpan& span = fTs[index];
-            SkOpSegment* other = span.fOther;
-            SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
-            SkOpAngle* oAngle = oSpan.fFromAngle;
+            SkOpSpanBase* oSpan = ptT->span();
+            if (oSpan == span) {
+                continue;
+            }
+            SkOpAngle* oAngle = oSpan->fromAngle();
             if (oAngle) {
 #if DEBUG_ANGLE
                 if (!wroteAfterHeader) {
-                    SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
-                            index);
+                    SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
+                            span->t(), span->debugID());
                     wroteAfterHeader = true;
                 }
 #endif
-                if (!oAngle->loopContains(*baseAngle)) {
+                if (!oAngle->loopContains(baseAngle)) {
                     baseAngle->insert(oAngle);
                 }
             }
-            oAngle = oSpan.fToAngle;
-            if (oAngle) {
+            if (!oSpan->final()) {
+                oAngle = oSpan->upCast()->toAngle();
+                if (oAngle) {
 #if DEBUG_ANGLE
-                if (!wroteAfterHeader) {
-                    SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
-                            index);
-                    wroteAfterHeader = true;
-                }
+                    if (!wroteAfterHeader) {
+                        SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(),
+                                span->t(), span->debugID());
+                        wroteAfterHeader = true;
+                    }
 #endif
-                if (!oAngle->loopContains(*baseAngle)) {
-                    baseAngle->insert(oAngle);
+                    if (!oAngle->loopContains(baseAngle)) {
+                        baseAngle->insert(oAngle);
+                    }
                 }
             }
-            if (++index == spanCount) {
-                break;
+        } while ((ptT = ptT->next()) != stopPtT);
+        if (baseAngle->loopCount() == 1) {
+            span->setFromAngle(NULL);
+            if (toAngle) {
+                span->upCast()->setToAngle(NULL);
             }
-            nextFrom = fTs[index].fFromAngle;
-            nextTo = fTs[index].fToAngle;
-        } while (fromAngle == nextFrom && toAngle == nextTo);
-        if (baseAngle && baseAngle->loopCount() == 1) {
-            index = firstIndex;
-            do {
-                SkOpSpan& span = fTs[index];
-                span.fFromAngle = span.fToAngle = NULL;
-                if (++index == spanCount) {
-                    break;
-                }
-                nextFrom = fTs[index].fFromAngle;
-                nextTo = fTs[index].fToAngle;
-            } while (fromAngle == nextFrom && toAngle == nextTo);
             baseAngle = NULL;
         }
 #if DEBUG_SORT
         SkASSERT(!baseAngle || baseAngle->loopCount() > 1);
 #endif
-    } while (index < spanCount);
+    } while (!span->final() && (span = span->upCast()->next()));
 }
 
 // return true if midpoints were computed
-bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
+bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
+        SkOpCurve* edge) const {
     SkASSERT(start != end);
-    edge[0] = fTs[start].fPt;
+    const SkOpPtT& startPtT = *start->ptT();
+    const SkOpPtT& endPtT = *end->ptT();
+    SkDEBUGCODE(edge->fVerb = fVerb);
+    edge->fPts[0] = startPtT.fPt;
     int points = SkPathOpsVerbToPoints(fVerb);
-    edge[points] = fTs[end].fPt;
+    edge->fPts[points] = endPtT.fPt;
+    edge->fWeight = 1;
     if (fVerb == SkPath::kLine_Verb) {
         return false;
     }
-    double startT = fTs[start].fT;
-    double endT = fTs[end].fT;
+    double startT = startPtT.fT;
+    double endT = endPtT.fT;
     if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
         // don't compute midpoints if we already have them
         if (fVerb == SkPath::kQuad_Verb) {
-            edge[1] = fPts[1];
+            edge->fPts[1] = fPts[1];
+            return false;
+        }
+        if (fVerb == SkPath::kConic_Verb) {
+            edge->fPts[1] = fPts[1];
+            edge->fWeight = fWeight;
             return false;
         }
         SkASSERT(fVerb == SkPath::kCubic_Verb);
         if (start < end) {
-            edge[1] = fPts[1];
-            edge[2] = fPts[2];
+            edge->fPts[1] = fPts[1];
+            edge->fPts[2] = fPts[2];
             return false;
         }
-        edge[1] = fPts[2];
-        edge[2] = fPts[1];
+        edge->fPts[1] = fPts[2];
+        edge->fPts[2] = fPts[1];
         return false;
     }
-    const SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[points].fX, edge[points].fY }};
+    const SkDPoint sub[2] = {{ edge->fPts[0].fX, edge->fPts[0].fY},
+            {edge->fPts[points].fX, edge->fPts[points].fY }};
     if (fVerb == SkPath::kQuad_Verb) {
-        edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
+        edge->fPts[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint();
+    } else if (fVerb == SkPath::kConic_Verb) {
+        edge->fPts[1] = SkDConic::SubDivide(fPts, fWeight, sub[0], sub[1],
+                startT, endT, &edge->fWeight).asSkPoint();
     } else {
         SkASSERT(fVerb == SkPath::kCubic_Verb);
         SkDPoint ctrl[2];
         SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl);
-        edge[1] = ctrl[0].asSkPoint();
-        edge[2] = ctrl[1].asSkPoint();
+        edge->fPts[1] = ctrl[0].asSkPoint();
+        edge->fPts[2] = ctrl[1].asSkPoint();
     }
     return true;
 }
 
-// return true if midpoints were computed
-bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const {
+bool SkOpSegment::subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end,
+        SkDCurve* edge) const {
     SkASSERT(start != end);
-    (*result)[0].set(fTs[start].fPt);
+    const SkOpPtT& startPtT = *start->ptT();
+    const SkOpPtT& endPtT = *end->ptT();
+    SkDEBUGCODE(edge->fVerb = fVerb);
+    edge->fCubic[0].set(startPtT.fPt);
     int points = SkPathOpsVerbToPoints(fVerb);
-    (*result)[points].set(fTs[end].fPt);
+    edge->fCubic[points].set(endPtT.fPt);
     if (fVerb == SkPath::kLine_Verb) {
         return false;
     }
-    double startT = fTs[start].fT;
-    double endT = fTs[end].fT;
+    double startT = startPtT.fT;
+    double endT = endPtT.fT;
     if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) {
         // don't compute midpoints if we already have them
         if (fVerb == SkPath::kQuad_Verb) {
-            (*result)[1].set(fPts[1]);
+            edge->fLine[1].set(fPts[1]);
+            return false;
+        }
+        if (fVerb == SkPath::kConic_Verb) {
+            edge->fConic[1].set(fPts[1]);
+            edge->fConic.fWeight = fWeight;
             return false;
         }
         SkASSERT(fVerb == SkPath::kCubic_Verb);
-        if (start < end) {
-            (*result)[1].set(fPts[1]);
-            (*result)[2].set(fPts[2]);
+        if (startT == 0) {
+            edge->fCubic[1].set(fPts[1]);
+            edge->fCubic[2].set(fPts[2]);
             return false;
         }
-        (*result)[1].set(fPts[2]);
-        (*result)[2].set(fPts[1]);
+        edge->fCubic[1].set(fPts[2]);
+        edge->fCubic[2].set(fPts[1]);
         return false;
     }
     if (fVerb == SkPath::kQuad_Verb) {
-        (*result)[1] = SkDQuad::SubDivide(fPts, (*result)[0], (*result)[2], startT, endT);
+        edge->fQuad[1] = SkDQuad::SubDivide(fPts, edge->fQuad[0], edge->fQuad[2], startT, endT);
+    } else if (fVerb == SkPath::kConic_Verb) {
+        edge->fConic[1] = SkDConic::SubDivide(fPts, fWeight, edge->fQuad[0], edge->fQuad[2],
+            startT, endT, &edge->fConic.fWeight);
     } else {
         SkASSERT(fVerb == SkPath::kCubic_Verb);
-        SkDCubic::SubDivide(fPts, (*result)[0], (*result)[3], startT, endT, &(*result)[1]);
+        SkDCubic::SubDivide(fPts, edge->fCubic[0], edge->fCubic[3], startT, endT, &edge->fCubic[1]);
     }
     return true;
 }
 
-void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
-    SkPoint edge[4];
-    subDivide(start, end, edge);
-    (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
-}
-
-void SkOpSegment::TrackOutsidePair(SkTArray<SkPoint, true>* outsidePts, const SkPoint& endPt,
-        const SkPoint& startPt) {
-    int outCount = outsidePts->count();
-    if (outCount == 0 || endPt != (*outsidePts)[outCount - 2]) {
-        outsidePts->push_back(endPt);
-        outsidePts->push_back(startPt);
-    }
-}
-
-void SkOpSegment::TrackOutside(SkTArray<SkPoint, true>* outsidePts, const SkPoint& startPt) {
-    int outCount = outsidePts->count();
-    if (outCount == 0 || startPt != (*outsidePts)[outCount - 1]) {
-        outsidePts->push_back(startPt);
-    }
-}
-
-void SkOpSegment::undoneSpan(int* start, int* end) {
-    int tCount = fTs.count();
-    int index;
-    for (index = 0; index < tCount; ++index) {
-        if (!fTs[index].fDone) {
+void SkOpSegment::undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end) {
+    SkOpSpan* span = this->head();
+    do {
+        if (!span->done()) {
             break;
         }
-    }
-    SkASSERT(index < tCount - 1);
-    *start = index;
-    double startT = fTs[index].fT;
-    while (approximately_negative(fTs[++index].fT - startT))
-        SkASSERT(index < tCount);
-    SkASSERT(index < tCount);
-    *end = index;
+    } while ((span = span->next()->upCastable()));
+    SkASSERT(span);
+    *start = span;
+    *end = span->next();
 }
 
-int SkOpSegment::updateOppWinding(int index, int endIndex) const {
-    int lesser = SkMin32(index, endIndex);
-    int oppWinding = oppSum(lesser);
-    int oppSpanWinding = oppSign(index, endIndex);
+int SkOpSegment::updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const {
+    const SkOpSpan* lesser = start->starter(end);
+    int oppWinding = lesser->oppSum();
+    int oppSpanWinding = SkOpSegment::OppSign(start, end);
     if (oppSpanWinding && UseInnerWinding(oppWinding - oppSpanWinding, oppWinding)
             && oppWinding != SK_MaxS32) {
         oppWinding -= oppSpanWinding;
@@ -4724,24 +1624,27 @@
 }
 
 int SkOpSegment::updateOppWinding(const SkOpAngle* angle) const {
-    int startIndex = angle->start();
-    int endIndex = angle->end();
-    return updateOppWinding(endIndex, startIndex);
+    const SkOpSpanBase* startSpan = angle->start();
+    const SkOpSpanBase* endSpan = angle->end();
+    return updateOppWinding(endSpan, startSpan);
 }
 
 int SkOpSegment::updateOppWindingReverse(const SkOpAngle* angle) const {
-    int startIndex = angle->start();
-    int endIndex = angle->end();
-    return updateOppWinding(startIndex, endIndex);
+    const SkOpSpanBase* startSpan = angle->start();
+    const SkOpSpanBase* endSpan = angle->end();
+    return updateOppWinding(startSpan, endSpan);
 }
 
-int SkOpSegment::updateWinding(int index, int endIndex) const {
-    int lesser = SkMin32(index, endIndex);
-    int winding = windSum(lesser);
+int SkOpSegment::updateWinding(SkOpSpanBase* start, SkOpSpanBase* end) {
+    SkOpSpan* lesser = start->starter(end);
+    int winding = lesser->windSum();
+    if (winding == SK_MinS32) {
+        winding = lesser->computeWindSum();
+    }
     if (winding == SK_MinS32) {
         return winding;
     }
-    int spanWinding = spanSign(index, endIndex);
+    int spanWinding = SkOpSegment::SpanSign(start, end);
     if (winding && UseInnerWinding(winding - spanWinding, winding)
             && winding != SK_MaxS32) {
         winding -= spanWinding;
@@ -4749,27 +1652,16 @@
     return winding;
 }
 
-int SkOpSegment::updateWinding(const SkOpAngle* angle) const {
-    int startIndex = angle->start();
-    int endIndex = angle->end();
-    return updateWinding(endIndex, startIndex);
+int SkOpSegment::updateWinding(SkOpAngle* angle) {
+    SkOpSpanBase* startSpan = angle->start();
+    SkOpSpanBase* endSpan = angle->end();
+    return updateWinding(endSpan, startSpan);
 }
 
-int SkOpSegment::updateWindingReverse(int index, int endIndex) const {
-    int lesser = SkMin32(index, endIndex);
-    int winding = windSum(lesser);
-    int spanWinding = spanSign(endIndex, index);
-    if (winding && UseInnerWindingReverse(winding - spanWinding, winding)
-            && winding != SK_MaxS32) {
-        winding -= spanWinding;
-    }
-    return winding;
-}
-
-int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) const {
-    int startIndex = angle->start();
-    int endIndex = angle->end();
-    return updateWindingReverse(endIndex, startIndex);
+int SkOpSegment::updateWindingReverse(const SkOpAngle* angle) {
+    SkOpSpanBase* startSpan = angle->start();
+    SkOpSpanBase* endSpan = angle->end();
+    return updateWinding(startSpan, endSpan);
 }
 
 // OPTIMIZATION: does the following also work, and is it any faster?
@@ -4784,64 +1676,7 @@
     return result;
 }
 
-bool SkOpSegment::UseInnerWindingReverse(int outerWinding, int innerWinding) {
-    SkASSERT(outerWinding != SK_MaxS32);
-    SkASSERT(innerWinding != SK_MaxS32);
-    int absOut = abs(outerWinding);
-    int absIn = abs(innerWinding);
-    bool result = absOut == absIn ? true : absOut < absIn;
-    return result;
-}
-
-int SkOpSegment::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_AT_T
-    SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
-            debugID(), crossOpp, tHit, t(tIndex), winding, windVal);
-#endif
-    // see if a + change in T results in a +/- change in X (compute x'(T))
-    *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
-    if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
-        *dx = fPts[2].fX - fPts[1].fX - *dx;
-    }
-    if (*dx == 0) {
-#if DEBUG_WINDING_AT_T
-        SkDebugf(" dx=0 winding=SK_MinS32\n");
-#endif
-        return SK_MinS32;
-    }
-    if (windVal < 0) {  // reverse sign if opp contour traveled in reverse
-            *dx = -*dx;
-    }
-    if (winding * *dx > 0) {  // if same signs, result is negative
-        winding += *dx > 0 ? -windVal : windVal;
-    }
-#if DEBUG_WINDING_AT_T
-    SkDebugf(" dx=%c winding=%d\n", *dx > 0 ? '+' : '-', winding);
-#endif
-    return winding;
-}
-
 int SkOpSegment::windSum(const SkOpAngle* angle) const {
-    int start = angle->start();
-    int end = angle->end();
-    int index = SkMin32(start, end);
-    return windSum(index);
-}
-
-void SkOpSegment::zeroSpan(SkOpSpan* span) {
-    SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
-    span->fWindValue = 0;
-    span->fOppValue = 0;
-    if (span->fTiny || span->fSmall) {
-        return;
-    }
-    SkASSERT(!span->fDone);
-    span->fDone = true;
-    ++fDoneSpans;
+    const SkOpSpan* minSpan = angle->start()->starter(angle->end());
+    return minSpan->windSum();
 }
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index b4da929..1162c2c 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -9,561 +9,392 @@
 
 #include "SkOpAngle.h"
 #include "SkOpSpan.h"
+#include "SkOpTAllocator.h"
 #include "SkPathOpsBounds.h"
+#include "SkPathOpsCubic.h"
 #include "SkPathOpsCurve.h"
-#include "SkTArray.h"
-#include "SkTDArray.h"
 
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-#include "SkThread.h"
-#endif
-
-struct SkCoincidence;
+struct SkDCurve;
+class SkOpCoincidence;
+class SkOpContour;
+enum class SkOpRayDir;
+struct SkOpRayHit;
 class SkPathWriter;
 
 class SkOpSegment {
 public:
-    SkOpSegment() {
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-        fID = sk_atomic_inc(&SkPathOpsDebug::gSegmentID);
-#endif
-    }
+    enum AllowAlias {
+        kAllowAlias,
+        kNoAlias
+    };
 
     bool operator<(const SkOpSegment& rh) const {
         return fBounds.fTop < rh.fBounds.fTop;
     }
 
-    struct AlignedSpan  {
-        double fOldT;
-        double fT;
-        SkPoint fOldPt;
-        SkPoint fPt;
-        const SkOpSegment* fSegment;
-        const SkOpSegment* fOther1;
-        const SkOpSegment* fOther2;
-    };
+    SkOpAngle* activeAngle(SkOpSpanBase* start, SkOpSpanBase** startPtr, SkOpSpanBase** endPtr,
+                            bool* done);
+    SkOpAngle* activeAngleInner(SkOpSpanBase* start, SkOpSpanBase** startPtr,
+                                       SkOpSpanBase** endPtr, bool* done);
+    SkOpAngle* activeAngleOther(SkOpSpanBase* start, SkOpSpanBase** startPtr,
+                                       SkOpSpanBase** endPtr, bool* done);
+    bool activeOp(SkOpSpanBase* start, SkOpSpanBase* end, int xorMiMask, int xorSuMask,
+                  SkPathOp op);
+    bool activeOp(int xorMiMask, int xorSuMask, SkOpSpanBase* start, SkOpSpanBase* end, SkPathOp op,
+                  int* sumMiWinding, int* sumSuWinding);
+
+    bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end);
+    bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding);
+
+    SkOpSegment* addConic(SkPoint pts[3], SkScalar weight, SkOpContour* parent) {
+        init(pts, weight, parent, SkPath::kConic_Verb);
+        SkDCurve curve;
+        curve.fConic.set(pts, weight);
+        curve.setConicBounds(pts, weight, 0, 1, &fBounds);
+        return this;
+    }
+
+    SkOpSegment* addCubic(SkPoint pts[4], SkOpContour* parent) {
+        init(pts, 1, parent, SkPath::kCubic_Verb);
+        SkDCurve curve;
+        curve.fCubic.set(pts);
+        curve.setCubicBounds(pts, 1, 0, 1, &fBounds);
+        return this;
+    }
+
+    void addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path,
+                    bool active) const;
+
+    SkOpAngle* addEndSpan(SkChunkAlloc* allocator) {
+        SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+        angle->set(&fTail, fTail.prev());
+        fTail.setFromAngle(angle);
+        return angle;
+    }
+
+    SkOpSegment* addLine(SkPoint pts[2], SkOpContour* parent) {
+        init(pts, 1, parent, SkPath::kLine_Verb);
+        fBounds.set(pts, 2);
+        return this;
+    }
+
+    SkOpPtT* addMissing(double t, SkOpSegment* opp, SkChunkAlloc* );
+
+    SkOpAngle* addStartSpan(SkChunkAlloc* allocator) {
+        SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+        angle->set(&fHead, fHead.next());
+        fHead.setToAngle(angle);
+        return angle;
+    }
+
+    SkOpSegment* addQuad(SkPoint pts[3], SkOpContour* parent) {
+        init(pts, 1, parent, SkPath::kQuad_Verb);
+        SkDCurve curve;
+        curve.fQuad.set(pts);
+        curve.setQuadBounds(pts, 1, 0, 1, &fBounds);
+        return this;
+    }
+
+    SkOpPtT* addT(double t, AllowAlias , SkChunkAlloc* );
+
+    void align();
 
     const SkPathOpsBounds& bounds() const {
         return fBounds;
     }
 
-    // OPTIMIZE
-    // when the edges are initially walked, they don't automatically get the prior and next
-    // edges assigned to positions t=0 and t=1. Doing that would remove the need for this check,
-    // and would additionally remove the need for similar checks in condition edges. It would
-    // also allow intersection code to assume end of segment intersections (maybe?)
-    bool complete() const {
-        int count = fTs.count();
-        return count > 1 && fTs[0].fT == 0 && fTs[--count].fT == 1;
+    void bumpCount() {
+        ++fCount;
+    }
+
+    void calcAngles(SkChunkAlloc*);
+    void checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
+    void checkNearCoincidence(SkOpAngle* );
+    bool collapsed() const;
+    static void ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+                              SkOpAngle::IncludeType );
+    static void ComputeOneSumReverse(SkOpAngle* baseAngle, SkOpAngle* nextAngle,
+                                     SkOpAngle::IncludeType );
+    int computeSum(SkOpSpanBase* start, SkOpSpanBase* end, SkOpAngle::IncludeType includeType);
+
+    SkOpContour* contour() const {
+        return fContour;
     }
 
     int count() const {
-        return fTs.count();
+        return fCount;
     }
 
+    void debugAddAngle(double startT, double endT, SkChunkAlloc*);
+    const SkOpAngle* debugAngle(int id) const;
+    SkOpContour* debugContour(int id);
+
+    int debugID() const {
+        return SkDEBUGRELEASE(fID, -1);
+    }
+
+    SkOpAngle* debugLastAngle();
+    const SkOpPtT* debugPtT(int id) const;
+    void debugReset();
+    const SkOpSegment* debugSegment(int id) const;
+
+#if DEBUG_ACTIVE_SPANS
+    void debugShowActiveSpans() const;
+#endif
+#if DEBUG_MARK_DONE
+    void debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding);
+    void debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding, int oppWinding);
+#endif
+
+    const SkOpSpanBase* debugSpan(int id) const;
+    void debugValidate() const;
+    void detach(const SkOpSpan* );
+    double distSq(double t, SkOpAngle* opp);
+
     bool done() const {
-        SkASSERT(fDoneSpans <= fTs.count());
-        return fDoneSpans == fTs.count();
-    }
-
-    bool done(int min) const {
-        return fTs[min].fDone;
+        SkASSERT(fDoneCount <= fCount);
+        return fDoneCount == fCount;
     }
 
     bool done(const SkOpAngle* angle) const {
-        return done(SkMin32(angle->start(), angle->end()));
+        return angle->start()->starter(angle->end())->done();
     }
 
     SkDPoint dPtAtT(double mid) const {
-        return (*CurveDPointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
+        return (*CurveDPointAtT[fVerb])(fPts, fWeight, mid);
     }
 
-    SkVector dxdy(int index) const {
-        return (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[index].fT);
+    SkDVector dSlopeAtT(double mid) const {
+        return (*CurveDSlopeAtT[fVerb])(fPts, fWeight, mid);
     }
 
-    SkScalar dy(int index) const {
-        return dxdy(index).fY;
+    void dump() const;
+    void dumpAll() const;
+    void dumpAngles() const;
+    void dumpCoin() const;
+    void dumpPts() const;
+    void dumpPtsInner() const;
+
+    SkOpSegment* findNextOp(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
+                             SkOpSpanBase** nextEnd, bool* unsortable, SkPathOp op,
+                             int xorMiMask, int xorSuMask);
+    SkOpSegment* findNextWinding(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** nextStart,
+                                  SkOpSpanBase** nextEnd, bool* unsortable);
+    SkOpSegment* findNextXor(SkOpSpanBase** nextStart, SkOpSpanBase** nextEnd, bool* unsortable);
+    SkOpSpan* findSortableTop(SkOpContour* );
+    SkOpGlobalState* globalState() const;
+
+    const SkOpSpan* head() const {
+        return &fHead;
     }
 
-    bool hasMultiples() const {
-        return fMultiples;
+    SkOpSpan* head() {
+        return &fHead;
     }
 
-    bool hasSmall() const {
-        return fSmall;
+    void init(SkPoint pts[], SkScalar weight, SkOpContour* parent, SkPath::Verb verb);
+
+    SkOpSpan* insert(SkOpSpan* prev, SkChunkAlloc* allocator) {
+        SkOpSpan* result = SkOpTAllocator<SkOpSpan>::Allocate(allocator);
+        SkOpSpanBase* next = prev->next();
+        result->setPrev(prev);
+        prev->setNext(result);
+        SkDEBUGCODE(result->ptT()->fT = 0);
+        result->setNext(next);
+        if (next) {
+            next->setPrev(result);
+        }
+        return result;
     }
 
-    bool hasTiny() const {
-        return fTiny;
-    }
-
-    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;
-    }
+    bool isClose(double t, const SkOpSegment* opp) const;
 
     bool isHorizontal() const {
         return fBounds.fTop == fBounds.fBottom;
     }
 
+    SkOpSegment* isSimple(SkOpSpanBase** end, int* step) {
+        return nextChase(end, step, NULL, NULL);
+    }
+
     bool isVertical() const {
         return fBounds.fLeft == fBounds.fRight;
     }
 
-    bool isVertical(int start, int end) const {
-        return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start, end);
+    bool isVertical(SkOpSpanBase* start, SkOpSpanBase* end) const {
+        return (*CurveIsVertical[fVerb])(fPts, fWeight, start->t(), end->t());
     }
 
-    bool operand() const {
-        return fOperand;
+    bool isXor() const;
+
+    const SkPoint& lastPt() const {
+        return fPts[SkPathOpsVerbToPoints(fVerb)];
     }
 
-    int oppSign(const SkOpAngle* angle) const {
-        SkASSERT(angle->segment() == this);
-        return oppSign(angle->start(), angle->end());
+    SkOpSpanBase* markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end);
+    bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
+            SkOpSpanBase** lastPtr);
+    bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
+            int oppWinding, SkOpSpanBase** lastPtr);
+    SkOpSpanBase* markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle);
+    SkOpSpanBase* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
+                         const SkOpAngle* angle);
+    void markDone(SkOpSpan* );
+    bool markWinding(SkOpSpan* , int winding);
+    bool markWinding(SkOpSpan* , int winding, int oppWinding);
+    bool match(const SkOpPtT* span, const SkOpSegment* parent, double t, const SkPoint& pt) const;
+    void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator);
+    void moveMultiples();
+    void moveNearby();
+
+    SkOpSegment* next() const {
+        return fNext;
     }
 
-    int oppSign(int startIndex, int endIndex) const {
-        int result = startIndex < endIndex ? -fTs[startIndex].fOppValue : fTs[endIndex].fOppValue;
-#if DEBUG_WIND_BUMP
-        SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
-#endif
+    SkOpSegment* nextChase(SkOpSpanBase** , int* step, SkOpSpan** , SkOpSpanBase** last) const;
+    bool operand() const;
+
+    static int OppSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
+        int result = start->t() < end->t() ? -start->upCast()->oppValue()
+                : end->upCast()->oppValue();
         return result;
     }
 
-    int oppSum(int tIndex) const {
-        return fTs[tIndex].fOppSum;
-    }
+    bool oppXor() const;
 
-    int oppSum(const SkOpAngle* angle) const {
-        int lesser = SkMin32(angle->start(), angle->end());
-        return fTs[lesser].fOppSum;
+    const SkOpSegment* prev() const {
+        return fPrev;
     }
 
-    int oppValue(int tIndex) const {
-        return fTs[tIndex].fOppValue;
-    }
-
-    int oppValue(const SkOpAngle* angle) const {
-        int lesser = SkMin32(angle->start(), angle->end());
-        return fTs[lesser].fOppValue;
-    }
-
-#if DEBUG_VALIDATE
-    bool oppXor() const {
-        return fOppXor;
-    }
-#endif
-
     SkPoint ptAtT(double mid) const {
-        return (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
+        return (*CurvePointAtT[fVerb])(fPts, fWeight, mid);
     }
 
     const SkPoint* pts() const {
         return fPts;
     }
 
-    void reset() {
-        init(NULL, (SkPath::Verb) -1, false, false);
-        fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
-        fTs.reset();
+    bool ptsDisjoint(const SkOpPtT& span, const SkOpPtT& test) const {
+        return ptsDisjoint(span.fT, span.fPt, test.fT, test.fPt);
     }
 
-    bool reversePoints(const SkPoint& p1, const SkPoint& p2) const;
-
-    void setOppXor(bool isOppXor) {
-        fOppXor = isOppXor;
+    bool ptsDisjoint(const SkOpPtT& span, double t, const SkPoint& pt) const {
+        return ptsDisjoint(span.fT, span.fPt, t, pt);
     }
 
-    void setUpWinding(int index, int endIndex, int* maxWinding, int* sumWinding) {
-        int deltaSum = spanSign(index, endIndex);
+    bool ptsDisjoint(double t1, const SkPoint& pt1, double t2, const SkPoint& pt2) const;
+
+    void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits,
+                  SkChunkAlloc* allocator);
+
+    void resetVisited() {
+        fVisited = false;
+    }
+
+    void setContour(SkOpContour* contour) {
+        fContour = contour;
+    }
+
+    void setCubicType(SkDCubic::CubicType cubicType) {
+        fCubicType = cubicType;
+    }
+
+    void setNext(SkOpSegment* next) {
+        fNext = next;
+    }
+
+    void setPrev(SkOpSegment* prev) {
+        fPrev = prev;
+    }
+
+    void setVisited() {
+        fVisited = true;
+    }
+
+    void setUpWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* maxWinding, int* sumWinding) {
+        int deltaSum = SpanSign(start, end);
         *maxWinding = *sumWinding;
         *sumWinding -= deltaSum;
     }
 
-    const SkOpSpan& span(int tIndex) const {
-        return fTs[tIndex];
-    }
+    void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding,
+                       int* maxWinding, int* sumWinding);
+    void setUpWindings(SkOpSpanBase* start, SkOpSpanBase* end, int* sumMiWinding, int* sumSuWinding,
+                       int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
+    void sortAngles();
 
-    const SkOpAngle* spanToAngle(int tStart, int tEnd) const {
-        SkASSERT(tStart != tEnd);
-        const SkOpSpan& span = fTs[tStart];
-        return tStart < tEnd ? span.fToAngle : span.fFromAngle;
-    }
-
-    // FIXME: create some sort of macro or template that avoids casting
-    SkOpAngle* spanToAngle(int tStart, int tEnd) {
-        const SkOpAngle* cAngle = (const_cast<const SkOpSegment*>(this))->spanToAngle(tStart, tEnd);
-        return const_cast<SkOpAngle*>(cAngle);
-    }
-
-    int spanSign(const SkOpAngle* angle) const {
-        SkASSERT(angle->segment() == this);
-        return spanSign(angle->start(), angle->end());
-    }
-
-    int spanSign(int startIndex, int endIndex) const {
-        int result = startIndex < endIndex ? -fTs[startIndex].fWindValue : fTs[endIndex].fWindValue;
-#if DEBUG_WIND_BUMP
-        SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
-#endif
+    static int SpanSign(const SkOpSpanBase* start, const SkOpSpanBase* end) {
+        int result = start->t() < end->t() ? -start->upCast()->windValue()
+                : end->upCast()->windValue();
         return result;
     }
 
-    double t(int tIndex) const {
-        return fTs[tIndex].fT;
+    SkOpAngle* spanToAngle(SkOpSpanBase* start, SkOpSpanBase* end) {
+        SkASSERT(start != end);
+        return start->t() < end->t() ? start->upCast()->toAngle() : start->fromAngle();
     }
 
-    double tAtMid(int start, int end, double mid) const {
-        return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
+    bool subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end, SkDCurve* result) const;
+    bool subDivide(const SkOpSpanBase* start, const SkOpSpanBase* end, SkOpCurve* result) const;
+
+    const SkOpSpanBase* tail() const {
+        return &fTail;
     }
 
-    void updatePts(const SkPoint pts[]) {
-        fPts = pts;
+    SkOpSpanBase* tail() {
+        return &fTail;
     }
 
+    void undoneSpan(SkOpSpanBase** start, SkOpSpanBase** end);
+    int updateOppWinding(const SkOpSpanBase* start, const SkOpSpanBase* end) const;
+    int updateOppWinding(const SkOpAngle* angle) const;
+    int updateOppWindingReverse(const SkOpAngle* angle) const;
+    int updateWinding(SkOpSpanBase* start, SkOpSpanBase* end);
+    int updateWinding(SkOpAngle* angle);
+    int updateWindingReverse(const SkOpAngle* angle);
+
+    static bool UseInnerWinding(int outerWinding, int innerWinding);
+
     SkPath::Verb verb() const {
         return fVerb;
     }
 
-    int windSum(int tIndex) const {
-        return fTs[tIndex].fWindSum;
+    // look for two different spans that point to the same opposite segment
+    bool visited() {
+        if (!fVisited) {
+            fVisited = true;
+            return false;
+        }
+        return true;
     }
 
-    int windValue(int tIndex) const {
-        return fTs[tIndex].fWindValue;
+    SkScalar weight() const {
+        return fWeight;
     }
 
-#if defined(SK_DEBUG) || DEBUG_WINDING
-    SkScalar xAtT(int index) const {
-        return xAtT(&fTs[index]);
-    }
-#endif
-
-#if DEBUG_VALIDATE
-    bool _xor() const {  // FIXME: used only by SkOpAngle::debugValidateLoop()
-        return fXor;
-    }
-#endif
-
-    const SkPoint& xyAtT(const SkOpSpan* span) const {
-        return span->fPt;
-    }
-
-    const SkPoint& xyAtT(int index) const {
-        return xyAtT(&fTs[index]);
-    }
-
-#if defined(SK_DEBUG) || DEBUG_WINDING
-    SkScalar yAtT(int index) const {
-        return yAtT(&fTs[index]);
-    }
-#endif
-
-    const SkOpAngle* activeAngle(int index, int* start, int* end, bool* done,
-                                 bool* sortable) const;
-    SkPoint activeLeftTop(int* firstT) const;
-    bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, SkPathOp op);
-    bool activeWinding(int index, int endIndex);
-    void addCubic(const SkPoint pts[4], bool operand, bool evenOdd);
-    void addCurveTo(int start, int end, SkPathWriter* path, bool active) const;
-    void addEndSpan(int endIndex);
-    void addLine(const SkPoint pts[2], bool operand, bool evenOdd);
-    void addOtherT(int index, double otherT, int otherIndex);
-    void addQuad(const SkPoint pts[3], bool operand, bool evenOdd);
-    void addSimpleAngle(int endIndex);
-    int addSelfT(const SkPoint& pt, double newT);
-    void addStartSpan(int endIndex);
-    int addT(SkOpSegment* other, const SkPoint& pt, double newT);
-    void addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
-    bool addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
-                        SkOpSegment* other);
-    const SkOpSpan* addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
-                             const SkPoint& pt);
-    const SkOpSpan* addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
-                             const SkPoint& pt, const SkPoint& oPt);
-    void alignMultiples(SkTDArray<AlignedSpan>* aligned);
-    bool alignSpan(int index, double thisT, const SkPoint& thisPt);
-    void alignSpanState(int start, int end);
-    bool betweenTs(int lesser, double testT, int greater) const;
-    void blindCancel(const SkCoincidence& coincidence, SkOpSegment* other);
-    void blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other);
-    bool calcAngles();
-    double calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
-                           double hiEnd, const SkOpSegment* other, int thisEnd);
-    double calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
-                             double hiEnd, const SkOpSegment* other, int thisEnd);
-    void checkDuplicates();
-    bool checkEnds();
-    void checkMultiples();
-    void checkSmall();
-    bool checkSmall(int index) const;
-    void checkTiny();
-    int computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType);
-    bool containsPt(const SkPoint& , int index, int endIndex) const;
-    int crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT, bool* hitSomething,
-                     double mid, bool opp, bool current) const;
-    bool findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart, int oEnd,
-                             int step, SkPoint* startPt, SkPoint* endPt, double* endT) const;
-    SkOpSegment* findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
-                            bool* unsortable, SkPathOp op, int xorMiMask, int xorSuMask);
-    SkOpSegment* findNextWinding(SkTDArray<SkOpSpan*>* chase, int* nextStart, int* nextEnd,
-                                 bool* unsortable);
-    SkOpSegment* findNextXor(int* nextStart, int* nextEnd, bool* unsortable);
-    int findExactT(double t, const SkOpSegment* ) const;
-    int findOtherT(double t, const SkOpSegment* ) const;
-    int findT(double t, const SkPoint& , const SkOpSegment* ) const;
-    SkOpSegment* findTop(int* tIndex, int* endIndex, bool* unsortable, bool firstPass);
-    void fixOtherTIndex();
-    bool inconsistentAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
-                        const SkOpAngle* angle) const;
-    void initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType);
-    bool initWinding(int start, int end, double tHit, int winding, SkScalar hitDx, int oppWind,
-                     SkScalar hitOppDx);
-    bool isMissing(double startT, const SkPoint& pt) const;
-    bool isTiny(const SkOpAngle* angle) const;
-    bool joinCoincidence(SkOpSegment* other, double otherT, const SkPoint& otherPt, int step,
-                         bool cancel);
-    SkOpSpan* markAndChaseDoneBinary(int index, int endIndex);
-    SkOpSpan* markAndChaseDoneUnary(int index, int endIndex);
-    bool markAndChaseWinding(const SkOpAngle* angle, int winding, int oppWinding,
-                             SkOpSpan** lastPtr);
-    SkOpSpan* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
-                        const SkOpAngle* angle);
-    void markDone(int index, int winding);
-    void markDoneBinary(int index);
-    void markDoneFinal(int index);
-    void markDoneUnary(int index);
-    bool nextCandidate(int* start, int* end) const;
-    int nextSpan(int from, int step) const;
-    void pinT(const SkPoint& pt, double* t);
-    void setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
-            int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding);
-    void sortAngles();
-    bool subDivide(int start, int end, SkPoint edge[4]) const;
-    bool subDivide(int start, int end, SkDCubic* result) const;
-    void undoneSpan(int* start, int* end);
-    int updateOppWindingReverse(const SkOpAngle* angle) const;
-    int updateWindingReverse(const SkOpAngle* angle) const;
-    static bool UseInnerWinding(int outerWinding, int innerWinding);
-    static bool UseInnerWindingReverse(int outerWinding, int innerWinding);
-    int windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx) const;
+    SkOpSpan* windingSpanAtT(double tHit);
     int windSum(const SkOpAngle* angle) const;
-// available for testing only
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-    int debugID() const {
-        return fID;
+
+    SkPoint* writablePt(bool end) {
+        return &fPts[end ? SkPathOpsVerbToPoints(fVerb) : 0];
     }
-#else
-    int debugID() const {
-        return -1;
-    }
-#endif
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
-    void debugShowActiveSpans() const;
-#endif
-#if DEBUG_CONCIDENT
-    void debugShowTs(const char* prefix) const;
-#endif
-#if DEBUG_SHOW_WINDING
-    int debugShowWindingValues(int slotCount, int ofInterest) const;
-#endif
-    const SkTDArray<SkOpSpan>& debugSpans() const;
-    void debugValidate() const;
-    // available to testing only
-    const SkOpAngle* debugLastAngle() const;
-    void dumpAngles() const;
-    void dumpContour(int firstID, int lastID) const;
-    void dumpPts() const;
-    void dumpSpans() const;
 
 private:
-    struct MissingSpan  {
-        double fT;
-        double fEndT;
-        SkOpSegment* fSegment;
-        SkOpSegment* fOther;
-        double fOtherT;
-        SkPoint fPt;
-    };
-
-    const SkOpAngle* activeAngleInner(int index, int* start, int* end, bool* done,
-                                      bool* sortable) const;
-    const SkOpAngle* activeAngleOther(int index, int* start, int* end, bool* done,
-                                      bool* sortable) const;
-    bool activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, SkPathOp op,
-                  int* sumMiWinding, int* sumSuWinding);
-    bool activeWinding(int index, int endIndex, int* sumWinding);
-    void addCancelOutsides(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
-    void addCoinOutsides(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
-    SkOpAngle* addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** );
-    SkOpAngle* addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** );
-    SkOpAngle* addSingletonAngles(int step);
-    void alignRange(int lower, int upper, const SkOpSegment* other, int oLower, int oUpper);
-    void alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other, double otherT,
-                   const SkOpSegment* other2, SkOpSpan* oSpan, SkTDArray<AlignedSpan>* );
-    bool betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const;
-    void bumpCoincidentBlind(bool binary, int index, int last);
-    bool bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* index,
-                            SkTArray<SkPoint, true>* outsideTs);
-    void bumpCoincidentOBlind(int index, int last);
-    bool bumpCoincidentOther(const SkOpSpan& oTest, int* index,
-                             SkTArray<SkPoint, true>* outsideTs, const SkPoint& endPt);
-    bool bumpSpan(SkOpSpan* span, int windDelta, int oppDelta);
-    bool calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts);
-    bool checkForSmall(const SkOpSpan* span, const SkPoint& pt, double newT,
-                       int* less, int* more) const;
-    void checkLinks(const SkOpSpan* ,
-                    SkTArray<MissingSpan, true>* missingSpans) const;
-    static void CheckOneLink(const SkOpSpan* test, const SkOpSpan* oSpan,
-                             const SkOpSpan* oFirst, const SkOpSpan* oLast,
-                             const SkOpSpan** missingPtr,
-                             SkTArray<MissingSpan, true>* missingSpans);
-    int checkSetAngle(int tIndex) const;
-    void checkSmallCoincidence(const SkOpSpan& span, SkTArray<MissingSpan, true>* );
-    bool coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const;
-    bool clockwise(int tStart, int tEnd, bool* swap) const;
-    static void ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
-                              SkOpAngle::IncludeType );
-    static void ComputeOneSumReverse(const SkOpAngle* baseAngle, SkOpAngle* nextAngle,
-                                     SkOpAngle::IncludeType );
-    bool containsT(double t, const SkOpSegment* other, double otherT) const;
-    bool decrementSpan(SkOpSpan* span);
-    int findEndSpan(int endIndex) const;
-    int findStartSpan(int startIndex) const;
-    int firstActive(int tIndex) const;
-    const SkOpSpan& firstSpan(const SkOpSpan& thisSpan) const;
-    void init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd);
-    bool inCoincidentSpan(double t, const SkOpSegment* other) const;
-    bool inconsistentWinding(const SkOpAngle* , int maxWinding, int oppMaxWinding) const;
-    bool inconsistentWinding(int min, int maxWinding, int oppMaxWinding) const;
-    bool inconsistentWinding(const char* funName, int tIndex, int winding, int oppWinding) const;
-    bool inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPtr) const;
-#if OLD_CHASE
-    bool isSimple(int end) const;
-#else
-    SkOpSegment* isSimple(int* end, int* step);
-#endif
-    bool isTiny(int index) const;
-    const SkOpSpan& lastSpan(const SkOpSpan& thisSpan) const;
-    void matchWindingValue(int tIndex, double t, bool borrowWind);
-    SkOpSpan* markAndChaseDone(int index, int endIndex, int winding);
-    SkOpSpan* markAndChaseDoneBinary(const SkOpAngle* angle, int winding, int oppWinding);
-    bool markAndChaseWinding(const SkOpAngle* angle, int winding, SkOpSpan** lastPtr);
-    bool markAndChaseWinding(int index, int endIndex, int winding, SkOpSpan** lastPtr);
-    bool markAndChaseWinding(int index, int endIndex, int winding, int oppWinding,
-                             SkOpSpan** lastPtr);
-    SkOpSpan* markAngle(int maxWinding, int sumWinding, const SkOpAngle* angle);
-    void markDoneBinary(int index, int winding, int oppWinding);
-    SkOpSpan* markAndChaseDoneUnary(const SkOpAngle* angle, int winding);
-    void markOneDone(const char* funName, int tIndex, int winding);
-    void markOneDoneBinary(const char* funName, int tIndex);
-    void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding);
-    void markOneDoneFinal(const char* funName, int tIndex);
-    void markOneDoneUnary(const char* funName, int tIndex);
-    bool markOneWinding(const char* funName, int tIndex, int winding, SkOpSpan** lastPtr);
-    bool markOneWinding(const char* funName, int tIndex, int winding, int oppWinding,
-                        SkOpSpan** lastPtr);
-    bool markWinding(int index, int winding);
-    bool markWinding(int index, int winding, int oppWinding);
-    bool monotonicInY(int tStart, int tEnd) const;
-
-    bool multipleEnds() const { return fTs[count() - 2].fT == 1; }
-    bool multipleStarts() const { return fTs[1].fT == 0; }
-
-    SkOpSegment* nextChase(int* index, int* step, int* min, SkOpSpan** last) const;
-    int nextExactSpan(int from, int step) const;
-    void resetSpanFlags();
-    bool serpentine(int tStart, int tEnd) const;
-    void setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt,  SkOpSegment* other);
-    void setFromAngle(int endIndex, SkOpAngle* );
-    void setSpanFlags(const SkPoint& pt, double newT, SkOpSpan* span);
-    void setToAngle(int endIndex, SkOpAngle* );
-    void setUpWindings(int index, int endIndex, int* sumMiWinding,
-            int* maxWinding, int* sumWinding);
-    void subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const;
-    static void TrackOutsidePair(SkTArray<SkPoint, true>* outsideTs, const SkPoint& endPt,
-            const SkPoint& startPt);
-    static void TrackOutside(SkTArray<SkPoint, true>* outsideTs, const SkPoint& startPt);
-    int updateOppWinding(int index, int endIndex) const;
-    int updateOppWinding(const SkOpAngle* angle) const;
-    int updateWinding(int index, int endIndex) const;
-    int updateWinding(const SkOpAngle* angle) const;
-    int updateWindingReverse(int index, int endIndex) const;
-    SkOpSpan* verifyOneWinding(const char* funName, int tIndex);
-    SkOpSpan* verifyOneWindingU(const char* funName, int tIndex);
-
-    SkScalar xAtT(const SkOpSpan* span) const {
-        return xyAtT(span).fX;
-    }
-
-    SkScalar yAtT(const SkOpSpan* span) const {
-        return xyAtT(span).fY;
-    }
-
-    void zeroSpan(SkOpSpan* span);
-
-#if DEBUG_SWAP_TOP
-    bool controlsContainedByEnds(int tStart, int tEnd) const;
-#endif
-    void debugAddAngle(int start, int end);
-#if DEBUG_CONCIDENT
-    void debugAddTPair(double t, const SkOpSegment& other, double otherT) const;
-#endif
-#if DEBUG_ANGLE
-    void debugCheckPointsEqualish(int tStart, int tEnd) const;
-#endif
-#if DEBUG_SWAP_TOP
-    int debugInflections(int index, int endIndex) const;
-#endif
-#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
-    void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding);
-    void debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding, int oppWinding);
-#endif
-#if DEBUG_WINDING
-    static char as_digit(int value) {
-        return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
-    }
-#endif
-    // available to testing only
-    void debugConstruct();
-    void debugConstructCubic(SkPoint shortQuad[4]);
-    void debugConstructLine(SkPoint shortQuad[2]);
-    void debugConstructQuad(SkPoint shortQuad[3]);
-    void debugReset();
-    void dumpDPts() const;
-    void dumpHexPts() const;
-    void dumpSpan(int index) const;
-
-    const SkPoint* fPts;
-    SkPathOpsBounds fBounds;
-    // FIXME: can't convert to SkTArray because it uses insert
-    SkTDArray<SkOpSpan> fTs;  // 2+ (always includes t=0 t=1) -- at least (number of spans) + 1
-    SkOpAngleSet fAngles;  // empty or 2+ -- (number of non-zero spans) * 2
-    // OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
-    int fDoneSpans;  // quick check that segment is finished
-    // OPTIMIZATION: force the following to be byte-sized
+    SkOpSpan fHead;  // the head span always has its t set to zero
+    SkOpSpanBase fTail;  // the tail span always has its t set to one
+    SkOpContour* fContour;
+    SkOpSegment* fNext;  // forward-only linked list used by contour to walk the segments
+    const SkOpSegment* fPrev;
+    SkPoint* fPts;  // pointer into array of points owned by edge builder that may be tweaked
+    SkPathOpsBounds fBounds;  // tight bounds
+    SkScalar fWeight;
+    int fCount;  // number of spans (one for a non-intersecting segment)
+    int fDoneCount;  // number of processed spans (zero initially)
     SkPath::Verb fVerb;
-    bool fLoop;   // set if cubic intersects itself
-    bool fMultiples;  // set if curve intersects multiple other curves at one interior point
-    bool fOperand;
-    bool fXor;  // set if original contour had even-odd fill
-    bool fOppXor;  // set if opposite operand had even-odd fill
-    bool fSmall;  // set if some span is small
-    bool fTiny;  // set if some span is tiny
-#if defined(SK_DEBUG) || !FORCE_RELEASE
-    int fID;
-#endif
-
-    friend class PathOpsSegmentTester;
+    SkDCubic::CubicType fCubicType;
+    bool fTopsFound;
+    bool fVisited;  // used by missing coincidence check
+    SkDEBUGCODE(int fID);
 };
 
 #endif
diff --git a/src/pathops/SkOpSpan.cpp b/src/pathops/SkOpSpan.cpp
new file mode 100755
index 0000000..e89ec3e
--- /dev/null
+++ b/src/pathops/SkOpSpan.cpp
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkOpCoincidence.h"
+#include "SkOpContour.h"
+#include "SkOpSegment.h"
+#include "SkPathWriter.h"
+
+bool SkOpPtT::alias() const {
+    return this->span()->ptT() != this;
+}
+
+SkOpContour* SkOpPtT::contour() const {
+    return segment()->contour();
+}
+
+SkOpGlobalState* SkOpPtT::globalState() const {
+    return contour()->globalState(); 
+}
+
+void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
+    fT = t;
+    fPt = pt;
+    fSpan = span;
+    fNext = this;
+    fDuplicatePt = duplicate;
+    fDeleted = false;
+    SkDEBUGCODE(fID = span->globalState()->nextPtTID());
+}
+
+bool SkOpPtT::onEnd() const {
+    const SkOpSpanBase* span = this->span();
+    if (span->ptT() != this) {
+        return false;
+    }
+    const SkOpSegment* segment = this->segment();
+    return span == segment->head() || span == segment->tail();
+}
+
+SkOpPtT* SkOpPtT::prev() {
+    SkOpPtT* result = this;
+    SkOpPtT* next = this;
+    while ((next = next->fNext) != this) {
+        result = next;
+    }
+    SkASSERT(result->fNext == this);
+    return result;
+}
+
+SkOpPtT* SkOpPtT::remove() {
+    SkOpPtT* prev = this;
+    do {
+        SkOpPtT* next = prev->fNext;
+        if (next == this) {
+            prev->removeNext(this);
+            SkASSERT(prev->fNext != prev);
+            fDeleted = true;
+            return prev;
+        }
+        prev = next;
+    } while (prev != this);
+    SkASSERT(0);
+    return NULL;
+}
+
+void SkOpPtT::removeNext(SkOpPtT* kept) {
+    SkASSERT(this->fNext);
+    SkOpPtT* next = this->fNext;
+    SkASSERT(this != next->fNext);
+    this->fNext = next->fNext;
+    SkOpSpanBase* span = next->span();
+    next->setDeleted();
+    if (span->ptT() == next) {
+        span->upCast()->detach(kept);
+    }
+}
+
+const SkOpSegment* SkOpPtT::segment() const {
+    return span()->segment();
+}
+
+SkOpSegment* SkOpPtT::segment() {
+    return span()->segment();
+}
+
+void SkOpSpanBase::align() {
+    if (this->fAligned) {
+        return;
+    }
+    SkASSERT(!zero_or_one(this->fPtT.fT));
+    SkASSERT(this->fPtT.next());
+    // if a linked pt/t pair has a t of zero or one, use it as the base for alignment
+    SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
+    while ((ptT = ptT->next()) != stopPtT) {
+        if (zero_or_one(ptT->fT)) {
+            SkOpSegment* segment = ptT->segment();
+            SkASSERT(this->segment() != segment);
+            SkASSERT(segment->head()->ptT() == ptT || segment->tail()->ptT() == ptT);
+            if (ptT->fT) {
+                segment->tail()->alignEnd(1, segment->lastPt());
+            } else {
+                segment->head()->alignEnd(0, segment->pts()[0]);
+            }
+            return;
+        }
+    }
+    alignInner();
+    this->fAligned = true;
+}
+
+
+// FIXME: delete spans that collapse
+// delete segments that collapse
+// delete contours that collapse
+void SkOpSpanBase::alignEnd(double t, const SkPoint& pt) {
+    SkASSERT(zero_or_one(t));
+    SkOpSegment* segment = this->segment();
+    SkASSERT(t ? segment->lastPt() == pt : segment->pts()[0] == pt);
+    alignInner();
+    *segment->writablePt(!!t) = pt;
+    SkOpPtT* ptT = &this->fPtT;
+    SkASSERT(t == ptT->fT);
+    SkASSERT(pt == ptT->fPt);
+    SkOpPtT* test = ptT, * stopPtT = ptT;
+    while ((test = test->next()) != stopPtT) {
+        SkOpSegment* other = test->segment();
+        if (other == this->segment()) {
+            continue;
+        }
+        if (!zero_or_one(test->fT)) {
+            continue;
+        }
+        *other->writablePt(!!test->fT) = pt;
+    }
+    this->fAligned = true;
+}
+
+void SkOpSpanBase::alignInner() {
+    // force the spans to share points and t values
+    SkOpPtT* ptT = &this->fPtT, * stopPtT = ptT;
+    const SkPoint& pt = ptT->fPt;
+    do {
+        ptT->fPt = pt;
+        const SkOpSpanBase* span = ptT->span();
+        SkOpPtT* test = ptT;
+        do {
+            SkOpPtT* prev = test;
+            if ((test = test->next()) == stopPtT) {
+                break;
+            }
+            if (span == test->span() && !span->segment()->ptsDisjoint(*ptT, *test)) {
+                // omit aliases that alignment makes redundant
+                if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
+                    SkASSERT(test->alias());
+                    prev->removeNext(ptT);
+                    test = prev;
+                } else {
+                    SkASSERT(ptT->alias());
+                    stopPtT = ptT = ptT->remove();
+                    break;
+                }
+            }
+        } while (true);
+    } while ((ptT = ptT->next()) != stopPtT);
+}
+
+bool SkOpSpanBase::contains(const SkOpSpanBase* span) const {
+    const SkOpPtT* start = &fPtT;
+    const SkOpPtT* check = &span->fPtT;
+    SkASSERT(start != check);
+    const SkOpPtT* walk = start;
+    while ((walk = walk->next()) != start) {
+        if (walk == check) {
+            return true;
+        }
+    }
+    return false;
+}
+
+SkOpPtT* SkOpSpanBase::contains(const SkOpSegment* segment) {
+    SkOpPtT* start = &fPtT;
+    SkOpPtT* walk = start;
+    while ((walk = walk->next()) != start) {
+        if (walk->segment() == segment) {
+            return walk;
+        }
+    }
+    return NULL;
+}
+
+bool SkOpSpanBase::containsCoinEnd(const SkOpSegment* segment) const {
+    SkASSERT(this->segment() != segment);
+    const SkOpSpanBase* next = this;
+    while ((next = next->fCoinEnd) != this) {
+        if (next->segment() == segment) {
+            return true;
+        }
+    }
+    return false;
+}
+
+SkOpContour* SkOpSpanBase::contour() const {
+    return segment()->contour();
+}
+
+SkOpGlobalState* SkOpSpanBase::globalState() const {
+    return contour()->globalState(); 
+}
+
+void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
+    fSegment = segment;
+    fPtT.init(this, t, pt, false);
+    fCoinEnd = this;
+    fFromAngle = NULL;
+    fPrev = prev;
+    fSpanAdds = 0;
+    fAligned = true;
+    fChased = false;
+    SkDEBUGCODE(fCount = 1);
+    SkDEBUGCODE(fID = globalState()->nextSpanID());
+}
+
+// this pair of spans share a common t value or point; merge them and eliminate duplicates
+// this does not compute the best t or pt value; this merely moves all data into a single list
+void SkOpSpanBase::merge(SkOpSpan* span) {
+    SkOpPtT* spanPtT = span->ptT();
+    SkASSERT(this->t() != spanPtT->fT);
+    SkASSERT(!zero_or_one(spanPtT->fT));
+    span->detach(this->ptT());
+    SkOpPtT* remainder = spanPtT->next();
+    ptT()->insert(spanPtT);
+    while (remainder != spanPtT) {
+        SkOpPtT* next = remainder->next();
+        SkOpPtT* compare = spanPtT->next();
+        while (compare != spanPtT) {
+            SkOpPtT* nextC = compare->next();
+            if (nextC->span() == remainder->span() && nextC->fT == remainder->fT) {
+                goto tryNextRemainder;
+            }
+            compare = nextC;
+        }
+        spanPtT->insert(remainder);
+tryNextRemainder:
+        remainder = next;
+    }
+    fSpanAdds += span->fSpanAdds;
+}
+
+int SkOpSpan::computeWindSum() {
+    SkOpGlobalState* globals = this->globalState();
+    SkOpContour* contourHead = globals->contourHead();
+    int windTry = 0;
+    while (!this->sortableTop(contourHead) && ++windTry < SkOpGlobalState::kMaxWindingTries) {
+        ;
+    }
+    return this->windSum();
+}
+
+bool SkOpSpan::containsCoincidence(const SkOpSegment* segment) const {
+    SkASSERT(this->segment() != segment);
+    const SkOpSpan* next = fCoincident;
+    do {
+        if (next->segment() == segment) {
+            return true;
+        }
+    } while ((next = next->fCoincident) != this);
+    return false;
+}
+
+void SkOpSpan::detach(SkOpPtT* kept) {
+    SkASSERT(!final());
+    SkOpSpan* prev = this->prev();
+    SkASSERT(prev);
+    SkOpSpanBase* next = this->next();
+    SkASSERT(next);
+    prev->setNext(next);
+    next->setPrev(prev);
+    this->segment()->detach(this);
+    this->globalState()->coincidence()->fixUp(this->ptT(), kept);
+    this->ptT()->setDeleted();
+}
+
+void SkOpSpan::init(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
+    SkASSERT(t != 1);
+    initBase(segment, prev, t, pt);
+    fCoincident = this;
+    fToAngle = NULL;
+    fWindSum = fOppSum = SK_MinS32;
+    fWindValue = 1;
+    fOppValue = 0;
+    fTopTTry = 0;
+    fChased = fDone = false;
+    segment->bumpCount();
+}
+
+void SkOpSpan::setOppSum(int oppSum) {
+    SkASSERT(!final());
+    if (fOppSum != SK_MinS32 && fOppSum != oppSum) {
+        this->globalState()->setWindingFailed();
+        return;
+    }
+    SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(oppSum) <= DEBUG_LIMIT_WIND_SUM);
+    fOppSum = oppSum;
+}
+
+void SkOpSpan::setWindSum(int windSum) {
+    SkASSERT(!final());
+    if (fWindSum != SK_MinS32 && fWindSum != windSum) {
+        this->globalState()->setWindingFailed();
+        return;
+    }
+    SkASSERT(!DEBUG_LIMIT_WIND_SUM || abs(windSum) <= DEBUG_LIMIT_WIND_SUM);
+    fWindSum = windSum;
+}
diff --git a/src/pathops/SkOpSpan.h b/src/pathops/SkOpSpan.h
index d9ce44e..9b44247 100644
--- a/src/pathops/SkOpSpan.h
+++ b/src/pathops/SkOpSpan.h
@@ -7,36 +7,465 @@
 #ifndef SkOpSpan_DEFINED
 #define SkOpSpan_DEFINED
 
+#include "SkPathOpsDebug.h"
 #include "SkPoint.h"
 
-class SkOpAngle;
+class SkChunkAlloc;
+struct SkOpAngle;
+class SkOpContour;
+class SkOpGlobalState;
 class SkOpSegment;
+class SkOpSpanBase;
+class SkOpSpan;
 
-struct SkOpSpan {
-    SkPoint fPt;  // computed when the curves are intersected
-    double fT;
-    double fOtherT;  // value at fOther[fOtherIndex].fT
-    SkOpSegment* fOther;
-    SkOpAngle* fFromAngle;  // (if t > 0) index into segment's angle array going negative in t
-    SkOpAngle* fToAngle;  // (if t < 1) index into segment's angle array going positive in t
-    int fOtherIndex;  // can't be used during intersection
+// subset of op span used by terminal span (when t is equal to one)
+class SkOpPtT {
+public:
+    enum {
+        kIsAlias = 1,
+        kIsDuplicate = 1
+    };
+
+    void addOpp(SkOpPtT* opp) {
+        // find the fOpp ptr to opp
+        SkOpPtT* oppPrev = opp->fNext;
+        if (oppPrev == this) {
+            return;
+        }
+        while (oppPrev->fNext != opp) {
+            oppPrev = oppPrev->fNext;
+             if (oppPrev == this) {
+                 return;
+             }
+        }
+        
+        SkOpPtT* oldNext = this->fNext;
+        SkASSERT(this != opp);
+        this->fNext = opp;
+        SkASSERT(oppPrev != oldNext);
+        oppPrev->fNext = oldNext;
+    }
+
+    bool alias() const;
+    SkOpContour* contour() const;
+
+    int debugID() const {
+        return SkDEBUGRELEASE(fID, -1);
+    }
+
+    const SkOpAngle* debugAngle(int id) const;
+    SkOpContour* debugContour(int id);
+    int debugLoopLimit(bool report) const;
+    bool debugMatchID(int id) const;
+    const SkOpPtT* debugPtT(int id) const;
+    const SkOpSegment* debugSegment(int id) const;
+    const SkOpSpanBase* debugSpan(int id) const;
+    SkOpGlobalState* globalState() const;
+    void debugValidate() const;
+
+    bool deleted() const {
+        return fDeleted;
+    }
+
+    bool duplicate() const {
+        return fDuplicatePt;
+    }
+
+    void dump() const;  // available to testing only
+    void dumpAll() const;
+    void dumpBase() const;
+
+    void init(SkOpSpanBase* , double t, const SkPoint& , bool dup);
+
+    void insert(SkOpPtT* span) {
+        SkASSERT(span != this);
+        span->fNext = fNext;
+        fNext = span;
+    }
+
+    const SkOpPtT* next() const {
+        return fNext;
+    }
+
+    SkOpPtT* next() {
+        return fNext;
+    }
+
+    bool onEnd() const;
+    SkOpPtT* prev();
+    SkOpPtT* remove();
+    void removeNext(SkOpPtT* kept);
+
+    const SkOpSegment* segment() const;
+    SkOpSegment* segment();
+
+    void setDeleted() {
+        SkASSERT(!fDeleted);
+        fDeleted = true;
+    }
+
+    const SkOpSpanBase* span() const {
+        return fSpan;
+    }
+
+    SkOpSpanBase* span() {
+        return fSpan;
+    }
+
+    double fT; 
+    SkPoint fPt;   // cache of point value at this t
+protected:
+    SkOpSpanBase* fSpan;  // contains winding data
+    SkOpPtT* fNext;  // intersection on opposite curve or alias on this curve
+    bool fDeleted;  // set if removed from span list 
+    bool fDuplicatePt;  // set if identical pt is somewhere in the next loop
+    SkDEBUGCODE(int fID);
+};
+
+class SkOpSpanBase {
+public:
+    void align();
+
+    bool aligned() const {
+        return fAligned;
+    }
+
+    void alignEnd(double t, const SkPoint& pt);
+
+    void bumpSpanAdds() {
+        ++fSpanAdds;
+    }
+
+    bool chased() const {
+        return fChased;
+    }
+
+    void clearCoinEnd() {
+        SkASSERT(fCoinEnd != this);
+        fCoinEnd = this;
+    }
+
+    const SkOpSpanBase* coinEnd() const {
+        return fCoinEnd;
+    }
+
+    bool contains(const SkOpSpanBase* ) const;
+    SkOpPtT* contains(const SkOpSegment* );
+
+    bool containsCoinEnd(const SkOpSpanBase* coin) const {
+        SkASSERT(this != coin);
+        const SkOpSpanBase* next = this;
+        while ((next = next->fCoinEnd) != this) {
+            if (next == coin) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool containsCoinEnd(const SkOpSegment* ) const;
+    SkOpContour* contour() const;
+
+    int debugBumpCount() {
+        return SkDEBUGRELEASE(++fCount, -1);
+    }
+
+    int debugID() const {
+        return SkDEBUGRELEASE(fID, -1);
+    }
+
+    const SkOpAngle* debugAngle(int id) const;
+    bool debugCoinEndLoopCheck() const;
+    SkOpContour* debugContour(int id);
+    const SkOpPtT* debugPtT(int id) const;
+    const SkOpSegment* debugSegment(int id) const;
+    const SkOpSpanBase* debugSpan(int id) const;
+    SkOpGlobalState* globalState() const;
+    void debugValidate() const;
+
+    bool deleted() const {
+        return fPtT.deleted();
+    }
+
+    void dump() const;  // available to testing only
+    void dumpCoin() const;
+    void dumpAll() const;
+    void dumpBase() const;
+
+    bool final() const {
+        return fPtT.fT == 1;
+    }
+
+    SkOpAngle* fromAngle() const {
+        return fFromAngle;
+    }
+
+    void initBase(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
+
+    void insertCoinEnd(SkOpSpanBase* coin) {
+        if (containsCoinEnd(coin)) {
+            SkASSERT(coin->containsCoinEnd(this));
+            return;
+        }
+        debugValidate();
+        SkASSERT(this != coin);
+        SkOpSpanBase* coinNext = coin->fCoinEnd;
+        coin->fCoinEnd = this->fCoinEnd;
+        this->fCoinEnd = coinNext;
+        debugValidate();
+    }
+
+    void merge(SkOpSpan* span);
+
+    SkOpSpan* prev() const {
+        return fPrev;
+    }
+
+    const SkPoint& pt() const {
+        return fPtT.fPt;
+    }
+
+    const SkOpPtT* ptT() const {
+        return &fPtT;
+    }
+
+    SkOpPtT* ptT() {
+        return &fPtT;
+    }
+
+    SkOpSegment* segment() const {
+        return fSegment;
+    }
+
+    void setChased(bool chased) {
+        fChased = chased;
+    }
+
+    SkOpPtT* setCoinEnd(SkOpSpanBase* oldCoinEnd, SkOpSegment* oppSegment);
+
+    void setFromAngle(SkOpAngle* angle) {
+        fFromAngle = angle;
+    }
+
+    void setPrev(SkOpSpan* prev) {
+        fPrev = prev;
+    }
+
+    bool simple() const {
+        fPtT.debugValidate();
+        return fPtT.next()->next() == &fPtT; 
+    }
+
+    int spanAddsCount() const {
+        return fSpanAdds;
+    }
+
+    const SkOpSpan* starter(const SkOpSpanBase* end) const {
+        const SkOpSpanBase* result = t() < end->t() ? this : end;
+        return result->upCast();
+    }
+
+    SkOpSpan* starter(SkOpSpanBase* end) {
+        SkASSERT(this->segment() == end->segment());
+        SkOpSpanBase* result = t() < end->t() ? this : end;
+        return result->upCast();
+    }
+
+    SkOpSpan* starter(SkOpSpanBase** endPtr) {
+        SkOpSpanBase* end = *endPtr;
+        SkASSERT(this->segment() == end->segment());
+        SkOpSpanBase* result;
+        if (t() < end->t()) {
+            result = this;
+        } else {
+            result = end;
+            *endPtr = this;
+        }
+        return result->upCast();
+    }
+
+    int step(const SkOpSpanBase* end) const {
+        return t() < end->t() ? 1 : -1;
+    }
+
+    double t() const {
+        return fPtT.fT;
+    }
+
+    void unaligned() {
+        fAligned = false;
+    }
+
+    SkOpSpan* upCast() {
+        SkASSERT(!final());
+        return (SkOpSpan*) this;
+    }
+
+    const SkOpSpan* upCast() const {
+        SkASSERT(!final());
+        return (const SkOpSpan*) this;
+    }
+
+    SkOpSpan* upCastable() {
+        return final() ? NULL : upCast();
+    }
+
+    const SkOpSpan* upCastable() const {
+        return final() ? NULL : upCast();
+    }
+
+private:
+    void alignInner();
+
+protected:  // no direct access to internals to avoid treating a span base as a span
+    SkOpPtT fPtT;  // list of points and t values associated with the start of this span
+    SkOpSegment* fSegment;  // segment that contains this span
+    SkOpSpanBase* fCoinEnd;  // linked list of coincident spans that end here (may point to itself)
+    SkOpAngle* fFromAngle;  // points to next angle from span start to end
+    SkOpSpan* fPrev;  // previous intersection point
+    int fSpanAdds;  // number of times intersections have been added to span
+    bool fAligned;
+    bool fChased;  // set after span has been added to chase array
+    SkDEBUGCODE(int fCount);  // number of pt/t pairs added
+    SkDEBUGCODE(int fID);
+};
+
+class SkOpSpan : public SkOpSpanBase {
+public:
+    bool clearCoincident() {
+        SkASSERT(!final());
+        if (fCoincident == this) {
+            return false;
+        }
+        fCoincident = this;
+        return true;
+    }
+
+    int computeWindSum();
+    bool containsCoincidence(const SkOpSegment* ) const;
+
+    bool containsCoincidence(const SkOpSpan* coin) const {
+        SkASSERT(this != coin);
+        const SkOpSpan* next = this;
+        while ((next = next->fCoincident) != this) {
+            if (next == coin) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool debugCoinLoopCheck() const;
+    void detach(SkOpPtT* );
+
+    bool done() const {
+        SkASSERT(!final());
+        return fDone;
+    }
+
+    void dumpCoin() const;
+    bool dumpSpan() const;
+    void init(SkOpSegment* parent, SkOpSpan* prev, double t, const SkPoint& pt);
+
+    void insertCoincidence(SkOpSpan* coin) {
+        if (containsCoincidence(coin)) {
+            SkASSERT(coin->containsCoincidence(this));
+            return;
+        }
+        debugValidate();
+        SkASSERT(this != coin);
+        SkOpSpan* coinNext = coin->fCoincident;
+        coin->fCoincident = this->fCoincident;
+        this->fCoincident = coinNext;
+        debugValidate();
+    }
+
+    bool isCanceled() const {
+        SkASSERT(!final());
+        return fWindValue == 0 && fOppValue == 0;
+    }
+
+    bool isCoincident() const {
+        SkASSERT(!final());
+        return fCoincident != this;
+    }
+
+    SkOpSpanBase* next() const {
+        SkASSERT(!final());
+        return fNext;
+    }
+
+    int oppSum() const {
+        SkASSERT(!final());
+        return fOppSum;
+    }
+
+    int oppValue() const {
+        SkASSERT(!final());
+        return fOppValue;
+    }
+
+    SkOpPtT* setCoinStart(SkOpSpan* oldCoinStart, SkOpSegment* oppSegment);
+
+    void setDone(bool done) {
+        SkASSERT(!final());
+        fDone = done;
+    }
+
+    void setNext(SkOpSpanBase* nextT) {
+        SkASSERT(!final());
+        fNext = nextT;
+    }
+
+    void setOppSum(int oppSum);
+
+    void setOppValue(int oppValue) {
+        SkASSERT(!final());
+        SkASSERT(fOppSum == SK_MinS32);
+        fOppValue = oppValue;
+    }
+
+    void setToAngle(SkOpAngle* angle) {
+        SkASSERT(!final());
+        fToAngle = angle;
+    }
+
+    void setWindSum(int windSum);
+
+    void setWindValue(int windValue) {
+        SkASSERT(!final());
+        SkASSERT(windValue >= 0);
+        SkASSERT(fWindSum == SK_MinS32);
+        fWindValue = windValue;
+    }
+
+    bool sortableTop(SkOpContour* );
+
+    SkOpAngle* toAngle() const {
+        SkASSERT(!final());
+        return fToAngle;
+    }
+
+    int windSum() const {
+        SkASSERT(!final());
+        return fWindSum;
+    }
+
+    int windValue() const {
+        SkASSERT(!final());
+        return fWindValue;
+    }
+
+private:  // no direct access to internals to avoid treating a span base as a span
+    SkOpSpan* fCoincident;  // linked list of spans coincident with this one (may point to itself)
+    SkOpAngle* fToAngle;  // points to next angle from span start to end
+    SkOpSpanBase* fNext;  // next intersection point
     int fWindSum;  // accumulated from contours surrounding this one.
     int fOppSum;  // for binary operators: the opposite winding sum
     int fWindValue;  // 0 == canceled; 1 == normal; >1 == coincident
     int fOppValue;  // normally 0 -- when binary coincident edges combine, opp value goes here
-    bool fChased;  // set after span has been added to chase array
-    bool fCoincident;  // set if span is bumped -- if set additional points aren't inserted
+    int fTopTTry; // specifies direction and t value to try next
     bool fDone;  // if set, this span to next higher T has been processed
-    bool fLoop;  // set when a cubic loops back to this point
-    bool fMultiple;  // set if this is one of mutiple spans with identical t and pt values
-    bool fNear;  // set if opposite end point is near but not equal to this one
-    bool fSmall;   // if set, consecutive points are almost equal
-    bool fTiny;  // if set, consecutive points are equal but consecutive ts are not precisely equal
-
-    // available to testing only
-    const SkOpSegment* debugToSegment(ptrdiff_t* ) const;
-    void dump() const;
-    void dumpOne() const;
 };
 
 #endif
diff --git a/src/pathops/SkOpTAllocator.h b/src/pathops/SkOpTAllocator.h
index c80c12f..e8835f0 100644
--- a/src/pathops/SkOpTAllocator.h
+++ b/src/pathops/SkOpTAllocator.h
@@ -19,6 +19,12 @@
         return record;
     }
 
+    static T* AllocateArray(SkChunkAlloc* allocator, int count) {
+        void* ptr = allocator->allocThrow(sizeof(T) * count);
+        T* record = (T*) ptr;
+        return record;
+    }
+
     static T* New(SkChunkAlloc* allocator) {
         return new (Allocate(allocator)) T();
     }
diff --git a/src/pathops/SkPathOpsBounds.cpp b/src/pathops/SkPathOpsBounds.cpp
deleted file mode 100644
index e5b26ee..0000000
--- a/src/pathops/SkPathOpsBounds.cpp
+++ /dev/null
@@ -1,40 +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.
- */
-#include "SkPathOpsBounds.h"
-#include "SkPathOpsCubic.h"
-#include "SkPathOpsLine.h"
-#include "SkPathOpsQuad.h"
-
-void SkPathOpsBounds::setCubicBounds(const SkPoint a[4]) {
-    SkDCubic cubic;
-    cubic.set(a);
-    SkDRect dRect;
-    dRect.setBounds(cubic);
-    set(SkDoubleToScalar(dRect.fLeft), SkDoubleToScalar(dRect.fTop),
-            SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
-}
-
-void SkPathOpsBounds::setLineBounds(const SkPoint a[2]) {
-    setPointBounds(a[0]);
-    add(a[1]);
-}
-
-void SkPathOpsBounds::setQuadBounds(const SkPoint a[3]) {
-    SkDQuad quad;
-    quad.set(a);
-    SkDRect dRect;
-    dRect.setBounds(quad);
-    set(SkDoubleToScalar(dRect.fLeft), SkDoubleToScalar(dRect.fTop),
-            SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
-}
-
-void (SkPathOpsBounds::* const SetCurveBounds[])(const SkPoint[]) = {
-    NULL,
-    &SkPathOpsBounds::setLineBounds,
-    &SkPathOpsBounds::setQuadBounds,
-    &SkPathOpsBounds::setCubicBounds
-};
diff --git a/src/pathops/SkPathOpsBounds.h b/src/pathops/SkPathOpsBounds.h
index cabc639..b99f36f 100644
--- a/src/pathops/SkPathOpsBounds.h
+++ b/src/pathops/SkPathOpsBounds.h
@@ -33,11 +33,11 @@
         add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
     }
 
-    void add(const SkPoint& pt) {
-        if (pt.fX < fLeft) fLeft = pt.fX;
-        if (pt.fY < fTop) fTop = pt.fY;
-        if (pt.fX > fRight) fRight = pt.fX;
-        if (pt.fY > fBottom) fBottom = pt.fY;
+    void add(const SkDPoint& pt) {
+        if (pt.fX < fLeft) fLeft = SkDoubleToScalar(pt.fX);
+        if (pt.fY < fTop) fTop = SkDoubleToScalar(pt.fY);
+        if (pt.fX > fRight) fRight = SkDoubleToScalar(pt.fX);
+        if (pt.fY > fBottom) fBottom = SkDoubleToScalar(pt.fY);
     }
 
     bool almostContains(const SkPoint& pt) {
@@ -47,26 +47,7 @@
                 && AlmostLessOrEqualUlps(pt.fY, fBottom);
     }
 
-    // unlike isEmpty(), this permits lines, but not points
-    // FIXME: unused for now
-    bool isReallyEmpty() const {
-        // use !<= instead of > to detect NaN values
-        return !(fLeft <= fRight) || !(fTop <= fBottom)
-                || (fLeft == fRight && fTop == fBottom);
-    }
-
-    void setCubicBounds(const SkPoint a[4]);
-    void setLineBounds(const SkPoint a[2]);
-    void setQuadBounds(const SkPoint a[3]);
-
-    void setPointBounds(const SkPoint& pt) {
-        fLeft = fRight = pt.fX;
-        fTop = fBottom = pt.fY;
-    }
-
     typedef SkRect INHERITED;
 };
 
-extern void (SkPathOpsBounds::* const SetCurveBounds[])(const SkPoint[]);
-
 #endif
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp
index 1a5bfc1..98cce15 100644
--- a/src/pathops/SkPathOpsCommon.cpp
+++ b/src/pathops/SkPathOpsCommon.cpp
@@ -5,149 +5,86 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
 #include "SkOpEdgeBuilder.h"
 #include "SkPathOpsCommon.h"
 #include "SkPathWriter.h"
 #include "SkTSort.h"
 
-static void alignMultiples(SkTArray<SkOpContour*, true>* contourList,
-        SkTDArray<SkOpSegment::AlignedSpan>* aligned) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        if (contour->hasMultiples()) {
-            contour->alignMultiples(aligned);
-        }
+const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
+        bool* sortablePtr) {
+    // find first angle, initialize winding to computed fWindSum
+    SkOpSegment* segment = start->segment();
+    const SkOpAngle* angle = segment->spanToAngle(start, end);
+    if (!angle) {
+        *windingPtr = SK_MinS32;
+        return NULL;
     }
-}
-
-static void alignCoincidence(SkTArray<SkOpContour*, true>* contourList,
-        const SkTDArray<SkOpSegment::AlignedSpan>& aligned) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        int count = aligned.count();
-        for (int index = 0; index < count; ++index) {
-            contour->alignCoincidence(aligned[index]);
+    bool computeWinding = false;
+    const SkOpAngle* firstAngle = angle;
+    bool loop = false;
+    bool unorderable = false;
+    int winding = SK_MinS32;
+    do {
+        angle = angle->next();
+        unorderable |= angle->unorderable();
+        if ((computeWinding = unorderable || (angle == firstAngle && loop))) {
+            break;    // if we get here, there's no winding, loop is unorderable
         }
-    }    
-}
-
-static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** currentPtr,
-                              int* indexPtr, int* endIndexPtr, double* bestHit, SkScalar* bestDx,
-                              bool* tryAgain, double* midPtr, bool opp) {
-    const int index = *indexPtr;
-    const int endIndex = *endIndexPtr;
-    const double mid = *midPtr;
-    const SkOpSegment* current = *currentPtr;
-    double tAtMid = current->tAtMid(index, endIndex, mid);
-    SkPoint basePt = current->ptAtT(tAtMid);
-    int contourCount = contourList.count();
-    SkScalar bestY = SK_ScalarMin;
-    SkOpSegment* bestSeg = NULL;
-    int bestTIndex = 0;
-    bool bestOpp;
-    bool hitSomething = false;
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = contourList[cTest];
-        bool testOpp = contour->operand() ^ current->operand() ^ opp;
-        if (basePt.fY < contour->bounds().fTop) {
-            continue;
-        }
-        if (bestY > contour->bounds().fBottom) {
-            continue;
-        }
-        int segmentCount = contour->segments().count();
-        for (int test = 0; test < segmentCount; ++test) {
-            SkOpSegment* 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;
+        loop |= angle == firstAngle;
+        segment = angle->segment();
+        winding = segment->windSum(angle);
+    } while (winding == SK_MinS32);
+    // if the angle loop contains an unorderable span, the angle order may be useless
+    // directly compute the winding in this case for each span
+    if (computeWinding) {
+        firstAngle = angle;
+        winding = SK_MinS32;
+        do {
+            SkOpSpanBase* startSpan = angle->start();
+            SkOpSpanBase* endSpan = angle->end();
+            SkOpSpan* lesser = startSpan->starter(endSpan);
+            int testWinding = lesser->windSum();
+            if (testWinding == SK_MinS32) {
+                testWinding = lesser->computeWindSum();
             }
-            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
-                double midT = current->tAtMid(index, endIndex, mid);
-                SkPoint midXY = current->xyAtT(midT);
-                double newMidT = current->tAtMid(index, endIndex, newMid);
-                SkPoint newXY = current->xyAtT(newMidT);
-                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
-                *midPtr = newMid * 2;  // calling loop with divide by 2 before continuing
-                return SK_MinS32;
+            if (testWinding != SK_MinS32) {
+                segment = angle->segment();
+                winding = testWinding;
             }
-            bestSeg = testSeg;
-            *bestHit = testHit;
-            bestOpp = testOpp;
-            bestTIndex = testTIndex;
-            bestY = testY;
-        }
+            angle = angle->next();
+        } while (angle != firstAngle);
     }
-abortContours:
-    int result;
-    if (!bestSeg) {
-        result = hitSomething ? SK_MinS32 : 0;
-    } else {
-        if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
-            *currentPtr = bestSeg;
-            *indexPtr = bestTIndex;
-            *endIndexPtr = bestSeg->nextSpan(bestTIndex, 1);
-            SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
-            *tryAgain = true;
-            return 0;
-        }
-        result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx);
-        SkASSERT(result == SK_MinS32 || *bestDx);
-    }
-    double baseT = current->t(index);
-    double endT = current->t(endIndex);
-    *bestHit = baseT + mid * (endT - baseT);
-    return result;
+    *sortablePtr = !unorderable;
+    *windingPtr = winding;
+    return angle;
 }
 
-SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end) {
-    int contourCount = contourList.count();
+SkOpSegment* FindUndone(SkOpContourHead* contourList, SkOpSpanBase** startPtr,
+         SkOpSpanBase** endPtr) {
     SkOpSegment* result;
-    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
-        SkOpContour* contour = contourList[cIndex];
-        result = contour->undoneSegment(start, end);
+    SkOpContour* contour = contourList;
+    do {
+        result = contour->undoneSegment(startPtr, endPtr);
         if (result) {
             return result;
         }
-    }
+    } while ((contour = contour->next()));
     return NULL;
 }
 
-SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex) {
+SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
+        SkOpSpanBase** endPtr) {
     while (chase->count()) {
-        SkOpSpan* span;
+        SkOpSpanBase* span;
         chase->pop(&span);
-        const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
-        SkOpSegment* segment = backPtr.fOther;
-        *tIndex = backPtr.fOtherIndex;
-        bool sortable = true;
+        SkOpSegment* segment = span->segment();
+        *startPtr = span->ptT()->next()->span();
         bool done = true;
-        *endIndex = -1;
-        if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
-                &sortable)) {
-            *tIndex = last->start();
-            *endIndex = last->end();
+        *endPtr = NULL;
+        if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
+            *startPtr = last->start();
+            *endPtr = last->end();
     #if TRY_ROTATE
             *chase->insert(0) = span;
     #else
@@ -158,385 +95,90 @@
         if (done) {
             continue;
         }
-        if (!sortable) {
+        // find first angle, initialize winding to computed wind sum
+        int winding;
+        bool sortable;
+        const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
+        if (winding == SK_MinS32) {
             continue;
         }
-        // find first angle, initialize winding to computed wind sum
-        const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
-        const SkOpAngle* firstAngle;
-        SkDEBUGCODE(firstAngle = angle);
-        SkDEBUGCODE(bool loop = false);
-        int winding;
-        do {
-            angle = angle->next();
-            SkASSERT(angle != firstAngle || !loop);
-            SkDEBUGCODE(loop |= angle == firstAngle);
+        int sumWinding SK_INIT_TO_AVOID_WARNING;
+        if (sortable) {
             segment = angle->segment();
-            winding = segment->windSum(angle);
-        } while (winding == SK_MinS32);
-        int spanWinding = segment->spanSign(angle->start(), angle->end());
-    #if DEBUG_WINDING
-        SkDebugf("%s winding=%d spanWinding=%d\n", __FUNCTION__, winding, spanWinding);
-    #endif
-        // turn span winding into contour winding
-        if (spanWinding * winding < 0) {
-            winding += spanWinding;
+            sumWinding = segment->updateWindingReverse(angle);
         }
-        // we care about first sign and whether wind sum indicates this
-        // edge is inside or outside. Maybe need to pass span winding
-        // or first winding or something into this function?
-        // advance to first undone angle, then return it and winding
-        // (to set whether edges are active or not)
-        firstAngle = angle;
-        winding -= firstAngle->segment()->spanSign(firstAngle);
+        SkOpSegment* first = NULL;
+        const SkOpAngle* firstAngle = angle;
         while ((angle = angle->next()) != firstAngle) {
             segment = angle->segment();
-            int maxWinding = winding;
-            winding -= segment->spanSign(angle);
-    #if DEBUG_SORT
-            SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
-                    segment->debugID(), maxWinding, winding, angle->sign());
-    #endif
-            *tIndex = angle->start();
-            *endIndex = angle->end();
-            int lesser = SkMin32(*tIndex, *endIndex);
-            const SkOpSpan& nextSpan = segment->span(lesser);
-            if (!nextSpan.fDone) {
-            // 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 (SkOpSegment::UseInnerWinding(maxWinding, winding)) {
-                    maxWinding = winding;
+            SkOpSpanBase* start = angle->start();
+            SkOpSpanBase* end = angle->end();
+            int maxWinding;
+            if (sortable) {
+                segment->setUpWinding(start, end, &maxWinding, &sumWinding);
+            }
+            if (!segment->done(angle)) {
+                if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
+                    first = segment;
+                    *startPtr = start;
+                    *endPtr = end;
                 }
-                // allowed to do nothing
-                (void) segment->markAndChaseWinding(angle, maxWinding, 0, NULL);
-                break;
+                // OPTIMIZATION: should this also add to the chase?
+                if (sortable) {
+                    (void) segment->markAngle(maxWinding, sumWinding, angle);
+                }
             }
         }
-        *chase->insert(0) = span;
-        return segment;
+        if (first) {
+       #if TRY_ROTATE
+            *chase->insert(0) = span;
+       #else
+            *chase->append() = span;
+       #endif
+            return first;
+        }
     }
     return NULL;
 }
 
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
-void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList) {
-    int index;
-    for (index = 0; index < contourList.count(); ++ index) {
-        contourList[index]->debugShowActiveSpans();
-    }
+#if DEBUG_ACTIVE_SPANS
+void DebugShowActiveSpans(SkOpContourHead* contourList) {
+    SkOpContour* contour = contourList;
+    do {
+        contour->debugShowActiveSpans();
+    } while ((contour = contour->next()));
 }
 #endif
 
-static SkOpSegment* findTopSegment(const SkTArray<SkOpContour*, true>& contourList, int* index,
-        int* endIndex, SkPoint* topLeft, bool* unsortable, bool* done, bool firstPass) {
-    SkOpSegment* result;
-    const SkOpSegment* lastTopStart = NULL;
-    int lastIndex = -1, lastEndIndex = -1;
+bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOdd) {
+    SkTDArray<SkOpContour* > list;
+    SkOpContour* contour = *contourList;
     do {
-        SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
-        int contourCount = contourList.count();
-        SkOpSegment* topStart = NULL;
-        *done = true;
-        for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
-            SkOpContour* contour = contourList[cIndex];
-            if (contour->done()) {
-                continue;
-            }
-            const SkPathOpsBounds& bounds = contour->bounds();
-            if (bounds.fBottom < topLeft->fY) {
-                *done = false;
-                continue;
-            }
-            if (bounds.fBottom == topLeft->fY && bounds.fRight < topLeft->fX) {
-                *done = false;
-                continue;
-            }
-            contour->topSortableSegment(*topLeft, &bestXY, &topStart);
-            if (!contour->done()) {
-                *done = false;
-            }
+        if (contour->count()) {
+            contour->setOppXor(contour->operand() ? evenOdd : oppEvenOdd);
+            *list.append() = contour;
         }
-        if (!topStart) {
-            return NULL;
-        }
-        *topLeft = bestXY;
-        result = topStart->findTop(index, endIndex, unsortable, firstPass);
-        if (!result) {
-            if (lastTopStart == topStart && lastIndex == *index && lastEndIndex == *endIndex) {
-                *done = true;
-                return NULL;
-            }
-            lastTopStart = topStart;
-            lastIndex = *index;
-            lastEndIndex = *endIndex;
-        }
-    } while (!result);
-    return result;
-}
-
-static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList,
-        SkOpSegment** currentPtr, int* indexPtr, int* endIndexPtr, double* tHit,
-        SkScalar* hitDx, bool* tryAgain, bool* onlyVertical, bool opp) {
-    double test = 0.9;
-    int contourWinding;
-    do {
-        contourWinding = contourRangeCheckY(contourList, currentPtr, indexPtr, endIndexPtr,
-                tHit, hitDx, tryAgain, &test, opp);
-        if (contourWinding != SK_MinS32 || *tryAgain) {
-            return contourWinding;
-        }
-        if (*currentPtr && (*currentPtr)->isVertical()) {
-            *onlyVertical = true;
-            return contourWinding;
-        }
-        test /= 2;
-    } while (!approximately_negative(test));
-    SkASSERT(0);  // FIXME: incomplete functionality
-    return contourWinding;
-}
-
-static void skipVertical(const SkTArray<SkOpContour*, true>& contourList,
-        SkOpSegment** current, int* index, int* endIndex) {
-    if (!(*current)->isVertical(*index, *endIndex)) {
-        return;
+    } while ((contour = contour->next()));
+    int count = list.count();
+    if (!count) {
+        return false;
     }
-    int contourCount = contourList.count();
-    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
-        SkOpContour* contour = contourList[cIndex];
-        if (contour->done()) {
-            continue;
-        }
-        SkOpSegment* nonVertical = contour->nonVerticalSegment(index, endIndex);
-        if (nonVertical) {
-            *current = nonVertical;
-            return;
-        }
+    if (count > 1) {
+        SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
     }
-    return;
-}
-
-struct SortableTop {  // error if local in pre-C++11
-    SkOpSegment* fSegment;
-    int fIndex;
-    int fEndIndex;
-};
-
-SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList,
-        SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr,
-        int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical,
-        bool firstPass) {
-    SkOpSegment* current = findTopSegment(contourList, indexPtr, endIndexPtr, topLeft, unsortable,
-            done, firstPass);
-    if (!current) {
-        return NULL;
+    contour = list[0];
+    SkOpContourHead* contourHead = static_cast<SkOpContourHead*>(contour);
+    contour->globalState()->setContourHead(contourHead);
+    *contourList = contourHead;
+    for (int index = 1; index < count; ++index) {
+        SkOpContour* next = list[index];
+        contour->setNext(next);
+        contour = next;
     }
-    const int startIndex = *indexPtr;
-    const int endIndex = *endIndexPtr;
-    if (*firstContour) {
-        current->initWinding(startIndex, endIndex, angleIncludeType);
-        *firstContour = false;
-        return current;
-    }
-    int minIndex = SkMin32(startIndex, endIndex);
-    int sumWinding = current->windSum(minIndex);
-    if (sumWinding == SK_MinS32) {
-        int index = endIndex;
-        int oIndex = startIndex;
-        do { 
-            const SkOpSpan& span = current->span(index);
-            if ((oIndex < index ? span.fFromAngle : span.fToAngle) == NULL) {
-                current->addSimpleAngle(index);
-            }
-            sumWinding = current->computeSum(oIndex, index, angleIncludeType);
-            SkTSwap(index, oIndex);
-        } while (sumWinding == SK_MinS32 && index == startIndex);
-    }
-    if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) {
-        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;
-    // keep track of subsequent returns to detect infinite loops
-    SkTDArray<SortableTop> sortableTops;
-    do {
-        // if current is vertical, find another candidate which is not
-        // if only remaining candidates are vertical, then they can be marked done
-        SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
-        skipVertical(contourList, &current, indexPtr, endIndexPtr);
-        SkASSERT(current);  // FIXME: if null, all remaining are vertical
-        SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0);
-        tryAgain = false;
-        contourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
-                &hitDx, &tryAgain, onlyVertical, false);
-        if (tryAgain) {
-            bool giveUp = false;
-            int count = sortableTops.count();
-            for (int index = 0; index < count; ++index) {
-                const SortableTop& prev = sortableTops[index];
-                if (giveUp) {
-                    prev.fSegment->markDoneFinal(prev.fIndex);
-                } else if (prev.fSegment == current
-                        && (prev.fIndex == *indexPtr || prev.fEndIndex == *endIndexPtr)) {
-                    // remaining edges are non-vertical and cannot have their winding computed
-                    // mark them as done and return, and hope that assembly can fill the holes
-                    giveUp = true;
-                    index = -1;
-                }
-            }
-            if (giveUp) {
-                *done = true;
-                return NULL;
-            }
-        }
-        SortableTop* sortableTop = sortableTops.append();
-        sortableTop->fSegment = current;
-        sortableTop->fIndex = *indexPtr;
-        sortableTop->fEndIndex = *endIndexPtr;
-#if DEBUG_SORT
-        SkDebugf("%s current=%d index=%d endIndex=%d tHit=%1.9g hitDx=%1.9g try=%d vert=%d\n",
-                __FUNCTION__, current->debugID(), *indexPtr, *endIndexPtr, tHit, hitDx, tryAgain,
-                *onlyVertical);
-#endif
-        if (*onlyVertical) {
-            return current;
-        }
-        if (tryAgain) {
-            continue;
-        }
-        if (angleIncludeType < SkOpAngle::kBinarySingle) {
-            break;
-        }
-        oppContourWinding = rightAngleWinding(contourList, &current, indexPtr, endIndexPtr, &tHit,
-                &hitOppDx, &tryAgain, NULL, true);
-    } while (tryAgain);
-    bool success = current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx,
-            oppContourWinding, hitOppDx);
-    if (current->done()) {
-        return NULL;
-    } else if (!success) {  // check if the span has a valid winding
-        int min = SkTMin(*indexPtr, *endIndexPtr);
-        const SkOpSpan& span = current->span(min);
-        if (span.fWindSum == SK_MinS32) {
-            return NULL;
-        }
-    }
-    return current;
-}
-
-static bool calcAngles(SkTArray<SkOpContour*, true>* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        if (!contour->calcAngles()) {
-            return false;
-        }
-    }
+    contour->setNext(NULL);
     return true;
 }
 
-static void checkDuplicates(SkTArray<SkOpContour*, true>* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->checkDuplicates();
-    }
-}
-
-static bool checkEnds(SkTArray<SkOpContour*, true>* contourList) {
-    // it's hard to determine if the end of a cubic or conic nearly intersects another curve.
-    // instead, look to see if the connecting curve intersected at that same end.
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        if (!contour->checkEnds()) {
-            return false;
-        }
-    }
-    return true;
-}
-
-static bool checkMultiples(SkTArray<SkOpContour*, true>* contourList) {
-    bool hasMultiples = false;
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->checkMultiples();
-        hasMultiples |= contour->hasMultiples();
-    }
-    return hasMultiples;
-}
-
-// A small interval of a pair of curves may collapse to lines for each, triggering coincidence
-static void checkSmall(SkTArray<SkOpContour*, true>* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->checkSmall();
-    }
-}
-
-// A tiny interval may indicate an undiscovered coincidence. Find and fix.
-static void checkTiny(SkTArray<SkOpContour*, true>* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->checkTiny();
-    }
-}
-
-static void fixOtherTIndex(SkTArray<SkOpContour*, true>* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->fixOtherTIndex();
-    }
-}
-
-static void joinCoincidence(SkTArray<SkOpContour*, true>* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->joinCoincidence();
-    }
-}
-
-static void sortAngles(SkTArray<SkOpContour*, true>* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->sortAngles();
-    }
-}
-
-static void sortSegments(SkTArray<SkOpContour*, true>* contourList) {
-    int contourCount = (*contourList).count();
-    for (int cTest = 0; cTest < contourCount; ++cTest) {
-        SkOpContour* contour = (*contourList)[cTest];
-        contour->sortSegments();
-    }
-}
-
-void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
-                     bool evenOdd, bool oppEvenOdd) {
-    int count = contours.count();
-    if (count == 0) {
-        return;
-    }
-    for (int index = 0; index < count; ++index) {
-        SkOpContour& contour = contours[index];
-        contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
-        list.push_back(&contour);
-    }
-    SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
-}
-
 class DistanceLessThan {
 public:
     DistanceLessThan(double* distances) : fDistances(distances) { }
@@ -554,19 +196,25 @@
         reassemble contour pieces into new path
     */
 void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
+    SkChunkAlloc allocator(4096);  // FIXME: constant-ize, tune
+    SkOpContourHead contour;
+    SkOpGlobalState globalState(NULL, &contour);
+#if DEBUG_SHOW_TEST_NAME
+    SkDebugf("</div>\n");
+#endif
 #if DEBUG_PATH_CONSTRUCTION
     SkDebugf("%s\n", __FUNCTION__);
 #endif
-    SkTArray<SkOpContour> contours;
-    SkOpEdgeBuilder builder(path, contours);
-    builder.finish();
-    int count = contours.count();
-    int outer;
-    SkTArray<int, true> runs(count);  // indices of partial contours
-    for (outer = 0; outer < count; ++outer) {
-        const SkOpContour& eContour = contours[outer];
-        const SkPoint& eStart = eContour.start();
-        const SkPoint& eEnd = eContour.end();
+    SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
+    builder.finish(&allocator);
+    SkTDArray<const SkOpContour* > runs;  // indices of partial contours
+    const SkOpContour* eContour = builder.head();
+    do {
+        if (!eContour->count()) {
+            continue;
+        }
+        const SkPoint& eStart = eContour->start();
+        const SkPoint& eEnd = eContour->end();
 #if DEBUG_ASSEMBLE
         SkDebugf("%s contour", __FUNCTION__);
         if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
@@ -578,44 +226,42 @@
                 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
 #endif
         if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
-            eContour.toPath(simple);
+            eContour->toPath(simple);
             continue;
         }
-        runs.push_back(outer);
-    }
-    count = runs.count();
+        *runs.append() = eContour;
+    } while ((eContour = eContour->next()));
+    int count = runs.count();
     if (count == 0) {
         return;
     }
-    SkTArray<int, true> sLink, eLink;
-    sLink.push_back_n(count);
-    eLink.push_back_n(count);
+    SkTDArray<int> sLink, eLink;
+    sLink.append(count);
+    eLink.append(count);
     int rIndex, iIndex;
     for (rIndex = 0; rIndex < count; ++rIndex) {
         sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
     }
     const int ends = count * 2;  // all starts and ends
     const int entries = (ends - 1) * count;  // folded triangle : n * (n - 1) / 2
-    SkTArray<double, true> distances;
-    distances.push_back_n(entries);
+    SkTDArray<double> distances;
+    distances.append(entries);
     for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
-        outer = runs[rIndex >> 1];
-        const SkOpContour& oContour = contours[outer];
-        const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
+        const SkOpContour* oContour = runs[rIndex >> 1];
+        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 SkOpContour& iContour = contours[inner];
-            const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
+            const SkOpContour* iContour = runs[iIndex >> 1];
+            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;
             distances[row + iIndex] = dist;  // oStart distance from iStart
         }
     }
-    SkTArray<int, true> sortedDist;
-    sortedDist.push_back_n(entries);
+    SkTDArray<int> sortedDist;
+    sortedDist.append(entries);
     for (rIndex = 0; rIndex < entries; ++rIndex) {
         sortedDist[rIndex] = rIndex;
     }
@@ -678,17 +324,16 @@
                     eIndex < 0 ? ~eIndex : eIndex);
 #endif
         do {
-            outer = runs[rIndex];
-            const SkOpContour& contour = contours[outer];
+            const SkOpContour* contour = runs[rIndex];
             if (first) {
                 first = false;
-                const SkPoint* startPtr = &contour.start();
+                const SkPoint* startPtr = &contour->start();
                 simple->deferredMove(startPtr[0]);
             }
             if (forward) {
-                contour.toPartialForward(simple);
+                contour->toPartialForward(simple);
             } else {
-                contour.toPartialBackward(simple);
+                contour->toPartialBackward(simple);
             }
 #if DEBUG_ASSEMBLE
             SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
@@ -742,37 +387,81 @@
 #endif
 }
 
-bool HandleCoincidence(SkTArray<SkOpContour*, true>* contourList, int total) {
-#if DEBUG_SHOW_WINDING
-    SkOpContour::debugShowWindingValues(contourList);
+static void align(SkOpContourHead* contourList) {
+    SkOpContour* contour = contourList;
+    do {
+        contour->align();
+    } while ((contour = contour->next()));
+}
+
+static void calcAngles(SkOpContourHead* contourList, SkChunkAlloc* allocator) {
+    SkOpContour* contour = contourList;
+    do {
+        contour->calcAngles(allocator);
+    } while ((contour = contour->next()));
+}
+
+static void missingCoincidence(SkOpContourHead* contourList,
+        SkOpCoincidence* coincidence, SkChunkAlloc* allocator) {
+    SkOpContour* contour = contourList;
+    do {
+        contour->missingCoincidence(coincidence, allocator);
+    } while ((contour = contour->next()));
+}
+
+static void moveMultiples(SkOpContourHead* contourList) {
+    SkOpContour* contour = contourList;
+    do {
+        contour->moveMultiples();
+    } while ((contour = contour->next()));
+}
+
+static void moveNearby(SkOpContourHead* contourList) {
+    SkOpContour* contour = contourList;
+    do {
+        contour->moveNearby();
+    } while ((contour = contour->next()));
+}
+
+static void sortAngles(SkOpContourHead* contourList) {
+    SkOpContour* contour = contourList;
+    do {
+        contour->sortAngles();
+    } while ((contour = contour->next()));
+}
+
+bool HandleCoincidence(SkOpContourHead* contourList, SkOpCoincidence* coincidence,
+        SkChunkAlloc* allocator) {
+    SkOpGlobalState* globalState = contourList->globalState();
+    // combine t values when multiple intersections occur on some segments but not others
+    moveMultiples(contourList);
+    // move t values and points together to eliminate small/tiny gaps
+    moveNearby(contourList);
+    align(contourList);  // give all span members common values
+#if DEBUG_VALIDATE
+    globalState->setPhase(SkOpGlobalState::kIntersecting);
 #endif
-    if (!CoincidenceCheck(contourList, total)) {
-        return false;
-    }
-#if DEBUG_SHOW_WINDING
-    SkOpContour::debugShowWindingValues(contourList);
+    coincidence->addMissing(allocator);
+#if DEBUG_VALIDATE
+    globalState->setPhase(SkOpGlobalState::kWalking);
 #endif
-    fixOtherTIndex(contourList);
-    if (!checkEnds(contourList)) {  // check if connecting curve intersected at the same end
+    coincidence->expand();  // check to see if, loosely, coincident ranges may be expanded
+    coincidence->mark();  // mark spans of coincident segments as coincident
+    missingCoincidence(contourList, coincidence, allocator);  // look for coincidence missed earlier
+    if (!coincidence->apply()) {  // adjust the winding value to account for coincident edges
         return false;
     }
-    bool hasM = checkMultiples(contourList);  // check if intersections agree on t and point values
-    SkTDArray<SkOpSegment::AlignedSpan> aligned;
-    if (hasM) {
-        alignMultiples(contourList, &aligned);  // align pairs of identical points
-        alignCoincidence(contourList, aligned);
-    }
-    checkDuplicates(contourList);  // check if spans have the same number on the other end
-    checkTiny(contourList);  // if pair have the same end points, mark them as parallel
-    checkSmall(contourList);  // a pair of curves with a small span may turn into coincident lines
-    joinCoincidence(contourList);  // join curves that connect to a coincident pair
-    sortSegments(contourList);
-    if (!calcAngles(contourList)) {
-        return false;
-    }
+    calcAngles(contourList, allocator);
     sortAngles(contourList);
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
-    DebugShowActiveSpans(*contourList);
+    if (globalState->angleCoincidence()) {
+        missingCoincidence(contourList, coincidence, allocator);
+        if (!coincidence->apply()) {
+            return false;
+        }
+    }
+#if DEBUG_ACTIVE_SPANS
+    coincidence->debugShowCoincidence();
+    DebugShowActiveSpans(contourList);
 #endif
     return true;
 }
diff --git a/src/pathops/SkPathOpsCommon.h b/src/pathops/SkPathOpsCommon.h
index 0d8cfc4..b7fbf38 100644
--- a/src/pathops/SkPathOpsCommon.h
+++ b/src/pathops/SkPathOpsCommon.h
@@ -8,24 +8,25 @@
 #define SkPathOpsCommon_DEFINED
 
 #include "SkOpAngle.h"
-#include "SkOpContour.h"
 #include "SkTDArray.h"
 
+class SkOpCoincidence;
+class SkOpContour;
 class SkPathWriter;
 
+const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
+                              bool* sortable);
 void Assemble(const SkPathWriter& path, SkPathWriter* simple);
-// FIXME: find chase uses insert, so it can't be converted to SkTArray yet
-SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex);
-SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& , SkOpAngle::IncludeType ,
-                             bool* firstContour, int* index, int* endIndex, SkPoint* topLeft,
-                             bool* unsortable, bool* done, bool* onlyVertical, bool firstPass);
-SkOpSegment* FindUndone(SkTArray<SkOpContour*, true>& contourList, int* start, int* end);
-void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, true>& list,
-                     bool evenOdd, bool oppEvenOdd);
-bool HandleCoincidence(SkTArray<SkOpContour*, true>* , int );
-
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
-void DebugShowActiveSpans(SkTArray<SkOpContour*, true>& contourList);
+SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
+                       SkOpSpanBase** endPtr);
+SkOpSpan* FindSortableTop(SkOpContourHead* );
+SkOpSegment* FindUndone(SkOpContourHead* , SkOpSpanBase** startPtr,
+                        SkOpSpanBase** endPtr);
+bool SortContourList(SkOpContourHead** , bool evenOdd, bool oppEvenOdd);
+bool HandleCoincidence(SkOpContourHead* , SkOpCoincidence* , SkChunkAlloc* );
+bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result, bool expectSuccess);
+#if DEBUG_ACTIVE_SPANS
+void DebugShowActiveSpans(SkOpContourHead* );
 #endif
 
 #endif
diff --git a/src/pathops/SkPathOpsConic.cpp b/src/pathops/SkPathOpsConic.cpp
new file mode 100644
index 0000000..b9b0cda
--- /dev/null
+++ b/src/pathops/SkPathOpsConic.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkIntersections.h"
+#include "SkLineParameters.h"
+#include "SkPathOpsConic.h"
+#include "SkPathOpsCubic.h"
+#include "SkPathOpsQuad.h"
+
+// cribbed from the float version in SkGeometry.cpp
+static void conic_deriv_coeff(const double src[],
+                              SkScalar w,
+                              double coeff[3]) {
+    const double P20 = src[4] - src[0];
+    const double P10 = src[2] - src[0];
+    const double wP10 = w * P10;
+    coeff[0] = w * P20 - P20;
+    coeff[1] = P20 - 2 * wP10;
+    coeff[2] = wP10;
+}
+
+static double conic_eval_tan(const double coord[], SkScalar w, double t) {
+    double coeff[3];
+    conic_deriv_coeff(coord, w, coeff);
+    return t * (t * coeff[0] + coeff[1]) + coeff[2];
+}
+
+int SkDConic::FindExtrema(const double src[], SkScalar w, double t[1]) {
+    double coeff[3];
+    conic_deriv_coeff(src, w, coeff);
+
+    double tValues[2];
+    int roots = SkDQuad::RootsValidT(coeff[0], coeff[1], coeff[2], tValues);
+    SkASSERT(0 == roots || 1 == roots);
+
+    if (1 == roots) {
+        t[0] = tValues[0];
+        return 1;
+    }
+    return 0;
+}
+
+SkDVector SkDConic::dxdyAtT(double t) const {
+    SkDVector result = {
+        conic_eval_tan(&fPts[0].fX, fWeight, t),
+        conic_eval_tan(&fPts[0].fY, fWeight, t)
+    };
+    return result;
+}
+
+static double conic_eval_numerator(const double src[], SkScalar w, double t) {
+    SkASSERT(src);
+    SkASSERT(t >= 0 && t <= 1);
+    double src2w = src[2] * w;
+    double C = src[0];
+    double A = src[4] - 2 * src2w + C;
+    double B = 2 * (src2w - C);
+    return (A * t + B) * t + C;
+}
+
+
+static double conic_eval_denominator(SkScalar w, double t) {
+    double B = 2 * (w - 1);
+    double C = 1;
+    double A = -B;
+    return (A * t + B) * t + C;
+}
+
+bool SkDConic::hullIntersects(const SkDCubic& cubic, bool* isLinear) const {
+    return cubic.hullIntersects(*this, isLinear);
+}
+
+SkDPoint SkDConic::ptAtT(double t) const {
+    double denominator = conic_eval_denominator(fWeight, t);
+    SkDPoint result = {
+        conic_eval_numerator(&fPts[0].fX, fWeight, t) / denominator,
+        conic_eval_numerator(&fPts[0].fY, fWeight, t) / denominator
+    };
+    return result;
+}
+
+/* see quad subdivide for rationale */
+SkDConic SkDConic::subDivide(double t1, double t2) const {
+    double ax = conic_eval_numerator(&fPts[0].fX, fWeight, t1);
+    double ay = conic_eval_numerator(&fPts[0].fY, fWeight, t1);
+    double az = conic_eval_denominator(fWeight, t1);
+    double midT = (t1 + t2) / 2;
+    double dx = conic_eval_numerator(&fPts[0].fX, fWeight, midT);
+    double dy = conic_eval_numerator(&fPts[0].fY, fWeight, midT);
+    double dz = conic_eval_denominator(fWeight, midT);
+    double cx = conic_eval_numerator(&fPts[0].fX, fWeight, t2);
+    double cy = conic_eval_numerator(&fPts[0].fY, fWeight, t2);
+    double cz = conic_eval_denominator(fWeight, t2);
+    double bx = 2 * dx - (ax + cx) / 2;
+    double by = 2 * dy - (ay + cy) / 2;
+    double bz = 2 * dz - (az + cz) / 2;
+    double dt = t2 - t1;
+    double dt_1 = 1 - dt;
+    SkScalar w = SkDoubleToScalar((1 + dt * (fWeight - 1))
+            / sqrt(dt * dt + 2 * dt * dt_1 * fWeight + dt_1 * dt_1));
+    SkDConic dst = {{{{ax / az, ay / az}, {bx / bz, by / bz}, {cx / cz, cy / cz}}}, w };
+    return dst;
+}
+
+SkDPoint SkDConic::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2,
+        SkScalar* weight) const {
+    SkDConic chopped = this->subDivide(t1, t2);
+    *weight = chopped.fWeight;
+    return chopped[1];
+}
diff --git a/src/pathops/SkPathOpsConic.h b/src/pathops/SkPathOpsConic.h
new file mode 100644
index 0000000..bc73049
--- /dev/null
+++ b/src/pathops/SkPathOpsConic.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPathOpsConic_DEFINED
+#define SkPathOpsConic_DEFINED
+
+#include "SkPathOpsPoint.h"
+#include "SkPathOpsQuad.h"
+
+struct SkDConic {
+    static const int kPointCount = 3;
+    static const int kPointLast = kPointCount - 1;
+    static const int kMaxIntersections = 4;
+
+    SkDQuad fPts;
+    SkScalar fWeight;
+
+    bool collapsed() const {
+        return fPts.collapsed();
+    }
+
+    bool controlsInside() const {
+        return fPts.controlsInside();
+    }
+
+    void debugInit() {
+        fPts.debugInit();
+    }
+
+    SkDConic flip() const {
+        SkDConic result = {{{fPts[2], fPts[1], fPts[0]}}, fWeight};
+        return result;
+    }
+
+    static bool IsCubic() { return false; }
+
+    const SkDConic& set(const SkPoint pts[kPointCount], SkScalar weight) {
+        fPts.set(pts);
+        fWeight = weight;
+        return *this;
+    }
+
+    const SkDPoint& operator[](int n) const { return fPts[n]; }
+    SkDPoint& operator[](int n) { return fPts[n]; }
+
+    static int AddValidTs(double s[], int realRoots, double* t) {
+        return SkDQuad::AddValidTs(s, realRoots, t);
+    }
+
+    void align(int endIndex, SkDPoint* dstPt) const {
+        fPts.align(endIndex, dstPt);
+    }
+
+    SkDVector dxdyAtT(double t) const;
+    static int FindExtrema(const double src[], SkScalar weight, double tValue[1]);
+
+    bool hullIntersects(const SkDQuad& quad, bool* isLinear) const {
+        return fPts.hullIntersects(quad, isLinear);
+    }
+
+    bool hullIntersects(const SkDConic& conic, bool* isLinear) const {
+        return fPts.hullIntersects(conic.fPts, isLinear);
+    }
+
+    bool hullIntersects(const SkDCubic& cubic, bool* isLinear) const;
+
+    bool isLinear(int startIndex, int endIndex) const {
+        return fPts.isLinear(startIndex, endIndex);
+    }
+
+    bool monotonicInX() const {
+        return fPts.monotonicInX();
+    }
+
+    bool monotonicInY() const {
+        return fPts.monotonicInY();
+    }
+
+    void otherPts(int oddMan, const SkDPoint* endPt[2]) const {
+        fPts.otherPts(oddMan, endPt);
+    }
+
+    SkDPoint ptAtT(double t) const;
+
+    static int RootsReal(double A, double B, double C, double t[2]) {
+        return SkDQuad::RootsReal(A, B, C, t);
+    }
+
+    static int RootsValidT(const double A, const double B, const double C, double s[2]) {
+        return SkDQuad::RootsValidT(A, B, C, s);
+    }
+
+    SkDConic subDivide(double t1, double t2) const;
+
+    static SkDConic SubDivide(const SkPoint a[kPointCount], SkScalar weight, double t1, double t2) {
+        SkDConic conic;
+        conic.set(a, weight);
+        return conic.subDivide(t1, t2);
+    }
+
+    SkDPoint subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2,
+            SkScalar* weight) const;
+
+    static SkDPoint SubDivide(const SkPoint pts[kPointCount], SkScalar weight,
+                              const SkDPoint& a, const SkDPoint& c,
+                              double t1, double t2, SkScalar* newWeight) {
+        SkDConic conic;
+        conic.set(pts, weight);
+        return conic.subDivide(a, c, t1, t2, newWeight);
+    }
+
+    // utilities callable by the user from the debugger when the implementation code is linked in
+    void dump() const;
+    void dumpID(int id) const;
+    void dumpInner() const;
+};
+
+
+#endif
diff --git a/src/pathops/SkPathOpsCubic.cpp b/src/pathops/SkPathOpsCubic.cpp
index 9d70d58..972c510 100644
--- a/src/pathops/SkPathOpsCubic.cpp
+++ b/src/pathops/SkPathOpsCubic.cpp
@@ -4,8 +4,11 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+#include "SkGeometry.h"
 #include "SkLineParameters.h"
+#include "SkPathOpsConic.h"
 #include "SkPathOpsCubic.h"
+#include "SkPathOpsCurve.h"
 #include "SkPathOpsLine.h"
 #include "SkPathOpsQuad.h"
 #include "SkPathOpsRect.h"
@@ -13,6 +16,15 @@
 
 const int SkDCubic::gPrecisionUnit = 256;  // FIXME: test different values in test framework
 
+void SkDCubic::align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const {
+    if (fPts[endIndex].fX == fPts[ctrlIndex].fX) {
+        dstPt->fX = fPts[endIndex].fX;
+    }
+    if (fPts[endIndex].fY == fPts[ctrlIndex].fY) {
+        dstPt->fY = fPts[endIndex].fY;
+    }
+}
+
 // give up when changing t no longer moves point
 // also, copy point rather than recompute it when it does change
 double SkDCubic::binarySearch(double min, double max, double axisIntercept,
@@ -26,8 +38,8 @@
         double priorT = t - step;
         SkASSERT(priorT >= min);
         SkDPoint lessPt = ptAtT(priorT);
-        if (approximately_equal(lessPt.fX, cubicAtT.fX)
-                && approximately_equal(lessPt.fY, cubicAtT.fY)) {
+        if (approximately_equal_half(lessPt.fX, cubicAtT.fX)
+                && approximately_equal_half(lessPt.fY, cubicAtT.fY)) {
             return -1;  // binary search found no point at this axis intercept
         }
         double lessDist = (&lessPt.fX)[xAxis] - axisIntercept;
@@ -41,10 +53,12 @@
             t = priorT;
         } else {
             double nextT = t + lastStep;
-            SkASSERT(nextT <= max);
+            if (nextT > max) {
+                return -1;
+            }
             SkDPoint morePt = ptAtT(nextT);
-            if (approximately_equal(morePt.fX, cubicAtT.fX)
-                    && approximately_equal(morePt.fY, cubicAtT.fY)) {
+            if (approximately_equal_half(morePt.fX, cubicAtT.fX)
+                    && approximately_equal_half(morePt.fY, cubicAtT.fY)) {
                 return -1;  // binary search found no point at this axis intercept
             }
             double moreDist = (&morePt.fX)[xAxis] - axisIntercept;
@@ -70,12 +84,45 @@
     return (width > height ? width : height) / gPrecisionUnit;
 }
 
-bool SkDCubic::clockwise() const {
-    double sum = (fPts[0].fX - fPts[3].fX) * (fPts[0].fY + fPts[3].fY);
-    for (int idx = 0; idx < 3; ++idx) {
-        sum += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY);
+
+/* classic one t subdivision */
+static void interp_cubic_coords(const double* src, double* dst, double t) {
+    double ab = SkDInterp(src[0], src[2], t);
+    double bc = SkDInterp(src[2], src[4], t);
+    double cd = SkDInterp(src[4], src[6], t);
+    double abc = SkDInterp(ab, bc, t);
+    double bcd = SkDInterp(bc, cd, t);
+    double abcd = SkDInterp(abc, bcd, t);
+
+    dst[0] = src[0];
+    dst[2] = ab;
+    dst[4] = abc;
+    dst[6] = abcd;
+    dst[8] = bcd;
+    dst[10] = cd;
+    dst[12] = src[6];
+}
+
+SkDCubicPair SkDCubic::chopAt(double t) const {
+    SkDCubicPair dst;
+    if (t == 0.5) {
+        dst.pts[0] = fPts[0];
+        dst.pts[1].fX = (fPts[0].fX + fPts[1].fX) / 2;
+        dst.pts[1].fY = (fPts[0].fY + fPts[1].fY) / 2;
+        dst.pts[2].fX = (fPts[0].fX + 2 * fPts[1].fX + fPts[2].fX) / 4;
+        dst.pts[2].fY = (fPts[0].fY + 2 * fPts[1].fY + fPts[2].fY) / 4;
+        dst.pts[3].fX = (fPts[0].fX + 3 * (fPts[1].fX + fPts[2].fX) + fPts[3].fX) / 8;
+        dst.pts[3].fY = (fPts[0].fY + 3 * (fPts[1].fY + fPts[2].fY) + fPts[3].fY) / 8;
+        dst.pts[4].fX = (fPts[1].fX + 2 * fPts[2].fX + fPts[3].fX) / 4;
+        dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4;
+        dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2;
+        dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2;
+        dst.pts[6] = fPts[3];
+        return dst;
     }
-    return sum <= 0;
+    interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t);
+    interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t);
+    return dst;
 }
 
 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C, double* D) {
@@ -88,35 +135,6 @@
     *C -= 3 * *D;           // C = -3*a + 3*b
 }
 
-bool SkDCubic::controlsContainedByEnds() const {
-    SkDVector startTan = fPts[1] - fPts[0];
-    if (startTan.fX == 0 && startTan.fY == 0) {
-        startTan = fPts[2] - fPts[0];
-    }
-    SkDVector endTan = fPts[2] - fPts[3];
-    if (endTan.fX == 0 && endTan.fY == 0) {
-        endTan = fPts[1] - fPts[3];
-    }
-    if (startTan.dot(endTan) >= 0) {
-        return false;
-    }
-    SkDLine startEdge = {{fPts[0], fPts[0]}};
-    startEdge[1].fX -= startTan.fY;
-    startEdge[1].fY += startTan.fX;
-    SkDLine endEdge = {{fPts[3], fPts[3]}};
-    endEdge[1].fX -= endTan.fY;
-    endEdge[1].fY += endTan.fX;
-    double leftStart1 = startEdge.isLeft(fPts[1]);
-    if (leftStart1 * startEdge.isLeft(fPts[2]) < 0) {
-        return false;
-    }
-    double leftEnd1 = endEdge.isLeft(fPts[1]);
-    if (leftEnd1 * endEdge.isLeft(fPts[2]) < 0) {
-        return false;
-    }
-    return leftStart1 * leftEnd1 >= 0;
-}
-
 bool SkDCubic::endsAreExtremaInXOrY() const {
     return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
             && between(fPts[0].fX, fPts[2].fX, fPts[3].fX))
@@ -124,22 +142,170 @@
             && between(fPts[0].fY, fPts[2].fY, fPts[3].fY));
 }
 
+// Do a quick reject by rotating all points relative to a line formed by
+// a pair of one cubic's points. If the 2nd cubic's points
+// are on the line or on the opposite side from the 1st cubic's 'odd man', the
+// curves at most intersect at the endpoints.
+/* if returning true, check contains true if cubic's hull collapsed, making the cubic linear
+   if returning false, check contains true if the the cubic pair have only the end point in common
+*/
+bool SkDCubic::hullIntersects(const SkDPoint* pts, int ptCount, bool* isLinear) const {
+    bool linear = true;
+    char hullOrder[4];
+    int hullCount = convexHull(hullOrder);
+    int end1 = hullOrder[0];
+    int hullIndex = 0;
+    const SkDPoint* endPt[2];
+    endPt[0] = &fPts[end1];
+    do {
+        hullIndex = (hullIndex + 1) % hullCount;
+        int end2 = hullOrder[hullIndex];
+        endPt[1] = &fPts[end2];
+        double origX = endPt[0]->fX;
+        double origY = endPt[0]->fY;
+        double adj = endPt[1]->fX - origX;
+        double opp = endPt[1]->fY - origY;
+        int oddManMask = other_two(end1, end2);
+        int oddMan = end1 ^ oddManMask;
+        double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
+        int oddMan2 = end2 ^ oddManMask;
+        double sign2 = (fPts[oddMan2].fY - origY) * adj - (fPts[oddMan2].fX - origX) * opp;
+        if (sign * sign2 < 0) {
+            continue;
+        }
+        if (approximately_zero(sign)) {
+            sign = sign2;
+            if (approximately_zero(sign)) {
+                continue;
+            }
+        }
+        linear = false;
+        bool foundOutlier = false;
+        for (int n = 0; n < ptCount; ++n) {
+            double test = (pts[n].fY - origY) * adj - (pts[n].fX - origX) * opp;
+            if (test * sign > 0 && !precisely_zero(test)) {
+                foundOutlier = true;
+                break;
+            }
+        }
+        if (!foundOutlier) {
+            return false;
+        }
+        endPt[0] = endPt[1];
+        end1 = end2;
+    } while (hullIndex);
+    *isLinear = linear;
+    return true;
+}
+
+bool SkDCubic::hullIntersects(const SkDCubic& c2, bool* isLinear) const {
+    return hullIntersects(c2.fPts, c2.kPointCount, isLinear);
+}
+
+bool SkDCubic::hullIntersects(const SkDQuad& quad, bool* isLinear) const {
+    return hullIntersects(quad.fPts, quad.kPointCount, isLinear);
+}
+
+bool SkDCubic::hullIntersects(const SkDConic& conic, bool* isLinear) const {
+
+    return hullIntersects(conic.fPts, isLinear);
+}
+
 bool SkDCubic::isLinear(int startIndex, int endIndex) const {
     SkLineParameters lineParameters;
     lineParameters.cubicEndPoints(*this, startIndex, endIndex);
     // FIXME: maybe it's possible to avoid this and compare non-normalized
     lineParameters.normalize();
+    double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
+            fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
+    double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
+            fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
+    largest = SkTMax(largest, -tiniest);
     double distance = lineParameters.controlPtDistance(*this, 1);
-    if (!approximately_zero(distance)) {
+    if (!approximately_zero_when_compared_to(distance, largest)) {
         return false;
     }
     distance = lineParameters.controlPtDistance(*this, 2);
-    return approximately_zero(distance);
+    return approximately_zero_when_compared_to(distance, largest);
+}
+
+bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t, CubicType* resultType) {
+    SkScalar d[3];
+    SkCubicType cubicType = SkClassifyCubic(pointsPtr, d);
+    if (cubicType == kLoop_SkCubicType) {
+        // crib code from gpu path utils that finds t values where loop self-intersects
+        // use it to find mid of t values which should be a friendly place to chop
+        SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]);
+        SkScalar ls = d[1] - tempSqrt;
+        SkScalar lt = 2.f * d[0];
+        SkScalar ms = d[1] + tempSqrt;
+        SkScalar mt = 2.f * d[0];
+        if (between(0, ls, lt) || between(0, ms, mt)) {
+            ls = ls / lt;
+            ms = ms / mt;
+            SkScalar smaller = SkTMax(0.f, SkTMin(ls, ms));
+            SkScalar larger = SkTMin(1.f, SkTMax(ls, ms));
+            *t = (smaller + larger) / 2;
+            *resultType = kSplitAtLoop_SkDCubicType;
+            return *t > 0 && *t < 1;
+        }
+    } else if (kSerpentine_SkCubicType == cubicType || kCusp_SkCubicType == cubicType) {
+        SkDCubic cubic;
+        cubic.set(pointsPtr);
+        double inflectionTs[2];
+        int infTCount = cubic.findInflections(inflectionTs);
+        if (infTCount == 2) {
+            double maxCurvature[3];
+            int roots = cubic.findMaxCurvature(maxCurvature);
+#if DEBUG_CUBIC_SPLIT
+            SkDebugf("%s\n", __FUNCTION__);
+            cubic.dump();
+            for (int index = 0; index < infTCount; ++index) {
+                SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]);
+                SkDPoint pt = cubic.ptAtT(inflectionTs[index]);
+                SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]);
+                SkDLine perp = {{pt - dPt, pt + dPt}};
+                perp.dump();
+            }
+            for (int index = 0; index < roots; ++index) {
+                SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]);
+                SkDPoint pt = cubic.ptAtT(maxCurvature[index]);
+                SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]);
+                SkDLine perp = {{pt - dPt, pt + dPt}};
+                perp.dump();
+            }
+#endif
+            for (int index = 0; index < roots; ++index) {
+                if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
+                    *t = maxCurvature[index];
+                    *resultType = kSplitAtMaxCurvature_SkDCubicType;
+                    return true;
+                }
+            }
+        } else if (infTCount == 1) {
+            *t = inflectionTs[0];
+            *resultType = kSplitAtInflection_SkDCubicType;
+            return *t > 0 && *t < 1;
+        }
+    }
+    return false;
+}
+
+bool SkDCubic::monotonicInX() const {
+    return precisely_between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
+            && precisely_between(fPts[0].fX, fPts[2].fX, fPts[3].fX);
 }
 
 bool SkDCubic::monotonicInY() const {
-    return between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
-            && between(fPts[0].fY, fPts[2].fY, fPts[3].fY);
+    return precisely_between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
+            && precisely_between(fPts[0].fY, fPts[2].fY, fPts[3].fY);
+}
+
+void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const {
+    int offset = (int) !SkToBool(index);
+    o1Pts[0] = &fPts[offset];
+    o1Pts[1] = &fPts[++offset];
+    o1Pts[2] = &fPts[++offset];
 }
 
 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept,
@@ -163,26 +329,6 @@
     return validCount;
 }
 
-bool SkDCubic::serpentine() const {
-#if 0  // FIXME: enabling this fixes cubicOp114 but breaks cubicOp58d and cubicOp53d
-    double tValues[2];
-    // OPTIMIZATION : another case where caching the present of cubic inflections would be useful
-    return findInflections(tValues) > 1;
-#endif
-    if (!controlsContainedByEnds()) {
-        return false;
-    }
-    double wiggle = (fPts[0].fX - fPts[2].fX) * (fPts[0].fY + fPts[2].fY);
-    for (int idx = 0; idx < 2; ++idx) {
-        wiggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY);
-    }
-    double waggle = (fPts[1].fX - fPts[3].fX) * (fPts[1].fY + fPts[3].fY);
-    for (int idx = 1; idx < 3; ++idx) {
-        waggle += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY);
-    }
-    return wiggle * waggle < 0;
-}
-
 // cubic roots
 
 static const double PI = 3.141592653589793;
@@ -192,6 +338,28 @@
     double s[3];
     int realRoots = RootsReal(A, B, C, D, s);
     int foundRoots = SkDQuad::AddValidTs(s, realRoots, t);
+    for (int index = 0; index < realRoots; ++index) {
+        double tValue = s[index];
+        if (!approximately_one_or_less(tValue) && between(1, tValue, 1.00005)) {
+            for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
+                if (approximately_equal(t[idx2], 1)) {
+                    goto nextRoot;
+                }
+            }
+            SkASSERT(foundRoots < 3);
+            t[foundRoots++] = 1;
+        } else if (!approximately_zero_or_more(tValue) && between(-0.00005, tValue, 0)) {
+            for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
+                if (approximately_equal(t[idx2], 0)) {
+                    goto nextRoot;
+                }
+            }
+            SkASSERT(foundRoots < 3);
+            t[foundRoots++] = 0;
+        }
+nextRoot:
+        ;
+    }
     return foundRoots;
 }
 
@@ -336,10 +504,14 @@
     C = 3(b - a)
     Solve for t, keeping only those that fit between 0 < t < 1
 */
-int SkDCubic::FindExtrema(double a, double b, double c, double d, double tValues[2]) {
+int SkDCubic::FindExtrema(const double src[], double tValues[2]) {
     // we divide A,B,C by 3 to simplify
-    double A = d - a + 3*(b - c);
-    double B = 2*(a - b - b + c);
+    double a = src[0];
+    double b = src[2];
+    double c = src[4];
+    double d = src[6];
+    double A = d - a + 3 * (b - c);
+    double B = 2 * (a - b - b + c);
     double C = b - a;
 
     return SkDQuad::RootsValidT(A, B, C, tValues);
@@ -368,26 +540,6 @@
     return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues);
 }
 
-SkDPoint SkDCubic::top(double startT, double endT) const {
-    SkDCubic sub = subDivide(startT, endT);
-    SkDPoint topPt = sub[0];
-    if (topPt.fY > sub[3].fY || (topPt.fY == sub[3].fY && topPt.fX > sub[3].fX)) {
-        topPt = sub[3];
-    }
-    double extremeTs[2];
-    if (!sub.monotonicInY()) {
-        int roots = FindExtrema(sub[0].fY, sub[1].fY, sub[2].fY, sub[3].fY, extremeTs);
-        for (int index = 0; index < roots; ++index) {
-            double t = startT + (endT - startT) * extremeTs[index];
-            SkDPoint mid = ptAtT(t);
-            if (topPt.fY > mid.fY || (topPt.fY == mid.fY && topPt.fX > mid.fX)) {
-                topPt = mid;
-            }
-        }
-    }
-    return topPt;
-}
-
 SkDPoint SkDCubic::ptAtT(double t) const {
     if (0 == t) {
         return fPts[0];
@@ -493,37 +645,13 @@
     return dst;
 }
 
-void SkDCubic::align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const {
-    if (fPts[endIndex].fX == fPts[ctrlIndex].fX) {
-        dstPt->fX = fPts[endIndex].fX;
-    }
-    if (fPts[endIndex].fY == fPts[ctrlIndex].fY) {
-        dstPt->fY = fPts[endIndex].fY;
-    }
-}
-
 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d,
                          double t1, double t2, SkDPoint dst[2]) const {
     SkASSERT(t1 != t2);
-#if 0
-    double ex = interp_cubic_coords(&fPts[0].fX, (t1 * 2 + t2) / 3);
-    double ey = interp_cubic_coords(&fPts[0].fY, (t1 * 2 + t2) / 3);
-    double fx = interp_cubic_coords(&fPts[0].fX, (t1 + t2 * 2) / 3);
-    double fy = interp_cubic_coords(&fPts[0].fY, (t1 + t2 * 2) / 3);
-    double mx = ex * 27 - a.fX * 8 - d.fX;
-    double my = ey * 27 - a.fY * 8 - d.fY;
-    double nx = fx * 27 - a.fX - d.fX * 8;
-    double ny = fy * 27 - a.fY - d.fY * 8;
-    /* bx = */ dst[0].fX = (mx * 2 - nx) / 18;
-    /* by = */ dst[0].fY = (my * 2 - ny) / 18;
-    /* cx = */ dst[1].fX = (nx * 2 - mx) / 18;
-    /* cy = */ dst[1].fY = (ny * 2 - my) / 18;
-#else
     // this approach assumes that the control points computed directly are accurate enough
     SkDCubic sub = subDivide(t1, t2);
     dst[0] = sub[1] + (a - sub[0]);
     dst[1] = sub[2] + (d - sub[3]);
-#endif
     if (t1 == 0 || t2 == 0) {
         align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
     }
@@ -544,42 +672,17 @@
     }
 }
 
-/* classic one t subdivision */
-static void interp_cubic_coords(const double* src, double* dst, double t) {
-    double ab = SkDInterp(src[0], src[2], t);
-    double bc = SkDInterp(src[2], src[4], t);
-    double cd = SkDInterp(src[4], src[6], t);
-    double abc = SkDInterp(ab, bc, t);
-    double bcd = SkDInterp(bc, cd, t);
-    double abcd = SkDInterp(abc, bcd, t);
-
-    dst[0] = src[0];
-    dst[2] = ab;
-    dst[4] = abc;
-    dst[6] = abcd;
-    dst[8] = bcd;
-    dst[10] = cd;
-    dst[12] = src[6];
-}
-
-SkDCubicPair SkDCubic::chopAt(double t) const {
-    SkDCubicPair dst;
-    if (t == 0.5) {
-        dst.pts[0] = fPts[0];
-        dst.pts[1].fX = (fPts[0].fX + fPts[1].fX) / 2;
-        dst.pts[1].fY = (fPts[0].fY + fPts[1].fY) / 2;
-        dst.pts[2].fX = (fPts[0].fX + 2 * fPts[1].fX + fPts[2].fX) / 4;
-        dst.pts[2].fY = (fPts[0].fY + 2 * fPts[1].fY + fPts[2].fY) / 4;
-        dst.pts[3].fX = (fPts[0].fX + 3 * (fPts[1].fX + fPts[2].fX) + fPts[3].fX) / 8;
-        dst.pts[3].fY = (fPts[0].fY + 3 * (fPts[1].fY + fPts[2].fY) + fPts[3].fY) / 8;
-        dst.pts[4].fX = (fPts[1].fX + 2 * fPts[2].fX + fPts[3].fX) / 4;
-        dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4;
-        dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2;
-        dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2;
-        dst.pts[6] = fPts[3];
-        return dst;
+double SkDCubic::top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const {
+    double extremeTs[2];
+    double topT = -1;
+    int roots = SkDCubic::FindExtrema(&fPts[0].fY, extremeTs);
+    for (int index = 0; index < roots; ++index) {
+        double t = startT + (endT - startT) * extremeTs[index];
+        SkDPoint mid = dCurve.ptAtT(t);
+        if (topPt->fY > mid.fY || (topPt->fY == mid.fY && topPt->fX > mid.fX)) {
+            topT = t;
+            *topPt = mid;
+        }
     }
-    interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t);
-    interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t);
-    return dst;
+    return topT;
 }
diff --git a/src/pathops/SkPathOpsCubic.h b/src/pathops/SkPathOpsCubic.h
index 1037cae..7a57e94 100644
--- a/src/pathops/SkPathOpsCubic.h
+++ b/src/pathops/SkPathOpsCubic.h
@@ -10,7 +10,6 @@
 
 #include "SkPath.h"
 #include "SkPathOpsPoint.h"
-#include "SkTArray.h"
 
 struct SkDCubicPair {
     const SkDCubic& first() const { return (const SkDCubic&) pts[0]; }
@@ -19,78 +18,140 @@
 };
 
 struct SkDCubic {
+    static const int kPointCount = 4;
+    static const int kPointLast = kPointCount - 1;
+    static const int kMaxIntersections = 9;
+
     enum SearchAxis {
         kXAxis,
         kYAxis
     };
 
-    const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < 4); return fPts[n]; }
-    SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < 4); return fPts[n]; }
+    enum CubicType {
+        kUnsplit_SkDCubicType,
+        kSplitAtLoop_SkDCubicType,
+        kSplitAtInflection_SkDCubicType,
+        kSplitAtMaxCurvature_SkDCubicType,
+    };
+
+    bool collapsed() const {
+        return fPts[0].approximatelyEqual(fPts[1]) && fPts[0].approximatelyEqual(fPts[2])
+                && fPts[0].approximatelyEqual(fPts[3]);
+    }
+
+    bool controlsInside() const {
+        SkDVector v01 = fPts[0] - fPts[1];
+        SkDVector v02 = fPts[0] - fPts[2];
+        SkDVector v03 = fPts[0] - fPts[3];
+        SkDVector v13 = fPts[1] - fPts[3];
+        SkDVector v23 = fPts[2] - fPts[3];
+        return v03.dot(v01) > 0 && v03.dot(v02) > 0 && v03.dot(v13) > 0 && v03.dot(v23) > 0;
+    }
+
+    static bool IsCubic() { return true; }
+
+    const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
+    SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
 
     void align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const;
     double binarySearch(double min, double max, double axisIntercept, SearchAxis xAxis) const;
     double calcPrecision() const;
     SkDCubicPair chopAt(double t) const;
-    bool clockwise() const;
     static void Coefficients(const double* cubic, double* A, double* B, double* C, double* D);
-    bool controlsContainedByEnds() const;
+    static bool ComplexBreak(const SkPoint pts[4], SkScalar* t, CubicType* cubicType);
+    int convexHull(char order[kPointCount]) const;
+
+    void debugInit() {
+        sk_bzero(fPts, sizeof(fPts));
+    }
+
+    void dump() const;  // callable from the debugger when the implementation code is linked in
+    void dumpID(int id) const;
+    void dumpInner() const;
     SkDVector dxdyAtT(double t) const;
     bool endsAreExtremaInXOrY() const;
-    static int FindExtrema(double a, double b, double c, double d, double tValue[2]);
+    static int FindExtrema(const double src[], double tValue[2]);
     int findInflections(double tValues[2]) const;
 
-    static int FindInflections(const SkPoint a[4], double tValues[2]) {
+    static int FindInflections(const SkPoint a[kPointCount], double tValues[2]) {
         SkDCubic cubic;
-        cubic.set(a);
-        return cubic.findInflections(tValues);
+        return cubic.set(a).findInflections(tValues);
     }
 
     int findMaxCurvature(double tValues[]) const;
+    bool hullIntersects(const SkDCubic& c2, bool* isLinear) const;
+    bool hullIntersects(const SkDConic& c, bool* isLinear) const;
+    bool hullIntersects(const SkDQuad& c2, bool* isLinear) const;
+    bool hullIntersects(const SkDPoint* pts, int ptCount, bool* isLinear) const;
     bool isLinear(int startIndex, int endIndex) const;
+    bool monotonicInX() const;
     bool monotonicInY() const;
+    void otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const;
     SkDPoint ptAtT(double t) const;
     static int RootsReal(double A, double B, double C, double D, double t[3]);
     static int RootsValidT(const double A, const double B, const double C, double D, double s[3]);
 
     int searchRoots(double extremes[6], int extrema, double axisIntercept,
                     SearchAxis xAxis, double* validRoots) const;
-    bool serpentine() const;
 
-    void set(const SkPoint pts[4]) {
+    /**
+     *  Return the number of valid roots (0 < root < 1) for this cubic intersecting the
+     *  specified horizontal line.
+     */
+    int horizontalIntersect(double yIntercept, double roots[3]) const;
+    /**
+     *  Return the number of valid roots (0 < root < 1) for this cubic intersecting the
+     *  specified vertical line.
+     */
+    int verticalIntersect(double xIntercept, double roots[3]) const;
+
+    const SkDCubic& set(const SkPoint pts[kPointCount]) {
         fPts[0] = pts[0];
         fPts[1] = pts[1];
         fPts[2] = pts[2];
         fPts[3] = pts[3];
+        return *this;
     }
 
     SkDCubic subDivide(double t1, double t2) const;
 
-    static SkDCubic SubDivide(const SkPoint a[4], double t1, double t2) {
+    static SkDCubic SubDivide(const SkPoint a[kPointCount], double t1, double t2) {
         SkDCubic cubic;
-        cubic.set(a);
-        return cubic.subDivide(t1, t2);
+        return cubic.set(a).subDivide(t1, t2);
     }
 
     void subDivide(const SkDPoint& a, const SkDPoint& d, double t1, double t2, SkDPoint p[2]) const;
 
-    static void SubDivide(const SkPoint pts[4], const SkDPoint& a, const SkDPoint& d, double t1,
+    static void SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& d, double t1,
                           double t2, SkDPoint p[2]) {
         SkDCubic cubic;
-        cubic.set(pts);
-        cubic.subDivide(a, d, t1, t2, p);
+        cubic.set(pts).subDivide(a, d, t1, t2, p);
     }
 
-    SkDPoint top(double startT, double endT) const;
-    void toQuadraticTs(double precision, SkTArray<double, true>* ts) const;
+    double top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const;
     SkDQuad toQuad() const;
 
-    // utilities callable by the user from the debugger when the implementation code is linked in
-    void dump() const;
-    void dumpNumber() const;
-
     static const int gPrecisionUnit;
 
-    SkDPoint fPts[4];
+    SkDPoint fPts[kPointCount];
 };
 
+/* Given the set [0, 1, 2, 3], and two of the four members, compute an XOR mask
+   that computes the other two. Note that:
+
+   one ^ two == 3 for (0, 3), (1, 2)
+   one ^ two <  3 for (0, 1), (0, 2), (1, 3), (2, 3)
+   3 - (one ^ two) is either 0, 1, or 2
+   1 >> (3 - (one ^ two)) is either 0 or 1
+thus:
+   returned == 2 for (0, 3), (1, 2)
+   returned == 3 for (0, 1), (0, 2), (1, 3), (2, 3)
+given that:
+   (0, 3) ^ 2 -> (2, 1)  (1, 2) ^ 2 -> (3, 0)
+   (0, 1) ^ 3 -> (3, 2)  (0, 2) ^ 3 -> (3, 1)  (1, 3) ^ 3 -> (2, 0)  (2, 3) ^ 3 -> (1, 0)
+*/
+inline int other_two(int one, int two) {
+    return 1 >> (3 - (one ^ two)) ^ 3;
+}
+
 #endif
diff --git a/src/pathops/SkPathOpsCubicSect.h b/src/pathops/SkPathOpsCubicSect.h
deleted file mode 100644
index d763444..0000000
--- a/src/pathops/SkPathOpsCubicSect.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkCubicSpan_DEFINE
-#define SkCubicSpan_DEFINE
-
-#include "SkChunkAlloc.h"
-#include "SkPathOpsRect.h"
-#include "SkPathOpsCubic.h"
-#include "SkTArray.h"
-
-class SkIntersections;
-
-class SkCubicCoincident {
-public:
-    bool isCoincident() const {
-        return fCoincident;
-    }
-
-    void init() {
-        fCoincident = false;
-        SkDEBUGCODE(fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN);
-        SkDEBUGCODE(fPerpT = SK_ScalarNaN);
-    }
-
-    void markCoincident() {
-        if (!fCoincident) {
-            fPerpT = -1;
-        }
-        fCoincident = true;
-    }
-
-    const SkDPoint& perpPt() const {
-        return fPerpPt;
-    }
-
-    double perpT() const {
-        return fPerpT;
-    }
-
-    void setPerp(const SkDCubic& cubic1, double t, const SkDPoint& qPt, const SkDCubic& cubic2);
-
-private:
-    SkDPoint fPerpPt;
-    double fPerpT;  // perpendicular intersection on opposite Cubic
-    bool fCoincident;
-};
-
-class SkCubicSect;  // used only by debug id
-
-class SkCubicSpan {
-public:
-    void init(const SkDCubic& Cubic);
-    void initBounds(const SkDCubic& Cubic);
-
-    bool contains(double t) const {
-        return !! const_cast<SkCubicSpan*>(this)->innerFind(t);
-    }
-
-    bool contains(const SkCubicSpan* span) const;
-
-    SkCubicSpan* find(double t) {
-        SkCubicSpan* result = innerFind(t);
-        SkASSERT(result);
-        return result;
-    }
-
-    bool intersects(const SkCubicSpan* span) const;
-
-    const SkCubicSpan* next() const {
-        return fNext;
-    }
-
-    void reset() {
-        fBounded.reset();
-    }
-
-    bool split(SkCubicSpan* work) {
-        return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
-    }
-
-    bool splitAt(SkCubicSpan* work, double t);
-    bool tightBoundsIntersects(const SkCubicSpan* span) const;
-
-    // implementation is for testing only
-    void dump() const;
-
-private:
-    bool hullIntersects(const SkDCubic& ) const;
-    SkCubicSpan* innerFind(double t);
-    bool linearIntersects(const SkDCubic& ) const;
-
-    // implementation is for testing only
-#if DEBUG_BINARY_CUBIC
-    int debugID(const SkCubicSect* ) const { return fDebugID; }
-#else
-    int debugID(const SkCubicSect* ) const;
-#endif
-    void dump(const SkCubicSect* ) const;
-    void dumpID(const SkCubicSect* ) const;
-
-#if DEBUG_BINARY_CUBIC
-    void validate() const;
-#endif
-
-    SkDCubic fPart;
-    SkCubicCoincident fCoinStart;
-    SkCubicCoincident fCoinEnd;
-    SkSTArray<4, SkCubicSpan*, true> fBounded;
-    SkCubicSpan* fPrev;
-    SkCubicSpan* fNext;
-    SkDRect fBounds;
-    double fStartT;
-    double fEndT;
-    double fBoundsMax;
-    bool fCollapsed;
-    bool fHasPerp;
-    mutable bool fIsLinear;
-#if DEBUG_BINARY_CUBIC
-    int fDebugID;
-    bool fDebugDeleted;
-#endif
-    friend class SkCubicSect;
-};
-
-class SkCubicSect {
-public:
-    SkCubicSect(const SkDCubic& Cubic PATH_OPS_DEBUG_PARAMS(int id));
-    static void BinarySearch(SkCubicSect* sect1, SkCubicSect* sect2, SkIntersections* intersections);
-
-    // for testing only
-    void dumpCubics() const;
-private:
-    SkCubicSpan* addOne();
-    bool binarySearchCoin(const SkCubicSect& , double tStart, double tStep, double* t,
-            double* oppT);
-    SkCubicSpan* boundsMax() const;
-    void coincidentCheck(SkCubicSect* sect2);
-    bool intersects(const SkCubicSpan* span, const SkCubicSect* opp, const SkCubicSpan* oppSpan) const;
-    void onCurveCheck(SkCubicSect* sect2, SkCubicSpan* first, SkCubicSpan* last);
-    void recoverCollapsed();
-    void removeSpan(SkCubicSpan* span);
-    void removeOne(const SkCubicSpan* test, SkCubicSpan* span);
-    void removeSpans(SkCubicSpan* span, SkCubicSect* opp);
-    void setPerp(const SkDCubic& opp, SkCubicSpan* first, SkCubicSpan* last);
-    void trim(SkCubicSpan* span, SkCubicSect* opp);
-
-    // for testing only
-    void dump() const;
-    void dumpBoth(const SkCubicSect& opp) const;
-    void dumpBoth(const SkCubicSect* opp) const;
-
-#if DEBUG_BINARY_CUBIC
-    int debugID() const { return fDebugID; }
-    void validate() const;
-#else
-    int debugID() const { return 0; }
-#endif
-    const SkDCubic& fCubic;
-    SkChunkAlloc fHeap;
-    SkCubicSpan* fHead;
-    SkCubicSpan* fDeleted;
-    int fActiveCount;
-#if DEBUG_BINARY_CUBIC
-    int fDebugID;
-    int fDebugCount;
-    int fDebugAllocatedCount;
-#endif
-    friend class SkCubicSpan;  // only used by debug id
-};
-
-#endif
diff --git a/src/pathops/SkPathOpsCurve.cpp b/src/pathops/SkPathOpsCurve.cpp
new file mode 100644
index 0000000..bf44d25
--- /dev/null
+++ b/src/pathops/SkPathOpsCurve.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkPathOpsBounds.h"
+#include "SkPathOpsRect.h"
+#include "SkPathOpsCurve.h"
+
+void SkDCurve::setConicBounds(const SkPoint curve[3], SkScalar curveWeight,
+        double tStart, double tEnd, SkPathOpsBounds* bounds) {
+    SkDConic dCurve;
+    dCurve.set(curve, curveWeight);
+    SkDRect dRect;
+    dRect.setBounds(dCurve, fConic, tStart, tEnd);
+    bounds->set(SkDoubleToScalar(dRect.fLeft), SkDoubleToScalar(dRect.fTop),
+            SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
+}
+
+void SkDCurve::setCubicBounds(const SkPoint curve[4], SkScalar , 
+        double tStart, double tEnd, SkPathOpsBounds* bounds) {
+    SkDCubic dCurve;
+    dCurve.set(curve);
+    SkDRect dRect;
+    dRect.setBounds(dCurve, fCubic, tStart, tEnd);
+    bounds->set(SkDoubleToScalar(dRect.fLeft), SkDoubleToScalar(dRect.fTop),
+            SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
+}
+
+void SkDCurve::setQuadBounds(const SkPoint curve[3], SkScalar ,
+        double tStart, double tEnd, SkPathOpsBounds* bounds) {
+    SkDQuad dCurve;
+    dCurve.set(curve);
+    SkDRect dRect;
+    dRect.setBounds(dCurve, fQuad, tStart, tEnd);
+    bounds->set(SkDoubleToScalar(dRect.fLeft), SkDoubleToScalar(dRect.fTop),
+            SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
+}
diff --git a/src/pathops/SkPathOpsCurve.h b/src/pathops/SkPathOpsCurve.h
index a7d3e81..c830e66 100644
--- a/src/pathops/SkPathOpsCurve.h
+++ b/src/pathops/SkPathOpsCurve.h
@@ -8,134 +8,215 @@
 #define SkPathOpsCurve_DEFINE
 
 #include "SkIntersections.h"
-#include "SkPathOpsCubic.h"
-#include "SkPathOpsLine.h"
-#include "SkPathOpsQuad.h"
 
-static SkDPoint dline_xy_at_t(const SkPoint a[2], double t) {
+#ifndef SK_RELEASE
+#include "SkPath.h"
+#endif
+
+struct SkPathOpsBounds;
+
+struct SkOpCurve {
+    SkPoint fPts[4];
+    SkScalar fWeight;
+    SkDEBUGCODE(SkPath::Verb fVerb);
+
+    const SkPoint& operator[](int n) const {
+        SkASSERT(n >= 0 && n <= SkPathOpsVerbToPoints(fVerb));
+        return fPts[n];
+    }
+
+    void dump() const;
+
+    void set(const SkDQuad& quad) {
+        for (int index = 0; index < SkDQuad::kPointCount; ++index) {
+            fPts[index] = quad[index].asSkPoint();
+        }
+        SkDEBUGCODE(fWeight = 1);
+        SkDEBUGCODE(fVerb = SkPath::kQuad_Verb);
+    }
+
+    void set(const SkDCubic& cubic) {
+        for (int index = 0; index < SkDCubic::kPointCount; ++index) {
+            fPts[index] = cubic[index].asSkPoint();
+        }
+        SkDEBUGCODE(fWeight = 1);
+        SkDEBUGCODE(fVerb = SkPath::kCubic_Verb);
+    }
+
+};
+
+struct SkDCurve {
+    union {
+        SkDLine fLine;
+        SkDQuad fQuad;
+        SkDConic fConic;
+        SkDCubic fCubic;
+    };
+    SkDEBUGCODE(SkPath::Verb fVerb);
+
+    const SkDPoint& operator[](int n) const {
+        SkASSERT(n >= 0 && n <= SkPathOpsVerbToPoints(fVerb));
+        return fCubic[n];
+    }
+
+    SkDPoint& operator[](int n) {
+        SkASSERT(n >= 0 && n <= SkPathOpsVerbToPoints(fVerb));
+        return fCubic[n];
+    }
+
+    SkDPoint conicTop(const SkPoint curve[3], SkScalar curveWeight, 
+                      double s, double e, double* topT);
+    SkDPoint cubicTop(const SkPoint curve[4], SkScalar , double s, double e, double* topT);
+    void dumpID(int ) const;
+    SkDPoint lineTop(const SkPoint[2], SkScalar , double , double , double* topT);
+    SkDPoint quadTop(const SkPoint curve[3], SkScalar , double s, double e, double* topT);
+
+    void setConicBounds(const SkPoint curve[3], SkScalar curveWeight, 
+                        double s, double e, SkPathOpsBounds* );
+    void setCubicBounds(const SkPoint curve[4], SkScalar ,
+                        double s, double e, SkPathOpsBounds* );
+    void setQuadBounds(const SkPoint curve[3], SkScalar ,
+                       double s, double e, SkPathOpsBounds*);
+};
+
+
+extern SkDPoint (SkDCurve::* const Top[])(const SkPoint curve[], SkScalar cWeight,
+    double tStart, double tEnd, double* topT);
+
+static SkDPoint dline_xy_at_t(const SkPoint a[2], SkScalar , double t) {
     SkDLine line;
     line.set(a);
     return line.ptAtT(t);
 }
 
-static SkDPoint dquad_xy_at_t(const SkPoint a[3], double t) {
+static SkDPoint dquad_xy_at_t(const SkPoint a[3], SkScalar , double t) {
     SkDQuad quad;
     quad.set(a);
     return quad.ptAtT(t);
 }
 
-static SkDPoint dcubic_xy_at_t(const SkPoint a[4], double t) {
+static SkDPoint dconic_xy_at_t(const SkPoint a[3], SkScalar weight, double t) {
+    SkDConic conic;
+    conic.set(a, weight);
+    return conic.ptAtT(t);
+}
+
+static SkDPoint dcubic_xy_at_t(const SkPoint a[4], SkScalar , double t) {
     SkDCubic cubic;
     cubic.set(a);
     return cubic.ptAtT(t);
 }
 
-static SkDPoint (* const CurveDPointAtT[])(const SkPoint[], double ) = {
+static SkDPoint (* const CurveDPointAtT[])(const SkPoint[], SkScalar , double ) = {
     NULL,
     dline_xy_at_t,
     dquad_xy_at_t,
+    dconic_xy_at_t,
     dcubic_xy_at_t
 };
 
-static SkPoint fline_xy_at_t(const SkPoint a[2], double t) {
-    return dline_xy_at_t(a, t).asSkPoint();
+static SkPoint fline_xy_at_t(const SkPoint a[2], SkScalar weight, double t) {
+    return dline_xy_at_t(a, weight, t).asSkPoint();
 }
 
-static SkPoint fquad_xy_at_t(const SkPoint a[3], double t) {
-    return dquad_xy_at_t(a, t).asSkPoint();
+static SkPoint fquad_xy_at_t(const SkPoint a[3], SkScalar weight, double t) {
+    return dquad_xy_at_t(a, weight, t).asSkPoint();
 }
 
-static SkPoint fcubic_xy_at_t(const SkPoint a[4], double t) {
-    return dcubic_xy_at_t(a, t).asSkPoint();
+static SkPoint fconic_xy_at_t(const SkPoint a[3], SkScalar weight, double t) {
+    return dconic_xy_at_t(a, weight, t).asSkPoint();
 }
 
-static SkPoint (* const CurvePointAtT[])(const SkPoint[], double ) = {
+static SkPoint fcubic_xy_at_t(const SkPoint a[4], SkScalar weight, double t) {
+    return dcubic_xy_at_t(a, weight, t).asSkPoint();
+}
+
+static SkPoint (* const CurvePointAtT[])(const SkPoint[], SkScalar , double ) = {
     NULL,
     fline_xy_at_t,
     fquad_xy_at_t,
+    fconic_xy_at_t,
     fcubic_xy_at_t
 };
 
-static SkDVector dline_dxdy_at_t(const SkPoint a[2], double ) {
+static SkDVector dline_dxdy_at_t(const SkPoint a[2], SkScalar , double ) {
     SkDLine line;
     line.set(a);
     return line[1] - line[0];
 }
 
-static SkDVector dquad_dxdy_at_t(const SkPoint a[3], double t) {
+static SkDVector dquad_dxdy_at_t(const SkPoint a[3], SkScalar , double t) {
     SkDQuad quad;
     quad.set(a);
     return quad.dxdyAtT(t);
 }
 
-static SkDVector dcubic_dxdy_at_t(const SkPoint a[4], double t) {
+static SkDVector dconic_dxdy_at_t(const SkPoint a[3], SkScalar weight, double t) {
+    SkDConic conic;
+    conic.set(a, weight);
+    return conic.dxdyAtT(t);
+}
+
+static SkDVector dcubic_dxdy_at_t(const SkPoint a[4], SkScalar , double t) {
     SkDCubic cubic;
     cubic.set(a);
     return cubic.dxdyAtT(t);
 }
 
-static SkDVector (* const CurveDSlopeAtT[])(const SkPoint[], double ) = {
+static SkDVector (* const CurveDSlopeAtT[])(const SkPoint[], SkScalar , double ) = {
     NULL,
     dline_dxdy_at_t,
     dquad_dxdy_at_t,
+    dconic_dxdy_at_t,
     dcubic_dxdy_at_t
 };
 
-static SkVector fline_dxdy_at_t(const SkPoint a[2], double ) {
+static SkVector fline_dxdy_at_t(const SkPoint a[2], SkScalar , double ) {
     return a[1] - a[0];
 }
 
-static SkVector fquad_dxdy_at_t(const SkPoint a[3], double t) {
-    return dquad_dxdy_at_t(a, t).asSkVector();
+static SkVector fquad_dxdy_at_t(const SkPoint a[3], SkScalar weight, double t) {
+    return dquad_dxdy_at_t(a, weight, t).asSkVector();
 }
 
-static SkVector fcubic_dxdy_at_t(const SkPoint a[4], double t) {
-    return dcubic_dxdy_at_t(a, t).asSkVector();
+static SkVector fconic_dxdy_at_t(const SkPoint a[3], SkScalar weight, double t) {
+    return dconic_dxdy_at_t(a, weight, t).asSkVector();
 }
 
-static SkVector (* const CurveSlopeAtT[])(const SkPoint[], double ) = {
+static SkVector fcubic_dxdy_at_t(const SkPoint a[4], SkScalar weight, double t) {
+    return dcubic_dxdy_at_t(a, weight, t).asSkVector();
+}
+
+static SkVector (* const CurveSlopeAtT[])(const SkPoint[], SkScalar , double ) = {
     NULL,
     fline_dxdy_at_t,
     fquad_dxdy_at_t,
+    fconic_dxdy_at_t,
     fcubic_dxdy_at_t
 };
 
-static SkPoint quad_top(const SkPoint a[3], double startT, double endT) {
-    SkDQuad quad;
-    quad.set(a);
-    SkDPoint topPt = quad.top(startT, endT);
-    return topPt.asSkPoint();
-}
-
-static SkPoint cubic_top(const SkPoint a[4], double startT, double endT) {
-    SkDCubic cubic;
-    cubic.set(a);
-    SkDPoint topPt = cubic.top(startT, endT);
-    return topPt.asSkPoint();
-}
-
-static SkPoint (* const CurveTop[])(const SkPoint[], double , double ) = {
-    NULL,
-    NULL,
-    quad_top,
-    cubic_top
-};
-
-static bool line_is_vertical(const SkPoint a[2], double startT, double endT) {
+static bool line_is_vertical(const SkPoint a[2], SkScalar , double startT, double endT) {
     SkDLine line;
     line.set(a);
     SkDPoint dst[2] = { line.ptAtT(startT), line.ptAtT(endT) };
     return AlmostEqualUlps(dst[0].fX, dst[1].fX);
 }
 
-static bool quad_is_vertical(const SkPoint a[3], double startT, double endT) {
+static bool quad_is_vertical(const SkPoint a[3], SkScalar , double startT, double endT) {
     SkDQuad quad;
     quad.set(a);
     SkDQuad dst = quad.subDivide(startT, endT);
     return AlmostEqualUlps(dst[0].fX, dst[1].fX) && AlmostEqualUlps(dst[1].fX, dst[2].fX);
 }
 
-static bool cubic_is_vertical(const SkPoint a[4], double startT, double endT) {
+static bool conic_is_vertical(const SkPoint a[3], SkScalar weight, double startT, double endT) {
+    SkDConic conic;
+    conic.set(a, weight);
+    SkDConic dst = conic.subDivide(startT, endT);
+    return AlmostEqualUlps(dst[0].fX, dst[1].fX) && AlmostEqualUlps(dst[1].fX, dst[2].fX);
+}
+
+static bool cubic_is_vertical(const SkPoint a[4], SkScalar , double startT, double endT) {
     SkDCubic cubic;
     cubic.set(a);
     SkDCubic dst = cubic.subDivide(startT, endT);
@@ -143,36 +224,104 @@
             && AlmostEqualUlps(dst[2].fX, dst[3].fX);
 }
 
-static bool (* const CurveIsVertical[])(const SkPoint[], double , double) = {
+static bool (* const CurveIsVertical[])(const SkPoint[], SkScalar , double , double) = {
     NULL,
     line_is_vertical,
     quad_is_vertical,
+    conic_is_vertical,
     cubic_is_vertical
 };
 
-static void line_intersect_ray(const SkPoint a[2], const SkDLine& ray, SkIntersections* i) {
+static void line_intersect_ray(const SkPoint a[2], SkScalar , const SkDLine& ray,
+        SkIntersections* i) {
     SkDLine line;
     line.set(a);
     i->intersectRay(line, ray);
 }
 
-static void quad_intersect_ray(const SkPoint a[3], const SkDLine& ray, SkIntersections* i) {
+static void quad_intersect_ray(const SkPoint a[3], SkScalar , const SkDLine& ray,
+        SkIntersections* i) {
     SkDQuad quad;
     quad.set(a);
     i->intersectRay(quad, ray);
 }
 
-static void cubic_intersect_ray(const SkPoint a[4], const SkDLine& ray, SkIntersections* i) {
+static void conic_intersect_ray(const SkPoint a[3], SkScalar weight, const SkDLine& ray,
+        SkIntersections* i) {
+    SkDConic conic;
+    conic.set(a, weight);
+    i->intersectRay(conic, ray);
+}
+
+static void cubic_intersect_ray(const SkPoint a[4], SkScalar , const SkDLine& ray,
+        SkIntersections* i) {
     SkDCubic cubic;
     cubic.set(a);
     i->intersectRay(cubic, ray);
 }
 
-static void (* const CurveIntersectRay[])(const SkPoint[] , const SkDLine& , SkIntersections* ) = {
+static void (* const CurveIntersectRay[])(const SkPoint[] , SkScalar , const SkDLine& ,
+        SkIntersections* ) = {
     NULL,
     line_intersect_ray,
     quad_intersect_ray,
+    conic_intersect_ray,
     cubic_intersect_ray
 };
 
+static int line_intercept_h(const SkPoint a[2], SkScalar , SkScalar y, double* roots) {
+    SkDLine line;
+    roots[0] = SkIntersections::HorizontalIntercept(line.set(a), y);
+    return between(0, roots[0], 1);
+}
+
+static int line_intercept_v(const SkPoint a[2], SkScalar , SkScalar x, double* roots) {
+    SkDLine line;
+    roots[0] = SkIntersections::VerticalIntercept(line.set(a), x);
+    return between(0, roots[0], 1);
+}
+
+static int quad_intercept_h(const SkPoint a[2], SkScalar , SkScalar y, double* roots) {
+    SkDQuad quad;
+    return SkIntersections::HorizontalIntercept(quad.set(a), y, roots);
+}
+
+static int quad_intercept_v(const SkPoint a[2], SkScalar , SkScalar x, double* roots) {
+    SkDQuad quad;
+    return SkIntersections::VerticalIntercept(quad.set(a), x, roots);
+}
+
+static int conic_intercept_h(const SkPoint a[2], SkScalar w, SkScalar y, double* roots) {
+    SkDConic conic;
+    return SkIntersections::HorizontalIntercept(conic.set(a, w), y, roots);
+}
+
+static int conic_intercept_v(const SkPoint a[2], SkScalar w, SkScalar x, double* roots) {
+    SkDConic conic;
+    return SkIntersections::VerticalIntercept(conic.set(a, w), x, roots);
+}
+
+static int cubic_intercept_h(const SkPoint a[3], SkScalar , SkScalar y, double* roots) {
+    SkDCubic cubic;
+    return cubic.set(a).horizontalIntersect(y, roots);
+}
+
+static int cubic_intercept_v(const SkPoint a[3], SkScalar , SkScalar x, double* roots) {
+    SkDCubic cubic;
+    return cubic.set(a).verticalIntersect(x, roots);
+}
+
+static int (* const CurveIntercept[])(const SkPoint[] , SkScalar , SkScalar , double* ) = {
+    NULL,
+    NULL,
+    line_intercept_h,
+    line_intercept_v,
+    quad_intercept_h,
+    quad_intercept_v,
+    conic_intercept_h,
+    conic_intercept_v,
+    cubic_intercept_h,
+    cubic_intercept_v,
+};
+
 #endif
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index 7db93f5..d28dc72 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -7,6 +7,21 @@
 
 #include "SkPathOpsDebug.h"
 #include "SkPath.h"
+#include "SkString.h"
+#include "SkThread.h"
+
+#if DEBUG_VALIDATE
+extern bool FLAGS_runFail;
+#endif
+
+#if DEBUG_SORT
+int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
+int SkPathOpsDebug::gSortCount;
+#endif
+
+#if DEBUG_ACTIVE_OP
+const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
+#endif
 
 #if defined SK_DEBUG || !FORCE_RELEASE
 
@@ -17,19 +32,10 @@
 int SkPathOpsDebug::gSegmentID = 0;
 #endif
 
-#if DEBUG_SORT || DEBUG_SWAP_TOP
-int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
-int SkPathOpsDebug::gSortCount;
-#endif
-
-#if DEBUG_ACTIVE_OP
-const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
-#endif
-
-bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpan *>& chaseArray,
-        const SkOpSpan* span) {
+bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpanBase* >& chaseArray,
+        const SkOpSpanBase* span) {
     for (int index = 0; index < chaseArray.count(); ++index) {
-        const SkOpSpan* entry = chaseArray[index];
+        const SkOpSpanBase* entry = chaseArray[index];
         if (entry == span) {
             return true;
         }
@@ -65,6 +71,8 @@
         SkDebugf("%d", wind);
     }
 }
+#endif //  defined SK_DEBUG || !FORCE_RELEASE
+
 
 #if DEBUG_SHOW_TEST_NAME
 void* SkPathOpsDebug::CreateNameStr() {
@@ -92,15 +100,214 @@
 }
 #endif
 
-#if !DEBUG_SHOW_TEST_NAME  // enable when building without extended test
-void SkPathOpsDebug::ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name) {
+static void show_function_header(const char* functionName) {
+    SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
+    if (strcmp("skphealth_com76", functionName) == 0) {
+        SkDebugf("found it\n");
+    }
+}
+
+static const char* gOpStrs[] = {
+    "kDifference_SkPathOp",
+    "kIntersect_SkPathOp",
+    "kUnion_SkPathOp",
+    "kXor_PathOp",
+    "kReverseDifference_SkPathOp",
+};
+
+const char* SkPathOpsDebug::OpStr(SkPathOp op) {
+    return gOpStrs[op];
+}
+
+static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
+    SkDebugf("    testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
+    SkDebugf("}\n");
+}
+
+SK_DECLARE_STATIC_MUTEX(gTestMutex);
+
+void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
+        const char* testName) {
+    SkAutoMutexAcquire ac(gTestMutex);
+    show_function_header(testName);
+    ShowOnePath(a, "path", true);
+    ShowOnePath(b, "pathB", true);
+    show_op(shapeOp, "path", "pathB");
+}
+
+#include "SkPathOpsCubic.h"
+#include "SkPathOpsQuad.h"
+
+SkDCubic SkDQuad::debugToCubic() const {
+    SkDCubic cubic;
+    cubic[0] = fPts[0];
+    cubic[2] = fPts[1];
+    cubic[3] = fPts[2];
+    cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
+    cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
+    cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
+    cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
+    return cubic;
+}
+
+#include "SkOpAngle.h"
+#include "SkOpCoincidence.h"
+#include "SkOpSegment.h"
+
+SkOpAngle* SkOpSegment::debugLastAngle() {
+    SkOpAngle* result = NULL;
+    SkOpSpan* span = this->head();
+    do {
+        if (span->toAngle()) {
+            SkASSERT(!result);
+            result = span->toAngle();
+        }
+    } while ((span = span->next()->upCastable()));
+    SkASSERT(result);
+    return result;
+}
+
+void SkOpSegment::debugReset() {
+    this->init(this->fPts, this->fWeight, this->contour(), this->verb());
+}
+
+#if DEBUG_ACTIVE_SPANS
+void SkOpSegment::debugShowActiveSpans() const {
+    debugValidate();
+    if (done()) {
+        return;
+    }
+    int lastId = -1;
+    double lastT = -1;
+    const SkOpSpan* span = &fHead;
+    do {
+        if (span->done()) {
+            continue;
+        }
+        if (lastId == this->debugID() && lastT == span->t()) {
+            continue;
+        }
+        lastId = this->debugID();
+        lastT = span->t();
+        SkDebugf("%s id=%d", __FUNCTION__, this->debugID());
+        SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
+        for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
+            SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
+        }
+        if (SkPath::kConic_Verb == fVerb) {
+            SkDebugf(" %1.9gf", fWeight);
+        }
+        const SkOpPtT* ptT = span->ptT();
+        SkDebugf(") t=%1.9g (%1.9g,%1.9g)", ptT->fT, ptT->fPt.fX, ptT->fPt.fY);
+        SkDebugf(" tEnd=%1.9g", span->next()->t());
+        if (span->windSum() == SK_MinS32) {
+            SkDebugf(" windSum=?");
+        } else {
+            SkDebugf(" windSum=%d", span->windSum());
+        }
+        if (span->oppValue() && span->oppSum() == SK_MinS32) {
+            SkDebugf(" oppSum=?");
+        } else if (span->oppValue() || span->oppSum() != SK_MinS32) {
+            SkDebugf(" oppSum=%d", span->oppSum());
+        }
+        SkDebugf(" windValue=%d", span->windValue());
+        if (span->oppValue() || span->oppSum() != SK_MinS32) {
+            SkDebugf(" oppValue=%d", span->oppValue());
+        }
+        SkDebugf("\n");
+   } while ((span = span->next()->upCastable()));
 }
 #endif
 
-#endif //  defined SK_DEBUG || !FORCE_RELEASE
+#if DEBUG_MARK_DONE
+void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding) {
+    const SkPoint& pt = span->ptT()->fPt;
+    SkDebugf("%s id=%d", fun, this->debugID());
+    SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
+    for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
+        SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
+    }
+    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
+            span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t());
+    if (winding == SK_MinS32) {
+        SkDebugf("?");
+    } else {
+        SkDebugf("%d", winding);
+    }
+    SkDebugf(" windSum=");
+    if (span->windSum() == SK_MinS32) {
+        SkDebugf("?");
+    } else {
+        SkDebugf("%d", span->windSum());
+    }
+    SkDebugf(" windValue=%d\n", span->windValue());
+}
 
-#include "SkOpAngle.h"
-#include "SkOpSegment.h"
+void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan* span, int winding,
+                                      int oppWinding) {
+    const SkPoint& pt = span->ptT()->fPt;
+    SkDebugf("%s id=%d", fun, this->debugID());
+    SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
+    for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
+        SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
+    }
+    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=",
+            span->t(), span->debugID(), pt.fX, pt.fY, span->next()->t(), winding, oppWinding);
+    if (winding == SK_MinS32) {
+        SkDebugf("?");
+    } else {
+        SkDebugf("%d", winding);
+    }
+    SkDebugf(" newOppSum=");
+    if (oppWinding == SK_MinS32) {
+        SkDebugf("?");
+    } else {
+        SkDebugf("%d", oppWinding);
+    }
+    SkDebugf(" oppSum=");
+    if (span->oppSum() == SK_MinS32) {
+        SkDebugf("?");
+    } else {
+        SkDebugf("%d", span->oppSum());
+    }
+    SkDebugf(" windSum=");
+    if (span->windSum() == SK_MinS32) {
+        SkDebugf("?");
+    } else {
+        SkDebugf("%d", span->windSum());
+    }
+    SkDebugf(" windValue=%d oppValue=%d\n", span->windValue(), span->oppValue());
+}
+
+#endif
+
+#if DEBUG_ANGLE
+SkString SkOpAngle::debugPart() const {
+    SkString result;
+    switch (this->segment()->verb()) {
+        case SkPath::kLine_Verb:
+            result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart),
+                    this->segment()->debugID());
+            break;
+        case SkPath::kQuad_Verb:
+            result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart),
+                    this->segment()->debugID());
+            break;
+        case SkPath::kConic_Verb:
+            result.printf(CONIC_DEBUG_STR " id=%d",
+                    CONIC_DEBUG_DATA(fCurvePart, fCurvePart.fConic.fWeight),
+                    this->segment()->debugID());
+            break;
+        case SkPath::kCubic_Verb:
+            result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fCurvePart),
+                    this->segment()->debugID());
+            break;
+        default:
+            SkASSERT(0);
+    } 
+    return result;
+}
+#endif
 
 #if DEBUG_SORT
 void SkOpAngle::debugLoop() const {
@@ -111,25 +318,59 @@
         SkDebugf("\n");
         next = next->fNext;
     } while (next && next != first);
+    next = first;
+    do {
+        next->debugValidate();
+        next = next->fNext;
+    } while (next && next != first);
 }
 #endif
 
-#if DEBUG_ANGLE
-void SkOpAngle::debugSameAs(const SkOpAngle* compare) const {
-    SK_ALWAYSBREAK(fSegment == compare->fSegment);
-    const SkOpSpan& startSpan = fSegment->span(fStart);
-    const SkOpSpan& oStartSpan = fSegment->span(compare->fStart);
-    SK_ALWAYSBREAK(startSpan.fToAngle == oStartSpan.fToAngle);
-    SK_ALWAYSBREAK(startSpan.fFromAngle == oStartSpan.fFromAngle);
-    const SkOpSpan& endSpan = fSegment->span(fEnd);
-    const SkOpSpan& oEndSpan = fSegment->span(compare->fEnd);
-    SK_ALWAYSBREAK(endSpan.fToAngle == oEndSpan.fToAngle);
-    SK_ALWAYSBREAK(endSpan.fFromAngle == oEndSpan.fFromAngle);
-}
-#endif
-
+void SkOpAngle::debugValidate() const {
 #if DEBUG_VALIDATE
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    int wind = 0;
+    int opp = 0;
+    int lastXor = -1;
+    int lastOppXor = -1;
+    do {
+        if (next->unorderable()) {
+            return;
+        }
+        const SkOpSpan* minSpan = next->start()->starter(next->end());
+        if (minSpan->windValue() == SK_MinS32) {
+            return;
+        }
+        bool op = next->segment()->operand();
+        bool isXor = next->segment()->isXor();
+        bool oppXor = next->segment()->oppXor();
+        SkASSERT(!DEBUG_LIMIT_WIND_SUM || between(0, minSpan->windValue(), DEBUG_LIMIT_WIND_SUM));
+        SkASSERT(!DEBUG_LIMIT_WIND_SUM
+                || between(-DEBUG_LIMIT_WIND_SUM, minSpan->oppValue(), DEBUG_LIMIT_WIND_SUM));
+        bool useXor = op ? oppXor : isXor;
+        SkASSERT(lastXor == -1 || lastXor == (int) useXor);
+        lastXor = (int) useXor;
+        wind += next->debugSign() * (op ? minSpan->oppValue() : minSpan->windValue());
+        if (useXor) {
+            wind &= 1;
+        }
+        useXor = op ? isXor : oppXor;
+        SkASSERT(lastOppXor == -1 || lastOppXor == (int) useXor);
+        lastOppXor = (int) useXor;
+        opp += next->debugSign() * (op ? minSpan->windValue() : minSpan->oppValue());
+        if (useXor) {
+            opp &= 1;
+        }
+        next = next->fNext;
+    } while (next && next != first);
+    SkASSERT(wind == 0 || !FLAGS_runFail);
+    SkASSERT(opp == 0 || !FLAGS_runFail);
+#endif
+}
+
 void SkOpAngle::debugValidateNext() const {
+#if !FORCE_RELEASE
     const SkOpAngle* first = this;
     const SkOpAngle* next = first;
     SkTDArray<const SkOpAngle*>(angles);
@@ -145,422 +386,277 @@
             return;
         }
     } while (true);
-}
-
-void SkOpAngle::debugValidateLoop() const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = first;
-    SK_ALWAYSBREAK(first->next() != first);
-    int signSum = 0;
-    int oppSum = 0;
-    bool firstOperand = fSegment->operand();
-    bool unorderable = false;
-    do {
-        unorderable |= next->fUnorderable;
-        const SkOpSegment* segment = next->fSegment;
-        bool operandsMatch = firstOperand == segment->operand();
-        signSum += operandsMatch ? segment->spanSign(next) : segment->oppSign(next);
-        oppSum += operandsMatch ? segment->oppSign(next) : segment->spanSign(next);
-        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
-        if (segment->_xor()) {
-//            SK_ALWAYSBREAK(span.fWindValue == 1);
-//            SK_ALWAYSBREAK(span.fWindSum == SK_MinS32 || span.fWindSum == 1);
-        }
-        if (segment->oppXor()) {
-            SK_ALWAYSBREAK(span.fOppValue == 0 || abs(span.fOppValue) == 1);
-//            SK_ALWAYSBREAK(span.fOppSum == SK_MinS32 || span.fOppSum == 0 || abs(span.fOppSum) == 1);
-        }
-        next = next->next();
-        if (!next) {
-            return;
-        }
-    } while (next != first);
-    if (unorderable) {
-        return;
-    }
-    SK_ALWAYSBREAK(!signSum || fSegment->_xor());
-    SK_ALWAYSBREAK(!oppSum || fSegment->oppXor());
-    int lastWinding;
-    int lastOppWinding;
-    int winding;
-    int oppWinding;
-    do {
-        const SkOpSegment* segment = next->fSegment;
-        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
-        winding = span.fWindSum;
-        if (winding != SK_MinS32) {
-//            SK_ALWAYSBREAK(winding != 0);
-            SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
-            lastWinding = winding;
-            int diffWinding = segment->spanSign(next);
-            if (!segment->_xor()) {
-                SK_ALWAYSBREAK(diffWinding != 0);
-                bool sameSign = (winding > 0) == (diffWinding > 0);
-                winding -= sameSign ? diffWinding : -diffWinding;
-                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
-                SK_ALWAYSBREAK(abs(winding) <= abs(lastWinding));
-                if (!sameSign) {
-                    SkTSwap(winding, lastWinding);
-                }
-            }
-            lastOppWinding = oppWinding = span.fOppSum;
-            if (oppWinding != SK_MinS32 && !segment->oppXor()) {
-                int oppDiffWinding = segment->oppSign(next);
-//                SK_ALWAYSBREAK(abs(oppDiffWinding) <= abs(diffWinding) || segment->_xor());
-                if (oppDiffWinding) {
-                    bool oppSameSign = (oppWinding > 0) == (oppDiffWinding > 0);
-                    oppWinding -= oppSameSign ? oppDiffWinding : -oppDiffWinding;
-                    SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
-                    SK_ALWAYSBREAK(abs(oppWinding) <= abs(lastOppWinding));
-                    if (!oppSameSign) {
-                        SkTSwap(oppWinding, lastOppWinding);
-                    }
-                }
-            }
-            firstOperand = segment->operand();
-            break;
-        }
-        SK_ALWAYSBREAK(span.fOppSum == SK_MinS32);
-        next = next->next();
-    } while (next != first);
-    if (winding == SK_MinS32) {
-        return;
-    }
-    SK_ALWAYSBREAK(oppWinding == SK_MinS32 || SkPathOpsDebug::ValidWind(oppWinding));
-    first = next;
-    next = next->next();
-    do {
-        const SkOpSegment* segment = next->fSegment;
-        lastWinding = winding;
-        lastOppWinding = oppWinding;
-        bool operandsMatch = firstOperand == segment->operand();
-        if (operandsMatch) {
-            if (!segment->_xor()) {
-                winding -= segment->spanSign(next);
-                SK_ALWAYSBREAK(winding != lastWinding);
-                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
-            }
-            if (!segment->oppXor()) {
-                int oppDiffWinding = segment->oppSign(next);
-                if (oppWinding != SK_MinS32) {
-                    oppWinding -= oppDiffWinding;
-                    SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
-                } else {
-                    SK_ALWAYSBREAK(oppDiffWinding == 0);
-                }
-            }
-        } else {
-            if (!segment->oppXor()) {
-                winding -= segment->oppSign(next);
-                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
-            }
-            if (!segment->_xor()) {
-                oppWinding -= segment->spanSign(next);
-                SK_ALWAYSBREAK(oppWinding != lastOppWinding);
-                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
-            }
-        }
-        bool useInner = SkOpSegment::UseInnerWinding(lastWinding, winding);
-        int sumWinding = useInner ? winding : lastWinding;
-        bool oppUseInner = SkOpSegment::UseInnerWinding(lastOppWinding, oppWinding);
-        int oppSumWinding = oppUseInner ? oppWinding : lastOppWinding;
-        if (!operandsMatch) {
-            SkTSwap(useInner, oppUseInner);
-            SkTSwap(sumWinding, oppSumWinding);
-        }
-        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
-        if (winding == -lastWinding) {
-            if (span.fWindSum != SK_MinS32) {
-                SkDebugf("%s useInner=%d spanSign=%d lastWinding=%d winding=%d windSum=%d\n",
-                        __FUNCTION__,
-                        useInner, segment->spanSign(next), lastWinding, winding, span.fWindSum);
-            }
-        }
-        if (oppWinding != SK_MinS32) {
-            if (span.fOppSum != SK_MinS32) {
-                SK_ALWAYSBREAK(span.fOppSum == oppSumWinding || segment->oppXor() || segment->_xor());
-            }
-        } else {
-            SK_ALWAYSBREAK(!firstOperand);
-            SK_ALWAYSBREAK(!segment->operand());
-            SK_ALWAYSBREAK(!span.fOppValue);
-        }
-        next = next->next();
-    } while (next != first);
-}
 #endif
-
-#if DEBUG_SWAP_TOP
-bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const {
-    if (fVerb != SkPath::kCubic_Verb) {
-        return false;
-    }
-    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
-    return dst.controlsContainedByEnds();
-}
-#endif
-
-#if DEBUG_CONCIDENT
-// SK_ALWAYSBREAK if pair has not already been added
-void SkOpSegment::debugAddTPair(double t, const SkOpSegment& 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) {
-            return;
-        }
-    }
-    SK_ALWAYSBREAK(0);
-}
-#endif
-
-#if DEBUG_ANGLE
-void SkOpSegment::debugCheckPointsEqualish(int tStart, int tEnd) const {
-    const SkPoint& basePt = fTs[tStart].fPt;
-    while (++tStart < tEnd) {
-       const SkPoint& cmpPt = fTs[tStart].fPt;
-       SK_ALWAYSBREAK(SkDPoint::ApproximatelyEqual(basePt, cmpPt));
-    }
-}
-#endif
-
-#if DEBUG_SWAP_TOP
-int SkOpSegment::debugInflections(int tStart, int tEnd) const {
-    if (fVerb != SkPath::kCubic_Verb) {
-        return false;
-    }
-    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
-    double inflections[2];
-    return dst.findInflections(inflections);
-}
-#endif
-
-const SkOpAngle* SkOpSegment::debugLastAngle() const {
-    const SkOpAngle* result = NULL;
-    for (int index = 0; index < count(); ++index) {
-        const SkOpSpan& span = this->span(index);
-        if (span.fToAngle) {
-            SkASSERT(!result);
-            result = span.fToAngle;
-        }
-    }
-    SkASSERT(result);
-    return result;
 }
 
-void SkOpSegment::debugReset() {
-    fTs.reset();
-    fAngles.reset();
-}
-
-#if DEBUG_CONCIDENT
-void SkOpSegment::debugShowTs(const char* prefix) const {
-    SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID);
-    int lastWind = -1;
-    int lastOpp = -1;
-    double lastT = -1;
-    int i;
-    for (i = 0; i < fTs.count(); ++i) {
-        bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
-                || lastOpp != fTs[i].fOppValue;
-        if (change && lastWind >= 0) {
-            SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
-                    lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
-        }
-        if (change) {
-            SkDebugf(" [o=%d", fTs[i].fOther->fID);
-            lastWind = fTs[i].fWindValue;
-            lastOpp = fTs[i].fOppValue;
-            lastT = fTs[i].fT;
-        } else {
-            SkDebugf(",%d", fTs[i].fOther->fID);
-        }
-    }
-    if (i <= 0) {
-        return;
-    }
-    SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
-            lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
-    if (fOperand) {
-        SkDebugf(" operand");
-    }
-    if (done()) {
-        SkDebugf(" done");
-    }
-    SkDebugf("\n");
-}
-#endif
-
-#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
-void SkOpSegment::debugShowActiveSpans() const {
-    debugValidate();
-    if (done()) {
-        return;
-    }
-#if DEBUG_ACTIVE_SPANS_SHORT_FORM
-    int lastId = -1;
-    double lastT = -1;
-#endif
-    for (int i = 0; i < fTs.count(); ++i) {
-        if (fTs[i].fDone) {
-            continue;
-        }
-        SK_ALWAYSBREAK(i < fTs.count() - 1);
-#if DEBUG_ACTIVE_SPANS_SHORT_FORM
-        if (lastId == fID && lastT == fTs[i].fT) {
-            continue;
-        }
-        lastId = fID;
-        lastT = fTs[i].fT;
-#endif
-        SkDebugf("%s id=%d", __FUNCTION__, fID);
-        SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
-        for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
-            SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
-        }
-        const SkOpSpan* span = &fTs[i];
-        SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span));
-        int iEnd = i + 1;
-        while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) {
-            ++iEnd;
-        }
-        SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT);
-        const SkOpSegment* other = fTs[i].fOther;
-        SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
-                other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
-        if (fTs[i].fWindSum == SK_MinS32) {
-            SkDebugf("?");
-        } else {
-            SkDebugf("%d", fTs[i].fWindSum);
-        }
-        SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
+void SkOpCoincidence::debugShowCoincidence() const {
+    SkCoincidentSpans* span = fHead;
+    while (span) {
+        SkDebugf("%s - id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
+                span->fCoinPtTStart->segment()->debugID(),
+                span->fCoinPtTStart->fT, span->fCoinPtTEnd->fT);
+        SkDebugf("%s + id=%d t=%1.9g tEnd=%1.9g\n", __FUNCTION__,
+                span->fOppPtTStart->segment()->debugID(),
+                span->fOppPtTStart->fT, span->fOppPtTEnd->fT);
+        span = span->fNext;
     }
 }
-#endif
-
-#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) {
-    const SkPoint& pt = xyAtT(&span);
-    SkDebugf("%s id=%d", fun, fID);
-    SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
-    for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
-        SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
-    }
-    SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
-            fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
-    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=",
-            span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
-            (&span)[1].fT, winding);
-    if (span.fWindSum == SK_MinS32) {
-        SkDebugf("?");
-    } else {
-        SkDebugf("%d", span.fWindSum);
-    }
-    SkDebugf(" windValue=%d\n", span.fWindValue);
-}
-
-void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding,
-                                      int oppWinding) {
-    const SkPoint& pt = xyAtT(&span);
-    SkDebugf("%s id=%d", fun, fID);
-    SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
-    for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
-        SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
-    }
-    SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
-            fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
-    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=",
-            span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
-            (&span)[1].fT, winding, oppWinding);
-    if (span.fOppSum == SK_MinS32) {
-        SkDebugf("?");
-    } else {
-        SkDebugf("%d", span.fOppSum);
-    }
-    SkDebugf(" windSum=");
-    if (span.fWindSum == SK_MinS32) {
-        SkDebugf("?");
-    } else {
-        SkDebugf("%d", span.fWindSum);
-    }
-    SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue);
-}
-#endif
-
-#if DEBUG_SHOW_WINDING
-int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const {
-    if (!(1 << fID & ofInterest)) {
-        return 0;
-    }
-    int sum = 0;
-    SkTArray<char, true> slots(slotCount * 2);
-    memset(slots.begin(), ' ', slotCount * 2);
-    for (int i = 0; i < fTs.count(); ++i) {
-   //     if (!(1 << fTs[i].fOther->fID & ofInterest)) {
-   //         continue;
-   //     }
-        sum += fTs[i].fWindValue;
-        slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
-        sum += fTs[i].fOppValue;
-        slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
-    }
-    SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
-            slots.begin() + slotCount);
-    return sum;
-}
-#endif
 
 void SkOpSegment::debugValidate() const {
 #if DEBUG_VALIDATE
-    int count = fTs.count();
-    SK_ALWAYSBREAK(count >= 2);
-    SK_ALWAYSBREAK(fTs[0].fT == 0);
-    SK_ALWAYSBREAK(fTs[count - 1].fT == 1);
+    const SkOpSpanBase* span = &fHead;
+    double lastT = -1;
+    const SkOpSpanBase* prev = NULL;
+    int count = 0;
     int done = 0;
-    double t = -1;
-    const SkOpSpan* last = NULL;
-    bool tinyTFound = false;
-    bool hasLoop = false;
-    for (int i = 0; i < count; ++i) {
-        const SkOpSpan& span = fTs[i];
-        SK_ALWAYSBREAK(t <= span.fT);
-        t = span.fT;
-        int otherIndex = span.fOtherIndex;
-        const SkOpSegment* other = span.fOther;
-        SK_ALWAYSBREAK(other != this || fVerb == SkPath::kCubic_Verb);
-        const SkOpSpan& otherSpan = other->fTs[otherIndex];
-        SK_ALWAYSBREAK(otherSpan.fPt == span.fPt);
-        SK_ALWAYSBREAK(otherSpan.fOtherT == t);
-        SK_ALWAYSBREAK(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]);
-        done += span.fDone;
-        if (last) {
-            SK_ALWAYSBREAK(last->fT != span.fT || last->fOther != span.fOther);
-            bool tsEqual = last->fT == span.fT;
-            bool tsPreciselyEqual = precisely_equal(last->fT, span.fT);
-            SK_ALWAYSBREAK(!tsEqual || tsPreciselyEqual);
-            bool pointsEqual = last->fPt == span.fPt;
-            bool pointsNearlyEqual = AlmostEqualUlps(last->fPt, span.fPt);
-#if 0  // bufferOverflow test triggers this
-            SK_ALWAYSBREAK(!tsPreciselyEqual || pointsNearlyEqual);
+    do {
+        if (!span->final()) {
+            ++count;
+            done += span->upCast()->done() ? 1 : 0;
+        }
+        SkASSERT(span->segment() == this);
+        SkASSERT(!prev || prev->upCast()->next() == span);
+        SkASSERT(!prev || prev == span->prev());
+        prev = span;
+        double t = span->ptT()->fT;
+        SkASSERT(lastT < t);
+        lastT = t;
+        span->debugValidate();
+    } while (!span->final() && (span = span->upCast()->next()));
+    SkASSERT(count == fCount);
+    SkASSERT(done == fDoneCount);
+    SkASSERT(count >= fDoneCount);
+    SkASSERT(span->final());
+    span->debugValidate();
 #endif
-//            SK_ALWAYSBREAK(!last->fTiny || !tsPreciselyEqual || span.fTiny || tinyTFound);
-            SK_ALWAYSBREAK(last->fTiny || tsPreciselyEqual || !pointsEqual || hasLoop);
-            SK_ALWAYSBREAK(!last->fTiny || pointsEqual);
-            SK_ALWAYSBREAK(!last->fTiny || last->fDone);
-            SK_ALWAYSBREAK(!last->fSmall || pointsNearlyEqual);
-            SK_ALWAYSBREAK(!last->fSmall || last->fDone);
-//            SK_ALWAYSBREAK(!last->fSmall || last->fTiny);
-//            SK_ALWAYSBREAK(last->fTiny || !pointsEqual || last->fDone == span.fDone);
-            if (last->fTiny) {
-                tinyTFound |= !tsPreciselyEqual;
-            } else {
-                tinyTFound = false;
+}
+
+bool SkOpSpanBase::debugCoinEndLoopCheck() const {
+    int loop = 0;
+    const SkOpSpanBase* next = this;
+    SkOpSpanBase* nextCoin;
+    do {
+        nextCoin = next->fCoinEnd;
+        SkASSERT(nextCoin == this || nextCoin->fCoinEnd != nextCoin);
+        for (int check = 1; check < loop - 1; ++check) {
+            const SkOpSpanBase* checkCoin = this->fCoinEnd;
+            const SkOpSpanBase* innerCoin = checkCoin;
+            for (int inner = check + 1; inner < loop; ++inner) {
+                innerCoin = innerCoin->fCoinEnd;
+                if (checkCoin == innerCoin) {
+                    SkDebugf("*** bad coincident end loop ***\n");
+                    return false;
+                }
             }
         }
-        last = &span;
-        hasLoop |= last->fLoop;
+        ++loop;
+    } while ((next = nextCoin) && next != this);
+    return true;
+}
+
+void SkOpSpanBase::debugValidate() const {
+#if DEBUG_VALIDATE
+    const SkOpPtT* ptT = &fPtT;
+    SkASSERT(ptT->span() == this);
+    do {
+//        SkASSERT(SkDPoint::RoughlyEqual(fPtT.fPt, ptT->fPt));
+        ptT->debugValidate();
+        ptT = ptT->next();
+    } while (ptT != &fPtT);
+    SkASSERT(this->debugCoinEndLoopCheck());
+    if (!this->final()) {
+        SkASSERT(this->upCast()->debugCoinLoopCheck());
     }
-    SK_ALWAYSBREAK(done == fDoneSpans);
-//    if (fAngles.count() ) {
-//        fAngles.begin()->debugValidateLoop();
-//    }
+    if (fFromAngle) {
+        fFromAngle->debugValidate();
+    }
+    if (!this->final() && this->upCast()->toAngle()) {
+        this->upCast()->toAngle()->debugValidate();
+    }
 #endif
 }
+
+bool SkOpSpan::debugCoinLoopCheck() const {
+    int loop = 0;
+    const SkOpSpan* next = this;
+    SkOpSpan* nextCoin;
+    do {
+        nextCoin = next->fCoincident;
+        SkASSERT(nextCoin == this || nextCoin->fCoincident != nextCoin);
+        for (int check = 1; check < loop - 1; ++check) {
+            const SkOpSpan* checkCoin = this->fCoincident;
+            const SkOpSpan* innerCoin = checkCoin;
+            for (int inner = check + 1; inner < loop; ++inner) {
+                innerCoin = innerCoin->fCoincident;
+                if (checkCoin == innerCoin) {
+                    SkDebugf("*** bad coincident loop ***\n");
+                    return false;
+                }
+            }
+        }
+        ++loop;
+    } while ((next = nextCoin) && next != this);
+    return true;
+}
+
+// called only by test code
+int SkIntersections::debugCoincidentUsed() const {
+    if (!fIsCoincident[0]) {
+        SkASSERT(!fIsCoincident[1]);
+        return 0;
+    }
+    int count = 0;
+    SkDEBUGCODE(int count2 = 0;)
+    for (int index = 0; index < fUsed; ++index) {
+        if (fIsCoincident[0] & (1 << index)) {
+            ++count;
+        }
+#ifdef SK_DEBUG
+        if (fIsCoincident[1] & (1 << index)) {
+            ++count2;
+        }
+#endif
+    }
+    SkASSERT(count == count2);
+    return count;
+}
+
+#include "SkOpContour.h"
+
+int SkOpPtT::debugLoopLimit(bool report) const {
+    int loop = 0;
+    const SkOpPtT* next = this;
+    do {
+        for (int check = 1; check < loop - 1; ++check) {
+            const SkOpPtT* checkPtT = this->fNext;
+            const SkOpPtT* innerPtT = checkPtT;
+            for (int inner = check + 1; inner < loop; ++inner) {
+                innerPtT = innerPtT->fNext;
+                if (checkPtT == innerPtT) {
+                    if (report) {
+                        SkDebugf("*** bad ptT loop ***\n");
+                    }
+                    return loop;
+                }
+            }
+        }
+        ++loop;
+    } while ((next = next->fNext) && next != this);
+    return 0;
+}
+
+void SkOpPtT::debugValidate() const {
+#if DEBUG_VALIDATE
+    if (contour()->globalState()->phase() == SkOpGlobalState::kIntersecting) {
+        return;
+    }
+    SkASSERT(fNext);
+    SkASSERT(fNext != this);
+    SkASSERT(fNext->fNext);
+    SkASSERT(debugLoopLimit(false) == 0);
+#endif
+}
+
+static void output_scalar(SkScalar num) {
+    if (num == (int) num) {
+        SkDebugf("%d", (int) num);
+    } else {
+        SkString str;
+        str.printf("%1.9g", num);
+        int width = (int) str.size();
+        const char* cStr = str.c_str();
+        while (cStr[width - 1] == '0') {
+            --width;
+        }
+        str.resize(width);
+        SkDebugf("%sf", str.c_str());
+    }
+}
+
+static void output_points(const SkPoint* pts, int count) {
+    for (int index = 0; index < count; ++index) {
+        output_scalar(pts[index].fX);
+        SkDebugf(", ");
+        output_scalar(pts[index].fY);
+        if (index + 1 < count) {
+            SkDebugf(", ");
+        }
+    }
+}
+
+static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
+    uint8_t verb;
+    SkPoint pts[4];
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                SkDebugf("    %s.moveTo(", pathName);
+                output_points(&pts[0], 1);
+                SkDebugf(");\n");
+                continue;
+            case SkPath::kLine_Verb:
+                SkDebugf("    %s.lineTo(", pathName);
+                output_points(&pts[1], 1);
+                SkDebugf(");\n");
+                break;
+            case SkPath::kQuad_Verb:
+                SkDebugf("    %s.quadTo(", pathName);
+                output_points(&pts[1], 2);
+                SkDebugf(");\n");
+                break;
+            case SkPath::kConic_Verb:
+                SkDebugf("    %s.conicTo(", pathName);
+                output_points(&pts[1], 2);
+                SkDebugf(", %1.9gf);\n", iter.conicWeight());
+                break;
+            case SkPath::kCubic_Verb:
+                SkDebugf("    %s.cubicTo(", pathName);
+                output_points(&pts[1], 3);
+                SkDebugf(");\n");
+                break;
+            case SkPath::kClose_Verb:
+                SkDebugf("    %s.close();\n", pathName);
+                break;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return;
+        }
+    }
+}
+
+static const char* gFillTypeStr[] = {
+    "kWinding_FillType",
+    "kEvenOdd_FillType",
+    "kInverseWinding_FillType",
+    "kInverseEvenOdd_FillType"
+};
+
+void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
+    SkPath::RawIter iter(path);
+#define SUPPORT_RECT_CONTOUR_DETECTION 0
+#if SUPPORT_RECT_CONTOUR_DETECTION
+    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;
+    }
+#endif
+    SkPath::FillType fillType = path.getFillType();
+    SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
+    if (includeDeclaration) {
+        SkDebugf("    SkPath %s;\n", name);
+    }
+    SkDebugf("    %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
+    iter.setPath(path);
+    showPathContours(iter, name);
+}
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index 5770aef..61c2eca 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -39,35 +39,23 @@
 
 #define DEBUG_ACTIVE_OP 0
 #define DEBUG_ACTIVE_SPANS 0
-#define DEBUG_ACTIVE_SPANS_FIRST_ONLY 0
-#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
 #define DEBUG_ADD_INTERSECTING_TS 0
-#define DEBUG_ADD_T_PAIR 0
+#define DEBUG_ADD_T 0
 #define DEBUG_ANGLE 0
-#define DEBUG_AS_C_CODE 1
 #define DEBUG_ASSEMBLE 0
-#define DEBUG_CHECK_ALIGN 0
-#define DEBUG_CHECK_TINY 0
-#define DEBUG_CONCIDENT 0
-#define DEBUG_CROSS 0
 #define DEBUG_CUBIC_BINARY_SEARCH 0
-#define DEBUG_DUPLICATES 0
-#define DEBUG_FLAT_QUADS 0
+#define DEBUG_CUBIC_SPLIT 0
+#define DEBUG_DUMP_SEGMENTS 0
 #define DEBUG_FLOW 0
 #define DEBUG_LIMIT_WIND_SUM 0
 #define DEBUG_MARK_DONE 0
 #define DEBUG_PATH_CONSTRUCTION 0
+#define DEBUG_PERP 0
 #define DEBUG_SHOW_TEST_NAME 0
-#define DEBUG_SHOW_TEST_PROGRESS 0
-#define DEBUG_SHOW_WINDING 0
 #define DEBUG_SORT 0
-#define DEBUG_SORT_COMPACT 0
-#define DEBUG_SORT_RAW 0
-#define DEBUG_SORT_SINGLE 0
-#define DEBUG_SWAP_TOP 0
-#define DEBUG_UNSORTABLE 0
+#define DEBUG_T_SECT 0
+#define DEBUG_T_SECT_DUMP 0
 #define DEBUG_VALIDATE 0
-#define DEBUG_WIND_BUMP 0
 #define DEBUG_WINDING 0
 #define DEBUG_WINDING_AT_T 0
 
@@ -75,54 +63,62 @@
 
 #define DEBUG_ACTIVE_OP 1
 #define DEBUG_ACTIVE_SPANS 1
-#define DEBUG_ACTIVE_SPANS_FIRST_ONLY 0
-#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
 #define DEBUG_ADD_INTERSECTING_TS 1
-#define DEBUG_ADD_T_PAIR 1
+#define DEBUG_ADD_T 1
 #define DEBUG_ANGLE 1
-#define DEBUG_AS_C_CODE 1
 #define DEBUG_ASSEMBLE 1
-#define DEBUG_CHECK_ALIGN 1
-#define DEBUG_CHECK_TINY 1
-#define DEBUG_CONCIDENT 1
-#define DEBUG_CROSS 01
 #define DEBUG_CUBIC_BINARY_SEARCH 0
-#define DEBUG_DUPLICATES 1
-#define DEBUG_FLAT_QUADS 0
+#define DEBUG_CUBIC_SPLIT 1
+#define DEBUG_DUMP_SEGMENTS 1
 #define DEBUG_FLOW 1
-#define DEBUG_LIMIT_WIND_SUM 4
+#define DEBUG_LIMIT_WIND_SUM 5
 #define DEBUG_MARK_DONE 1
 #define DEBUG_PATH_CONSTRUCTION 1
+#define DEBUG_PERP 1
 #define DEBUG_SHOW_TEST_NAME 1
-#define DEBUG_SHOW_TEST_PROGRESS 1
-#define DEBUG_SHOW_WINDING 0
 #define DEBUG_SORT 1
-#define DEBUG_SORT_COMPACT 0
-#define DEBUG_SORT_RAW 0
-#define DEBUG_SORT_SINGLE 0
-#define DEBUG_SWAP_TOP 1
-#define DEBUG_UNSORTABLE 1
-#define DEBUG_VALIDATE 0
-#define DEBUG_WIND_BUMP 0
+#define DEBUG_T_SECT 0
+#define DEBUG_T_SECT_DUMP 0
+#define DEBUG_VALIDATE 1
 #define DEBUG_WINDING 1
 #define DEBUG_WINDING_AT_T 1
 
 #endif
 
-#if DEBUG_AS_C_CODE
-#define CUBIC_DEBUG_STR "{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}"
-#define QUAD_DEBUG_STR  "{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}"
-#define LINE_DEBUG_STR  "{{%1.9g,%1.9g}, {%1.9g,%1.9g}}"
-#define PT_DEBUG_STR "{{%1.9g,%1.9g}}"
+#ifdef SK_RELEASE
+    #define SkDEBUGRELEASE(a, b) b
+    #define SkDEBUGPARAMS(...)
+    #define SkDEBUGCODE_(...)
 #else
-#define CUBIC_DEBUG_STR "(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
-#define QUAD_DEBUG_STR  "(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
-#define LINE_DEBUG_STR  "(%1.9g,%1.9g %1.9g,%1.9g)"
-#define PT_DEBUG_STR "(%1.9g,%1.9g)"
+    #define SkDEBUGRELEASE(a, b) a
+    #define SkDEBUGPARAMS(...) , __VA_ARGS__
+    #define SkDEBUGCODE_(...) __VA_ARGS__  // temporary until SkDEBUGCODE is fixed
 #endif
+
+#if DEBUG_T_SECT == 0
+    #define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) b
+    #define PATH_OPS_DEBUG_T_SECT_PARAMS(...)
+    #define PATH_OPS_DEBUG_T_SECT_CODE(...)
+#else
+    #define PATH_OPS_DEBUG_T_SECT_RELEASE(a, b) a
+    #define PATH_OPS_DEBUG_T_SECT_PARAMS(...) , __VA_ARGS__
+    #define PATH_OPS_DEBUG_T_SECT_CODE(...) __VA_ARGS__
+#endif
+
+#if DEBUG_T_SECT_DUMP > 1
+    extern int gDumpTSectNum;
+#endif
+
+#define CUBIC_DEBUG_STR  "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
+#define CONIC_DEBUG_STR "{{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}, %1.9g}"
+#define QUAD_DEBUG_STR   "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
+#define LINE_DEBUG_STR   "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
+#define PT_DEBUG_STR "{{%1.9g,%1.9g}}"
+
 #define T_DEBUG_STR(t, n) #t "[" #n "]=%1.9g"
 #define TX_DEBUG_STR(t) #t "[%d]=%1.9g"
 #define CUBIC_DEBUG_DATA(c) c[0].fX, c[0].fY, c[1].fX, c[1].fY, c[2].fX, c[2].fY, c[3].fX, c[3].fY
+#define CONIC_DEBUG_DATA(c, w) c[0].fX, c[0].fY, c[1].fX, c[1].fY, c[2].fX, c[2].fY, w
 #define QUAD_DEBUG_DATA(q)  q[0].fX, q[0].fY, q[1].fX, q[1].fY, q[2].fX, q[2].fY
 #define LINE_DEBUG_DATA(l)  l[0].fX, l[0].fY, l[1].fX, l[1].fY
 #define PT_DEBUG_DATA(i, n) i.pt(n).asSkPoint().fX, i.pt(n).asSkPoint().fY
@@ -135,9 +131,6 @@
 #include "SkTLS.h"
 #endif
 
-#include "SkTArray.h"
-#include "SkTDArray.h"
-
 class SkPathOpsDebug {
 public:
     static const char* kLVerbStr[];
@@ -147,7 +140,7 @@
     static int gSegmentID;
 #endif
 
-#if DEBUG_SORT || DEBUG_SWAP_TOP
+#if DEBUG_SORT
     static int gSortCountDefault;
     static int gSortCount;
 #endif
@@ -156,7 +149,6 @@
     static const char* kPathOpStr[];
 #endif
 
-    static bool ChaseContains(const SkTDArray<struct SkOpSpan *>& , const struct SkOpSpan * );
     static void MathematicaIze(char* str, size_t bufferSize);
     static bool ValidWind(int winding);
     static void WindingPrintf(int winding);
@@ -169,68 +161,47 @@
         SkPathOpsDebug::DeleteNameStr)))
     static void BumpTestName(char* );
 #endif
+    static const char* OpStr(SkPathOp );
     static void ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration);
     static void ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name);
-    static void DumpCoincidence(const SkTArray<class SkOpContour, true>& contours);
-    static void DumpCoincidence(const SkTArray<class SkOpContour* , true>& contours);
-    static void DumpContours(const SkTArray<class SkOpContour, true>& contours);
-    static void DumpContours(const SkTArray<class SkOpContour* , true>& contours);
-    static void DumpContourAngles(const SkTArray<class SkOpContour, true>& contours);
-    static void DumpContourAngles(const SkTArray<class SkOpContour* , true>& contours);
-    static void DumpContourPt(const SkTArray<class SkOpContour, true>& contours, int id);
-    static void DumpContourPt(const SkTArray<class SkOpContour* , true>& contours, int id);
-    static void DumpContourPts(const SkTArray<class SkOpContour, true>& contours);
-    static void DumpContourPts(const SkTArray<class SkOpContour* , true>& contours);
-    static void DumpContourSpan(const SkTArray<class SkOpContour, true>& contours, int id);
-    static void DumpContourSpan(const SkTArray<class SkOpContour* , true>& contours, int id);
-    static void DumpContourSpans(const SkTArray<class SkOpContour, true>& contours);
-    static void DumpContourSpans(const SkTArray<class SkOpContour* , true>& contours);
-    static void DumpSpans(const SkTDArray<struct SkOpSpan *>& );
-    static void DumpSpans(const SkTDArray<struct SkOpSpan *>* );
+
+    static bool ChaseContains(const SkTDArray<class SkOpSpanBase*>& , const class SkOpSpanBase* );
+
+    static const struct SkOpAngle* DebugAngleAngle(const struct SkOpAngle*, int id);
+    static class SkOpContour* DebugAngleContour(struct SkOpAngle*, int id);
+    static const class SkOpPtT* DebugAnglePtT(const struct SkOpAngle*, int id);
+    static const class SkOpSegment* DebugAngleSegment(const struct SkOpAngle*, int id);
+    static const class SkOpSpanBase* DebugAngleSpan(const struct SkOpAngle*, int id);
+
+    static const struct SkOpAngle* DebugContourAngle(class SkOpContour*, int id);
+    static class SkOpContour* DebugContourContour(class SkOpContour*, int id);
+    static const class SkOpPtT* DebugContourPtT(class SkOpContour*, int id);
+    static const class SkOpSegment* DebugContourSegment(class SkOpContour*, int id);
+    static const class SkOpSpanBase* DebugContourSpan(class SkOpContour*, int id);
+
+    static const struct SkOpAngle* DebugPtTAngle(const class SkOpPtT*, int id);
+    static class SkOpContour* DebugPtTContour(class SkOpPtT*, int id);
+    static const class SkOpPtT* DebugPtTPtT(const class SkOpPtT*, int id);
+    static const class SkOpSegment* DebugPtTSegment(const class SkOpPtT*, int id);
+    static const class SkOpSpanBase* DebugPtTSpan(const class SkOpPtT*, int id);
+
+    static const struct SkOpAngle* DebugSegmentAngle(const class SkOpSegment*, int id);
+    static class SkOpContour* DebugSegmentContour(class SkOpSegment*, int id);
+    static const class SkOpPtT* DebugSegmentPtT(const class SkOpSegment*, int id);
+    static const class SkOpSegment* DebugSegmentSegment(const class SkOpSegment*, int id);
+    static const class SkOpSpanBase* DebugSegmentSpan(const class SkOpSegment*, int id);
+
+    static const struct SkOpAngle* DebugSpanAngle(const class SkOpSpanBase*, int id);
+    static class SkOpContour* DebugSpanContour(class SkOpSpanBase*, int id);
+    static const class SkOpPtT* DebugSpanPtT(const class SkOpSpanBase*, int id);
+    static const class SkOpSegment* DebugSpanSegment(const class SkOpSpanBase*, int id);
+    static const class SkOpSpanBase* DebugSpanSpan(const class SkOpSpanBase*, int id);
 };
 
-// shorthand for calling from debugger
-void Dump(const SkTArray<class SkOpContour, true>& contours);
-void Dump(const SkTArray<class SkOpContour* , true>& contours);
-void Dump(const SkTArray<class SkOpContour, true>* contours);
-void Dump(const SkTArray<class SkOpContour* , true>* contours);
-
-void Dump(const SkTDArray<SkOpSpan* >& chase);
-void Dump(const SkTDArray<SkOpSpan* >* chase);
-
-void DumpAngles(const SkTArray<class SkOpContour, true>& contours);
-void DumpAngles(const SkTArray<class SkOpContour* , true>& contours);
-void DumpAngles(const SkTArray<class SkOpContour, true>* contours);
-void DumpAngles(const SkTArray<class SkOpContour* , true>* contours);
-
-void DumpCoin(const SkTArray<class SkOpContour, true>& contours);
-void DumpCoin(const SkTArray<class SkOpContour* , true>& contours);
-void DumpCoin(const SkTArray<class SkOpContour, true>* contours);
-void DumpCoin(const SkTArray<class SkOpContour* , true>* contours);
-
-void DumpPts(const SkTArray<class SkOpContour, true>& contours);
-void DumpPts(const SkTArray<class SkOpContour* , true>& contours);
-void DumpPts(const SkTArray<class SkOpContour, true>* contours);
-void DumpPts(const SkTArray<class SkOpContour* , true>* contours);
-
-void DumpPt(const SkTArray<class SkOpContour, true>& contours, int segmentID);
-void DumpPt(const SkTArray<class SkOpContour* , true>& contours, int segmentID);
-void DumpPt(const SkTArray<class SkOpContour, true>* contours, int segmentID);
-void DumpPt(const SkTArray<class SkOpContour* , true>* contours, int segmentID);
-
-void DumpSpans(const SkTArray<class SkOpContour, true>& contours);
-void DumpSpans(const SkTArray<class SkOpContour* , true>& contours);
-void DumpSpans(const SkTArray<class SkOpContour, true>* contours);
-void DumpSpans(const SkTArray<class SkOpContour* , true>* contours);
-
-void DumpSpan(const SkTArray<class SkOpContour, true>& contours, int segmentID);
-void DumpSpan(const SkTArray<class SkOpContour* , true>& contours, int segmentID);
-void DumpSpan(const SkTArray<class SkOpContour, true>* contours, int segmentID);
-void DumpSpan(const SkTArray<class SkOpContour* , true>* contours, int segmentID);
+struct SkDQuad;
 
 // generates tools/path_sorter.htm and path_visualizer.htm compatible data
-void DumpQ(const struct SkDQuad& quad1, const struct SkDQuad& quad2, int testNo);
-
-void DumpT(const struct SkDQuad& quad, double t);
+void DumpQ(const SkDQuad& quad1, const SkDQuad& quad2, int testNo);
+void DumpT(const SkDQuad& quad, double t);
 
 #endif
diff --git a/src/pathops/SkPathOpsLine.cpp b/src/pathops/SkPathOpsLine.cpp
index e4fc97b..70f2e12 100644
--- a/src/pathops/SkPathOpsLine.cpp
+++ b/src/pathops/SkPathOpsLine.cpp
@@ -6,14 +6,6 @@
  */
 #include "SkPathOpsLine.h"
 
-SkDLine SkDLine::subDivide(double t1, double t2) const {
-    SkDVector delta = tangent();
-    SkDLine dst = {{{
-            fPts[0].fX - t1 * delta.fX, fPts[0].fY - t1 * delta.fY}, {
-            fPts[0].fX - t2 * delta.fX, fPts[0].fY - t2 * delta.fY}}};
-    return dst;
-}
-
 // may have this below somewhere else already:
 // copying here because I thought it was clever
 
@@ -28,6 +20,7 @@
 //    Point with coordinates {float x, y;}
 //===================================================================
 
+// (only used by testing)
 // isLeft(): tests if a point is Left|On|Right of an infinite line.
 //    Input:  three points P0, P1, and P2
 //    Return: >0 for P2 left of the line through P0 and P1
@@ -110,19 +103,6 @@
     return RoughlyEqualUlps(largest, largest + dist); // is the dist within ULPS tolerance?
 }
 
-// Returns true if a ray from (0,0) to (x1,y1) is coincident with a ray (0,0) to (x2,y2)
-// OPTIMIZE: a specialty routine could speed this up -- may not be called very often though
-bool SkDLine::NearRay(double x1, double y1, double x2, double y2) {
-    double denom1 = x1 * x1 + y1 * y1;
-    double denom2 = x2 * x2 + y2 * y2;
-    SkDLine line = {{{0, 0}, {x1, y1}}};
-    SkDPoint pt = {x2, y2};
-    if (denom2 > denom1) {
-        SkTSwap(line[1], pt);
-    }
-    return line.nearRay(pt);
-}
-
 double SkDLine::ExactPointH(const SkDPoint& xy, double left, double right, double y) {
     if (xy.fY == y) {
         if (xy.fX == left) {
diff --git a/src/pathops/SkPathOpsLine.h b/src/pathops/SkPathOpsLine.h
index 74eb615..ce50201 100644
--- a/src/pathops/SkPathOpsLine.h
+++ b/src/pathops/SkPathOpsLine.h
@@ -15,32 +15,28 @@
     const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < 2); return fPts[n]; }
     SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < 2); return fPts[n]; }
 
-    void set(const SkPoint pts[2]) {
+    const SkDLine& set(const SkPoint pts[2]) {
         fPts[0] = pts[0];
         fPts[1] = pts[1];
-    }
-
-    static SkDLine SubDivide(const SkPoint a[2], double t1, double t2) {
-        SkDLine line;
-        line.set(a);
-        return line.subDivide(t1, t2);
+        return *this;
     }
 
     double exactPoint(const SkDPoint& xy) const;
     static double ExactPointH(const SkDPoint& xy, double left, double right, double y);
     static double ExactPointV(const SkDPoint& xy, double top, double bottom, double x);
+
+    // only used by testing
     double isLeft(const SkDPoint& pt) const;
+
     double nearPoint(const SkDPoint& xy, bool* unequal) const;
     bool nearRay(const SkDPoint& xy) const;
     static double NearPointH(const SkDPoint& xy, double left, double right, double y);
     static double NearPointV(const SkDPoint& xy, double top, double bottom, double x);
-    static bool NearRay(double dx1, double dy1, double dx2, double dy2);
     SkDPoint ptAtT(double t) const;
-    SkDLine subDivide(double t1, double t2) const;
 
     void dump() const;
-private:
-    SkDVector tangent() const { return fPts[0] - fPts[1]; }
+    void dumpID(int ) const;
+    void dumpInner() const;
 };
 
 #endif
diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp
index f2b25c0..9c09eaf 100644
--- a/src/pathops/SkPathOpsOp.cpp
+++ b/src/pathops/SkPathOpsOp.cpp
@@ -5,27 +5,24 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
 #include "SkOpEdgeBuilder.h"
 #include "SkPathOpsCommon.h"
 #include "SkPathWriter.h"
 
-static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* endIndex) {
+static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
+        SkOpSpanBase** endPtr) {
     while (chase.count()) {
-        SkOpSpan* span;
+        SkOpSpanBase* span;
         chase.pop(&span);
-        const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
-        SkOpSegment* segment = backPtr.fOther;
-        *tIndex = backPtr.fOtherIndex;
-        bool sortable = true;
+        // OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
+        *startPtr = span->ptT()->prev()->span();
+        SkOpSegment* segment = (*startPtr)->segment();
         bool done = true;
-        *endIndex = -1;
-        if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done,
-                &sortable)) {
-            if (last->unorderable()) {
-                continue;
-            }
-            *tIndex = last->start();
-            *endIndex = last->end();
+        *endPtr = NULL;
+        if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
+            *startPtr = last->start();
+            *endPtr = last->end();
    #if TRY_ROTATE
             *chase.insert(0) = span;
    #else
@@ -36,61 +33,44 @@
         if (done) {
             continue;
         }
-        if (!sortable) {
-            continue;
-        }
-        // find first angle, initialize winding to computed fWindSum
-        const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex);
-        if (!angle) {
-            continue;
-        }
-        const SkOpAngle* firstAngle = angle;
-        bool loop = false;
-        int winding = SK_MinS32;
-        do {
-            angle = angle->next();
-            if (angle == firstAngle && loop) {
-                break;    // if we get here, there's no winding, loop is unorderable
-            }
-            loop |= angle == firstAngle;
-            segment = angle->segment();
-            winding = segment->windSum(angle);
-        } while (winding == SK_MinS32);
+        int winding;
+        bool sortable;
+        const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
         if (winding == SK_MinS32) {
             continue;
         }
-        int sumMiWinding = segment->updateWindingReverse(angle);
-        int sumSuWinding = segment->updateOppWindingReverse(angle);
-        if (segment->operand()) {
-            SkTSwap<int>(sumMiWinding, sumSuWinding);
-        }
-        SkOpSegment* first = NULL;
-        bool badData = false;
-        while ((angle = angle->next()) != firstAngle && !badData) {
+        int sumMiWinding, sumSuWinding;
+        if (sortable) {
             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)) {
-                if (!first) {
-                    first = segment;
-                    *tIndex = start;
-                    *endIndex = end;
-                }
-                if (segment->inconsistentAngle(maxWinding, sumWinding, oppMaxWinding,
-                        oppSumWinding, angle)) {
-                    badData = true;
-                    break;
-                }
-                // OPTIMIZATION: should this also add to the chase?
-                (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
-                    oppSumWinding, angle);
+            sumMiWinding = segment->updateWindingReverse(angle);
+            sumSuWinding = segment->updateOppWindingReverse(angle);
+            if (segment->operand()) {
+                SkTSwap<int>(sumMiWinding, sumSuWinding);
             }
         }
-        if (badData) {
-            continue;
+        SkOpSegment* first = NULL;
+        const SkOpAngle* firstAngle = angle;
+        while ((angle = angle->next()) != firstAngle) {
+            segment = angle->segment();
+            SkOpSpanBase* start = angle->start();
+            SkOpSpanBase* end = angle->end();
+            int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
+            if (sortable) {
+                segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
+                        &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
+            }
+            if (!segment->done(angle)) {
+                if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
+                    first = segment;
+                    *startPtr = start;
+                    *endPtr = end;
+                }
+                // OPTIMIZATION: should this also add to the chase?
+                if (sortable) {
+                    (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
+                        oppSumWinding, angle);
+                }
+            }
         }
         if (first) {
        #if TRY_ROTATE
@@ -104,130 +84,76 @@
     return NULL;
 }
 
-/*
-static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
-        bool windingIsOp, PathOp op) {
-    bool active = windingIsActive(winding, spanWinding);
-    if (!active) {
-        return false;
-    }
-    if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
-        switch (op) {
-            case kIntersect_Op:
-            case kUnion_Op:
-                return true;
-            case kDifference_Op: {
-                int absSpan = abs(spanWinding);
-                int absOpp = abs(oppSpanWinding);
-                return windingIsOp ? absSpan < absOpp : absSpan > absOpp;
-            }
-            case kXor_Op:
-                return spanWinding != oppSpanWinding;
-            default:
-                SkASSERT(0);
-        }
-    }
-    bool opActive = oppWinding != 0;
-    return gOpLookup[op][opActive][windingIsOp];
-}
-*/
-
-static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp op,
-        const int xorMask, const int xorOpMask, SkPathWriter* simple) {
-    bool firstContour = true;
+static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
+        const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAlloc* allocator) {
     bool unsortable = false;
-    bool topUnsortable = false;
-    bool firstPass = true;
-    SkPoint lastTopLeft;
-    SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
     do {
-        int index, endIndex;
-        bool topDone;
-        bool onlyVertical = false;
-        lastTopLeft = topLeft;
-        SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySingle, &firstContour,
-                &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
-        if (!current) {
-            if ((!topUnsortable || firstPass) && !topDone) {
-                SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
-                if (lastTopLeft.fX == SK_ScalarMin && lastTopLeft.fY == SK_ScalarMin) {
-                    if (firstPass) {
-                        firstPass = false;
-                    } else {
-                        break;
-                    }
-                }
-                topLeft.fX = topLeft.fY = SK_ScalarMin;
-                continue;
-            }
-            break;
-        } else if (onlyVertical) {
+        SkOpSpan* span = FindSortableTop(contourList);
+        if (!span) {
             break;
         }
-        firstPass = !topUnsortable || lastTopLeft != topLeft;
-        SkTDArray<SkOpSpan*> chase;
+        SkOpSegment* current = span->segment();
+        SkOpSpanBase* start = span->next();
+        SkOpSpanBase* end = span;
+        SkTDArray<SkOpSpanBase*> chase;
         do {
-            if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
+            if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
                 do {
                     if (!unsortable && current->done()) {
                         break;
                     }
                     SkASSERT(unsortable || !current->done());
-                    int nextStart = index;
-                    int nextEnd = endIndex;
+                    SkOpSpanBase* nextStart = start;
+                    SkOpSpanBase* nextEnd = end;
                     SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd,
                             &unsortable, op, xorMask, xorOpMask);
                     if (!next) {
                         if (!unsortable && simple->hasMove()
                                 && current->verb() != SkPath::kLine_Verb
                                 && !simple->isClosed()) {
-                            current->addCurveTo(index, endIndex, simple, true);
+                            current->addCurveTo(start, end, simple, true);
                     #if DEBUG_ACTIVE_SPANS
                             if (!simple->isClosed()) {
                                 DebugShowActiveSpans(contourList);
                             }
                     #endif
-//                            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);
+                    SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
+                            current->debugID(), start->pt().fX, start->pt().fY,
+                            end->pt().fX, end->pt().fY);
         #endif
-                    current->addCurveTo(index, endIndex, simple, true);
+                    current->addCurveTo(start, end, simple, true);
                     current = next;
-                    index = nextStart;
-                    endIndex = nextEnd;
-                } while (!simple->isClosed() && (!unsortable
-                        || !current->done(SkMin32(index, endIndex))));
-                if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
-                    // FIXME : add to simplify, xor cpaths
-                    int min = SkMin32(index, endIndex);
-                    if (!unsortable && !simple->isEmpty()) {
-                        unsortable = current->checkSmall(min);
-                    }
-                    if (!current->done(min)) {
-                        current->addCurveTo(index, endIndex, simple, true);
-                        current->markDoneBinary(min);
+                    start = nextStart;
+                    end = nextEnd;
+                } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
+                if (current->activeWinding(start, end) && !simple->isClosed()) {
+                    SkOpSpan* spanStart = start->starter(end);
+                    if (!spanStart->done()) {
+                        current->addCurveTo(start, end, simple, true);
+                        current->markDone(spanStart);
                     }
                 }
                 simple->close();
             } else {
-                SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
-                if (last && !last->fChased && !last->fLoop) {
-                    last->fChased = true;
+                SkOpSpanBase* last = current->markAndChaseDone(start, end);
+                if (last && !last->chased()) {
+                    last->setChased(true);
                     SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
                     *chase.append() = last;
 #if DEBUG_WINDING
-                    SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
-                            last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
-                            last->fSmall);
+                    SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
+                    if (!last->final()) {
+                         SkDebugf(" windSum=%d", last->upCast()->windSum());
+                    }
+                    SkDebugf("\n");
 #endif
                 }
             }
-            current = findChaseOp(chase, &index, &endIndex);
+            current = findChaseOp(chase, &start, &end);
         #if DEBUG_ACTIVE_SPANS
             DebugShowActiveSpans(contourList);
         #endif
@@ -241,17 +167,17 @@
 
 // pretty picture:
 // https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b-7vD9JP2V-kn9Ss/edit?usp=sharing
-static const SkPathOp gOpInverse[kReverseDifference_PathOp + 1][2][2] = {
+static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = {
 //                  inside minuend                               outside minuend
 //     inside subtrahend     outside subtrahend      inside subtrahend     outside subtrahend
-    {{ kDifference_PathOp,    kIntersect_PathOp }, { kUnion_PathOp, kReverseDifference_PathOp }},
-    {{ kIntersect_PathOp,    kDifference_PathOp }, { kReverseDifference_PathOp, kUnion_PathOp }},
-    {{ kUnion_PathOp, kReverseDifference_PathOp }, { kDifference_PathOp,    kIntersect_PathOp }},
-    {{ kXOR_PathOp,                 kXOR_PathOp }, { kXOR_PathOp,                 kXOR_PathOp }},
-    {{ kReverseDifference_PathOp, kUnion_PathOp }, { kIntersect_PathOp,    kDifference_PathOp }},
+{{ kDifference_SkPathOp,   kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDifference_SkPathOp }},
+{{ kIntersect_SkPathOp,   kDifference_SkPathOp }, { kReverseDifference_SkPathOp, kUnion_SkPathOp }},
+{{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp,   kIntersect_SkPathOp }},
+{{ kXOR_SkPathOp,                 kXOR_SkPathOp }, { kXOR_SkPathOp,                kXOR_SkPathOp }},
+{{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp,   kDifference_SkPathOp }},
 };
 
-static const bool gOutInverse[kReverseDifference_PathOp + 1][2][2] = {
+static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = {
     {{ false, false }, { true, false }},  // diff
     {{ false, false }, { false, true }},  // sect
     {{ false, true }, { true, true }},    // union
@@ -291,16 +217,22 @@
     dump_path(file, two, false, true);
     fprintf(file, "    SkPath path2(path);\n");
     fprintf(file, "    testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
-    fprintf(file, "}\n");	
+    fprintf(file, "}\n");    
     fclose(file);
 }
 #endif
 
-bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
+bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result,
+        bool expectSuccess) {
+    SkChunkAlloc allocator(4096);  // FIXME: add a constant expression here, tune
+    SkOpContour contour;
+    SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
+    SkOpCoincidence coincidence;
+    SkOpGlobalState globalState(&coincidence, contourList);
 #if DEBUGGING_PATHOPS_FROM_HOST
     dump_op(one, two, op);
-#endif	
-#if DEBUG_SHOW_TEST_NAME
+#endif    
+#if 0 && DEBUG_SHOW_TEST_NAME
     char* debugName = DEBUG_FILENAME_STRING;
     if (debugName && debugName[0]) {
         SkPathOpsDebug::BumpTestName(debugName);
@@ -312,62 +244,54 @@
             ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
     const SkPath* minuend = &one;
     const SkPath* subtrahend = &two;
-    if (op == kReverseDifference_PathOp) {
+    if (op == kReverseDifference_SkPathOp) {
         minuend = &two;
         subtrahend = &one;
-        op = kDifference_PathOp;
+        op = kDifference_SkPathOp;
     }
-#if DEBUG_SORT || DEBUG_SWAP_TOP
+#if DEBUG_SORT
     SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
 #endif
     // turn path into list of segments
-    SkTArray<SkOpContour> contours;
-    // FIXME: add self-intersecting cubics' T values to segment
-    SkOpEdgeBuilder builder(*minuend, contours);
+    SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState);
     if (builder.unparseable()) {
         return false;
     }
     const int xorMask = builder.xorMask();
     builder.addOperand(*subtrahend);
-    if (!builder.finish()) {
+    if (!builder.finish(&allocator)) {
         return false;
     }
-    result->reset();
-    result->setFillType(fillType);
+#if DEBUG_DUMP_SEGMENTS
+    contour.dumpSegments(op);
+#endif
+
     const int xorOpMask = builder.xorMask();
-    SkTArray<SkOpContour*, true> contourList;
-    MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask,
-            xorOpMask == kEvenOdd_PathOpsMask);
-    SkOpContour** currentPtr = contourList.begin();
-    if (!currentPtr) {
+    if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask,
+            xorOpMask == kEvenOdd_PathOpsMask)) {
+        result->reset();
+        result->setFillType(fillType);
         return true;
     }
-    SkOpContour** listEnd = contourList.end();
     // find all intersections between segments
+    SkOpContour* current = contourList;
     do {
-        SkOpContour** nextPtr = currentPtr;
-        SkOpContour* current = *currentPtr++;
-        if (current->containsCubics()) {
-            AddSelfIntersectTs(current);
-        }
-        SkOpContour* next;
-        do {
-            next = *nextPtr++;
-        } while (AddIntersectTs(current, next) && nextPtr != listEnd);
-    } while (currentPtr != listEnd);
-    // eat through coincident edges
-
-    int total = 0;
-    int index;
-    for (index = 0; index < contourList.count(); ++index) {
-        total += contourList[index]->segments().count();
-    }
-    if (!HandleCoincidence(&contourList, total)) {
+        SkOpContour* next = current;
+        while (AddIntersectTs(current, next, &coincidence, &allocator)
+                && (next = next->next()))
+            ;
+    } while ((current = current->next()));
+#if DEBUG_VALIDATE
+    globalState.setPhase(SkOpGlobalState::kWalking);
+#endif
+    if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
         return false;
     }
     // construct closed contours
+    result->reset();
+    result->setFillType(fillType);
     SkPathWriter wrapper(*result);
-    bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
+    bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator);
     {  // if some edges could not be resolved, assemble remaining fragments
         SkPath temp;
         temp.setFillType(fillType);
@@ -378,3 +302,7 @@
     }
     return true;
 }
+
+bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
+    return OpDebug(one, two, op, result, true);
+}
diff --git a/src/pathops/SkPathOpsPoint.cpp b/src/pathops/SkPathOpsPoint.cpp
index dc9cde5..e0f175d 100644
--- a/src/pathops/SkPathOpsPoint.cpp
+++ b/src/pathops/SkPathOpsPoint.cpp
@@ -10,8 +10,3 @@
     SkDVector v = {a.fX - b.fX, a.fY - b.fY};
     return v;
 }
-
-SkDPoint operator+(const SkDPoint& a, const SkDVector& b) {
-    SkDPoint v = {a.fX + b.fX, a.fY + b.fY};
-    return v;
-}
diff --git a/src/pathops/SkPathOpsPoint.h b/src/pathops/SkPathOpsPoint.h
index 7ddfbfb..e3f722b 100644
--- a/src/pathops/SkPathOpsPoint.h
+++ b/src/pathops/SkPathOpsPoint.h
@@ -23,23 +23,25 @@
         fY = pt.fY;
     }
 
-    friend SkDPoint operator+(const SkDPoint& a, const SkDVector& b);
-
+    // only used by testing
     void operator+=(const SkDVector& v) {
         fX += v.fX;
         fY += v.fY;
     }
 
+    // only called by nearestT, which is currently only used by testing
     void operator-=(const SkDVector& v) {
         fX -= v.fX;
         fY -= v.fY;
     }
 
+    // only used by testing
     void operator/=(const double s) {
         fX /= s;
         fY /= s;
     }
 
+    // only used by testing
     void operator*=(const double s) {
         fX *= s;
         fY *= s;
@@ -50,6 +52,7 @@
         return v;
     }
 
+    // only used by testing
     double cross(const SkDVector& a) const {
         return fX * a.fY - fY * a.fX;
     }
@@ -98,16 +101,32 @@
         fY = pt.fY;
     }
 
+    // only used by testing
     void operator+=(const SkDVector& v) {
         fX += v.fX;
         fY += v.fY;
     }
 
+    // only used by testing
     void operator-=(const SkDVector& v) {
         fX -= v.fX;
         fY -= v.fY;
     }
 
+    // only used by testing
+    SkDPoint operator+(const SkDVector& v) {
+        SkDPoint result = *this;
+        result += v;
+        return result;
+    }
+
+    // only used by testing
+    SkDPoint operator-(const SkDVector& v) {
+        SkDPoint result = *this;
+        result -= v;
+        return result;
+    }
+
     // note: this can not be implemented with
     // return approximately_equal(a.fY, fY) && approximately_equal(a.fX, fX);
     // because that will not take the magnitude of the values into account
@@ -122,7 +141,7 @@
         double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
         double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
         largest = SkTMax(largest, -tiniest);
-        return AlmostBequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
+        return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
     }
 
     bool approximatelyEqual(const SkPoint& a) const {
@@ -145,44 +164,10 @@
         float tiniest = SkTMin(SkTMin(SkTMin(a.fX, b.fX), a.fY), b.fY);
         float largest = SkTMax(SkTMax(SkTMax(a.fX, b.fX), a.fY), b.fY);
         largest = SkTMax(largest, -tiniest);
-        return AlmostBequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
+        return AlmostPequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
     }
 
-    static bool RoughlyEqual(const SkPoint& a, const SkPoint& b) {
-        if (approximately_equal(a.fX, b.fX) && approximately_equal(a.fY, b.fY)) {
-            return true;
-        }
-        return RoughlyEqualUlps(a.fX, b.fX) && RoughlyEqualUlps(a.fY, b.fY);
-    }
-
-    bool approximatelyPEqual(const SkDPoint& a) const {
-        if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
-            return true;
-        }
-        if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
-            return false;
-        }
-        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
-        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
-        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
-        largest = SkTMax(largest, -tiniest);
-        return AlmostPequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
-    }
-
-    bool approximatelyDEqual(const SkDPoint& a) const {
-        if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
-            return true;
-        }
-        if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
-            return false;
-        }
-        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
-        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
-        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
-        largest = SkTMax(largest, -tiniest);
-        return AlmostDequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
-    }
-
+    // only used by testing
     bool approximatelyZero() const {
         return approximately_zero(fX) && approximately_zero(fY);
     }
@@ -209,7 +194,7 @@
         return result;
     }
 
-    bool moreRoughlyEqual(const SkDPoint& a) const {
+    bool roughlyEqual(const SkDPoint& a) const {
         if (roughly_equal(fX, a.fX) && roughly_equal(fY, a.fY)) {
             return true;
         }
@@ -220,8 +205,18 @@
         return RoughlyEqualUlps(largest, largest + dist); // is the dist within ULPS tolerance?
     }
 
-    bool roughlyEqual(const SkDPoint& a) const {
-        return roughly_equal(a.fY, fY) && roughly_equal(a.fX, fX);
+    static bool RoughlyEqual(const SkPoint& a, const SkPoint& b) {
+        if (!RoughlyEqualUlps(a.fX, b.fX) && !RoughlyEqualUlps(a.fY, b.fY)) {
+            return false;
+        }
+        SkDPoint dA, dB;
+        dA.set(a);
+        dB.set(b);
+        double dist = dA.distance(dB);  // OPTIMIZATION: can we compare against distSq instead ?
+        float tiniest = SkTMin(SkTMin(SkTMin(a.fX, b.fX), a.fY), b.fY);
+        float largest = SkTMax(SkTMax(SkTMax(a.fX, b.fX), a.fY), b.fY);
+        largest = SkTMax(largest, -tiniest);
+        return RoughlyEqualUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
     }
 
     // utilities callable by the user from the debugger when the implementation code is linked in
diff --git a/src/pathops/SkPathOpsPostSect.cpp b/src/pathops/SkPathOpsPostSect.cpp
old mode 100644
new mode 100755
index 15a1900..eb2d1ab
--- a/src/pathops/SkPathOpsPostSect.cpp
+++ b/src/pathops/SkPathOpsPostSect.cpp
@@ -17,8 +17,8 @@
     return segment()->contour();
 }
 
-SkOpDebugState* SkOpPtT::debugState() const {
-    return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL); 
+SkOpGlobalState* SkOpPtT::globalState() const {
+    return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), NULL); 
 }
 
 void SkOpPtT::init(SkOpSpanBase* span, double t, const SkPoint& pt, bool duplicate) {
@@ -28,7 +28,7 @@
     fNext = this;
     fDuplicatePt = duplicate;
     fDeleted = false;
-    PATH_OPS_DEBUG_CODE(fID = ++span->debugState()->fPtTID);
+    PATH_OPS_DEBUG_CODE(fID = ++span->globalState()->fPtTID);
 }
 
 bool SkOpPtT::onEnd() const {
@@ -45,7 +45,7 @@
     do {
         SkOpPtT* next = prev->fNext;
         if (next == this) {
-            prev->removeNext();
+            prev->removeNext(this);
             fDeleted = true;
             return prev;
         }
@@ -55,14 +55,14 @@
     return NULL;
 }
 
-void SkOpPtT::removeNext() {
+void SkOpPtT::removeNext(SkOpPtT* kept) {
     SkASSERT(this->fNext);
     SkOpPtT* next = this->fNext;
     this->fNext = next->fNext;
     SkOpSpanBase* span = next->span();
     next->setDeleted();
     if (span->ptT() == next) {
-        span->upCast()->detach();
+        span->upCast()->detach(kept);
     }
 }
 
@@ -199,7 +199,7 @@
                 // omit aliases that alignment makes redundant
                 if ((!ptT->alias() || test->alias()) && (ptT->onEnd() || !test->onEnd())) {
                     SkASSERT(test->alias());
-                    prev->removeNext();
+                    prev->removeNext(ptT);
                     test = prev;
                 } else {
                     SkASSERT(ptT->alias());
@@ -239,8 +239,8 @@
     return segment()->contour();
 }
 
-SkOpDebugState* SkOpSpanBase::debugState() const {
-    return PATH_OPS_DEBUG_RELEASE(contour()->debugState(), NULL); 
+SkOpGlobalState* SkOpSpanBase::globalState() const {
+    return PATH_OPS_DEBUG_RELEASE(contour()->globalState(), NULL); 
 }
 
 void SkOpSpanBase::initBase(SkOpSegment* segment, SkOpSpan* prev, double t, const SkPoint& pt) {
@@ -252,7 +252,7 @@
     fAligned = true;
     fChased = false;
     PATH_OPS_DEBUG_CODE(fCount = 1);
-    PATH_OPS_DEBUG_CODE(fID = ++debugState()->fSpanID);
+    PATH_OPS_DEBUG_CODE(fID = ++globalState()->fSpanID);
 }
 
 // this pair of spans share a common t value or point; merge them and eliminate duplicates
@@ -261,7 +261,7 @@
     SkOpPtT* spanPtT = span->ptT();
     SkASSERT(this->t() != spanPtT->fT);
     SkASSERT(!zero_or_one(spanPtT->fT));
-    span->detach();
+    span->detach(this->ptT());
     SkOpPtT* remainder = spanPtT->next();
     ptT()->insert(spanPtT);
     while (remainder != spanPtT) {
@@ -304,7 +304,7 @@
     return false;
 }
 
-void SkOpSpan::detach() {
+void SkOpSpan::detach(SkOpPtT* kept) {
     SkASSERT(!final());
     SkOpSpan* prev = this->prev();
     SkASSERT(prev);
@@ -313,6 +313,9 @@
     prev->setNext(next);
     next->setPrev(prev);
     this->segment()->detach(this);
+    if (this->coincident()) {
+        this->globalState()->fCoincidence->fixUp(this->ptT(), kept);
+    }
     this->ptT()->setDeleted();
 }
 
diff --git a/src/pathops/SkPathOpsQuad.cpp b/src/pathops/SkPathOpsQuad.cpp
index c1d068a..717d8bc 100644
--- a/src/pathops/SkPathOpsQuad.cpp
+++ b/src/pathops/SkPathOpsQuad.cpp
@@ -7,63 +7,70 @@
 #include "SkIntersections.h"
 #include "SkLineParameters.h"
 #include "SkPathOpsCubic.h"
+#include "SkPathOpsCurve.h"
 #include "SkPathOpsQuad.h"
-#include "SkPathOpsTriangle.h"
 
-// from http://blog.gludion.com/2009/08/distance-to-quadratic-bezier-curve.html
-// (currently only used by testing)
-double SkDQuad::nearestT(const SkDPoint& pt) const {
-    SkDVector pos = fPts[0] - pt;
-    // search points P of bezier curve with PM.(dP / dt) = 0
-    // a calculus leads to a 3d degree equation :
-    SkDVector A = fPts[1] - fPts[0];
-    SkDVector B = fPts[2] - fPts[1];
-    B -= A;
-    double a = B.dot(B);
-    double b = 3 * A.dot(B);
-    double c = 2 * A.dot(A) + pos.dot(B);
-    double d = pos.dot(A);
-    double ts[3];
-    int roots = SkDCubic::RootsValidT(a, b, c, d, ts);
-    double d0 = pt.distanceSquared(fPts[0]);
-    double d2 = pt.distanceSquared(fPts[2]);
-    double distMin = SkTMin(d0, d2);
-    int bestIndex = -1;
-    for (int index = 0; index < roots; ++index) {
-        SkDPoint onQuad = ptAtT(ts[index]);
-        double dist = pt.distanceSquared(onQuad);
-        if (distMin > dist) {
-            distMin = dist;
-            bestIndex = index;
+/* started with at_most_end_pts_in_common from SkDQuadIntersection.cpp */
+// Do a quick reject by rotating all points relative to a line formed by
+// a pair of one quad's points. If the 2nd quad's points
+// are on the line or on the opposite side from the 1st quad's 'odd man', the
+// curves at most intersect at the endpoints.
+/* if returning true, check contains true if quad's hull collapsed, making the cubic linear
+   if returning false, check contains true if the the quad pair have only the end point in common
+*/
+bool SkDQuad::hullIntersects(const SkDQuad& q2, bool* isLinear) const {
+    bool linear = true;
+    for (int oddMan = 0; oddMan < kPointCount; ++oddMan) {
+        const SkDPoint* endPt[2];
+        this->otherPts(oddMan, endPt);
+        double origX = endPt[0]->fX;
+        double origY = endPt[0]->fY;
+        double adj = endPt[1]->fX - origX;
+        double opp = endPt[1]->fY - origY;
+        double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
+        if (approximately_zero(sign)) {
+            continue;
         }
-    }
-    if (bestIndex >= 0) {
-        return ts[bestIndex];
-    }
-    return d0 < d2 ? 0 : 1;
-}
-
-bool SkDQuad::pointInHull(const SkDPoint& pt) const {
-    return ((const SkDTriangle&) fPts).contains(pt);
-}
-
-SkDPoint SkDQuad::top(double startT, double endT) const {
-    SkDQuad sub = subDivide(startT, endT);
-    SkDPoint topPt = sub[0];
-    if (topPt.fY > sub[2].fY || (topPt.fY == sub[2].fY && topPt.fX > sub[2].fX)) {
-        topPt = sub[2];
-    }
-    if (!between(sub[0].fY, sub[1].fY, sub[2].fY)) {
-        double extremeT;
-        if (FindExtrema(sub[0].fY, sub[1].fY, sub[2].fY, &extremeT)) {
-            extremeT = startT + (endT - startT) * extremeT;
-            SkDPoint test = ptAtT(extremeT);
-            if (topPt.fY > test.fY || (topPt.fY == test.fY && topPt.fX > test.fX)) {
-                topPt = test;
+        linear = false;
+        bool foundOutlier = false;
+        for (int n = 0; n < kPointCount; ++n) {
+            double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
+            if (test * sign > 0 && !precisely_zero(test)) {
+                foundOutlier = true;
+                break;
             }
         }
+        if (!foundOutlier) {
+            return false;
+        }
     }
-    return topPt;
+    *isLinear = linear;
+    return true;
+}
+
+bool SkDQuad::hullIntersects(const SkDConic& conic, bool* isLinear) const {
+    return conic.hullIntersects(*this, isLinear);
+}
+
+bool SkDQuad::hullIntersects(const SkDCubic& cubic, bool* isLinear) const {
+    return cubic.hullIntersects(*this, isLinear);
+}
+
+/* bit twiddling for finding the off curve index (x&~m is the pair in [0,1,2] excluding oddMan)
+oddMan    opp   x=oddMan^opp  x=x-oddMan  m=x>>2   x&~m
+    0       1         1            1         0       1
+            2         2            2         0       2
+    1       1         0           -1        -1       0
+            2         3            2         0       2
+    2       1         3            1         0       1
+            2         0           -2        -1       0
+*/
+void SkDQuad::otherPts(int oddMan, const SkDPoint* endPt[2]) const {
+    for (int opp = 1; opp < kPointCount; ++opp) {
+        int end = (oddMan ^ opp) - oddMan;  // choose a value not equal to oddMan
+        end &= ~(end >> 2);  // if the value went negative, set it to zero
+        endPt[opp - 1] = &fPts[end];
+    }
 }
 
 int SkDQuad::AddValidTs(double s[], int realRoots, double* t) {
@@ -140,19 +147,12 @@
     // FIXME: maybe it's possible to avoid this and compare non-normalized
     lineParameters.normalize();
     double distance = lineParameters.controlPtDistance(*this);
-    return approximately_zero(distance);
-}
-
-SkDCubic SkDQuad::toCubic() const {
-    SkDCubic cubic;
-    cubic[0] = fPts[0];
-    cubic[2] = fPts[1];
-    cubic[3] = fPts[2];
-    cubic[1].fX = (cubic[0].fX + cubic[2].fX * 2) / 3;
-    cubic[1].fY = (cubic[0].fY + cubic[2].fY * 2) / 3;
-    cubic[2].fX = (cubic[3].fX + cubic[2].fX * 2) / 3;
-    cubic[2].fY = (cubic[3].fY + cubic[2].fY * 2) / 3;
-    return cubic;
+    double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
+            fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
+    double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
+            fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY);
+    largest = SkTMax(largest, -tiniest);
+    return approximately_zero_when_compared_to(distance, largest);
 }
 
 SkDVector SkDQuad::dxdyAtT(double t) const {
@@ -181,6 +181,21 @@
     return result;
 }
 
+static double interp_quad_coords(const double* src, double t) {
+    double ab = SkDInterp(src[0], src[2], t);
+    double bc = SkDInterp(src[2], src[4], t);
+    double abc = SkDInterp(ab, bc, t);
+    return abc;
+}
+
+bool SkDQuad::monotonicInX() const {
+    return between(fPts[0].fX, fPts[1].fX, fPts[2].fX);
+}
+
+bool SkDQuad::monotonicInY() const {
+    return between(fPts[0].fY, fPts[1].fY, fPts[2].fY);
+}
+
 /*
 Given a quadratic q, t1, and t2, find a small quadratic segment.
 
@@ -204,17 +219,7 @@
 B   = D*2 - A/2 - C/2
 */
 
-static double interp_quad_coords(const double* src, double t) {
-    double ab = SkDInterp(src[0], src[2], t);
-    double bc = SkDInterp(src[2], src[4], t);
-    double abc = SkDInterp(ab, bc, t);
-    return abc;
-}
-
-bool SkDQuad::monotonicInY() const {
-    return between(fPts[0].fY, fPts[1].fY, fPts[2].fY);
-}
-
+// OPTIMIZE : special case either or both of t1 = 0, t2 = 1 
 SkDQuad SkDQuad::subDivide(double t1, double t2) const {
     SkDQuad dst;
     double ax = dst[0].fX = interp_quad_coords(&fPts[0].fX, t1);
@@ -223,8 +228,8 @@
     double dy = interp_quad_coords(&fPts[0].fY, (t1 + t2) / 2);
     double cx = dst[2].fX = interp_quad_coords(&fPts[0].fX, t2);
     double cy = dst[2].fY = interp_quad_coords(&fPts[0].fY, t2);
-    /* bx = */ dst[1].fX = 2*dx - (ax + cx)/2;
-    /* by = */ dst[1].fY = 2*dy - (ay + cy)/2;
+    /* bx = */ dst[1].fX = 2 * dx - (ax + cx) / 2;
+    /* by = */ dst[1].fY = 2 * dy - (ay + cy) / 2;
     return dst;
 }
 
@@ -240,13 +245,6 @@
 SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const {
     SkASSERT(t1 != t2);
     SkDPoint b;
-#if 0
-    // this approach assumes that the control point computed directly is accurate enough
-    double dx = interp_quad_coords(&fPts[0].fX, (t1 + t2) / 2);
-    double dy = interp_quad_coords(&fPts[0].fY, (t1 + t2) / 2);
-    b.fX = 2 * dx - (a.fX + c.fX) / 2;
-    b.fY = 2 * dy - (a.fY + c.fY) / 2;
-#else
     SkDQuad sub = subDivide(t1, t2);
     SkDLine b0 = {{a, sub[1] + (a - sub[0])}};
     SkDLine b1 = {{c, sub[1] + (c - sub[2])}};
@@ -258,7 +256,6 @@
         SkASSERT(i.used() <= 2);
         b = SkDPoint::Mid(b0[1], b1[1]);
     }
-#endif
     if (t1 == 0 || t2 == 0) {
         align(0, &b);
     }
@@ -319,10 +316,13 @@
     B = 2(b - a)
     Solve for t, only if it fits between 0 < t < 1
 */
-int SkDQuad::FindExtrema(double a, double b, double c, double tValue[1]) {
+int SkDQuad::FindExtrema(const double src[], double tValue[1]) {
     /*  At + B == 0
         t = -B / A
     */
+    double a = src[0];
+    double b = src[2];
+    double c = src[4];
     return valid_unit_divide(a - b, a - b - b + c, tValue);
 }
 
diff --git a/src/pathops/SkPathOpsQuad.h b/src/pathops/SkPathOpsQuad.h
index 932c5fb..01d4d64 100644
--- a/src/pathops/SkPathOpsQuad.h
+++ b/src/pathops/SkPathOpsQuad.h
@@ -10,6 +10,8 @@
 
 #include "SkPathOpsPoint.h"
 
+struct SkOpCurve;
+
 struct SkDQuadPair {
     const SkDQuad& first() const { return (const SkDQuad&) pts[0]; }
     const SkDQuad& second() const { return (const SkDQuad&) pts[2]; }
@@ -17,54 +19,79 @@
 };
 
 struct SkDQuad {
-    SkDPoint fPts[3];
+    static const int kPointCount = 3;
+    static const int kPointLast = kPointCount - 1;
+    static const int kMaxIntersections = 4;
+
+    SkDPoint fPts[kPointCount];
+
+    bool collapsed() const {
+        return fPts[0].approximatelyEqual(fPts[1]) && fPts[0].approximatelyEqual(fPts[2]);
+    }
+
+    bool controlsInside() const {
+        SkDVector v01 = fPts[0] - fPts[1];
+        SkDVector v02 = fPts[0] - fPts[2];
+        SkDVector v12 = fPts[1] - fPts[2];
+        return v02.dot(v01) > 0 && v02.dot(v12) > 0;
+    }
+
+    void debugInit() {
+        sk_bzero(fPts, sizeof(fPts));
+    }
 
     SkDQuad flip() const {
         SkDQuad result = {{fPts[2], fPts[1], fPts[0]}};
         return result;
     }
 
-    void set(const SkPoint pts[3]) {
+    static bool IsCubic() { return false; }
+
+    const SkDQuad& set(const SkPoint pts[kPointCount]) {
         fPts[0] = pts[0];
         fPts[1] = pts[1];
         fPts[2] = pts[2];
+        return *this;
     }
 
-    const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < 3); return fPts[n]; }
-    SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < 3); return fPts[n]; }
+    const SkDPoint& operator[](int n) const { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
+    SkDPoint& operator[](int n) { SkASSERT(n >= 0 && n < kPointCount); return fPts[n]; }
 
     static int AddValidTs(double s[], int realRoots, double* t);
     void align(int endIndex, SkDPoint* dstPt) const;
     SkDQuadPair chopAt(double t) const;
     SkDVector dxdyAtT(double t) const;
-    static int FindExtrema(double a, double b, double c, double tValue[1]);
+    static int FindExtrema(const double src[], double tValue[1]);
+    bool hullIntersects(const SkDQuad& , bool* isLinear) const;
+    bool hullIntersects(const SkDConic& , bool* isLinear) const;
+    bool hullIntersects(const SkDCubic& , bool* isLinear) const;
     bool isLinear(int startIndex, int endIndex) const;
+    bool monotonicInX() const;
     bool monotonicInY() const;
-    double nearestT(const SkDPoint&) const;
-    bool pointInHull(const SkDPoint&) const;
+    void otherPts(int oddMan, const SkDPoint* endPt[2]) const;
     SkDPoint ptAtT(double t) const;
     static int RootsReal(double A, double B, double C, double t[2]);
     static int RootsValidT(const double A, const double B, const double C, double s[2]);
     static void SetABC(const double* quad, double* a, double* b, double* c);
     SkDQuad subDivide(double t1, double t2) const;
-    static SkDQuad SubDivide(const SkPoint a[3], double t1, double t2) {
+    static SkDQuad SubDivide(const SkPoint a[kPointCount], double t1, double t2) {
         SkDQuad quad;
         quad.set(a);
         return quad.subDivide(t1, t2);
     }
     SkDPoint subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const;
-    static SkDPoint SubDivide(const SkPoint pts[3], const SkDPoint& a, const SkDPoint& c,
+    static SkDPoint SubDivide(const SkPoint pts[kPointCount], const SkDPoint& a, const SkDPoint& c,
                               double t1, double t2) {
         SkDQuad quad;
         quad.set(pts);
         return quad.subDivide(a, c, t1, t2);
     }
-    SkDCubic toCubic() const;
-    SkDPoint top(double startT, double endT) const;
 
+    SkDCubic debugToCubic() const;
     // utilities callable by the user from the debugger when the implementation code is linked in
     void dump() const;
-    void dumpComma(const char*) const;
+    void dumpID(int id) const;
+    void dumpInner() const;
 
 private:
 //  static double Tangent(const double* quadratic, double t);  // uncalled
diff --git a/src/pathops/SkPathOpsQuadSect.h b/src/pathops/SkPathOpsQuadSect.h
deleted file mode 100644
index 57f1aa0..0000000
--- a/src/pathops/SkPathOpsQuadSect.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkQuadSpan_DEFINE
-#define SkQuadSpan_DEFINE
-
-#include "SkChunkAlloc.h"
-#include "SkPathOpsRect.h"
-#include "SkPathOpsQuad.h"
-#include "SkTArray.h"
-
-class SkIntersections;
-
-class SkQuadCoincident {
-public:
-    bool isCoincident() const {
-        return fCoincident;
-    }
-
-    void init() {
-        fCoincident = false;
-        SkDEBUGCODE(fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN);
-        SkDEBUGCODE(fPerpT = SK_ScalarNaN);
-    }
-
-    void markCoincident() {
-        if (!fCoincident) {
-            fPerpT = -1;
-        }
-        fCoincident = true;
-    }
-
-    const SkDPoint& perpPt() const {
-        return fPerpPt;
-    }
-
-    double perpT() const {
-        return fPerpT;
-    }
-
-    void setPerp(const SkDQuad& quad1, double t, const SkDPoint& qPt, const SkDQuad& quad2);
-
-private:
-    SkDPoint fPerpPt;
-    double fPerpT;  // perpendicular intersection on opposite quad
-    bool fCoincident;
-};
-
-class SkQuadSect;  // used only by debug id
-
-class SkQuadSpan {
-public:
-    void init(const SkDQuad& quad);
-    void initBounds(const SkDQuad& quad);
-
-    bool contains(double t) const {
-        return !! const_cast<SkQuadSpan*>(this)->innerFind(t);
-    }
-
-    bool contains(const SkQuadSpan* span) const;
-
-    SkQuadSpan* find(double t) {
-        SkQuadSpan* result = innerFind(t);
-        SkASSERT(result);
-        return result;
-    }
-
-    bool intersects(const SkQuadSpan* span) const;
-
-    const SkQuadSpan* next() const {
-        return fNext;
-    }
-
-    void reset() {
-        fBounded.reset();
-    }
-
-    bool split(SkQuadSpan* work) {
-        return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
-    }
-
-    bool splitAt(SkQuadSpan* work, double t);
-    bool tightBoundsIntersects(const SkQuadSpan* span) const;
-
-    // implementation is for testing only
-    void dump() const;
-
-private:
-    bool hullIntersects(const SkDQuad& q2) const;
-    SkQuadSpan* innerFind(double t);
-    bool linearIntersects(const SkDQuad& q2) const;
-
-    // implementation is for testing only
-#if DEBUG_BINARY_QUAD
-    int debugID(const SkQuadSect* ) const { return fDebugID; }
-#else
-    int debugID(const SkQuadSect* ) const;
-#endif
-    void dump(const SkQuadSect* ) const;
-    void dumpID(const SkQuadSect* ) const;
-
-#if DEBUG_BINARY_QUAD
-    void validate() const;
-#endif
-
-    SkDQuad fPart;
-    SkQuadCoincident fCoinStart;
-    SkQuadCoincident fCoinEnd;
-    SkSTArray<4, SkQuadSpan*, true> fBounded;
-    SkQuadSpan* fPrev;
-    SkQuadSpan* fNext;
-    SkDRect fBounds;
-    double fStartT;
-    double fEndT;
-    double fBoundsMax;
-    bool fCollapsed;
-    bool fHasPerp;
-    mutable bool fIsLinear;
-#if DEBUG_BINARY_QUAD
-    int fDebugID;
-    bool fDebugDeleted;
-#endif
-    friend class SkQuadSect;
-};
-
-class SkQuadSect {
-public:
-    SkQuadSect(const SkDQuad& quad PATH_OPS_DEBUG_PARAMS(int id));
-    static void BinarySearch(SkQuadSect* sect1, SkQuadSect* sect2, SkIntersections* intersections);
-
-    // for testing only
-    void dumpQuads() const;
-private:
-    SkQuadSpan* addOne();
-    bool binarySearchCoin(const SkQuadSect& , double tStart, double tStep, double* t, double* oppT);
-    SkQuadSpan* boundsMax() const;
-    void coincidentCheck(SkQuadSect* sect2);
-    bool intersects(const SkQuadSpan* span, const SkQuadSect* opp, const SkQuadSpan* oppSpan) const;
-    void onCurveCheck(SkQuadSect* sect2, SkQuadSpan* first, SkQuadSpan* last);
-    void recoverCollapsed();
-    void removeSpan(SkQuadSpan* span);
-    void removeOne(const SkQuadSpan* test, SkQuadSpan* span);
-    void removeSpans(SkQuadSpan* span, SkQuadSect* opp);
-    void setPerp(const SkDQuad& opp, SkQuadSpan* first, SkQuadSpan* last);
-    const SkQuadSpan* tail() const;
-    void trim(SkQuadSpan* span, SkQuadSect* opp);
-
-    // for testing only
-    void dump() const;
-    void dumpBoth(const SkQuadSect& opp) const;
-    void dumpBoth(const SkQuadSect* opp) const;
-
-#if DEBUG_BINARY_QUAD
-    int debugID() const { return fDebugID; }
-    void validate() const;
-#else
-    int debugID() const { return 0; }
-#endif
-    const SkDQuad& fQuad;
-    SkChunkAlloc fHeap;
-    SkQuadSpan* fHead;
-    SkQuadSpan* fDeleted;
-    int fActiveCount;
-#if DEBUG_BINARY_QUAD
-    int fDebugID;
-    int fDebugCount;
-    int fDebugAllocatedCount;
-#endif
-    friend class SkQuadSpan;  // only used by debug id
-};
-
-#endif
diff --git a/src/pathops/SkPathOpsRect.cpp b/src/pathops/SkPathOpsRect.cpp
index 2ceed32..8c01153 100644
--- a/src/pathops/SkPathOpsRect.cpp
+++ b/src/pathops/SkPathOpsRect.cpp
@@ -4,62 +4,59 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+#include "SkPathOpsConic.h"
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsLine.h"
 #include "SkPathOpsQuad.h"
 #include "SkPathOpsRect.h"
 
-void SkDRect::setBounds(const SkDLine& line) {
-    set(line[0]);
-    add(line[1]);
-}
-
-void SkDRect::setBounds(const SkDQuad& quad) {
-    set(quad[0]);
-    add(quad[2]);
+void SkDRect::setBounds(const SkDQuad& curve, const SkDQuad& sub, double startT, double endT) {
+    set(sub[0]);
+    add(sub[2]);
     double tValues[2];
     int roots = 0;
-    if (!between(quad[0].fX, quad[1].fX, quad[2].fX)) {
-        roots = SkDQuad::FindExtrema(quad[0].fX, quad[1].fX, quad[2].fX, tValues);
+    if (!sub.monotonicInX()) {
+        roots = SkDQuad::FindExtrema(&sub[0].fX, tValues);
     }
-    if (!between(quad[0].fY, quad[1].fY, quad[2].fY)) {
-        roots += SkDQuad::FindExtrema(quad[0].fY, quad[1].fY, quad[2].fY, &tValues[roots]);
+    if (!sub.monotonicInY()) {
+        roots += SkDQuad::FindExtrema(&sub[0].fY, &tValues[roots]);
     }
-    for (int x = 0; x < roots; ++x) {
-        add(quad.ptAtT(tValues[x]));
+    for (int index = 0; index < roots; ++index) {
+        double t = startT + (endT - startT) * tValues[index];
+        add(curve.ptAtT(t));
     }
 }
 
-void SkDRect::setRawBounds(const SkDQuad& quad) {
-    set(quad[0]);
-    for (int x = 1; x < 3; ++x) {
-        add(quad[x]);
+void SkDRect::setBounds(const SkDConic& curve, const SkDConic& sub, double startT, double endT) {
+    set(sub[0]);
+    add(sub[2]);
+    double tValues[2];
+    int roots = 0;
+    if (!sub.monotonicInX()) {
+        roots = SkDConic::FindExtrema(&sub[0].fX, sub.fWeight, tValues);
+    }
+    if (!sub.monotonicInY()) {
+        roots += SkDConic::FindExtrema(&sub[0].fY, sub.fWeight, &tValues[roots]);
+    }
+    for (int index = 0; index < roots; ++index) {
+        double t = startT + (endT - startT) * tValues[index];
+        add(curve.ptAtT(t));
     }
 }
 
-static bool is_bounded_by_end_points(double a, double b, double c, double d) {
-    return between(a, b, d) && between(a, c, d);
-}
-
-void SkDRect::setBounds(const SkDCubic& c) {
-    set(c[0]);
-    add(c[3]);
+void SkDRect::setBounds(const SkDCubic& curve, const SkDCubic& sub, double startT, double endT) {
+    set(sub[0]);
+    add(sub[3]);
     double tValues[4];
     int roots = 0;
-    if (!is_bounded_by_end_points(c[0].fX, c[1].fX, c[2].fX, c[3].fX)) {
-        roots = SkDCubic::FindExtrema(c[0].fX, c[1].fX, c[2].fX, c[3].fX, tValues);
+    if (!sub.monotonicInX()) {
+        roots = SkDCubic::FindExtrema(&sub[0].fX, tValues);
     }
-    if (!is_bounded_by_end_points(c[0].fY, c[1].fY, c[2].fY, c[3].fY)) {
-        roots += SkDCubic::FindExtrema(c[0].fY, c[1].fY, c[2].fY, c[3].fY, &tValues[roots]);
+    if (!sub.monotonicInY()) {
+        roots += SkDCubic::FindExtrema(&sub[0].fY, &tValues[roots]);
     }
-    for (int x = 0; x < roots; ++x) {
-        add(c.ptAtT(tValues[x]));
-    }
-}
-
-void SkDRect::setRawBounds(const SkDCubic& cubic) {
-    set(cubic[0]);
-    for (int x = 1; x < 4; ++x) {
-        add(cubic[x]);
+    for (int index = 0; index < roots; ++index) {
+        double t = startT + (endT - startT) * tValues[index];
+        add(curve.ptAtT(t));
     }
 }
diff --git a/src/pathops/SkPathOpsRect.h b/src/pathops/SkPathOpsRect.h
index 2c47f43..7fec70a 100644
--- a/src/pathops/SkPathOpsRect.h
+++ b/src/pathops/SkPathOpsRect.h
@@ -13,18 +13,10 @@
     double fLeft, fTop, fRight, fBottom;
 
     void add(const SkDPoint& pt) {
-        if (fLeft > pt.fX) {
-            fLeft = pt.fX;
-        }
-        if (fTop > pt.fY) {
-            fTop = pt.fY;
-        }
-        if (fRight < pt.fX) {
-            fRight = pt.fX;
-        }
-        if (fBottom < pt.fY) {
-            fBottom = pt.fY;
-        }
+        fLeft = SkTMin(fLeft, pt.fX);
+        fTop = SkTMin(fTop, pt.fY);
+        fRight = SkTMax(fRight, pt.fX);
+        fBottom = SkTMax(fBottom, pt.fY);
     }
 
     bool contains(const SkDPoint& pt) const {
@@ -32,12 +24,15 @@
                 && approximately_between(fTop, pt.fY, fBottom);
     }
 
-    bool intersects(SkDRect* r) const {
+    bool intersects(const SkDRect& r) const {
+        if (fLeft > fRight) {
+            SkDebugf("!");
+        }
         SkASSERT(fLeft <= fRight);
         SkASSERT(fTop <= fBottom);
-        SkASSERT(r->fLeft <= r->fRight);
-        SkASSERT(r->fTop <= r->fBottom);
-        return r->fLeft <= fRight && fLeft <= r->fRight && r->fTop <= fBottom && fTop <= r->fBottom;
+        SkASSERT(r.fLeft <= r.fRight);
+        SkASSERT(r.fTop <= r.fBottom);
+        return r.fLeft <= fRight && fLeft <= r.fRight && r.fTop <= fBottom && fTop <= r.fBottom;
     }
 
     void set(const SkDPoint& pt) {
@@ -53,11 +48,23 @@
         return fBottom - fTop;
     }
 
-    void setBounds(const SkDLine&);
-    void setBounds(const SkDCubic&);
-    void setBounds(const SkDQuad&);
-    void setRawBounds(const SkDCubic&);
-    void setRawBounds(const SkDQuad&);
+    void setBounds(const SkDConic& curve) {
+        setBounds(curve, curve, 0, 1);
+    }
+
+    void setBounds(const SkDConic& curve, const SkDConic& sub, double tStart, double tEnd);
+
+    void setBounds(const SkDCubic& curve) {
+        setBounds(curve, curve, 0, 1);
+    }
+
+    void setBounds(const SkDCubic& curve, const SkDCubic& sub, double tStart, double tEnd);
+
+    void setBounds(const SkDQuad& curve) {
+        setBounds(curve, curve, 0, 1);
+    }
+
+    void setBounds(const SkDQuad& curve, const SkDQuad& sub, double tStart, double tEnd);
 };
 
 #endif
diff --git a/src/pathops/SkPathOpsSimplify.cpp b/src/pathops/SkPathOpsSimplify.cpp
index 57090ac..d37998b 100644
--- a/src/pathops/SkPathOpsSimplify.cpp
+++ b/src/pathops/SkPathOpsSimplify.cpp
@@ -5,91 +5,81 @@
  * found in the LICENSE file.
  */
 #include "SkAddIntersections.h"
+#include "SkOpCoincidence.h"
 #include "SkOpEdgeBuilder.h"
 #include "SkPathOpsCommon.h"
 #include "SkPathWriter.h"
 
-static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
-    bool firstContour = true;
+static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
+        SkChunkAlloc* allocator) {
     bool unsortable = false;
-    bool topUnsortable = false;
-    bool firstPass = true;
-    SkPoint lastTopLeft;
-    SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
     do {
-        int index, endIndex;
-        bool topDone;
-        bool onlyVertical = false;
-        lastTopLeft = topLeft;
-        SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
-                &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
-        if (!current) {
-            if ((!topUnsortable || firstPass) && !topDone) {
-                SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
-                topLeft.fX = topLeft.fY = SK_ScalarMin;
-                continue;
-            }
-            break;
-        } else if (onlyVertical) {
+        SkOpSpan* span = FindSortableTop(contourList);
+        if (!span) {
             break;
         }
-        firstPass = !topUnsortable || lastTopLeft != topLeft;
-        SkTDArray<SkOpSpan*> chase;
+        SkOpSegment* current = span->segment();
+        SkOpSpanBase* start = span->next();
+        SkOpSpanBase* end = span;
+        SkTDArray<SkOpSpanBase*> chase;
         do {
-            if (current->activeWinding(index, endIndex)) {
+            if (current->activeWinding(start, end)) {
                 do {
                     if (!unsortable && current->done()) {
                           break;
                     }
                     SkASSERT(unsortable || !current->done());
-                    int nextStart = index;
-                    int nextEnd = endIndex;
+                    SkOpSpanBase* nextStart = start;
+                    SkOpSpanBase* nextEnd = end;
                     SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
                             &unsortable);
                     if (!next) {
                         if (!unsortable && simple->hasMove()
                                 && current->verb() != SkPath::kLine_Verb
                                 && !simple->isClosed()) {
-                            current->addCurveTo(index, endIndex, simple, true);
-                            SkASSERT(simple->isClosed());
+                            current->addCurveTo(start, end, simple, true);
+                    #if DEBUG_ACTIVE_SPANS
+                            if (!simple->isClosed()) {
+                                DebugShowActiveSpans(contourList);
+                            }
+                    #endif
                         }
                         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);
+                    current->debugID(), start->pt().fX, start->pt().fY,
+                    end->pt().fX, end->pt().fY);
         #endif
-                    current->addCurveTo(index, endIndex, simple, true);
+                    current->addCurveTo(start, end, simple, true);
                     current = next;
-                    index = nextStart;
-                    endIndex = nextEnd;
-                } while (!simple->isClosed() && (!unsortable
-                        || !current->done(SkMin32(index, endIndex))));
-                if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
-//                    SkASSERT(unsortable || simple->isEmpty());
-                    int min = SkMin32(index, endIndex);
-                    if (!current->done(min)) {
-                        current->addCurveTo(index, endIndex, simple, true);
-                        current->markDoneUnary(min);
+                    start = nextStart;
+                    end = nextEnd;
+                } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
+                if (current->activeWinding(start, end) && !simple->isClosed()) {
+                    SkOpSpan* spanStart = start->starter(end);
+                    if (!spanStart->done()) {
+                        current->addCurveTo(start, end, simple, true);
+                        current->markDone(spanStart);
                     }
                 }
                 simple->close();
             } else {
-                SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
-                if (last && !last->fChased && !last->fLoop) {
-                    last->fChased = true;
+                SkOpSpanBase* last = current->markAndChaseDone(start, end);
+                if (last && !last->chased()) {
+                    last->setChased(true);
                     SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
-                    // assert that last isn't already in array
                     *chase.append() = last;
 #if DEBUG_WINDING
-                    SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
-                            last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
-                            last->fSmall);
+                    SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
+                    if (!last->final()) {
+                         SkDebugf(" windSum=%d", last->upCast()->windSum());
+                    }
+                    SkDebugf("\n");
 #endif
                 }
             }
-            current = FindChase(&chase, &index, &endIndex);
+            current = FindChase(&chase, &start, &end);
         #if DEBUG_ACTIVE_SPANS
             DebugShowActiveSpans(contourList);
         #endif
@@ -102,9 +92,11 @@
 }
 
 // returns true if all edges were processed
-static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
+static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
+        SkChunkAlloc* allocator) {
     SkOpSegment* current;
-    int start, end;
+    SkOpSpanBase* start;
+    SkOpSpanBase* end;
     bool unsortable = false;
     bool closable = true;
     while ((current = FindUndone(contourList, &start, &end))) {
@@ -115,34 +107,38 @@
             }
     #endif
             SkASSERT(unsortable || !current->done());
-            int nextStart = start;
-            int nextEnd = end;
+            SkOpSpanBase* nextStart = start;
+            SkOpSpanBase* nextEnd = end;
             SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
             if (!next) {
                 if (!unsortable && simple->hasMove()
                         && current->verb() != SkPath::kLine_Verb
                         && !simple->isClosed()) {
                     current->addCurveTo(start, end, simple, true);
-                    SkASSERT(simple->isClosed());
+            #if DEBUG_ACTIVE_SPANS
+                    if (!simple->isClosed()) {
+                        DebugShowActiveSpans(contourList);
+                    }
+            #endif
                 }
                 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(start).fX, current->xyAtT(start).fY,
-                    current->xyAtT(end).fX, current->xyAtT(end).fY);
+                    current->debugID(), start->pt().fX, start->pt().fY,
+                    end->pt().fX, end->pt().fY);
         #endif
             current->addCurveTo(start, end, simple, true);
             current = next;
             start = nextStart;
             end = nextEnd;
-        } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
+        } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
         if (!simple->isClosed()) {
             SkASSERT(unsortable);
-            int min = SkMin32(start, end);
-            if (!current->done(min)) {
+            SkOpSpan* spanStart = start->starter(end);
+            if (!spanStart->done()) {
                 current->addCurveTo(start, end, simple, true);
-                current->markDone(min, 1);
+                current->markDone(spanStart);
             }
             closable = false;
         }
@@ -156,52 +152,61 @@
 
 // FIXME : add this as a member of SkPath
 bool Simplify(const SkPath& path, SkPath* result) {
-#if DEBUG_SORT || DEBUG_SWAP_TOP
-    SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
-#endif
+    SkChunkAlloc allocator(4096);  // FIXME: constant-ize, tune
     // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
     SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
             : SkPath::kEvenOdd_FillType;
-
-    // turn path into list of segments
-    SkTArray<SkOpContour> contours;
-    SkOpEdgeBuilder builder(path, contours);
-    if (!builder.finish()) {
-        return false;
-    }
-    SkTArray<SkOpContour*, true> contourList;
-    MakeContourList(contours, contourList, false, false);
-    SkOpContour** currentPtr = contourList.begin();
-    result->reset();
-    result->setFillType(fillType);
-    if (!currentPtr) {
+    if (path.isConvex()) {
+        if (result != &path) {
+            *result = path;
+        }
+        result->setFillType(fillType);
         return true;
     }
-    SkOpContour** listEnd = contourList.end();
+    // turn path into list of segments
+    SkOpCoincidence coincidence;
+    SkOpContour contour;
+    SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
+    SkOpGlobalState globalState(&coincidence, contourList);
+#if DEBUG_SORT
+    SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
+#endif
+    SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
+    if (!builder.finish(&allocator)) {
+        return false;
+    }
+#if DEBUG_DUMP_SEGMENTS
+    contour.dumpSegments((SkPathOp) -1);
+#endif
+    if (!SortContourList(&contourList, false, false)) {
+        result->reset();
+        result->setFillType(fillType);
+        return true;
+    }
     // find all intersections between segments
+    SkOpContour* current = contourList;
     do {
-        SkOpContour** nextPtr = currentPtr;
-        SkOpContour* current = *currentPtr++;
-        if (current->containsCubics()) {
-            AddSelfIntersectTs(current);
-        }
-        SkOpContour* next;
-        do {
-            next = *nextPtr++;
-        } while (AddIntersectTs(current, next) && nextPtr != listEnd);
-    } while (currentPtr != listEnd);
-    if (!HandleCoincidence(&contourList, 0)) {
+        SkOpContour* next = current;
+        while (AddIntersectTs(current, next, &coincidence, &allocator)
+                && (next = next->next()));
+    } while ((current = current->next()));
+#if DEBUG_VALIDATE
+    globalState.setPhase(SkOpGlobalState::kWalking);
+#endif
+    if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
         return false;
     }
     // construct closed contours
-    SkPathWriter simple(*result);
-    if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
-                : !bridgeXor(contourList, &simple))
+    result->reset();
+    result->setFillType(fillType);
+    SkPathWriter wrapper(*result);
+    if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &wrapper, &allocator)
+                : !bridgeXor(contourList, &wrapper, &allocator))
     {  // if some edges could not be resolved, assemble remaining fragments
         SkPath temp;
         temp.setFillType(fillType);
         SkPathWriter assembled(temp);
-        Assemble(simple, &assembled);
+        Assemble(wrapper, &assembled);
         *result = *assembled.nativePath();
         result->setFillType(fillType);
     }
diff --git a/src/pathops/SkPathOpsTCubicSect.cpp b/src/pathops/SkPathOpsTCubicSect.cpp
deleted file mode 100644
index 0b3ddd7..0000000
--- a/src/pathops/SkPathOpsTCubicSect.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkPathOpsTSect.h"
-
-int SkIntersections::intersectB(const SkDCubic& cubic1, const SkDCubic& cubic2) {
-    SkTSect<SkDCubic> sect1(cubic1 PATH_OPS_DEBUG_PARAMS(1));
-    SkTSect<SkDCubic> sect2(cubic2 PATH_OPS_DEBUG_PARAMS(2));
-    SkTSect<SkDCubic>::BinarySearch(&sect1, &sect2, this);
-    return used();
-}
diff --git a/src/pathops/SkPathOpsTQuadSect.cpp b/src/pathops/SkPathOpsTQuadSect.cpp
deleted file mode 100644
index 46ce5cf..0000000
--- a/src/pathops/SkPathOpsTQuadSect.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkPathOpsTSect.h"
-
-int SkIntersections::intersectB(const SkDQuad& quad1, const SkDQuad& quad2) {
-    SkTSect<SkDQuad> sect1(quad1 PATH_OPS_DEBUG_PARAMS(1));
-    SkTSect<SkDQuad> sect2(quad2 PATH_OPS_DEBUG_PARAMS(2));
-    SkTSect<SkDQuad>::BinarySearch(&sect1, &sect2, this);
-    return used();
-}
diff --git a/src/pathops/SkPathOpsTSect.cpp b/src/pathops/SkPathOpsTSect.cpp
new file mode 100644
index 0000000..1549e6b
--- /dev/null
+++ b/src/pathops/SkPathOpsTSect.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPathOpsTSect.h"
+
+int SkIntersections::intersect(const SkDQuad& quad1, const SkDQuad& quad2) {
+    SkTSect<SkDQuad, SkDQuad> sect1(quad1 PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDQuad> sect2(quad2 PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+    SkTSect<SkDQuad, SkDQuad>::BinarySearch(&sect1, &sect2, this);
+    return used();
+}
+
+int SkIntersections::intersect(const SkDConic& conic, const SkDQuad& quad) {
+    SkTSect<SkDConic, SkDQuad> sect1(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> sect2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+    SkTSect<SkDConic, SkDQuad>::BinarySearch(&sect1, &sect2, this);
+    return used();
+}
+
+int SkIntersections::intersect(const SkDConic& conic1, const SkDConic& conic2) {
+    SkTSect<SkDConic, SkDConic> sect1(conic1 PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> sect2(conic2 PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+    SkTSect<SkDConic, SkDConic>::BinarySearch(&sect1, &sect2, this);
+    return used();
+}
+
+int SkIntersections::intersect(const SkDCubic& cubic, const SkDQuad& quad) {
+    SkTSect<SkDCubic, SkDQuad> sect1(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> sect2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+    SkTSect<SkDCubic, SkDQuad>::BinarySearch(&sect1, &sect2, this);
+    return used();
+}
+
+int SkIntersections::intersect(const SkDCubic& cubic, const SkDConic& conic) {
+    SkTSect<SkDCubic, SkDConic> sect1(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> sect2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+    SkTSect<SkDCubic, SkDConic>::BinarySearch(&sect1, &sect2, this);
+    return used();
+}
+
+int SkIntersections::intersect(const SkDCubic& cubic1, const SkDCubic& cubic2) {
+    SkTSect<SkDCubic, SkDCubic> sect1(cubic1 PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> sect2(cubic2 PATH_OPS_DEBUG_T_SECT_PARAMS(2));
+    SkTSect<SkDCubic, SkDCubic>::BinarySearch(&sect1, &sect2, this);
+    return used();
+}
diff --git a/src/pathops/SkPathOpsTSect.h b/src/pathops/SkPathOpsTSect.h
index 4e7d3b17..4cf5978 100644
--- a/src/pathops/SkPathOpsTSect.h
+++ b/src/pathops/SkPathOpsTSect.h
@@ -6,23 +6,29 @@
  */
 
 #include "SkChunkAlloc.h"
+#include "SkPathOpsBounds.h"
 #include "SkPathOpsRect.h"
-#include "SkPathOpsQuad.h"
 #include "SkIntersections.h"
-#include "SkTArray.h"
+#include "SkTSort.h"
 
-/* TCurve is either SkDQuadratic or SkDCubic */
-template<typename TCurve>
+/* TCurve and OppCurve are one of { SkDQuadratic, SkDConic, SkDCubic } */
+template<typename TCurve, typename OppCurve>
 class SkTCoincident {
 public:
+    SkTCoincident() {
+        this->init();
+    }
+
+    void dump() const;
+
     bool isCoincident() const {
         return fCoincident;
     }
 
     void init() {
+        fPerpT = -1;
         fCoincident = false;
-        SkDEBUGCODE(fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN);
-        SkDEBUGCODE(fPerpT = SK_ScalarNaN);
+        fPerpPt.fX = fPerpPt.fY = SK_ScalarNaN;
     }
 
     void markCoincident() {
@@ -40,7 +46,7 @@
         return fPerpT;
     }
 
-    void setPerp(const TCurve& c1, double t, const SkDPoint& cPt, const TCurve& );
+    void setPerp(const TCurve& c1, double t, const SkDPoint& cPt, const OppCurve& );
 
 private:
     SkDPoint fPerpPt;
@@ -48,85 +54,130 @@
     bool fCoincident;
 };
 
-template<typename TCurve> class SkTSect;
+template<typename TCurve, typename OppCurve> class SkTSect;
+template<typename TCurve, typename OppCurve> class SkTSpan;
+
+template<typename TCurve, typename OppCurve>
+struct SkTSpanBounded {
+    SkTSpan<TCurve, OppCurve>* fBounded;
+    SkTSpanBounded* fNext;
+};
 
 /* Curve is either TCurve or SkDCubic */
-template<typename TCurve>
+template<typename TCurve, typename OppCurve>
 class SkTSpan {
 public:
-    void init(const TCurve& );
-    void initBounds(const TCurve& );
-
+    void addBounded(SkTSpan<OppCurve, TCurve>* , SkChunkAlloc* );
     double closestBoundedT(const SkDPoint& pt) const;
+    bool contains(double t) const;
 
-    bool contains(double t) const {
-        return !! const_cast<SkTSpan*>(this)->innerFind(t);
+    void debugInit() {
+        TCurve dummy;
+        dummy.debugInit();
+        init(dummy);
+        initBounds(dummy);
+        fCoinStart.init();
+        fCoinEnd.init();
     }
 
-    bool contains(const SkTSpan* span) const;
+    const SkTSect<OppCurve, TCurve>* debugOpp() const;
+    const SkTSpan* debugSpan(int ) const;
+    const SkTSpan* debugT(double t) const;
+#ifdef SK_DEBUG
+    bool debugIsBefore(const SkTSpan* span) const;
+#endif
+    void dump() const;
+    void dumpBounded(int id) const;
+    void dumpBounds() const;
+    void dumpCoin() const;
 
     double endT() const {
         return fEndT;
     }
 
-    SkTSpan* find(double t) {
-        SkTSpan* result = innerFind(t);
+    SkTSpan<OppCurve, TCurve>* findOppSpan(const SkTSpan<OppCurve, TCurve>* opp) const;
+
+    SkTSpan<OppCurve, TCurve>* findOppT(double t) const {
+        SkTSpan<OppCurve, TCurve>* result = oppT(t);
         SkASSERT(result);
         return result;
     }
 
-    bool intersects(const SkTSpan* span, bool* check);
+    bool hasOppT(double t) const {
+        return SkToBool(oppT(t));
+    }
+
+    int hullsIntersect(SkTSpan<OppCurve, TCurve>* span, bool* start, bool* oppStart);
+    void init(const TCurve& );
+    void initBounds(const TCurve& );
+
+    bool isBounded() const {
+        return fBounded != NULL;
+    }
+
+    bool linearsIntersect(SkTSpan<OppCurve, TCurve>* span);
+    double linearT(const SkDPoint& ) const;
+
+    void markCoincident() {
+        fCoinStart.markCoincident();
+        fCoinEnd.markCoincident();
+    }
 
     const SkTSpan* next() const {
         return fNext;
     }
 
+    bool onlyEndPointsInCommon(const SkTSpan<OppCurve, TCurve>* opp, bool* start,
+            bool* oppStart, bool* ptsInCommon);
+
     const TCurve& part() const {
         return fPart;
     }
 
+    bool removeAllBounded();
+    bool removeBounded(const SkTSpan<OppCurve, TCurve>* opp);
+
     void reset() {
-        fBounded.reset();
+        fBounded = NULL;
     }
 
-    bool split(SkTSpan* work) {
-        return splitAt(work, (work->fStartT + work->fEndT) * 0.5);
+    void resetBounds(const TCurve& curve) {
+        fIsLinear = fIsLine = false;
+        initBounds(curve);
     }
 
-    bool splitAt(SkTSpan* work, double t);
+    bool split(SkTSpan* work, SkChunkAlloc* heap) {
+        return splitAt(work, (work->fStartT + work->fEndT) * 0.5, heap);
+    }
+
+    bool splitAt(SkTSpan* work, double t, SkChunkAlloc* heap);
 
     double startT() const {
         return fStartT;
     }
 
-    bool tightBoundsIntersects(const SkTSpan* span) const;
+private:
 
     // implementation is for testing only
-    void dump() const {
-        dump(NULL);
+    int debugID() const {
+        return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
     }
 
-private:
-    SkTSpan* innerFind(double t);
-    bool linearIntersects(const TCurve& ) const;
+    void dumpID() const;
 
-    // implementation is for testing only
-#if DEBUG_T_SECT
-    int debugID(const SkTSect<TCurve>* ) const { return fDebugID; }
-#else
-    int debugID(const SkTSect<TCurve>* ) const;
-#endif
-    void dump(const SkTSect<TCurve>* ) const;
-    void dumpID(const SkTSect<TCurve>* ) const;
+    int hullCheck(const SkTSpan<OppCurve, TCurve>* opp, bool* start, bool* oppStart);
+    int linearIntersects(const OppCurve& ) const;
+    SkTSpan<OppCurve, TCurve>* oppT(double t) const;
 
-#if DEBUG_T_SECT
     void validate() const;
-#endif
+    void validateBounded() const;
+    void validatePerpT(double oppT) const;
+    void validatePerpPt(double t, const SkDPoint& ) const;
 
     TCurve fPart;
-    SkTCoincident<TCurve> fCoinStart;
-    SkTCoincident<TCurve> fCoinEnd;
-    SkSTArray<4, SkTSpan*, true> fBounded;
+    SkTCoincident<TCurve, OppCurve> fCoinStart;
+    SkTCoincident<TCurve, OppCurve> fCoinEnd;
+    SkTSpanBounded<OppCurve, TCurve>* fBounded;
     SkTSpan* fPrev;
     SkTSpan* fNext;
     SkDRect fBounds;
@@ -136,23 +187,37 @@
     bool fCollapsed;
     bool fHasPerp;
     bool fIsLinear;
-#if DEBUG_T_SECT
-    int fDebugID;
-    bool fDebugDeleted;
-#endif
-    friend class SkTSect<TCurve>;
+    bool fIsLine;
+    bool fDeleted;
+    SkDEBUGCODE_(SkTSect<TCurve, OppCurve>* fDebugSect);
+    PATH_OPS_DEBUG_T_SECT_CODE(int fID);
+    friend class SkTSect<TCurve, OppCurve>;
+    friend class SkTSect<OppCurve, TCurve>;
+    friend class SkTSpan<OppCurve, TCurve>;
 };
 
-template<typename TCurve>
+template<typename TCurve, typename OppCurve>
 class SkTSect {
 public:
-    SkTSect(const TCurve& c  PATH_OPS_DEBUG_PARAMS(int id));
-    static void BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections);
+    SkTSect(const TCurve& c  PATH_OPS_DEBUG_T_SECT_PARAMS(int id));
+    static void BinarySearch(SkTSect* sect1, SkTSect<OppCurve, TCurve>* sect2,
+            SkIntersections* intersections);
 
     // for testing only
+    bool debugHasBounded(const SkTSpan<OppCurve, TCurve>* ) const;
+
+    const SkTSect<OppCurve, TCurve>* debugOpp() const {
+        return SkDEBUGRELEASE(fOppSect, NULL);
+    }
+
+    const SkTSpan<TCurve, OppCurve>* debugSpan(int id) const;
+    const SkTSpan<TCurve, OppCurve>* debugT(double t) const;
     void dump() const;
-    void dumpBoth(const SkTSect& opp) const;
-    void dumpBoth(const SkTSect* opp) const;
+    void dumpBoth(SkTSect<OppCurve, TCurve>* ) const;
+    void dumpBounded(int id) const;
+    void dumpBounds() const;
+    void dumpCoin() const;
+    void dumpCoinCurves() const;
     void dumpCurves() const;
 
 private:
@@ -163,53 +228,99 @@
         kOneS2Set = 8
     };
 
-    SkTSpan<TCurve>* addOne();
-    bool binarySearchCoin(const SkTSect& , double tStart, double tStep, double* t, double* oppT);
-    SkTSpan<TCurve>* boundsMax() const;
-    void coincidentCheck(SkTSect* sect2);
-    static int EndsEqual(const SkTSect* sect1, const SkTSect* sect2, SkIntersections* );
-    bool intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
-            const SkTSpan<TCurve>* oppSpan) const;
-    void onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
-    void recoverCollapsed();
-    void removeSpan(SkTSpan<TCurve>* span);
-    void removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span);
-    void removeSpans(SkTSpan<TCurve>* span, SkTSect* opp);
-    void setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last);
-    const SkTSpan<TCurve>* tail() const;
-    void trim(SkTSpan<TCurve>* span, SkTSect* opp);
+    SkTSpan<TCurve, OppCurve>* addFollowing(SkTSpan<TCurve, OppCurve>* prior);
+    void addForPerp(SkTSpan<OppCurve, TCurve>* span, double t);
+    SkTSpan<TCurve, OppCurve>* addOne();
+    
+    SkTSpan<TCurve, OppCurve>* addSplitAt(SkTSpan<TCurve, OppCurve>* span, double t) {
+        SkTSpan<TCurve, OppCurve>* result = this->addOne();
+        result->splitAt(span, t, &fHeap);
+        result->initBounds(fCurve);
+        span->initBounds(fCurve);
+        return result;
+    }
 
-#if DEBUG_T_SECT
-    int debugID() const { return fDebugID; }
+    bool binarySearchCoin(SkTSect<OppCurve, TCurve>* , double tStart, double tStep, double* t,
+                          double* oppT);
+    SkTSpan<TCurve, OppCurve>* boundsMax() const;
+    void coincidentCheck(SkTSect<OppCurve, TCurve>* sect2);
+    bool coincidentHasT(double t);
+    int collapsed() const;
+    void computePerpendiculars(SkTSect<OppCurve, TCurve>* sect2, SkTSpan<TCurve, OppCurve>* first,
+                               SkTSpan<TCurve, OppCurve>* last);
+    int countConsecutiveSpans(SkTSpan<TCurve, OppCurve>* first,
+                              SkTSpan<TCurve, OppCurve>** last) const;
+
+    int debugID() const {
+        return PATH_OPS_DEBUG_T_SECT_RELEASE(fID, -1);
+    }
+
+    void deleteEmptySpans();
+    void dumpCommon(const SkTSpan<TCurve, OppCurve>* ) const;
+    void dumpCommonCurves(const SkTSpan<TCurve, OppCurve>* ) const;
+    static int EndsEqual(const SkTSect* sect1, const SkTSect<OppCurve, TCurve>* sect2,
+                         SkIntersections* );
+    SkTSpan<TCurve, OppCurve>* extractCoincident(SkTSect<OppCurve, TCurve>* sect2,
+                                                  SkTSpan<TCurve, OppCurve>* first,
+                                                  SkTSpan<TCurve, OppCurve>* last);
+    SkTSpan<TCurve, OppCurve>* findCoincidentRun(SkTSpan<TCurve, OppCurve>* first,
+                                                  SkTSpan<TCurve, OppCurve>** lastPtr);
+    int intersects(SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp,
+                   SkTSpan<OppCurve, TCurve>* oppSpan, int* oppResult);
+    int linesIntersect(SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp,
+                       SkTSpan<OppCurve, TCurve>* oppSpan, SkIntersections* );
+    void markSpanGone(SkTSpan<TCurve, OppCurve>* span);
+    bool matchedDirection(double t, const SkTSect<OppCurve, TCurve>* sect2, double t2) const;
+    void matchedDirCheck(double t, const SkTSect<OppCurve, TCurve>* sect2, double t2,
+                         bool* calcMatched, bool* oppMatched) const;
+    void mergeCoincidence(SkTSect<OppCurve, TCurve>* sect2);
+    SkTSpan<TCurve, OppCurve>* prev(SkTSpan<TCurve, OppCurve>* ) const;
+    void removeByPerpendicular(SkTSect<OppCurve, TCurve>* opp);
+    void recoverCollapsed();
+    void removeCoincident(SkTSpan<TCurve, OppCurve>* span, bool isBetween);
+    void removeAllBut(const SkTSpan<OppCurve, TCurve>* keep, SkTSpan<TCurve, OppCurve>* span,
+                      SkTSect<OppCurve, TCurve>* opp);
+    void removeSpan(SkTSpan<TCurve, OppCurve>* span);
+    void removeSpanRange(SkTSpan<TCurve, OppCurve>* first, SkTSpan<TCurve, OppCurve>* last);
+    void removeSpans(SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp);
+    SkTSpan<TCurve, OppCurve>* spanAtT(double t, SkTSpan<TCurve, OppCurve>** priorSpan);
+    SkTSpan<TCurve, OppCurve>* tail();
+    void trim(SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp);
+    void unlinkSpan(SkTSpan<TCurve, OppCurve>* span);
+    bool updateBounded(SkTSpan<TCurve, OppCurve>* first, SkTSpan<TCurve, OppCurve>* last,
+                       SkTSpan<OppCurve, TCurve>* oppFirst);
     void validate() const;
-#else
-    int debugID() const { return 0; }
-#endif
+    void validateBounded() const;
+
     const TCurve& fCurve;
     SkChunkAlloc fHeap;
-    SkTSpan<TCurve>* fHead;
-    SkTSpan<TCurve>* fDeleted;
+    SkTSpan<TCurve, OppCurve>* fHead;
+    SkTSpan<TCurve, OppCurve>* fCoincident;
+    SkTSpan<TCurve, OppCurve>* fDeleted;
     int fActiveCount;
+    SkDEBUGCODE_(SkTSect<OppCurve, TCurve>* fOppSect);
+    PATH_OPS_DEBUG_T_SECT_CODE(int fID);
+    PATH_OPS_DEBUG_T_SECT_CODE(int fDebugCount);
 #if DEBUG_T_SECT
-    int fDebugID;
-    int fDebugCount;
     int fDebugAllocatedCount;
 #endif
-    friend class SkTSpan<TCurve>;  // only used by debug id
+    friend class SkTSpan<TCurve, OppCurve>;
+    friend class SkTSpan<OppCurve, TCurve>;
+    friend class SkTSect<OppCurve, TCurve>;
 };
 
 #define COINCIDENT_SPAN_COUNT 9
 
-template<typename TCurve>
-void SkTCoincident<TCurve>::setPerp(const TCurve& c1, double t,
-        const SkDPoint& cPt, const TCurve& c2) {
+template<typename TCurve, typename OppCurve>
+void SkTCoincident<TCurve, OppCurve>::setPerp(const TCurve& c1, double t,
+        const SkDPoint& cPt, const OppCurve& c2) {
     SkDVector dxdy = c1.dxdyAtT(t);
     SkDLine perp = {{ cPt, {cPt.fX + dxdy.fY, cPt.fY - dxdy.fX} }};
     SkIntersections i;
     int used = i.intersectRay(c2, perp);
     // only keep closest
-    if (used == 0) {
-        fPerpT = -1;
+    if (used == 0 || used == 3) {
+        this->init();
         return;
     } 
     fPerpT = i[0][0];
@@ -223,6 +334,11 @@
             fPerpPt = i.pt(1);
         }
     }
+#if DEBUG_T_SECT
+    SkDebugf("setPerp t=%1.9g cPt=(%1.9g,%1.9g) %s oppT=%1.9g fPerpPt=(%1.9g,%1.9g)\n",
+            t, cPt.fX, cPt.fY,
+            cPt.approximatelyEqual(fPerpPt) ? "==" : "!=", fPerpT, fPerpPt.fX, fPerpPt.fY);
+#endif
     fCoincident = cPt.approximatelyEqual(fPerpPt);
 #if DEBUG_T_SECT
     if (fCoincident) {
@@ -231,17 +347,188 @@
 #endif
 }
 
-template<typename TCurve>
-void SkTSpan<TCurve>::init(const TCurve& c) {
-    fPrev = fNext = NULL;
-    fIsLinear = false;
-    fStartT = 0;
-    fEndT = 1;
-    initBounds(c);
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::addBounded(SkTSpan<OppCurve, TCurve>* span, SkChunkAlloc* heap) {
+    SkTSpanBounded<OppCurve, TCurve>* bounded = SkNEW_PLACEMENT(heap->allocThrow(
+            sizeof(SkTSpanBounded<OppCurve, TCurve>)), (SkTSpanBounded<OppCurve, TCurve>));
+    bounded->fBounded = span;
+    bounded->fNext = fBounded;
+    fBounded = bounded;
 }
 
-template<typename TCurve>
-void SkTSpan<TCurve>::initBounds(const TCurve& c) {
+template<typename TCurve, typename OppCurve>
+SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::addFollowing(
+        SkTSpan<TCurve, OppCurve>* prior) {
+    SkTSpan<TCurve, OppCurve>* result = this->addOne();
+    result->fStartT = prior ? prior->fEndT : 0;
+    SkTSpan<TCurve, OppCurve>* next = prior ? prior->fNext : fHead;
+    result->fEndT = next ? next->fStartT : 1;
+    result->fPrev = prior;
+    result->fNext = next;
+    if (prior) {
+        prior->fNext = result;
+    } else {
+        fHead = result;
+    }
+    if (next) {
+        next->fPrev = result;
+    }
+    result->resetBounds(fCurve);
+    return result;
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::addForPerp(SkTSpan<OppCurve, TCurve>* span, double t) {
+    if (!span->hasOppT(t)) {
+        SkTSpan<TCurve, OppCurve>* priorSpan;
+        SkTSpan<TCurve, OppCurve>* opp = this->spanAtT(t, &priorSpan);
+        if (!opp) {
+            opp = this->addFollowing(priorSpan);
+#if DEBUG_PERP
+            SkDebugf("%s priorSpan=%d t=%1.9g opp=%d\n", __FUNCTION__, priorSpan->debugID(), t,
+                    opp->debugID());
+#endif
+        }
+#if DEBUG_PERP
+        opp->dump(); SkDebugf("\n");
+        SkDebugf("%s addBounded span=%d opp=%d\n", __FUNCTION__, priorSpan->debugID(),
+                opp->debugID());
+#endif
+        opp->addBounded(span, &fHeap);
+        span->addBounded(opp, &fHeap);
+    }
+    this->validate();
+#if DEBUG_T_SECT
+    span->validatePerpT(t);
+#endif
+}
+
+template<typename TCurve, typename OppCurve>
+double SkTSpan<TCurve, OppCurve>::closestBoundedT(const SkDPoint& pt) const {
+    double result = -1;
+    double closest = FLT_MAX;
+    const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded;
+    while (testBounded) {
+        const SkTSpan<OppCurve, TCurve>* test = testBounded->fBounded;
+        double startDist = test->fPart[0].distanceSquared(pt);
+        if (closest > startDist) {
+            closest = startDist;
+            result = test->fStartT;
+        }
+        double endDist = test->fPart[OppCurve::kPointLast].distanceSquared(pt);
+        if (closest > endDist) {
+            closest = endDist;
+            result = test->fEndT;
+        }
+        testBounded = testBounded->fNext;
+    }
+    SkASSERT(between(0, result, 1));
+    return result;
+}
+
+#ifdef SK_DEBUG
+template<typename TCurve, typename OppCurve>
+bool SkTSpan<TCurve, OppCurve>::debugIsBefore(const SkTSpan* span) const {
+    const SkTSpan* work = this;
+    do {
+        if (span == work) {
+            return true;
+        }
+    } while ((work = work->fNext));
+    return false;
+}
+#endif
+
+template<typename TCurve, typename OppCurve>
+bool SkTSpan<TCurve, OppCurve>::contains(double t) const {
+    const SkTSpan* work = this;
+    do {
+        if (between(work->fStartT, t, work->fEndT)) {
+            return true;
+        }
+    } while ((work = work->fNext));
+    return false;
+}
+
+template<typename TCurve, typename OppCurve>
+const SkTSect<OppCurve, TCurve>* SkTSpan<TCurve, OppCurve>::debugOpp() const {
+    return SkDEBUGRELEASE(fDebugSect->debugOpp(), NULL);
+}
+
+template<typename TCurve, typename OppCurve>
+SkTSpan<OppCurve, TCurve>* SkTSpan<TCurve, OppCurve>::findOppSpan(
+        const SkTSpan<OppCurve, TCurve>* opp) const {
+    SkTSpanBounded<OppCurve, TCurve>* bounded = fBounded;
+    while (bounded) {
+        SkTSpan<OppCurve, TCurve>* test = bounded->fBounded;
+        if (opp == test) {
+            return test;
+        }
+        bounded = bounded->fNext;
+    }
+    return NULL;
+}
+
+// returns 0 if no hull intersection
+//         1 if hulls intersect
+//         2 if hulls only share a common endpoint
+//        -1 if linear and further checking is required
+template<typename TCurve, typename OppCurve>
+int SkTSpan<TCurve, OppCurve>::hullCheck(const SkTSpan<OppCurve, TCurve>* opp,
+        bool* start, bool* oppStart) {
+    if (fIsLinear) {
+        return -1;
+    }
+    bool ptsInCommon;
+    if (onlyEndPointsInCommon(opp, start, oppStart, &ptsInCommon)) {
+        SkASSERT(ptsInCommon);
+        return 2;
+    }
+    bool linear;
+    if (fPart.hullIntersects(opp->fPart, &linear)) {
+        if (!linear) {  // check set true if linear
+            return 1;
+        }
+        fIsLinear = true;
+        fIsLine = fPart.controlsInside();
+        return ptsInCommon ? 2 : -1;
+    } else {  // hull is not linear; check set true if intersected at the end points
+        return ((int) ptsInCommon) << 1;  // 0 or 2
+    }
+    return 0;
+}
+
+// OPTIMIZE ? If at_most_end_pts_in_common detects that one quad is near linear,
+// use line intersection to guess a better split than 0.5
+// OPTIMIZE Once at_most_end_pts_in_common detects linear, mark span so all future splits are linear
+template<typename TCurve, typename OppCurve>
+int SkTSpan<TCurve, OppCurve>::hullsIntersect(SkTSpan<OppCurve, TCurve>* opp,
+        bool* start, bool* oppStart) {
+    if (!fBounds.intersects(opp->fBounds)) {
+        return 0;
+    }
+    int hullSect = this->hullCheck(opp, start, oppStart);
+    if (hullSect >= 0) {
+        return hullSect;
+    }
+    hullSect = opp->hullCheck(this, oppStart, start);
+    if (hullSect >= 0) {
+        return hullSect;
+    }
+    return -1;
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::init(const TCurve& c) {
+    fPrev = fNext = NULL;
+    fStartT = 0;
+    fEndT = 1;
+    fBounded = NULL;
+    resetBounds(c);
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::initBounds(const TCurve& c) {
     fPart = c.subDivide(fStartT, fEndT);
     fBounds.setBounds(fPart);
     fCoinStart.init();
@@ -249,85 +536,38 @@
     fBoundsMax = SkTMax(fBounds.width(), fBounds.height());
     fCollapsed = fPart.collapsed();
     fHasPerp = false;
+    fDeleted = false;
 #if DEBUG_T_SECT
-    fDebugDeleted = false;
     if (fCollapsed) {
         SkDebugf("");  // for convenient breakpoints
     }
 #endif
 }
 
-template<typename TCurve>
-double SkTSpan<TCurve>::closestBoundedT(const SkDPoint& pt) const {
-    int count = fBounded.count();
-    double result = -1;
-    double closest = FLT_MAX;
-    for (int index = 0; index < count; ++index) {
-        const SkTSpan* test = fBounded[index];
-        double startDist = test->fPart[0].distanceSquared(pt);
-        if (closest > startDist) {
-            closest = startDist;
-            result = test->fStartT;
-        }
-        double endDist = test->fPart[TCurve::kPointLast].distanceSquared(pt);
-        if (closest > endDist) {
-            closest = endDist;
-            result = test->fEndT;
-        }
+template<typename TCurve, typename OppCurve>
+bool SkTSpan<TCurve, OppCurve>::linearsIntersect(SkTSpan<OppCurve, TCurve>* span) {
+    int result = this->linearIntersects(span->fPart);
+    if (result <= 1) {
+        return SkToBool(result);
     }
-    SkASSERT(between(0, result, 1));
-    return result;
+    SkASSERT(span->fIsLinear);
+    result = span->linearIntersects(this->fPart);
+//    SkASSERT(result <= 1);
+    return SkToBool(result);
 }
 
-template<typename TCurve>
-bool SkTSpan<TCurve>::contains(const SkTSpan* span) const {
-    int count = fBounded.count();
-    for (int index = 0; index < count; ++index) {
-        const SkTSpan* test = fBounded[index];
-        if (span == test) {
-            return true;
-        }
-    }
-    return false;
+template<typename TCurve, typename OppCurve>
+double SkTSpan<TCurve, OppCurve>::linearT(const SkDPoint& pt) const {
+    SkDVector len = fPart[TCurve::kPointLast] - fPart[0];
+    return fabs(len.fX) > fabs(len.fY)
+            ? (pt.fX - fPart[0].fX) / len.fX
+            : (pt.fY - fPart[0].fY) / len.fY;
 }
 
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSpan<TCurve>::innerFind(double t) {
-    SkTSpan* work = this;
-    do {
-        if (between(work->fStartT, t, work->fEndT)) {
-            return work;
-        }
-    } while ((work = work->fNext));
-    return NULL;
-}
-
-// OPTIMIZE ? If at_most_end_pts_in_common detects that one quad is near linear,
-// use line intersection to guess a better split than 0.5
-// OPTIMIZE Once at_most_end_pts_in_common detects linear, mark span so all future splits are linear
-template<typename TCurve>
-bool SkTSpan<TCurve>::intersects(const SkTSpan* span, bool* check) {
-    if (!fBounds.intersects(span->fBounds)) {
-        *check = false;  // no need to check to see if the bounds have end points in common
-        return false;
-    }
-    if (!fIsLinear && fPart.hullIntersects(span->fPart, check)) {
-        if (!*check) {
-            return true;
-        }
-        fIsLinear = true;
-    }
-    if (fIsLinear) {
-        *check = false;
-        return linearIntersects(span->fPart);
-    }
-    return *check; 
-}
-
-template<typename TCurve>
-bool SkTSpan<TCurve>::linearIntersects(const TCurve& q2) const {
+template<typename TCurve, typename OppCurve>
+int SkTSpan<TCurve, OppCurve>::linearIntersects(const OppCurve& q2) const {
     // looks like q1 is near-linear
-    int start = 0, end = TCurve::kPointCount - 1;  // the outside points are usually the extremes
+    int start = 0, end = TCurve::kPointLast;  // the outside points are usually the extremes
     if (!fPart.controlsInside()) {
         double dist = 0;  // if there's any question, compute distance to find best outsiders
         for (int outer = 0; outer < TCurve::kPointCount - 1; ++outer) {
@@ -347,25 +587,132 @@
     double origY = fPart[start].fY;
     double adj = fPart[end].fX - origX;
     double opp = fPart[end].fY - origY;
-    double sign;
-    for (int n = 0; n < TCurve::kPointCount; ++n) {
+    double maxPart = SkTMax(fabs(adj), fabs(opp));
+    double sign = 0;  // initialization to shut up warning in release build
+    for (int n = 0; n < OppCurve::kPointCount; ++n) {
+        double dx = q2[n].fY - origY;
+        double dy = q2[n].fX - origX;
+        double maxVal = SkTMax(maxPart, SkTMax(fabs(dx), fabs(dy)));
         double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
-        if (precisely_zero(test)) {
-            return true;
+        if (precisely_zero_when_compared_to(test, maxVal)) {
+            return 1;
+        }
+        if (approximately_zero_when_compared_to(test, maxVal)) {
+            return 3;
         }
         if (n == 0) {
             sign = test;
             continue;
         }
         if (test * sign < 0) {
-            return true;
+            return 1;
         }
     }
+    return 0;
+}
+
+template<typename TCurve, typename OppCurve>
+bool SkTSpan<TCurve, OppCurve>::onlyEndPointsInCommon(const SkTSpan<OppCurve, TCurve>* opp,
+        bool* start, bool* oppStart, bool* ptsInCommon) {
+    if (opp->fPart[0] == fPart[0]) {
+        *start = *oppStart = true;
+    } else if (opp->fPart[0] == fPart[TCurve::kPointLast]) {
+        *start = false;
+        *oppStart = true;
+    } else if (opp->fPart[OppCurve::kPointLast] == fPart[0]) {
+        *start = true;
+        *oppStart = false;
+    } else if (opp->fPart[OppCurve::kPointLast] == fPart[TCurve::kPointLast]) {
+        *start = *oppStart = false;
+    } else {
+        *ptsInCommon = false;
+        return false;
+    }
+    *ptsInCommon = true;
+    const SkDPoint* otherPts[TCurve::kPointCount - 1], * oppOtherPts[OppCurve::kPointCount - 1];
+    int baseIndex = *start ? 0 : TCurve::kPointLast;
+    fPart.otherPts(baseIndex, otherPts);
+    opp->fPart.otherPts(*oppStart ? 0 : OppCurve::kPointLast, oppOtherPts);
+    const SkDPoint& base = fPart[baseIndex];
+    for (int o1 = 0; o1 < (int) SK_ARRAY_COUNT(otherPts); ++o1) {
+        SkDVector v1 = *otherPts[o1] - base;
+        for (int o2 = 0; o2 < (int) SK_ARRAY_COUNT(oppOtherPts); ++o2) {
+            SkDVector v2 = *oppOtherPts[o2] - base;
+            if (v2.dot(v1) >= 0) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+template<typename TCurve, typename OppCurve>
+SkTSpan<OppCurve, TCurve>* SkTSpan<TCurve, OppCurve>::oppT(double t) const {
+    SkTSpanBounded<OppCurve, TCurve>* bounded = fBounded;
+    while (bounded) {
+        SkTSpan<OppCurve, TCurve>* test = bounded->fBounded;
+        if (between(test->fStartT, t, test->fEndT)) {
+            return test;
+        }
+        bounded = bounded->fNext;
+    }
+    return NULL;
+}
+
+template<typename TCurve, typename OppCurve>
+bool SkTSpan<TCurve, OppCurve>::removeAllBounded() {
+    bool deleteSpan = false;
+    SkTSpanBounded<OppCurve, TCurve>* bounded = fBounded;
+    while (bounded) {
+        SkTSpan<OppCurve, TCurve>* opp = bounded->fBounded;
+        deleteSpan |= opp->removeBounded(this);
+        bounded = bounded->fNext;
+    }
+    return deleteSpan;
+}
+
+template<typename TCurve, typename OppCurve>
+bool SkTSpan<TCurve, OppCurve>::removeBounded(const SkTSpan<OppCurve, TCurve>* opp) {
+    if (fHasPerp) {
+        bool foundStart = false;
+        bool foundEnd = false;
+        SkTSpanBounded<OppCurve, TCurve>* bounded = fBounded;
+        while (bounded) {
+            SkTSpan<OppCurve, TCurve>* test = bounded->fBounded;
+            if (opp != test) {
+                foundStart |= between(test->fStartT, fCoinStart.perpT(), test->fEndT);
+                foundEnd |= between(test->fStartT, fCoinEnd.perpT(), test->fEndT);
+            }
+            bounded = bounded->fNext;
+        }
+        if (!foundStart || !foundEnd) {
+            fHasPerp = false;
+            fCoinStart.init();
+            fCoinEnd.init();
+        }
+    }
+    SkTSpanBounded<OppCurve, TCurve>* bounded = fBounded;
+    SkTSpanBounded<OppCurve, TCurve>* prev = NULL;
+    while (bounded) {
+        SkTSpanBounded<OppCurve, TCurve>* boundedNext = bounded->fNext;
+        if (opp == bounded->fBounded) {
+            if (prev) {
+                prev->fNext = boundedNext;
+                return false;
+            } else {
+                fBounded = boundedNext;
+                return fBounded == NULL;
+            }
+        }
+        prev = bounded;
+        bounded = boundedNext;
+    }
+    SkASSERT(0);
     return false;
 }
 
-template<typename TCurve>
-bool SkTSpan<TCurve>::splitAt(SkTSpan* work, double t) {
+template<typename TCurve, typename OppCurve>
+bool SkTSpan<TCurve, OppCurve>::splitAt(SkTSpan* work, double t, SkChunkAlloc* heap) {
     fStartT = t;
     fEndT = work->fEndT;
     if (fStartT == fEndT) {
@@ -380,150 +727,134 @@
     fPrev = work;
     fNext = work->fNext;
     fIsLinear = work->fIsLinear;
+    fIsLine = work->fIsLine;
+
     work->fNext = this;
     if (fNext) {
         fNext->fPrev = this;
     }
-    fBounded = work->fBounded;
-    int count = fBounded.count();
-    for (int index = 0; index < count; ++index) {
-        fBounded[index]->fBounded.push_back() = this;
+    SkTSpanBounded<OppCurve, TCurve>* bounded = work->fBounded;
+    fBounded = NULL;
+    while (bounded) {
+        this->addBounded(bounded->fBounded, heap);
+        bounded = bounded->fNext;
+    }
+    bounded = fBounded;
+    while (bounded) {
+        bounded->fBounded->addBounded(this, heap);
+        bounded = bounded->fNext;
     }
     return true;
 }
 
-template<typename TCurve>
-bool SkTSpan<TCurve>::tightBoundsIntersects(const SkTSpan* span) const {
-    // skew all to an axis
-    SkDVector v2_0 = fPart[TCurve::kPointLast] - fPart[0];
-    bool skewToXAxis = fabs(v2_0.fX) > fabs(v2_0.fY);
-    double ratio = skewToXAxis ? v2_0.fY / v2_0.fX : v2_0.fX / v2_0.fY;
-    TCurve r1 = fPart;
-    if (skewToXAxis) {
-        r1[1].fY -= (fPart[1].fX - r1[0].fX) * ratio;
-        if (TCurve::IsCubic()) {
-            r1[2].fY -= (fPart[2].fX - r1[0].fX) * ratio;
-            r1[3].fY = r1[0].fY;
-        } else {
-            r1[2].fY = r1[0].fY;
-        }
-    } else {
-        r1[1].fX -= (fPart[1].fY - r1[0].fY) * ratio;
-        if (TCurve::IsCubic()) {
-            r1[2].fX -= (fPart[2].fY - r1[0].fY) * ratio;
-            r1[3].fX = r1[0].fX;
-        } else {
-            r1[2].fX = r1[0].fX;
-        }
-    }
-    // compute the tight skewed bounds
-    SkDRect bounds;
-    bounds.setBounds(r1);
-    // see if opposite ends are within range of tight skewed bounds
-    TCurve r2 = span->fPart;
-    for (int i = 0; i < TCurve::kPointCount; i += 2) {
-        if (skewToXAxis) {
-            r2[i].fY -= (r2[i].fX - r1[0].fX) * ratio;
-            if (between(bounds.fTop, r2[i].fY, bounds.fBottom)) {
-                return true;
-            }
-        } else {
-            r2[i].fX -= (r2[i].fY - r1[0].fY) * ratio;
-            if (between(bounds.fLeft, r2[i].fX, bounds.fRight)) {
-                return true;
-            }
-        }
-    }
-    // see if opposite ends are on either side of tight skewed bounds
-    if ((skewToXAxis ? (r2[0].fY - r1[0].fY) * (r2[TCurve::kPointLast].fY - r1[0].fY)
-                        : (r2[0].fX - r1[0].fX) * (r2[TCurve::kPointLast].fX - r1[0].fX)) < 0) {
-        return true;
-    }
-    // compute opposite tight skewed bounds
-    if (skewToXAxis) {
-        r2[1].fY -= (r2[1].fX - r1[0].fX) * ratio;
-        if (TCurve::IsCubic()) {
-            r2[2].fY -= (r2[2].fX - r1[0].fX) * ratio;
-        }
-    } else {
-        r2[1].fX -= (r2[1].fY - r1[0].fY) * ratio;
-        if (TCurve::IsCubic()) {
-            r2[2].fX -= (r2[2].fY - r1[0].fY) * ratio;
-        }
-    }
-    SkDRect sBounds;
-    sBounds.setBounds(r2);
-    // see if tight bounds overlap
-    if (skewToXAxis) {
-        return bounds.fTop <= sBounds.fBottom && sBounds.fTop <= bounds.fBottom;  
-    } else {
-        return bounds.fLeft <= sBounds.fRight && sBounds.fLeft <= bounds.fRight;  
-    }
-}
-
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::validate() const {
 #if DEBUG_T_SECT
-template<typename TCurve>
-void SkTSpan<TCurve>::validate() const {
     SkASSERT(fNext == NULL || fNext != fPrev);
     SkASSERT(fNext == NULL || this == fNext->fPrev);
-    SkASSERT(fBounds.width() || fBounds.height());
+    SkASSERT(fPrev == NULL || this == fPrev->fNext);
+    SkASSERT(fBounds.width() || fBounds.height() || fCollapsed);
     SkASSERT(fBoundsMax == SkTMax(fBounds.width(), fBounds.height()));
     SkASSERT(0 <= fStartT);
     SkASSERT(fEndT <= 1);
-    SkASSERT(fStartT < fEndT);
-    SkASSERT(fBounded.count() > 0);
-    for (int index = 0; index < fBounded.count(); ++index) {
-        const SkTSpan* overlap = fBounded[index];
-        SkASSERT(((fDebugID ^ overlap->fDebugID) & 1) == 1);
-        SkASSERT(overlap->contains(this));
+    SkASSERT(fStartT <= fEndT);
+    SkASSERT(fBounded);
+    this->validateBounded();
+    if (fHasPerp) {
+        if (fCoinStart.isCoincident()) {
+            validatePerpT(fCoinStart.perpT());
+            validatePerpPt(fCoinStart.perpT(), fCoinStart.perpPt());
+        }
+        if (fCoinEnd.isCoincident()) {
+            validatePerpT(fCoinEnd.perpT());
+            validatePerpPt(fCoinEnd.perpT(), fCoinEnd.perpPt());
+        }
     }
-}
 #endif
+}
 
-template<typename TCurve>
-SkTSect<TCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_PARAMS(int id))
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::validateBounded() const {
+#if DEBUG_VALIDATE
+    const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded;
+    while (testBounded) {
+        SkDEBUGCODE_(const SkTSpan<OppCurve, TCurve>* overlap = testBounded->fBounded);
+        SkASSERT(!overlap->fDeleted);
+        SkASSERT(((this->debugID() ^ overlap->debugID()) & 1) == 1);
+        SkASSERT(overlap->findOppSpan(this));
+        testBounded = testBounded->fNext;
+    }
+#endif
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::validatePerpT(double oppT) const {
+    const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded;
+    while (testBounded) {
+        const SkTSpan<OppCurve, TCurve>* overlap = testBounded->fBounded;
+        if (between(overlap->fStartT, oppT, overlap->fEndT)) {
+            return;
+        }
+        testBounded = testBounded->fNext;
+    }
+    SkASSERT(0);
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::validatePerpPt(double t, const SkDPoint& pt) const {
+    SkASSERT(fDebugSect->fOppSect->fCurve.ptAtT(t) == pt);
+}
+
+
+template<typename TCurve, typename OppCurve>
+SkTSect<TCurve, OppCurve>::SkTSect(const TCurve& c PATH_OPS_DEBUG_T_SECT_PARAMS(int id))
     : fCurve(c)
-    , fHeap(sizeof(SkTSpan<TCurve>) * 4)
+    , fHeap(sizeof(SkTSpan<TCurve, OppCurve>) * 4)
+    , fCoincident(NULL)
     , fDeleted(NULL)
     , fActiveCount(0)
-    PATH_OPS_DEBUG_PARAMS(fDebugID(id))
-    PATH_OPS_DEBUG_PARAMS(fDebugCount(0))
-    PATH_OPS_DEBUG_PARAMS(fDebugAllocatedCount(0))
+    PATH_OPS_DEBUG_T_SECT_PARAMS(fID(id))
+    PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugCount(0))
+    PATH_OPS_DEBUG_T_SECT_PARAMS(fDebugAllocatedCount(0))
 {
     fHead = addOne();
     fHead->init(c);
 }
 
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::addOne() {
-    SkTSpan<TCurve>* result;
+template<typename TCurve, typename OppCurve>
+SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::addOne() {
+    SkTSpan<TCurve, OppCurve>* result;
     if (fDeleted) {
         result = fDeleted;
         result->reset();
         fDeleted = result->fNext;
     } else {
-        result = SkNEW_PLACEMENT(fHeap.allocThrow(sizeof(SkTSpan<TCurve>)), SkTSpan<TCurve>);
+        result = SkNEW_PLACEMENT(fHeap.allocThrow(sizeof(SkTSpan<TCurve, OppCurve>)),
+                (SkTSpan<TCurve, OppCurve>));
+        result->fBounded = NULL;
 #if DEBUG_T_SECT
         ++fDebugAllocatedCount;
 #endif
     }
+    result->fHasPerp = false;
+    result->fDeleted = false;
     ++fActiveCount; 
-#if DEBUG_T_SECT
-    result->fDebugID = fDebugCount++ * 2 + fDebugID;
-#endif
+    PATH_OPS_DEBUG_T_SECT_CODE(result->fID = fDebugCount++ * 2 + fID);
+    SkDEBUGCODE(result->fDebugSect = this);
     return result;
 }
 
-template<typename TCurve>
-bool SkTSect<TCurve>::binarySearchCoin(const SkTSect& sect2, double tStart, double tStep,
-        double* resultT, double* oppT) {
-    SkTSpan<TCurve> work;
+template<typename TCurve, typename OppCurve>
+bool SkTSect<TCurve, OppCurve>::binarySearchCoin(SkTSect<OppCurve, TCurve>* sect2, double tStart,
+        double tStep, double* resultT, double* oppT) {
+    SkTSpan<TCurve, OppCurve> work;
     double result = work.fStartT = work.fEndT = tStart;
+    SkDEBUGCODE(work.fDebugSect = this);
     SkDPoint last = fCurve.ptAtT(tStart);
     SkDPoint oppPt;
     bool flip = false;
     SkDEBUGCODE(bool down = tStep < 0);
-    const TCurve& opp = sect2.fCurve;
+    const OppCurve& opp = sect2->fCurve;
     do {
         tStep *= 0.5;
         work.fStartT += tStep;
@@ -541,8 +872,11 @@
         last = work.fPart[0];
         work.fCoinStart.setPerp(fCurve, work.fStartT, last, opp);
         if (work.fCoinStart.isCoincident()) {
+#if DEBUG_T_SECT
+            work.validatePerpPt(work.fCoinStart.perpT(), work.fCoinStart.perpPt());
+#endif
             double oppTTest = work.fCoinStart.perpT();
-            if (sect2.fHead->contains(oppTTest)) {
+            if (sect2->fHead->contains(oppTTest)) {
                 *oppT = oppTTest;
                 oppPt = work.fCoinStart.perpPt();
                 SkASSERT(down ? result > work.fStartT : result < work.fStartT);
@@ -560,7 +894,7 @@
     }
     if (oppPt.approximatelyEqual(opp[0])) {
         *oppT = 0;
-    } else if (oppPt.approximatelyEqual(opp[TCurve::kPointLast])) {
+    } else if (oppPt.approximatelyEqual(opp[OppCurve::kPointLast])) {
         *oppT = 1;
     }
     *resultT = result;
@@ -570,207 +904,577 @@
 // OPTIMIZE ? keep a sorted list of sizes in the form of a doubly-linked list in quad span
 //            so that each quad sect has a pointer to the largest, and can update it as spans
 //            are split
-template<typename TCurve>
-SkTSpan<TCurve>* SkTSect<TCurve>::boundsMax() const {
-    SkTSpan<TCurve>* test = fHead;
-    SkTSpan<TCurve>* largest = fHead;
-    bool largestCoin = largest->fCoinStart.isCoincident() && largest->fCoinEnd.isCoincident();
+template<typename TCurve, typename OppCurve>
+SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::boundsMax() const {
+    SkTSpan<TCurve, OppCurve>* test = fHead;
+    SkTSpan<TCurve, OppCurve>* largest = fHead;
+    bool lCollapsed = largest->fCollapsed;
     while ((test = test->fNext)) {
-        bool testCoin = test->fCoinStart.isCoincident() || test->fCoinEnd.isCoincident();
-        if ((largestCoin && !testCoin) || (largestCoin == testCoin
-                && (largest->fBoundsMax < test->fBoundsMax
-                || (largest->fCollapsed && !test->fCollapsed)))) {
+        bool tCollapsed = test->fCollapsed;
+        if ((lCollapsed && !tCollapsed) || (lCollapsed == tCollapsed &&
+                largest->fBoundsMax < test->fBoundsMax)) {
             largest = test;
-            largestCoin = testCoin;
+            lCollapsed = test->fCollapsed;
         }
     }
-    return largestCoin ? NULL : largest;
+    return largest;
 }
 
-template<typename TCurve>
-void SkTSect<TCurve>::coincidentCheck(SkTSect* sect2) {
-    SkTSpan<TCurve>* first = fHead;
-    SkTSpan<TCurve>* next;
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::coincidentCheck(SkTSect<OppCurve, TCurve>* sect2) {
+    SkTSpan<TCurve, OppCurve>* first = fHead;
+    SkTSpan<TCurve, OppCurve>* last, * next;
     do {
-        int consecutive = 1;
-        SkTSpan<TCurve>* last = first;
-        do {
-            next = last->fNext;
-            if (!next) {
-                break;
-            }
-            if (next->fStartT > last->fEndT) {
-                break;
-            }
-            ++consecutive;
-            last = next;
-        } while (true);
+        int consecutive = this->countConsecutiveSpans(first, &last);
+        next = last->fNext;
         if (consecutive < COINCIDENT_SPAN_COUNT) {
             continue;
         }
-        setPerp(sect2->fCurve, first, last);
+        this->validate();
+        sect2->validate();
+        this->computePerpendiculars(sect2, first, last);
+        this->validate();
+        sect2->validate();
         // check to see if a range of points are on the curve
-        onCurveCheck(sect2, first, last);
-        SkTSpan<TCurve>* removalCandidate = NULL;
-        if (!first->fCoinStart.isCoincident()) {
-            SkTSpan<TCurve>* firstCoin = first->fNext;
-            removalCandidate = first;
-            first = firstCoin;
-        }
-        if (!first->fCoinStart.isCoincident()) {
-            continue;
-        }
-        if (removalCandidate) {
-            removeSpans(removalCandidate, sect2);
-        }
-        if (!last->fCoinStart.isCoincident()) {
-            continue;
-        }
-        if (!last->fCoinEnd.isCoincident()) {
-            if (--consecutive < COINCIDENT_SPAN_COUNT) {
-                continue;
-            }
-            last = last->fPrev;
-            SkASSERT(last->fCoinStart.isCoincident());
-            SkASSERT(last->fCoinEnd.isCoincident());
-        }
-        SkASSERT(between(0, first->fCoinStart.perpT(), 1) || first->fCoinStart.perpT() == -1);
-        if (first->fCoinStart.perpT() < 0) {
-            first->fCoinStart.setPerp(fCurve, first->fStartT, first->fPart[0], sect2->fCurve);
-        }
-        SkASSERT(between(0, last->fCoinEnd.perpT(), 1) || last->fCoinEnd.perpT() == -1);
-        if (last->fCoinEnd.perpT() < 0) {
-            last->fCoinEnd.setPerp(fCurve, last->fEndT, last->fPart[TCurve::kPointLast],
-                    sect2->fCurve);
-        }
-        SkTSpan<TCurve>* removeMe = first->fNext;
-        while (removeMe != last) {
-            SkTSpan<TCurve>* removeNext = removeMe->fNext;
-            removeSpans(removeMe, sect2);
-            removeMe = removeNext;
-        }
+        SkTSpan<TCurve, OppCurve>* coinStart = first;
+        do {
+            coinStart = this->extractCoincident(sect2, coinStart, last);
+        } while (coinStart && !last->fDeleted);
     } while ((first = next));
 }
 
-template<typename TCurve>
-bool SkTSect<TCurve>::intersects(SkTSpan<TCurve>* span, const SkTSect* opp,
-        const SkTSpan<TCurve>* oppSpan) const {
-    bool check;  // we ignore whether the end points are in common or not
-    if (!span->intersects(oppSpan, &check)) {
-        return false;
+template<typename TCurve, typename OppCurve>
+bool SkTSect<TCurve, OppCurve>::coincidentHasT(double t) {
+    SkTSpan<TCurve, OppCurve>* test = fCoincident;
+    while (test) {
+        if (between(test->fStartT, t, test->fEndT)) {
+            return true;
+        }
+        test = test->fNext;
     }
-    if (fActiveCount < COINCIDENT_SPAN_COUNT || opp->fActiveCount < COINCIDENT_SPAN_COUNT) {
-        return true;
-    }
-    return span->tightBoundsIntersects(oppSpan);
+    return false;
 }
 
-template<typename TCurve>
-void SkTSect<TCurve>::onCurveCheck(SkTSect* sect2, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
-    SkTSpan<TCurve>* work = first;
-    first = NULL;
+template<typename TCurve, typename OppCurve>
+int SkTSect<TCurve, OppCurve>::collapsed() const {
+    int result = 0;
+    const SkTSpan<TCurve, OppCurve>* test = fHead;
+    while (test) {
+        if (test->fCollapsed) {
+            ++result;
+        }
+        test = test->next();
+    }
+    return result;
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::computePerpendiculars(SkTSect<OppCurve, TCurve>* sect2,
+        SkTSpan<TCurve, OppCurve>* first, SkTSpan<TCurve, OppCurve>* last) {
+    const OppCurve& opp = sect2->fCurve;
+    SkTSpan<TCurve, OppCurve>* work = first;
+    SkTSpan<TCurve, OppCurve>* prior = NULL;
     do {
-        if (work->fCoinStart.isCoincident()) {
-            if (!first) {
-                first = work;
+        if (!work->fHasPerp && !work->fCollapsed) {
+            if (prior) {
+                work->fCoinStart = prior->fCoinEnd;
+            } else {
+                work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp);
             }
-        } else if (first) {
-            break;
+            if (work->fCoinStart.isCoincident()) {
+                double perpT = work->fCoinStart.perpT();
+                if (sect2->coincidentHasT(perpT)) {
+                    work->fCoinStart.init();
+                } else {
+                    sect2->addForPerp(work, perpT);
+                }
+            }
+            work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp);
+            if (work->fCoinEnd.isCoincident()) {
+                double perpT = work->fCoinEnd.perpT();
+                if (sect2->coincidentHasT(perpT)) {
+                    work->fCoinEnd.init();
+                } else {
+                    sect2->addForPerp(work, perpT);
+                }
+            }
+            work->fHasPerp = true;
         }
         if (work == last) {
             break;
         }
+        prior = work;
         work = work->fNext;
         SkASSERT(work);
     } while (true);
-    if (!first) {
-        return;
-    }
-    // march outwards to find limit of coincidence from here to previous and next spans
-    double startT = first->fStartT;
-    double oppT;
-    SkTSpan<TCurve>* prev = first->fPrev;
-    if (prev) {
-        double coinStart;
-        if (binarySearchCoin(*sect2, startT, prev->fStartT - startT, &coinStart, &oppT)) {
-            if (coinStart < startT) {
-                SkASSERT(prev->fStartT < coinStart && coinStart < prev->fEndT);
-                SkTSpan<TCurve>* oppStart = sect2->fHead->find(oppT);
-                if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) {
-                    // split prev at coinStart if needed
-                    SkTSpan<TCurve>* half2 = addOne();
-                    half2->splitAt(prev, coinStart);
-                    half2->initBounds(fCurve);
-                    prev->initBounds(fCurve);
-                    prev->fCoinEnd.markCoincident();
-                    half2->fCoinStart.markCoincident();
-                    half2->fCoinEnd.markCoincident();
-                    // find span containing opposite t, and split that too
-                    SkTSpan<TCurve>* oppHalf = sect2->addOne();
-                    oppHalf->splitAt(oppStart, oppT);
-                    oppHalf->initBounds(sect2->fCurve);
-                    oppStart->initBounds(sect2->fCurve);
-                } else {
-                    SkASSERT(oppStart->fStartT == oppT || oppT == oppStart->fEndT);
-                    first->fStartT = coinStart;
-                    prev->fEndT = coinStart;
-                    first->initBounds(fCurve);
-                    prev->initBounds(fCurve);
-                    first->fCoinStart.markCoincident();
-                    first->fCoinEnd.markCoincident();
-                }
-            }
+}
+
+template<typename TCurve, typename OppCurve>
+int SkTSect<TCurve, OppCurve>::countConsecutiveSpans(SkTSpan<TCurve, OppCurve>* first,
+        SkTSpan<TCurve, OppCurve>** lastPtr) const {
+    int consecutive = 1;
+    SkTSpan<TCurve, OppCurve>* last = first;
+    do {
+        SkTSpan<TCurve, OppCurve>* next = last->fNext;
+        if (!next) {
+            break;
         }
-    }
-    if (!work->fCoinEnd.isCoincident()) {
-        if (work->fEndT == 1) {
-            SkDebugf("!");
+        if (next->fStartT > last->fEndT) {
+            break;
         }
-//        SkASSERT(work->fEndT < 1);
-        startT = work->fStartT;
-        double coinEnd;
-        if (binarySearchCoin(*sect2, startT, work->fEndT - startT, &coinEnd, &oppT)) {
-            if (coinEnd > startT) {
-                SkTSpan<TCurve>* oppStart = sect2->fHead->find(oppT);
-                if (oppStart->fStartT < oppT && oppT < oppStart->fEndT) {
-                    SkASSERT(coinEnd < work->fEndT);
-                    // split prev at coinEnd if needed
-                    SkTSpan<TCurve>* half2 = addOne();
-                    half2->splitAt(work, coinEnd);
-                    half2->initBounds(fCurve);
-                    work->initBounds(fCurve);
-                    work->fCoinStart.markCoincident();
-                    work->fCoinEnd.markCoincident();
-                    half2->fCoinStart.markCoincident();
-                    SkTSpan<TCurve>* oppHalf = sect2->addOne();
-                    oppHalf->splitAt(oppStart, oppT);
-                    oppHalf->initBounds(sect2->fCurve);
-                    oppStart->initBounds(sect2->fCurve);
-                } else {
-                    SkASSERT(oppStart->fStartT == oppT || oppT == oppStart->fEndT);
-                    SkTSpan<TCurve>* next = work->fNext;
-                    bool hasNext = next && work->fEndT == next->fStartT;
-                    work->fEndT = coinEnd;
-                    work->initBounds(fCurve);
-                    work->fCoinStart.markCoincident();
-                    work->fCoinEnd.markCoincident();
-                    if (hasNext) { 
-                        next->fStartT = coinEnd;
-                        next->initBounds(fCurve);
-                    }
-                }
-            }
+        ++consecutive;
+        last = next;
+    } while (true);
+    *lastPtr = last;
+    return consecutive;
+}
+
+template<typename TCurve, typename OppCurve>
+bool SkTSect<TCurve, OppCurve>::debugHasBounded(const SkTSpan<OppCurve, TCurve>* span) const {
+    const SkTSpan<TCurve, OppCurve>* test = fHead;
+    if (!test) {
+        return false;
+    }
+    do {
+        if (test->findOppSpan(span)) {
+            return true;
+        }
+    } while ((test = test->next()));
+    return false;
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::deleteEmptySpans() {
+    SkTSpan<TCurve, OppCurve>* test;
+    SkTSpan<TCurve, OppCurve>* next = fHead;
+    while ((test = next)) {
+        next = test->fNext;
+        if (!test->fBounded) {
+            this->removeSpan(test);
         }
     }
 }
 
-template<typename TCurve>
-void SkTSect<TCurve>::recoverCollapsed() {
-    SkTSpan<TCurve>* deleted = fDeleted;
+template<typename TCurve, typename OppCurve>
+SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::extractCoincident(
+        SkTSect<OppCurve, TCurve>* sect2,
+        SkTSpan<TCurve, OppCurve>* first, SkTSpan<TCurve, OppCurve>* last) {
+    first = findCoincidentRun(first, &last);
+    if (!first) {
+        return NULL;
+    }
+    // march outwards to find limit of coincidence from here to previous and next spans
+    double startT = first->fStartT;
+    double oppStartT SK_INIT_TO_AVOID_WARNING;
+    double oppEndT SK_INIT_TO_AVOID_WARNING;
+    SkTSpan<TCurve, OppCurve>* prev = first->fPrev;
+    SkASSERT(first->fCoinStart.isCoincident());
+    SkTSpan<OppCurve, TCurve>* oppFirst = first->findOppT(first->fCoinStart.perpT());
+    SkASSERT(last->fCoinEnd.isCoincident());
+    bool oppMatched = first->fCoinStart.perpT() < first->fCoinEnd.perpT();
+    double coinStart;
+    SkDEBUGCODE(double coinEnd);
+    SkTSpan<OppCurve, TCurve>* cutFirst;
+    if (prev && prev->fEndT == startT
+            && this->binarySearchCoin(sect2, startT, prev->fStartT - startT, &coinStart,
+                                      &oppStartT)
+            && prev->fStartT < coinStart && coinStart < startT
+            && (cutFirst = prev->oppT(oppStartT))) {
+        oppFirst = cutFirst;
+        first = this->addSplitAt(prev, coinStart);
+        first->markCoincident();
+        prev->fCoinEnd.markCoincident();
+        if (oppFirst->fStartT < oppStartT && oppStartT < oppFirst->fEndT) {
+            SkTSpan<OppCurve, TCurve>* oppHalf = sect2->addSplitAt(oppFirst, oppStartT);
+            if (oppMatched) {
+                oppFirst->fCoinEnd.markCoincident();
+                oppHalf->markCoincident();
+                oppFirst = oppHalf;
+            } else {
+                oppFirst->markCoincident();
+                oppHalf->fCoinStart.markCoincident();
+            }
+        }
+    } else {
+        SkDEBUGCODE(coinStart = first->fStartT);
+        SkDEBUGCODE(oppStartT = oppMatched ? oppFirst->fStartT : oppFirst->fEndT);
+    }
+    // FIXME: incomplete : if we're not at the end, find end of coin
+    SkTSpan<OppCurve, TCurve>* oppLast;
+    SkASSERT(last->fCoinEnd.isCoincident());
+    oppLast = last->findOppT(last->fCoinEnd.perpT());
+    SkDEBUGCODE(coinEnd = last->fEndT);
+    SkDEBUGCODE(oppEndT = oppMatched ? oppLast->fEndT : oppLast->fStartT);
+    if (!oppMatched) {
+        SkTSwap(oppFirst, oppLast);
+        SkTSwap(oppStartT, oppEndT);
+    }
+    SkASSERT(oppStartT < oppEndT);
+    SkASSERT(coinStart == first->fStartT);
+    SkASSERT(coinEnd == last->fEndT);
+    SkASSERT(oppStartT == oppFirst->fStartT);
+    SkASSERT(oppEndT == oppLast->fEndT);
+    // reduce coincident runs to single entries
+    this->validate();
+    sect2->validate();
+    bool deleteEmptySpans = this->updateBounded(first, last, oppFirst);
+    deleteEmptySpans |= sect2->updateBounded(oppFirst, oppLast, first);
+    this->removeSpanRange(first, last);
+    sect2->removeSpanRange(oppFirst, oppLast);
+    first->fEndT = last->fEndT;
+    first->resetBounds(this->fCurve);
+    first->fCoinStart.setPerp(fCurve, first->fStartT, first->fPart[0], sect2->fCurve);
+    first->fCoinEnd.setPerp(fCurve, first->fEndT, first->fPart[TCurve::kPointLast], sect2->fCurve);
+    oppStartT = first->fCoinStart.perpT();
+    oppEndT = first->fCoinEnd.perpT();
+    if (between(0, oppStartT, 1) && between(0, oppEndT, 1)) {
+        if (!oppMatched) {
+            SkTSwap(oppStartT, oppEndT);
+        }
+        oppFirst->fStartT = oppStartT;
+        oppFirst->fEndT = oppEndT;
+        oppFirst->resetBounds(sect2->fCurve);
+    }
+    this->validateBounded();
+    sect2->validateBounded();
+    last = first->fNext;
+    this->removeCoincident(first, false);
+    sect2->removeCoincident(oppFirst, true);
+    if (deleteEmptySpans) {
+        this->deleteEmptySpans();
+        sect2->deleteEmptySpans();
+    }
+    this->validate();
+    sect2->validate();
+    return last && !last->fDeleted ? last : NULL;
+}
+
+template<typename TCurve, typename OppCurve>
+SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::findCoincidentRun(
+        SkTSpan<TCurve, OppCurve>* first, SkTSpan<TCurve, OppCurve>** lastPtr) {
+    SkTSpan<TCurve, OppCurve>* work = first;
+    SkTSpan<TCurve, OppCurve>* lastCandidate = NULL;
+    first = NULL;
+    // find the first fully coincident span
+    do {
+        if (work->fCoinStart.isCoincident()) {
+#if DEBUG_T_SECT
+            work->validatePerpT(work->fCoinStart.perpT());
+            work->validatePerpPt(work->fCoinStart.perpT(), work->fCoinStart.perpPt());
+#endif
+            SkASSERT(work->hasOppT(work->fCoinStart.perpT()));
+            if (!work->fCoinEnd.isCoincident()) {
+                break;
+            }
+            lastCandidate = work;
+            if (!first) {
+                first = work;
+            }
+        } else if (first && work->fCollapsed) {
+            *lastPtr = lastCandidate;
+            return first;
+        } else {
+            lastCandidate = NULL;
+            SkASSERT(!first);
+        }
+        if (work == *lastPtr) {
+            return first;
+        }
+        work = work->fNext;
+        SkASSERT(work);
+    } while (true);
+    if (lastCandidate) {
+        *lastPtr = lastCandidate;
+    }
+    return first;
+}
+
+template<typename TCurve, typename OppCurve>
+int SkTSect<TCurve, OppCurve>::intersects(SkTSpan<TCurve, OppCurve>* span,
+        SkTSect<OppCurve, TCurve>* opp,
+        SkTSpan<OppCurve, TCurve>* oppSpan, int* oppResult) {
+    bool spanStart, oppStart;
+    int hullResult = span->hullsIntersect(oppSpan, &spanStart, &oppStart);
+    if (hullResult >= 0) {
+        if (hullResult == 2) {  // hulls have one point in common
+            if (!span->fBounded || !span->fBounded->fNext) {
+                SkASSERT(!span->fBounded || span->fBounded->fBounded == oppSpan);
+                if (spanStart) {
+                    span->fEndT = span->fStartT;
+                } else {
+                    span->fStartT = span->fEndT;
+                }
+            } else {
+                hullResult = 1;
+            }
+            if (!oppSpan->fBounded || !oppSpan->fBounded->fNext) {
+                SkASSERT(!oppSpan->fBounded || oppSpan->fBounded->fBounded == span);
+                if (oppStart) {
+                    oppSpan->fEndT = oppSpan->fStartT;
+                } else {
+                    oppSpan->fStartT = oppSpan->fEndT;
+                }
+                *oppResult = 2;
+            } else {
+                *oppResult = 1;
+            }
+        } else {
+            *oppResult = 1;
+        }
+        return hullResult;
+    }
+    if (span->fIsLine && oppSpan->fIsLine) {
+        SkIntersections i;
+        int sects = this->linesIntersect(span, opp, oppSpan, &i);
+        if (!sects) {
+            return -1;
+        }
+        span->fStartT = span->fEndT = i[0][0];
+        oppSpan->fStartT = oppSpan->fEndT = i[1][0];
+        return *oppResult = 2;
+    }
+    if (span->fIsLinear || oppSpan->fIsLinear) {
+        return *oppResult = (int) span->linearsIntersect(oppSpan);
+    }
+    return *oppResult = 1;
+}
+
+// while the intersection points are sufficiently far apart:
+// construct the tangent lines from the intersections
+// find the point where the tangent line intersects the opposite curve
+template<typename TCurve, typename OppCurve>
+int SkTSect<TCurve, OppCurve>::linesIntersect(SkTSpan<TCurve, OppCurve>* span,
+        SkTSect<OppCurve, TCurve>* opp,
+        SkTSpan<OppCurve, TCurve>* oppSpan, SkIntersections* i) {
+    SkIntersections thisRayI, oppRayI;
+    SkDLine thisLine = {{ span->fPart[0], span->fPart[TCurve::kPointLast] }};
+    SkDLine oppLine = {{ oppSpan->fPart[0], oppSpan->fPart[OppCurve::kPointLast] }};
+    int loopCount = 0;
+    double bestDistSq = DBL_MAX;
+    if (!thisRayI.intersectRay(opp->fCurve, thisLine)) {
+        return 0;
+    }
+    if (!oppRayI.intersectRay(this->fCurve, oppLine)) {
+        return 0;
+    }
+    do {
+        // pick the closest pair of points
+        double closest = DBL_MAX;
+        int closeIndex SK_INIT_TO_AVOID_WARNING;
+        int oppCloseIndex SK_INIT_TO_AVOID_WARNING;
+        for (int index = 0; index < oppRayI.used(); ++index) {
+            if (!roughly_between(span->fStartT, oppRayI[0][index], span->fEndT)) {
+                continue;
+            }
+            for (int oIndex = 0; oIndex < thisRayI.used(); ++oIndex) {
+                if (!roughly_between(oppSpan->fStartT, thisRayI[0][oIndex], oppSpan->fEndT)) {
+                    continue;
+                }
+                double distSq = thisRayI.pt(index).distanceSquared(oppRayI.pt(oIndex));
+                if (closest > distSq) {
+                    closest = distSq;
+                    closeIndex = index;
+                    oppCloseIndex = oIndex;
+                }
+            }
+        }
+        if (closest == DBL_MAX) {
+            break;
+        }
+        const SkDPoint& oppIPt = thisRayI.pt(oppCloseIndex);
+        const SkDPoint& iPt = oppRayI.pt(closeIndex);
+        if (between(span->fStartT, oppRayI[0][closeIndex], span->fEndT)
+                && between(oppSpan->fStartT, thisRayI[0][oppCloseIndex], oppSpan->fEndT)
+                && oppIPt.approximatelyEqual(iPt)) {
+            i->merge(oppRayI, closeIndex, thisRayI, oppCloseIndex);
+            return i->used();
+        }
+        double distSq = oppIPt.distanceSquared(iPt);
+        if (bestDistSq < distSq || ++loopCount > 5) {
+            return 0;
+        }
+        bestDistSq = distSq;
+        double oppStart = oppRayI[0][closeIndex];
+        thisLine[0] = fCurve.ptAtT(oppStart);
+        thisLine[1] = thisLine[0] + fCurve.dxdyAtT(oppStart);
+        if (!thisRayI.intersectRay(opp->fCurve, thisLine)) {
+            break;
+        }
+        double start = thisRayI[0][oppCloseIndex];
+        oppLine[0] = opp->fCurve.ptAtT(start);
+        oppLine[1] = oppLine[0] + opp->fCurve.dxdyAtT(start);
+        if (!oppRayI.intersectRay(this->fCurve, oppLine)) {
+            break;
+        }
+    } while (true);
+    // convergence may fail if the curves are nearly coincident
+    SkTCoincident<OppCurve, TCurve> oCoinS, oCoinE;
+    oCoinS.setPerp(opp->fCurve, oppSpan->fStartT, oppSpan->fPart[0], fCurve);
+    oCoinE.setPerp(opp->fCurve, oppSpan->fEndT, oppSpan->fPart[OppCurve::kPointLast], fCurve);
+    double tStart = oCoinS.perpT();
+    double tEnd = oCoinE.perpT();
+    bool swap = tStart > tEnd;
+    if (swap) {
+        SkTSwap(tStart, tEnd);
+    }
+    tStart = SkTMax(tStart, span->fStartT);
+    tEnd = SkTMin(tEnd, span->fEndT);
+    if (tStart > tEnd) {
+        return 0;
+    }
+    SkDVector perpS, perpE;
+    if (tStart == span->fStartT) {
+        SkTCoincident<TCurve, OppCurve> coinS;
+        coinS.setPerp(fCurve, span->fStartT, span->fPart[0], opp->fCurve);
+        perpS = span->fPart[0] - coinS.perpPt();
+    } else if (swap) {
+        perpS = oCoinE.perpPt() - oppSpan->fPart[OppCurve::kPointLast];
+    } else {
+        perpS = oCoinS.perpPt() - oppSpan->fPart[0];
+    }
+    if (tEnd == span->fEndT) {
+        SkTCoincident<TCurve, OppCurve> coinE;
+        coinE.setPerp(fCurve, span->fEndT, span->fPart[TCurve::kPointLast], opp->fCurve);
+        perpE = span->fPart[TCurve::kPointLast] - coinE.perpPt();
+    } else if (swap) {
+        perpE = oCoinS.perpPt() - oppSpan->fPart[0];
+    } else {
+        perpE = oCoinE.perpPt() - oppSpan->fPart[OppCurve::kPointLast];
+    }
+    if (perpS.dot(perpE) >= 0) {
+        return 0;
+    }
+    SkTCoincident<TCurve, OppCurve> coinW;
+    double workT = tStart;
+    double tStep = tEnd - tStart;
+    SkDPoint workPt;
+    do {
+        tStep *= 0.5;
+        if (precisely_zero(tStep)) {
+            return 0;
+        }
+        workT += tStep;
+        workPt = fCurve.ptAtT(workT);
+        coinW.setPerp(fCurve, workT, workPt, opp->fCurve);
+        SkDVector perpW = workPt - coinW.perpPt();
+        if ((perpS.dot(perpW) >= 0) == (tStep < 0)) {
+            tStep = -tStep;
+        }
+    } while (!workPt.approximatelyEqual(coinW.perpPt()));
+    double oppTTest = coinW.perpT();
+    if (!opp->fHead->contains(oppTTest)) {
+        return 0;
+    }
+    i->setMax(1);
+    i->insert(workT, oppTTest, workPt);
+    return 1;
+}
+
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::markSpanGone(SkTSpan<TCurve, OppCurve>* span) {
+    --fActiveCount;
+    span->fNext = fDeleted;
+    fDeleted = span;
+    SkASSERT(!span->fDeleted);
+    span->fDeleted = true;
+}
+
+template<typename TCurve, typename OppCurve>
+bool SkTSect<TCurve, OppCurve>::matchedDirection(double t, const SkTSect<OppCurve, TCurve>* sect2,
+        double t2) const {
+    SkDVector dxdy = this->fCurve.dxdyAtT(t);
+    SkDVector dxdy2 = sect2->fCurve.dxdyAtT(t2);
+    return dxdy.dot(dxdy2) >= 0;
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::matchedDirCheck(double t, const SkTSect<OppCurve, TCurve>* sect2,
+        double t2, bool* calcMatched, bool* oppMatched) const {
+    if (*calcMatched) {
+        SkASSERT(*oppMatched == this->matchedDirection(t, sect2, t2)); 
+    } else {
+        *oppMatched = this->matchedDirection(t, sect2, t2);
+        *calcMatched = true;
+    }
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::mergeCoincidence(SkTSect<OppCurve, TCurve>* sect2) {
+    double smallLimit = 0;
+    do {
+        // find the smallest unprocessed span
+        SkTSpan<TCurve, OppCurve>* smaller = NULL;
+        SkTSpan<TCurve, OppCurve>* test = fCoincident;
+        do {
+            if (test->fStartT < smallLimit) {
+                continue;
+            }
+            if (smaller && smaller->fEndT < test->fStartT) {
+                continue;
+            }
+            smaller = test;
+        } while ((test = test->fNext));
+        if (!smaller) {
+            return;
+        }
+        smallLimit = smaller->fEndT;
+        // find next larger span
+        SkTSpan<TCurve, OppCurve>* prior = NULL;
+        SkTSpan<TCurve, OppCurve>* larger = NULL;
+        SkTSpan<TCurve, OppCurve>* largerPrior = NULL;
+        test = fCoincident;
+        do {
+            if (test->fStartT < smaller->fEndT) {
+                continue;
+            }
+            SkASSERT(test->fStartT != smaller->fEndT);
+            if (larger && larger->fStartT < test->fStartT) {
+                continue;
+            }
+            largerPrior = prior;
+            larger = test;
+        } while ((prior = test), (test = test->fNext));
+        if (!larger) {
+            continue;
+        }
+        // check middle t value to see if it is coincident as well
+        double midT = (smaller->fEndT + larger->fStartT) / 2;
+        SkDPoint midPt = fCurve.ptAtT(midT);
+        SkTCoincident<TCurve, OppCurve> coin;
+        coin.setPerp(fCurve, midT, midPt, sect2->fCurve);
+        if (coin.isCoincident()) {
+            smaller->fEndT = larger->fEndT;
+            smaller->fCoinEnd = larger->fCoinEnd;
+            if (largerPrior) {
+                largerPrior->fNext = larger->fNext;
+            } else {
+                fCoincident = larger->fNext;
+            }
+        }
+    } while (true);
+}
+
+template<typename TCurve, typename OppCurve>
+SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::prev(
+        SkTSpan<TCurve, OppCurve>* span) const {
+    SkTSpan<TCurve, OppCurve>* result = NULL;
+    SkTSpan<TCurve, OppCurve>* test = fHead;
+    while (span != test) {
+        result = test;
+        test = test->fNext;
+        SkASSERT(test);
+    }
+    return result; 
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::recoverCollapsed() {
+    SkTSpan<TCurve, OppCurve>* deleted = fDeleted;
     while (deleted) {
-        SkTSpan<TCurve>* delNext = deleted->fNext;
+        SkTSpan<TCurve, OppCurve>* delNext = deleted->fNext;
         if (deleted->fCollapsed) {
-            SkTSpan<TCurve>** spanPtr = &fHead;
+            SkTSpan<TCurve, OppCurve>** spanPtr = &fHead;
             while (*spanPtr && (*spanPtr)->fEndT <= deleted->fStartT) {
                 spanPtr = &(*spanPtr)->fNext;
             }
@@ -781,10 +1485,167 @@
     }
 }
 
-template<typename TCurve>
-void SkTSect<TCurve>::removeSpan(SkTSpan<TCurve>* span) {
-    SkTSpan<TCurve>* prev = span->fPrev;
-    SkTSpan<TCurve>* next = span->fNext;
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::removeAllBut(const SkTSpan<OppCurve, TCurve>* keep,
+        SkTSpan<TCurve, OppCurve>* span, SkTSect<OppCurve, TCurve>* opp) {
+    const SkTSpanBounded<OppCurve, TCurve>* testBounded = span->fBounded;
+    while (testBounded) {
+        SkTSpan<OppCurve, TCurve>* bounded = testBounded->fBounded;
+        const SkTSpanBounded<OppCurve, TCurve>* next = testBounded->fNext;
+        // may have been deleted when opp did 'remove all but'
+        if (bounded != keep && !bounded->fDeleted) {
+            SkAssertResult(SkDEBUGCODE(!) span->removeBounded(bounded));
+            if (bounded->removeBounded(span)) {
+                opp->removeSpan(bounded);
+            }
+        }
+        testBounded = next;
+    }
+    SkASSERT(!span->fDeleted);
+    SkASSERT(span->findOppSpan(keep));
+    SkASSERT(keep->findOppSpan(span));
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::removeByPerpendicular(SkTSect<OppCurve, TCurve>* opp) {
+    SkTSpan<TCurve, OppCurve>* test = fHead;
+    SkTSpan<TCurve, OppCurve>* next;
+    do {
+        next = test->fNext;
+        if (test->fCoinStart.perpT() < 0 || test->fCoinEnd.perpT() < 0) {
+            continue;
+        }
+        SkDVector startV = test->fCoinStart.perpPt() - test->fPart[0];
+        SkDVector endV = test->fCoinEnd.perpPt() - test->fPart[TCurve::kPointLast];
+#if DEBUG_T_SECT
+        SkDebugf("%s startV=(%1.9g,%1.9g) endV=(%1.9g,%1.9g) dot=%1.9g\n", __FUNCTION__,
+                startV.fX, startV.fY, endV.fX, endV.fY, startV.dot(endV));
+#endif
+        if (startV.dot(endV) <= 0) {
+            continue;
+        }
+        this->removeSpans(test, opp);
+    } while ((test = next));
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::removeCoincident(SkTSpan<TCurve, OppCurve>* span, bool isBetween) {
+    this->unlinkSpan(span);
+    if (isBetween || between(0, span->fCoinStart.perpT(), 1)) {
+        --fActiveCount;
+        span->fNext = fCoincident;
+        fCoincident = span;
+    } else {
+        this->markSpanGone(span);
+    }
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::removeSpan(SkTSpan<TCurve, OppCurve>* span) {
+    this->unlinkSpan(span);
+    this->markSpanGone(span);
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::removeSpanRange(SkTSpan<TCurve, OppCurve>* first,
+        SkTSpan<TCurve, OppCurve>* last) {
+    if (first == last) {
+        return;
+    }
+    SkTSpan<TCurve, OppCurve>* span = first;
+    SkASSERT(span);
+    SkTSpan<TCurve, OppCurve>* final = last->fNext;
+    SkTSpan<TCurve, OppCurve>* next = span->fNext;
+    while ((span = next) && span != final) {
+        next = span->fNext;
+        this->markSpanGone(span);
+    }
+    if (final) {
+        final->fPrev = first;
+    }
+    first->fNext = final;
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::removeSpans(SkTSpan<TCurve, OppCurve>* span,
+        SkTSect<OppCurve, TCurve>* opp) {
+    SkTSpanBounded<OppCurve, TCurve>* bounded = span->fBounded;
+    while (bounded) {
+        SkTSpan<OppCurve, TCurve>* spanBounded = bounded->fBounded;
+        SkTSpanBounded<OppCurve, TCurve>* next = bounded->fNext;
+        if (span->removeBounded(spanBounded)) {  // shuffles last into position 0
+            this->removeSpan(span);
+        }
+        if (spanBounded->removeBounded(span)) {
+            opp->removeSpan(spanBounded);
+        }
+        SkASSERT(!span->fDeleted || !opp->debugHasBounded(span));
+        bounded = next;
+    }
+}
+
+template<typename TCurve, typename OppCurve>
+SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::spanAtT(double t,
+        SkTSpan<TCurve, OppCurve>** priorSpan) {
+    SkTSpan<TCurve, OppCurve>* test = fHead;
+    SkTSpan<TCurve, OppCurve>* prev = NULL;
+    while (test && test->fEndT < t) {
+        prev = test;
+        test = test->fNext;
+    }
+    *priorSpan = prev;
+    return test && test->fStartT <= t ? test : NULL;
+}
+
+template<typename TCurve, typename OppCurve>
+SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::tail() {
+    SkTSpan<TCurve, OppCurve>* result = fHead;
+    SkTSpan<TCurve, OppCurve>* next = fHead;
+    while ((next = next->fNext)) {
+        if (next->fEndT > result->fEndT) {
+            result = next;
+        }
+    }
+    return result;
+}
+
+/* Each span has a range of opposite spans it intersects. After the span is split in two,
+    adjust the range to its new size */
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::trim(SkTSpan<TCurve, OppCurve>* span,
+        SkTSect<OppCurve, TCurve>* opp) {
+    span->initBounds(fCurve);
+    const SkTSpanBounded<OppCurve, TCurve>* testBounded = span->fBounded;
+    while (testBounded) {
+        SkTSpan<OppCurve, TCurve>* test = testBounded->fBounded;
+        const SkTSpanBounded<OppCurve, TCurve>* next = testBounded->fNext;
+        int oppSects, sects = this->intersects(span, opp, test, &oppSects);
+        if (sects >= 1) {
+            if (oppSects == 2) {
+                test->initBounds(opp->fCurve);
+                opp->removeAllBut(span, test, this);
+            }
+            if (sects == 2) {
+                span->initBounds(fCurve);
+                this->removeAllBut(test, span, opp);
+                return;
+            }
+        } else {
+            if (span->removeBounded(test)) {
+                this->removeSpan(span);
+            }
+            if (test->removeBounded(span)) {
+                opp->removeSpan(test);
+            }
+        }
+        testBounded = next;
+    }
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::unlinkSpan(SkTSpan<TCurve, OppCurve>* span) {
+    SkTSpan<TCurve, OppCurve>* prev = span->fPrev;
+    SkTSpan<TCurve, OppCurve>* next = span->fNext;
     if (prev) {
         prev->fNext = next;
         if (next) {
@@ -796,174 +1657,132 @@
             next->fPrev = NULL;
         }
     }
-    --fActiveCount;
-    span->fNext = fDeleted;
-    fDeleted = span;
-#if DEBUG_T_SECT
-    SkASSERT(!span->fDebugDeleted);
-    span->fDebugDeleted = true;
-#endif
 }
 
-template<typename TCurve>
-void SkTSect<TCurve>::removeOne(const SkTSpan<TCurve>* test, SkTSpan<TCurve>* span) {
-    int last = span->fBounded.count() - 1;
-    for (int index = 0; index <= last; ++index) {
-        if (span->fBounded[index] == test) {
-            span->fBounded.removeShuffle(index);
-            if (!last) {
-                removeSpan(span);
-            }
-            return;
-        }
-    }
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::removeSpans(SkTSpan<TCurve>* span, SkTSect<TCurve>* opp) {
-    int count = span->fBounded.count();
-    for (int index = 0; index < count; ++index) {
-        SkTSpan<TCurve>* bounded = span->fBounded[0];
-        removeOne(bounded, span);  // shuffles last into position 0
-        opp->removeOne(span, bounded);
-    }
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::setPerp(const TCurve& opp, SkTSpan<TCurve>* first, SkTSpan<TCurve>* last) {
-    SkTSpan<TCurve>* work = first;
-    if (!work->fHasPerp) {
-        work->fCoinStart.setPerp(fCurve, work->fStartT, work->fPart[0], opp);
-    }
+template<typename TCurve, typename OppCurve>
+bool SkTSect<TCurve, OppCurve>::updateBounded(SkTSpan<TCurve, OppCurve>* first,
+        SkTSpan<TCurve, OppCurve>* last, SkTSpan<OppCurve, TCurve>* oppFirst) {
+    SkTSpan<TCurve, OppCurve>* test = first;
+    const SkTSpan<TCurve, OppCurve>* final = last->next();
+    bool deleteSpan = false;
     do {
-        if (!work->fHasPerp) {
-            work->fCoinEnd.setPerp(fCurve, work->fEndT, work->fPart[TCurve::kPointLast], opp);
-            work->fHasPerp = true;
-        }
-        if (work == last) {
-            break;
-        }
-        SkTSpan<TCurve>* last = work;
-        work = work->fNext;
-        SkASSERT(work);
-        if (!work->fHasPerp) {
-            work->fCoinStart = last->fCoinEnd;
-        }
-    } while (true);
+        deleteSpan |= test->removeAllBounded();
+    } while ((test = test->fNext) != final);
+    first->fBounded = NULL;
+    first->addBounded(oppFirst, &fHeap);
+    // cannot call validate until remove span range is called
+    return deleteSpan;
 }
 
-template<typename TCurve>
-const SkTSpan<TCurve>* SkTSect<TCurve>::tail() const {
-    const SkTSpan<TCurve>* result = fHead;
-    const SkTSpan<TCurve>* next = fHead;
-    while ((next = next->fNext)) {
-        if (next->fEndT > result->fEndT) {
-            result = next;
-        }
-    }
-    return result;
-}
 
-/* Each span has a range of opposite spans it intersects. After the span is split in two,
-    adjust the range to its new size */
-template<typename TCurve>
-void SkTSect<TCurve>::trim(SkTSpan<TCurve>* span, SkTSect* opp) {
-    span->initBounds(fCurve);
-    int count = span->fBounded.count();
-    for (int index = 0; index < count; ) {
-        SkTSpan<TCurve>* test = span->fBounded[index];
-        bool sects = intersects(span, opp, test);
-        if (sects) {
-            ++index;
-        } else {
-            removeOne(test, span);
-            opp->removeOne(span, test);
-            --count;
-        }
-    }
-}
-
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::validate() const {
 #if DEBUG_T_SECT
-template<typename TCurve>
-void SkTSect<TCurve>::validate() const {
     int count = 0;
     if (fHead) {
-        const SkTSpan<TCurve>* span = fHead;
+        const SkTSpan<TCurve, OppCurve>* span = fHead;
         SkASSERT(!span->fPrev);
-        double last = 0;
+        SkDEBUGCODE(double last = 0);
         do {
             span->validate();
             SkASSERT(span->fStartT >= last);
-            last = span->fEndT;
+            SkDEBUGCODE(last = span->fEndT);
             ++count;
         } while ((span = span->fNext) != NULL);
     }
     SkASSERT(count == fActiveCount);
     SkASSERT(fActiveCount <= fDebugAllocatedCount);
     int deletedCount = 0;
-    const SkTSpan<TCurve>* deleted = fDeleted;
+    const SkTSpan<TCurve, OppCurve>* deleted = fDeleted;
     while (deleted) {
         ++deletedCount;
         deleted = deleted->fNext;
     }
+    const SkTSpan<TCurve, OppCurve>* coincident = fCoincident;
+    while (coincident) {
+        ++deletedCount;
+        coincident = coincident->fNext;
+    }
     SkASSERT(fActiveCount + deletedCount == fDebugAllocatedCount);
-}
 #endif
+}
 
-template<typename TCurve>
-int SkTSect<TCurve>::EndsEqual(const SkTSect* sect1, const SkTSect* sect2,
-        SkIntersections* intersections) {
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::validateBounded() const {
+#if DEBUG_T_SECT
+    if (!fHead) {
+        return;
+    }
+    const SkTSpan<TCurve, OppCurve>* span = fHead;
+    do {
+        span->validateBounded();
+    } while ((span = span->fNext) != NULL);
+#endif
+}
+
+template<typename TCurve, typename OppCurve>
+int SkTSect<TCurve, OppCurve>::EndsEqual(const SkTSect<TCurve, OppCurve>* sect1,
+        const SkTSect<OppCurve, TCurve>* sect2, SkIntersections* intersections) {
     int zeroOneSet = 0;
-    // check for zero
-    if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
+    if (sect1->fCurve[0] == sect2->fCurve[0]) {
         zeroOneSet |= kZeroS1Set | kZeroS2Set;
-        if (sect1->fCurve[0] != sect2->fCurve[0]) {
-            intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]);
-        } else {
-            intersections->insert(0, 0, sect1->fCurve[0]);
-        }
-    } 
-    if (sect1->fCurve[0].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
+        intersections->insert(0, 0, sect1->fCurve[0]);
+    }
+    if (sect1->fCurve[0] == sect2->fCurve[OppCurve::kPointLast]) {
         zeroOneSet |= kZeroS1Set | kOneS2Set;
-        if (sect1->fCurve[0] != sect2->fCurve[TCurve::kPointLast]) {
-            intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[TCurve::kPointLast]);
-        } else {
-            intersections->insert(0, 1, sect1->fCurve[0]);
-        }
-    } 
-    // check for one
-    if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
+        intersections->insert(0, 1, sect1->fCurve[0]);
+    }
+    if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[0]) {
         zeroOneSet |= kOneS1Set | kZeroS2Set;
-        if (sect1->fCurve[TCurve::kPointLast] != sect2->fCurve[0]) {
-            intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]);
-        } else {
-            intersections->insert(1, 0, sect1->fCurve[TCurve::kPointLast]);
-        }
-    } 
-    if (sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[TCurve::kPointLast])) {
+        intersections->insert(1, 0, sect1->fCurve[TCurve::kPointLast]);
+    }
+    if (sect1->fCurve[TCurve::kPointLast] == sect2->fCurve[OppCurve::kPointLast]) {
         zeroOneSet |= kOneS1Set | kOneS2Set;
-        if (sect1->fCurve[TCurve::kPointLast] != sect2->fCurve[TCurve::kPointLast]) {
-            intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast],
-                    sect2->fCurve[TCurve::kPointLast]);
-        } else {
             intersections->insert(1, 1, sect1->fCurve[TCurve::kPointLast]);
-        }
+    }
+    // check for zero
+    if (!(zeroOneSet & (kZeroS1Set | kZeroS2Set))
+            && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[0])) {
+        zeroOneSet |= kZeroS1Set | kZeroS2Set;
+        intersections->insertNear(0, 0, sect1->fCurve[0], sect2->fCurve[0]);
+    }
+    if (!(zeroOneSet & (kZeroS1Set | kOneS2Set))
+            && sect1->fCurve[0].approximatelyEqual(sect2->fCurve[OppCurve::kPointLast])) {
+        zeroOneSet |= kZeroS1Set | kOneS2Set;
+        intersections->insertNear(0, 1, sect1->fCurve[0], sect2->fCurve[OppCurve::kPointLast]);
+    }
+    // check for one
+    if (!(zeroOneSet & (kOneS1Set | kZeroS2Set))
+            && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[0])) {
+        zeroOneSet |= kOneS1Set | kZeroS2Set;
+        intersections->insertNear(1, 0, sect1->fCurve[TCurve::kPointLast], sect2->fCurve[0]);
+    }
+    if (!(zeroOneSet & (kOneS1Set | kOneS2Set))
+            && sect1->fCurve[TCurve::kPointLast].approximatelyEqual(sect2->fCurve[
+            OppCurve::kPointLast])) {
+        zeroOneSet |= kOneS1Set | kOneS2Set;
+        intersections->insertNear(1, 1, sect1->fCurve[TCurve::kPointLast],
+                sect2->fCurve[OppCurve::kPointLast]);
     }
     return zeroOneSet;
 }
 
-template<typename TCurve>
+template<typename TCurve, typename OppCurve>
 struct SkClosestRecord {
+    bool operator<(const SkClosestRecord& rh) const {
+        return fClosest < rh.fClosest;
+    }
+
     void addIntersection(SkIntersections* intersections) const {
         double r1t = fC1Index ? fC1Span->endT() : fC1Span->startT();
         double r2t = fC2Index ? fC2Span->endT() : fC2Span->startT();
         intersections->insert(r1t, r2t, fC1Span->part()[fC1Index]);
     }
 
-    void findEnd(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2,
+    void findEnd(const SkTSpan<TCurve, OppCurve>* span1, const SkTSpan<OppCurve, TCurve>* span2,
             int c1Index, int c2Index) {
         const TCurve& c1 = span1->part();
-        const TCurve& c2 = span2->part();
+        const OppCurve& c2 = span2->part();
         if (!c1[c1Index].approximatelyEqual(c2[c2Index])) {
             return;
         }
@@ -1004,7 +1823,8 @@
     
     void reset() {
         fClosest = FLT_MAX;
-        SkDEBUGCODE(fC1Span = fC2Span = NULL);
+        SkDEBUGCODE(fC1Span = NULL);
+        SkDEBUGCODE(fC2Span = NULL);
         SkDEBUGCODE(fC1Index = fC2Index = -1);
     }
 
@@ -1015,8 +1835,8 @@
         fC2EndT = SkTMax(fC2EndT, mate.fC2EndT);
     }
 
-    const SkTSpan<TCurve>* fC1Span;
-    const SkTSpan<TCurve>* fC2Span;
+    const SkTSpan<TCurve, OppCurve>* fC1Span;
+    const SkTSpan<OppCurve, TCurve>* fC2Span;
     double fC1StartT;
     double fC1EndT;
     double fC2StartT;
@@ -1026,173 +1846,209 @@
     int fC2Index;
 };
 
-template<typename TCurve>
+template<typename TCurve, typename OppCurve>
 struct SkClosestSect {
     SkClosestSect()
         : fUsed(0) {
         fClosest.push_back().reset();
     }
 
-    void find(const SkTSpan<TCurve>* span1, const SkTSpan<TCurve>* span2) {
-        SkClosestRecord<TCurve>* record = &fClosest[fUsed];
+    bool find(const SkTSpan<TCurve, OppCurve>* span1, const SkTSpan<OppCurve, TCurve>* span2) {
+        SkClosestRecord<TCurve, OppCurve>* record = &fClosest[fUsed];
         record->findEnd(span1, span2, 0, 0);
-        record->findEnd(span1, span2, 0, TCurve::kPointLast);
+        record->findEnd(span1, span2, 0, OppCurve::kPointLast);
         record->findEnd(span1, span2, TCurve::kPointLast, 0);
-        record->findEnd(span1, span2, TCurve::kPointLast, TCurve::kPointLast);
+        record->findEnd(span1, span2, TCurve::kPointLast, OppCurve::kPointLast);
         if (record->fClosest == FLT_MAX) {
-            return;
+            return false;
         }
         for (int index = 0; index < fUsed; ++index) {
-            SkClosestRecord<TCurve>* test = &fClosest[index];
+            SkClosestRecord<TCurve, OppCurve>* test = &fClosest[index];
             if (test->matesWith(*record)) {
                 if (test->fClosest > record->fClosest) {
                     test->merge(*record);
                 }
                 test->update(*record);
                 record->reset();
-                return;
+                return false;
             }
         }
         ++fUsed;
         fClosest.push_back().reset();
+        return true;
     }
 
     void finish(SkIntersections* intersections) const {
+        SkSTArray<TCurve::kMaxIntersections * 3,
+                const SkClosestRecord<TCurve, OppCurve>*, true> closestPtrs;
         for (int index = 0; index < fUsed; ++index) {
-            const SkClosestRecord<TCurve>& test = fClosest[index];
-            test.addIntersection(intersections);
+            closestPtrs.push_back(&fClosest[index]);
+        }
+        SkTQSort<const SkClosestRecord<TCurve, OppCurve> >(closestPtrs.begin(), closestPtrs.end()
+                - 1);
+        for (int index = 0; index < fUsed; ++index) {
+            const SkClosestRecord<TCurve, OppCurve>* test = closestPtrs[index];
+            test->addIntersection(intersections);
         }
     }
 
-    // this is oversized by one so that an extra record can merge into final one
-    SkSTArray<TCurve::kMaxIntersections + 1, SkClosestRecord<TCurve>, true> fClosest;
+    // this is oversized so that an extra records can merge into final one
+    SkSTArray<TCurve::kMaxIntersections * 2, SkClosestRecord<TCurve, OppCurve>, true> fClosest;
     int fUsed;
 };
 
 // returns true if the rect is too small to consider
-template<typename TCurve>
-void SkTSect<TCurve>::BinarySearch(SkTSect* sect1, SkTSect* sect2, SkIntersections* intersections) {
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::BinarySearch(SkTSect<TCurve, OppCurve>* sect1,
+        SkTSect<OppCurve, TCurve>* sect2, SkIntersections* intersections) {
+#if DEBUG_T_SECT_DUMP > 1
+    gDumpTSectNum = 0;
+#endif
+    SkDEBUGCODE(sect1->fOppSect = sect2);
+    SkDEBUGCODE(sect2->fOppSect = sect1);
     intersections->reset();
-    intersections->setMax(TCurve::kMaxIntersections);
-    SkTSpan<TCurve>* span1 = sect1->fHead;
-    SkTSpan<TCurve>* span2 = sect2->fHead;
-    bool check;
-    if (!span1->intersects(span2, &check)) {
+    intersections->setMax(TCurve::kMaxIntersections * 3);  // give extra for slop
+    SkTSpan<TCurve, OppCurve>* span1 = sect1->fHead;
+    SkTSpan<OppCurve, TCurve>* span2 = sect2->fHead;
+    int oppSect, sect = sect1->intersects(span1, sect2, span2, &oppSect);
+//    SkASSERT(between(0, sect, 2));
+    if (!sect) {
         return;
     }
-    if (check) {
+    if (sect == 2 && oppSect == 2) {
         (void) EndsEqual(sect1, sect2, intersections);
         return;
     }
-    span1->fBounded.push_back() = span2;
-    span2->fBounded.push_back() = span1;
+    span1->addBounded(span2, &sect1->fHeap);
+    span2->addBounded(span1, &sect2->fHeap);
     do {
         // find the largest bounds
-        SkTSpan<TCurve>* largest1 = sect1->boundsMax();
+        SkTSpan<TCurve, OppCurve>* largest1 = sect1->boundsMax();
         if (!largest1) {
             break;
         }
-        SkTSpan<TCurve>* largest2 = sect2->boundsMax();
-        bool split1 = !largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsMax
-            || (!largest1->fCollapsed && largest2->fCollapsed)));
+        SkTSpan<OppCurve, TCurve>* largest2 = sect2->boundsMax();
         // split it
-        SkTSect* splitSect = split1 ? sect1 : sect2;
-        SkTSpan<TCurve>* half1 = split1 ? largest1 : largest2;
-        SkASSERT(half1);
-        if (half1->fCollapsed) {
-            break;
+        if (!largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsMax
+                || (!largest1->fCollapsed && largest2->fCollapsed)))) {
+            if (largest1->fCollapsed) {
+                break;
+            }
+            // trim parts that don't intersect the opposite
+            SkTSpan<TCurve, OppCurve>* half1 = sect1->addOne();
+            if (!half1->split(largest1, &sect1->fHeap)) {
+                break;
+            }
+            sect1->trim(largest1, sect2);
+            sect1->trim(half1, sect2);
+        } else {
+            if (largest2->fCollapsed) {
+                break;
+            }
+            // trim parts that don't intersect the opposite
+            SkTSpan<OppCurve, TCurve>* half2 = sect2->addOne();
+            if (!half2->split(largest2, &sect2->fHeap)) {
+                break;
+            }
+            sect2->trim(largest2, sect1);
+            sect2->trim(half2, sect1);
         }
-        // trim parts that don't intersect the opposite
-        SkTSpan<TCurve>* half2 = splitSect->addOne();
-        SkTSect* unsplitSect = split1 ? sect2 : sect1;
-        if (!half2->split(half1)) {
-            break;
-        }
-        splitSect->trim(half1, unsplitSect);
-        splitSect->trim(half2, unsplitSect);
+        sect1->validate();
+        sect2->validate();
         // if there are 9 or more continuous spans on both sects, suspect coincidence
         if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
                 && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
             sect1->coincidentCheck(sect2);
+            sect1->validate();
+            sect2->validate();
         }
-#if DEBUG_T_SECT
-        sect1->validate();
-        sect2->validate();
-#endif
-#if DEBUG_T_SECT_DUMP > 1
-        sect1->dumpBoth(*sect2);
+        if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
+                && sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) {
+            sect1->computePerpendiculars(sect2, sect1->fHead, sect1->tail());
+            sect2->computePerpendiculars(sect1, sect2->fHead, sect2->tail());
+            sect1->removeByPerpendicular(sect2);
+            sect1->validate();
+            sect2->validate();
+            if (sect1->collapsed() > TCurve::kMaxIntersections) {
+                break;
+            }
+        }
+#if DEBUG_T_SECT_DUMP
+        sect1->dumpBoth(sect2);
 #endif
         if (!sect1->fHead || !sect2->fHead) {
-            return;
+            break;
         }
     } while (true);
-    if (sect1->fActiveCount >= 2 && sect2->fActiveCount >= 2) {
-        // check for coincidence
-        SkTSpan<TCurve>* first = sect1->fHead;
+    SkTSpan<TCurve, OppCurve>* coincident = sect1->fCoincident;
+    if (coincident) {
+        // if there is more than one coincident span, check loosely to see if they should be joined
+        if (coincident->fNext) {
+            sect1->mergeCoincidence(sect2);
+            coincident = sect1->fCoincident;
+        }
+        SkASSERT(sect2->fCoincident);  // courtesy check : coincidence only looks at sect 1
         do {
-            if (!first->fCoinStart.isCoincident()) {
-                continue;
-            }
-            int spanCount = 1;
-            SkTSpan<TCurve>* last = first;
-            while (last->fCoinEnd.isCoincident()) {
-                SkTSpan<TCurve>* next = last->fNext;
-                if (!next || !next->fCoinEnd.isCoincident()) {
-                    break;
-                }
-                last = next;
-                ++spanCount;
-            }
-            if (spanCount < 2) {
-                first = last;
-                continue;
-            }
-            int index = intersections->insertCoincident(first->fStartT, first->fCoinStart.perpT(),
-                    first->fPart[0]);
-            if (intersections->insertCoincident(last->fEndT, last->fCoinEnd.perpT(),
-                    last->fPart[TCurve::kPointLast]) < 0) {
+            SkASSERT(coincident->fCoinStart.isCoincident());
+            SkASSERT(coincident->fCoinEnd.isCoincident());
+            int index = intersections->insertCoincident(coincident->fStartT,
+                    coincident->fCoinStart.perpT(), coincident->fPart[0]);
+            if ((intersections->insertCoincident(coincident->fEndT,
+                    coincident->fCoinEnd.perpT(),
+                    coincident->fPart[TCurve::kPointLast]) < 0) && index >= 0) {
                 intersections->clearCoincidence(index);
             }
-        } while ((first = first->fNext));
+        } while ((coincident = coincident->fNext));
     }
     int zeroOneSet = EndsEqual(sect1, sect2, intersections);
+    if (!sect1->fHead || !sect2->fHead) {
+        return;
+    }
     sect1->recoverCollapsed();
     sect2->recoverCollapsed();
-    SkTSpan<TCurve>* result1 = sect1->fHead;
+    SkTSpan<TCurve, OppCurve>* result1 = sect1->fHead;
     // check heads and tails for zero and ones and insert them if we haven't already done so
-    const SkTSpan<TCurve>* head1 = result1;
+    const SkTSpan<TCurve, OppCurve>* head1 = result1;
     if (!(zeroOneSet & kZeroS1Set) && approximately_less_than_zero(head1->fStartT)) {
         const SkDPoint& start1 = sect1->fCurve[0];
-        double t = head1->closestBoundedT(start1);
-        if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
-            intersections->insert(0, t, start1);
+        if (head1->isBounded()) {
+            double t = head1->closestBoundedT(start1);
+            if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
+                intersections->insert(0, t, start1);
+            }
         }
     }
-    const SkTSpan<TCurve>* head2 = sect2->fHead;
+    const SkTSpan<OppCurve, TCurve>* head2 = sect2->fHead;
     if (!(zeroOneSet & kZeroS2Set) && approximately_less_than_zero(head2->fStartT)) {
         const SkDPoint& start2 = sect2->fCurve[0];
-        double t = head2->closestBoundedT(start2);
-        if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
-            intersections->insert(t, 0, start2);
+        if (head2->isBounded()) {
+            double t = head2->closestBoundedT(start2);
+            if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
+                intersections->insert(t, 0, start2);
+            }
         }
     }
-    const SkTSpan<TCurve>* tail1 = sect1->tail();
+    const SkTSpan<TCurve, OppCurve>* tail1 = sect1->tail();
     if (!(zeroOneSet & kOneS1Set) && approximately_greater_than_one(tail1->fEndT)) {
         const SkDPoint& end1 = sect1->fCurve[TCurve::kPointLast];
-        double t = tail1->closestBoundedT(end1);
-        if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
-            intersections->insert(1, t, end1);
+        if (tail1->isBounded()) {
+            double t = tail1->closestBoundedT(end1);
+            if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
+                intersections->insert(1, t, end1);
+            }
         }
     }
-    const SkTSpan<TCurve>* tail2 = sect2->tail();
+    const SkTSpan<OppCurve, TCurve>* tail2 = sect2->tail();
     if (!(zeroOneSet & kOneS2Set) && approximately_greater_than_one(tail2->fEndT)) {
-        const SkDPoint& end2 = sect2->fCurve[TCurve::kPointLast];
-        double t = tail2->closestBoundedT(end2);
-        if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
-            intersections->insert(t, 1, end2);
+        const SkDPoint& end2 = sect2->fCurve[OppCurve::kPointLast];
+        if (tail2->isBounded()) {
+            double t = tail2->closestBoundedT(end2);
+            if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
+                intersections->insert(t, 1, end2);
+            }
         }
     }
-    SkClosestSect<TCurve> closest;
+    SkClosestSect<TCurve, OppCurve> closest;
     do {
         while (result1 && result1->fCoinStart.isCoincident() && result1->fCoinEnd.isCoincident()) {
             result1 = result1->fNext;
@@ -1200,12 +2056,40 @@
         if (!result1) {
             break;
         }
-        SkTSpan<TCurve>* result2 = sect2->fHead;
+        SkTSpan<OppCurve, TCurve>* result2 = sect2->fHead;
+        bool found = false;
         while (result2) {
-            closest.find(result1, result2);
+            found |= closest.find(result1, result2);
             result2 = result2->fNext;
         }
-        
     } while ((result1 = result1->fNext));
     closest.finish(intersections);
+    // if there is more than one intersection and it isn't already coincident, check
+    int last = intersections->used() - 1;
+    for (int index = 0; index < last; ) {
+        if (intersections->isCoincident(index) && intersections->isCoincident(index + 1)) {
+            ++index;
+            continue;
+        }
+        double midT = ((*intersections)[0][index] + (*intersections)[0][index + 1]) / 2;
+        SkDPoint midPt = sect1->fCurve.ptAtT(midT);
+        // intersect perpendicular with opposite curve
+        SkTCoincident<TCurve, OppCurve> perp;
+        perp.setPerp(sect1->fCurve, midT, midPt, sect2->fCurve);
+        if (!perp.isCoincident()) {
+            ++index;
+            continue;
+        }
+        if (intersections->isCoincident(index)) {
+            intersections->removeOne(index);
+            --last;
+        } else if (intersections->isCoincident(index + 1)) {
+            intersections->removeOne(index + 1);
+            --last;
+        } else {
+            intersections->setCoincident(index++);
+        }
+        intersections->setCoincident(index);
+    }
+    SkASSERT(intersections->used() <= TCurve::kMaxIntersections);
 }
diff --git a/src/pathops/SkPathOpsTightBounds.cpp b/src/pathops/SkPathOpsTightBounds.cpp
index 0f63f39..f60c291 100644
--- a/src/pathops/SkPathOpsTightBounds.cpp
+++ b/src/pathops/SkPathOpsTightBounds.cpp
@@ -8,24 +8,22 @@
 #include "SkPathOpsCommon.h"
 
 bool TightBounds(const SkPath& path, SkRect* result) {
+    SkChunkAlloc allocator(4096);  // FIXME: constant-ize, tune
+    SkOpContour contour;
+    SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
+    SkOpGlobalState globalState(NULL, contourList);
     // turn path into list of segments
-    SkTArray<SkOpContour> contours;
-    SkOpEdgeBuilder builder(path, contours);
-    if (!builder.finish()) {
+    SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
+    if (!builder.finish(&allocator)) {
         return false;
     }
-    SkTArray<SkOpContour*, true> contourList;
-    MakeContourList(contours, contourList, false, false);
-    SkOpContour** currentPtr = contourList.begin();
-    result->setEmpty();
-    if (!currentPtr) {
+    if (!SortContourList(&contourList, false, false)) {
+        result->setEmpty();
         return true;
     }
-    SkOpContour** listEnd = contourList.end();
-    SkOpContour* current = *currentPtr++;
+    SkOpContour* current = contourList;
     SkPathOpsBounds bounds = current->bounds();
-    while (currentPtr != listEnd) {
-        current = *currentPtr++;
+    while ((current = current->next())) {
         bounds.add(current->bounds());
     }
     *result = bounds;
diff --git a/src/pathops/SkPathOpsTriangle.cpp b/src/pathops/SkPathOpsTriangle.cpp
deleted file mode 100644
index 77845e0..0000000
--- a/src/pathops/SkPathOpsTriangle.cpp
+++ /dev/null
@@ -1,51 +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.
- */
-
-#include "SkPathOpsTriangle.h"
-
-// http://www.blackpawn.com/texts/pointinpoly/default.html
-// return true if pt is inside triangle; false if outside or on the line
-bool SkDTriangle::contains(const SkDPoint& pt) const {
-// Compute vectors
-    SkDVector v0 = fPts[2] - fPts[0];
-    SkDVector v1 = fPts[1] - fPts[0];
-    SkDVector v2 = pt - fPts[0];
-
-// 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);
-
-// original code doesn't handle degenerate input; isn't symmetric with inclusion of corner pts;
-// introduces error with divide; doesn't short circuit on early answer
-#if 0
-// 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);
-#else
-    double w = dot00 * dot11 - dot01 * dot01;
-    if (w == 0) {
-        return false;
-    }
-    double wSign = w < 0 ? -1 : 1;
-    double u = (dot11 * dot02 - dot01 * dot12) * wSign;
-    if (u <= 0) {
-        return false;
-    }
-    double v = (dot00 * dot12 - dot01 * dot02) * wSign;
-    if (v <= 0) {
-        return false;
-    }
-    return u + v < w * wSign;
-#endif
-}
diff --git a/src/pathops/SkPathOpsTriangle.h b/src/pathops/SkPathOpsTriangle.h
deleted file mode 100644
index 8cc8c6d..0000000
--- a/src/pathops/SkPathOpsTriangle.h
+++ /dev/null
@@ -1,20 +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 SkPathOpsTriangle_DEFINED
-#define SkPathOpsTriangle_DEFINED
-
-#include "SkPathOpsPoint.h"
-
-struct SkDTriangle {
-    SkDPoint fPts[3];
-
-    bool contains(const SkDPoint& pt) const;
-
-};
-
-#endif
diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h
index 01fec0d..845288f 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -22,6 +22,124 @@
     kEvenOdd_PathOpsMask = 1
 };
 
+class SkOpCoincidence;
+class SkOpContour;
+class SkOpContourHead;
+
+class SkOpGlobalState {
+public:
+    SkOpGlobalState(SkOpCoincidence* coincidence, SkOpContourHead* head)
+        : fCoincidence(coincidence)
+        , fContourHead(head)
+        , fWindingFailed(false)
+        , fAngleCoincidence(false)
+#if DEBUG_VALIDATE
+        , fPhase(kIntersecting)
+#endif
+        SkDEBUGPARAMS(fAngleID(0))
+        SkDEBUGPARAMS(fContourID(0))
+        SkDEBUGPARAMS(fPtTID(0))
+        SkDEBUGPARAMS(fSegmentID(0))
+        SkDEBUGPARAMS(fSpanID(0)) {
+    }
+
+#if DEBUG_VALIDATE
+    enum Phase {
+        kIntersecting,
+        kWalking
+    };
+#endif
+
+    enum {
+        kMaxWindingTries = 10
+    };
+
+    bool angleCoincidence() {
+        return fAngleCoincidence;
+    }
+
+    SkOpCoincidence* coincidence() {
+        return fCoincidence;
+    }
+
+    SkOpContourHead* contourHead() {
+        return fContourHead;
+    }
+
+#ifdef SK_DEBUG
+    const struct SkOpAngle* debugAngle(int id) const;
+    SkOpContour* debugContour(int id);
+    const class SkOpPtT* debugPtT(int id) const;
+    const class SkOpSegment* debugSegment(int id) const;
+    const class SkOpSpanBase* debugSpan(int id) const;
+
+    int nextAngleID() {
+        return ++fAngleID;
+    }
+
+    int nextContourID() {
+        return ++fContourID;
+    }
+    int nextPtTID() {
+        return ++fPtTID;
+    }
+
+    int nextSegmentID() {
+        return ++fSegmentID;
+    }
+
+    int nextSpanID() {
+        return ++fSpanID;
+    }
+#endif
+
+#if DEBUG_VALIDATE
+    Phase phase() const {
+        return fPhase;
+    }
+#endif
+
+    void setAngleCoincidence() {
+        fAngleCoincidence = true;
+    }
+    
+    void setContourHead(SkOpContourHead* contourHead) {
+        fContourHead = contourHead;
+    }
+
+#if DEBUG_VALIDATE
+    void setPhase(Phase phase) {
+        SkASSERT(fPhase != phase);
+        fPhase = phase;
+    }
+#endif
+
+    // called in very rare cases where angles are sorted incorrectly -- signfies op will fail
+    void setWindingFailed() {
+        fWindingFailed = true;
+    }
+
+    bool windingFailed() const {
+        return fWindingFailed;
+    }
+
+private:
+    SkOpCoincidence* fCoincidence;
+    SkOpContourHead* fContourHead;
+    bool fWindingFailed;
+    bool fAngleCoincidence;
+#if DEBUG_VALIDATE
+    Phase fPhase;
+#endif
+#ifdef SK_DEBUG
+    int fAngleID;
+    int fContourID;
+    int fPtTID;
+    int fSegmentID;
+    int fSpanID;
+#endif
+};
+
 // Use Almost Equal when comparing coordinates. Use epsilon to compare T values.
 bool AlmostEqualUlps(float a, float b);
 inline bool AlmostEqualUlps(double a, double b) {
@@ -92,6 +210,7 @@
 const double ROUGH_EPSILON = FLT_EPSILON * 64;
 const double MORE_ROUGH_EPSILON = FLT_EPSILON * 256;
 const double WAY_ROUGH_EPSILON = FLT_EPSILON * 2048;
+const double BUMP_EPSILON = FLT_EPSILON * 4096;
 
 inline bool zero_or_one(double x) {
     return x == 0 || x == 1;
@@ -141,12 +260,6 @@
     return fabs(x) < ROUGH_EPSILON;
 }
 
-#if 0  // unused for now
-inline bool way_roughly_zero(double x) {
-    return fabs(x) < WAY_ROUGH_EPSILON;
-}
-#endif
-
 inline bool approximately_zero_inverse(double x) {
     return fabs(x) > FLT_EPSILON_INVERSE;
 }
@@ -156,6 +269,10 @@
     return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
 }
 
+inline bool precisely_zero_when_compared_to(double x, double y) {
+    return x == 0 || fabs(x) < fabs(y * DBL_EPSILON);
+}
+
 // 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) {
@@ -304,7 +421,8 @@
 
 // returns true if (a <= b <= c) || (a >= b >= c)
 inline bool between(double a, double b, double c) {
-    SkASSERT(((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)
+            || (precisely_zero(a) && precisely_zero(b) && precisely_zero(c)));
     return (a - b) * (c - b) <= 0;
 }
 
@@ -312,6 +430,15 @@
     return fabs(x - y) < ROUGH_EPSILON;
 }
 
+inline bool roughly_negative(double x) {
+    return x < ROUGH_EPSILON;
+}
+
+inline bool roughly_between(double a, double b, double c) {
+    return a <= c ? roughly_negative(a - b) && roughly_negative(b - c)
+            : roughly_negative(b - a) && roughly_negative(c - b);
+}
+
 inline bool more_roughly_equal(double x, double y) {
     return fabs(x - y) < MORE_ROUGH_EPSILON;
 }
@@ -324,7 +451,7 @@
 struct SkDVector;
 struct SkDLine;
 struct SkDQuad;
-struct SkDTriangle;
+struct SkDConic;
 struct SkDCubic;
 struct SkDRect;
 
@@ -343,11 +470,12 @@
 }
 
 inline int SkPathOpsVerbToPoints(SkPath::Verb verb) {
-    int points = (int) verb - ((int) verb >> 2);
+    int points = (int) verb - (((int) verb + 1) >> 2);
 #ifdef SK_DEBUG
     switch (verb) {
         case SkPath::kLine_Verb: SkASSERT(1 == points); break;
         case SkPath::kQuad_Verb: SkASSERT(2 == points); break;
+        case SkPath::kConic_Verb: SkASSERT(2 == points); break;
         case SkPath::kCubic_Verb: SkASSERT(3 == points); break;
         default: SkDEBUGFAIL("should not get here");
     }
diff --git a/src/pathops/SkPathOpsWinding.cpp b/src/pathops/SkPathOpsWinding.cpp
new file mode 100644
index 0000000..53083e4
--- /dev/null
+++ b/src/pathops/SkPathOpsWinding.cpp
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// given a prospective edge, compute its initial winding by projecting a ray
+// if the ray hits another edge
+    // if the edge doesn't have a winding yet, hop up to that edge and start over
+        // concern : check for hops forming a loop
+    // if the edge is unsortable, or
+    // the intersection is nearly at the ends, or
+    // the tangent at the intersection is nearly coincident to the ray,
+        // choose a different ray and try again
+            // concern : if it is unable to succeed after N tries, try another edge? direction?
+// if no edge is hit, compute the winding directly
+
+// given the top span, project the most perpendicular ray and look for intersections
+    // let's try up and then down. What the hey
+
+// bestXY is initialized by caller with basePt
+
+#include "SkOpContour.h"
+#include "SkOpSegment.h"
+#include "SkPathOpsCurve.h"
+
+enum class SkOpRayDir {
+    kLeft,
+    kTop,
+    kRight,
+    kBottom,
+};
+
+#if DEBUG_WINDING
+const char* gDebugRayDirName[] = {
+    "kLeft",
+    "kTop",
+    "kRight",
+    "kBottom"
+};
+#endif
+
+static int xy_index(SkOpRayDir dir) {
+    return static_cast<int>(dir) & 1;
+}
+
+static SkScalar pt_xy(const SkPoint& pt, SkOpRayDir dir) {
+    return (&pt.fX)[xy_index(dir)];
+}
+
+static SkScalar pt_yx(const SkPoint& pt, SkOpRayDir dir) {
+    return (&pt.fX)[!xy_index(dir)];
+}
+
+static double pt_dxdy(const SkDVector& v, SkOpRayDir dir) {
+    return (&v.fX)[xy_index(dir)];
+}
+
+static double pt_dydx(const SkDVector& v, SkOpRayDir dir) {
+    return (&v.fX)[!xy_index(dir)];
+}
+
+static SkScalar rect_side(const SkRect& r, SkOpRayDir dir) {
+    return (&r.fLeft)[static_cast<int>(dir)];
+}
+
+static bool sideways_overlap(const SkRect& rect, const SkPoint& pt, SkOpRayDir dir) {
+    int i = !xy_index(dir);
+    return approximately_between((&rect.fLeft)[i], (&pt.fX)[i], (&rect.fRight)[i]);
+}
+
+static bool less_than(SkOpRayDir dir) {
+    return static_cast<bool>((static_cast<int>(dir) & 2) == 0);
+}
+
+static bool ccw_dxdy(const SkDVector& v, SkOpRayDir dir) {
+    bool vPartPos = pt_dydx(v, dir) > 0;
+    bool leftBottom = ((static_cast<int>(dir) + 1) & 2) != 0;
+    return vPartPos == leftBottom;
+}
+
+struct SkOpRayHit {
+    SkOpRayDir makeTestBase(SkOpSpan* span, double t) {
+        fNext = NULL;
+        fSpan = span;
+        fT = span->t() * (1 - t) + span->next()->t() * t;
+        SkOpSegment* segment = span->segment();
+        fSlope = segment->dSlopeAtT(fT);
+        fPt = segment->ptAtT(fT);
+        fValid = true;
+        return fabs(fSlope.fX) < fabs(fSlope.fY) ? SkOpRayDir::kLeft : SkOpRayDir::kTop;
+    }
+
+    SkOpRayHit* fNext;
+    SkOpSpan* fSpan;
+    SkPoint fPt;
+    double fT;
+    SkDVector fSlope;
+    bool fValid;
+};
+
+void SkOpContour::rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits,
+        SkChunkAlloc* allocator) {
+    // if the bounds extreme is outside the best, we're done
+    SkScalar baseXY = pt_xy(base.fPt, dir);
+    SkScalar boundsXY = rect_side(fBounds, dir);
+    bool checkLessThan = less_than(dir);
+    if (!approximately_equal(baseXY, boundsXY) && (baseXY < boundsXY) == checkLessThan) {
+        return;
+    }
+    SkOpSegment* testSegment = &fHead;
+    do {
+        testSegment->rayCheck(base, dir, hits, allocator);
+    } while ((testSegment = testSegment->next()));
+}
+
+void SkOpSegment::rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits,
+        SkChunkAlloc* allocator) {
+    if (!sideways_overlap(fBounds, base.fPt, dir)) {
+        return;
+    }
+    SkScalar baseXY = pt_xy(base.fPt, dir);
+    SkScalar boundsXY = rect_side(fBounds, dir);
+    bool checkLessThan = less_than(dir);
+    if (!approximately_equal(baseXY, boundsXY) && (baseXY < boundsXY) == checkLessThan) {
+        return;
+    }
+    double tVals[3];
+    SkScalar baseYX = pt_yx(base.fPt, dir);
+    int roots = (*CurveIntercept[fVerb * 2 + xy_index(dir)])(fPts, fWeight, baseYX, tVals);
+    for (int index = 0; index < roots; ++index) {
+        double t = tVals[index];
+        if (base.fSpan->segment() == this && approximately_equal(base.fT, t)) {
+            continue;
+        }
+        SkDVector slope;
+        SkPoint pt;
+        SkDEBUGCODE(sk_bzero(&slope, sizeof(slope)));
+        bool valid = false;
+        if (approximately_zero(t)) {
+            pt = fPts[0];
+        } else if (approximately_equal(t, 1)) {
+            pt = fPts[SkPathOpsVerbToPoints(fVerb)];
+        } else {
+            SkASSERT(between(0, t, 1));
+            pt = this->ptAtT(t);
+            if (SkDPoint::ApproximatelyEqual(pt, base.fPt)) {
+                if (base.fSpan->segment() == this) {
+                    continue;
+                }
+            } else {
+                SkScalar ptXY = pt_xy(pt, dir);
+                if (!approximately_equal(baseXY, ptXY) && (baseXY < ptXY) == checkLessThan) {
+                    continue;
+                }
+                slope = this->dSlopeAtT(t);
+                if (fVerb == SkPath::kCubic_Verb && base.fSpan->segment() == this
+                        && roughly_equal(base.fT, t)
+                        && SkDPoint::RoughlyEqual(pt, base.fPt)) {
+    #if DEBUG_WINDING
+                    SkDebugf("%s (rarely expect this)\n", __FUNCTION__);
+    #endif
+                    continue;
+                }
+                if (fabs(pt_dydx(slope, dir) * 10000) > fabs(pt_dxdy(slope, dir))) {
+                    valid = true;
+                }
+            }
+        }
+        SkOpSpan* span = this->windingSpanAtT(t);
+        if (!span) {
+            valid = false;
+        } else if (!span->windValue() && !span->oppValue()) {
+            continue;
+        }
+        SkOpRayHit* newHit = SkOpTAllocator<SkOpRayHit>::Allocate(allocator);
+        newHit->fNext = *hits;
+        newHit->fPt = pt;
+        newHit->fSlope = slope;
+        newHit->fSpan = span;
+        newHit->fT = t;
+        newHit->fValid = valid;
+        *hits = newHit;
+    }
+}
+
+SkOpSpan* SkOpSegment::windingSpanAtT(double tHit) {
+    SkOpSpan* span = &fHead;
+    SkOpSpanBase* next;
+    do {
+        next = span->next();
+        if (approximately_equal(tHit, next->t())) {
+            return NULL;
+        } 
+        if (tHit < next->t()) {
+            return span;
+        }
+    } while (!next->final() && (span = next->upCast()));
+    return NULL;
+}
+
+static bool hit_compare_x(const SkOpRayHit* a, const SkOpRayHit* b) {
+    return a->fPt.fX < b->fPt.fX;
+}
+
+static bool reverse_hit_compare_x(const SkOpRayHit* a, const SkOpRayHit* b) {
+    return b->fPt.fX  < a->fPt.fX;
+}
+
+static bool hit_compare_y(const SkOpRayHit* a, const SkOpRayHit* b) {
+    return a->fPt.fY < b->fPt.fY;
+}
+
+static bool reverse_hit_compare_y(const SkOpRayHit* a, const SkOpRayHit* b) {
+    return b->fPt.fY  < a->fPt.fY;
+}
+
+static double get_t_guess(int tTry, int* dirOffset) {
+    double t = 0.5;
+    *dirOffset = tTry & 1;
+    int tBase = tTry >> 1;
+    int tBits = 0;
+    while (tTry >>= 1) {
+        t /= 2;
+        ++tBits;
+    }
+    if (tBits) {
+        int tIndex = (tBase - 1) & ((1 << tBits) - 1);
+        t += t * 2 * tIndex;
+    }
+    return t;
+}
+
+bool SkOpSpan::sortableTop(SkOpContour* contourHead) {
+    SkChunkAlloc allocator(1024);
+    int dirOffset;
+    double t = get_t_guess(fTopTTry++, &dirOffset);
+    SkOpRayHit hitBase;
+    SkOpRayDir dir = hitBase.makeTestBase(this, t);
+    if (hitBase.fSlope.fX == 0 && hitBase.fSlope.fY == 0) {
+        return false;
+    }
+    SkOpRayHit* hitHead = &hitBase;
+    dir = static_cast<SkOpRayDir>(static_cast<int>(dir) + dirOffset);
+    SkOpContour* contour = contourHead;
+    do {
+        contour->rayCheck(hitBase, dir, &hitHead, &allocator);
+    } while ((contour = contour->next()));
+    // sort hits
+    SkSTArray<1, SkOpRayHit*> sorted;
+    SkOpRayHit* hit = hitHead;
+    while (hit) {
+        sorted.push_back(hit);
+        hit = hit->fNext;
+    }
+    int count = sorted.count();
+    SkTQSort(sorted.begin(), sorted.end() - 1, xy_index(dir)
+            ? less_than(dir) ? hit_compare_y : reverse_hit_compare_y 
+            : less_than(dir) ? hit_compare_x : reverse_hit_compare_x);
+    // verify windings
+#if DEBUG_WINDING
+    SkDebugf("%s dir=%s seg=%d t=%1.9g pt=(%1.9g,%1.9g)\n", __FUNCTION__, 
+            gDebugRayDirName[static_cast<int>(dir)], hitBase.fSpan->segment()->debugID(),
+            hitBase.fT, hitBase.fPt.fX, hitBase.fPt.fY);
+    for (int index = 0; index < count; ++index) {
+        hit = sorted[index];
+        SkOpSpan* span = hit->fSpan;
+        SkOpSegment* hitSegment = span ? span->segment() : NULL;
+        bool operand = span ? hitSegment->operand() : false;
+        bool ccw = ccw_dxdy(hit->fSlope, dir);
+        SkDebugf("%s [%d] valid=%d operand=%d span=%d ccw=%d ", __FUNCTION__, index,
+                hit->fValid, operand, span ? span->debugID() : -1, ccw);
+        if (span) {
+            hitSegment->dumpPtsInner();
+        }
+        SkDebugf(" t=%1.9g pt=(%1.9g,%1.9g) slope=(%1.9g,%1.9g)\n", hit->fT,
+                hit->fPt.fX, hit->fPt.fY, hit->fSlope.fX, hit->fSlope.fY);
+    }
+#endif
+    const SkPoint* last = NULL;
+    int wind = 0;
+    int oppWind = 0;
+    for (int index = 0; index < count; ++index) {
+        hit = sorted[index];
+        if (!hit->fValid) {
+            return false;
+        }
+        bool ccw = ccw_dxdy(hit->fSlope, dir);
+        SkASSERT(!approximately_zero(hit->fT) || !hit->fValid);
+        SkOpSpan* span = hit->fSpan;
+        SkOpSegment* hitSegment = span->segment();
+        if (!span) {
+            return false;
+        }
+        if (span->windValue() == 0 && span->oppValue() == 0) {
+            continue;
+        }
+        if (last && SkDPoint::ApproximatelyEqual(*last, hit->fPt)) {
+            return false;
+        }
+        if (index < count - 1) {
+            const SkPoint& next = sorted[index + 1]->fPt;
+            if (SkDPoint::ApproximatelyEqual(next, hit->fPt)) {
+                return false;
+            }
+        }
+        bool operand = hitSegment->operand();
+        if (operand) {
+            SkTSwap(wind, oppWind);
+        }
+        int lastWind = wind;
+        int lastOpp = oppWind;
+        int windValue = ccw ? -span->windValue() : span->windValue();
+        int oppValue = ccw ? -span->oppValue() : span->oppValue();
+        wind += windValue;
+        oppWind += oppValue;
+        bool sumSet = false;
+        int spanSum = span->windSum();
+        int windSum = SkOpSegment::UseInnerWinding(lastWind, wind) ? wind : lastWind;
+        if (spanSum == SK_MinS32) {
+            span->setWindSum(windSum);
+            sumSet = true;
+        } else {
+            // the need for this condition suggests that UseInnerWinding is flawed
+            // happened when last = 1 wind = -1
+#if 0
+            SkASSERT((hitSegment->isXor() ? (windSum & 1) == (spanSum & 1) : windSum == spanSum)
+                    || (abs(wind) == abs(lastWind)
+                    && (windSum ^ wind ^ lastWind) == spanSum));
+#endif
+        }
+        int oSpanSum = span->oppSum();
+        int oppSum = SkOpSegment::UseInnerWinding(lastOpp, oppWind) ? oppWind : lastOpp;
+        if (oSpanSum == SK_MinS32) {
+            span->setOppSum(oppSum);
+        } else {
+#if 0
+            SkASSERT(hitSegment->oppXor() ? (oppSum & 1) == (oSpanSum & 1) : oppSum == oSpanSum
+                    || (abs(oppWind) == abs(lastOpp)
+                    && (oppSum ^ oppWind ^ lastOpp) == oSpanSum));
+#endif
+        }
+        if (sumSet) {
+            (void) hitSegment->markAndChaseWinding(span, span->next(), windSum, oppSum, NULL);
+            (void) hitSegment->markAndChaseWinding(span->next(), span, windSum, oppSum, NULL);
+        }
+        if (operand) {
+            SkTSwap(wind, oppWind);
+        }
+        last = &hit->fPt;
+    }
+    return true;
+}
+
+SkOpSpan* SkOpSegment::findSortableTop(SkOpContour* contourHead) {
+    SkOpSpan* span = &fHead;
+    SkOpSpanBase* next;
+    do {
+        next = span->next();
+        if (span->done()) {
+            continue;
+        }
+        if (span->windSum() != SK_MinS32) {
+            return span;
+        }
+        if (span->sortableTop(contourHead)) {
+            return span;
+        }
+    } while (!next->final() && (span = next->upCast()));
+    return NULL;
+}
+
+SkOpSpan* SkOpContour::findSortableTop(SkOpContour* contourHead) {
+    SkOpSegment* testSegment = &fHead;
+    do {
+        if (testSegment->done()) {
+            continue;
+        }
+        SkOpSpan* result = testSegment->findSortableTop(contourHead);
+        if (result) {
+            return result;
+        }
+    } while ((testSegment = testSegment->next()));
+    return NULL;
+}
+
+SkOpSpan* FindSortableTop(SkOpContourHead* contourHead) {
+    for (int index = 0; index < SkOpGlobalState::kMaxWindingTries; ++index) {
+        SkOpContour* contour = contourHead;
+        do {
+            if (contour->done()) {
+                continue;
+            }
+            SkOpSpan* result = contour->findSortableTop(contourHead);
+            if (result) {
+                return result;
+            }
+        } while ((contour = contour->next()));
+    }
+    return NULL;
+}
diff --git a/src/pathops/SkPathWriter.cpp b/src/pathops/SkPathWriter.cpp
index 46ec7b9..bd8c721 100644
--- a/src/pathops/SkPathWriter.cpp
+++ b/src/pathops/SkPathWriter.cpp
@@ -35,6 +35,24 @@
     init();
 }
 
+void SkPathWriter::conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar weight) {
+    lineTo();
+    if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)) {
+        deferredLine(pt2);
+        return;
+    }
+    moveTo();
+    fDefer[1] = pt2;
+    nudge();
+    fDefer[0] = fDefer[1];
+#if DEBUG_PATH_CONSTRUCTION
+    SkDebugf("path.conicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g);\n",
+            pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY, weight);
+#endif
+    fPathPtr->conicTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY, weight);
+    fEmpty = false;
+}
+
 void SkPathWriter::cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
     lineTo();
     if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)
diff --git a/src/pathops/SkPathWriter.h b/src/pathops/SkPathWriter.h
index f32074d..820ddc5 100644
--- a/src/pathops/SkPathWriter.h
+++ b/src/pathops/SkPathWriter.h
@@ -13,6 +13,7 @@
 public:
     SkPathWriter(SkPath& path);
     void close();
+    void conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar weight);
     void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3);
     void deferredLine(const SkPoint& pt);
     void deferredMove(const SkPoint& pt);
diff --git a/src/pathops/SkQuarticRoot.cpp b/src/pathops/SkQuarticRoot.cpp
deleted file mode 100644
index f9a7bf5..0000000
--- a/src/pathops/SkQuarticRoot.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-// from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
-/*
- *  Roots3And4.c
- *
- *  Utility functions to find cubic and quartic roots,
- *  coefficients are passed like this:
- *
- *      c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0
- *
- *  The functions return the number of non-complex roots and
- *  put the values into the s array.
- *
- *  Author:         Jochen Schwarze (schwarze@isa.de)
- *
- *  Jan 26, 1990    Version for Graphics Gems
- *  Oct 11, 1990    Fixed sign problem for negative q's in SolveQuartic
- *                  (reported by Mark Podlipec),
- *                  Old-style function definitions,
- *                  IsZero() as a macro
- *  Nov 23, 1990    Some systems do not declare acos() and cbrt() in
- *                  <math.h>, though the functions exist in the library.
- *                  If large coefficients are used, EQN_EPS should be
- *                  reduced considerably (e.g. to 1E-30), results will be
- *                  correct but multiple roots might be reported more
- *                  than once.
- */
-
-#include "SkPathOpsCubic.h"
-#include "SkPathOpsQuad.h"
-#include "SkQuarticRoot.h"
-
-int SkReducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
-        const double t0, const bool oneHint, double roots[4]) {
-#ifdef 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];
-    sk_bzero(str, sizeof(str));
-    SK_SNPRINTF(str, sizeof(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);
-    SkPathOpsDebug::MathematicaIze(str, sizeof(str));
-#if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
-    SkDebugf("%s\n", str);
-#endif
-#endif
-    if (approximately_zero_when_compared_to(t4, t0)  // 0 is one root
-            && approximately_zero_when_compared_to(t4, t1)
-            && approximately_zero_when_compared_to(t4, t2)) {
-        if (approximately_zero_when_compared_to(t3, t0)
-            && approximately_zero_when_compared_to(t3, t1)
-            && approximately_zero_when_compared_to(t3, t2)) {
-            return SkDQuad::RootsReal(t2, t1, t0, roots);
-        }
-        if (approximately_zero_when_compared_to(t4, t3)) {
-            return SkDCubic::RootsReal(t3, t2, t1, t0, roots);
-        }
-    }
-    if ((approximately_zero_when_compared_to(t0, t1) || approximately_zero(t1))  // 0 is one root
-      //      && approximately_zero_when_compared_to(t0, t2)
-            && approximately_zero_when_compared_to(t0, t3)
-            && approximately_zero_when_compared_to(t0, t4)) {
-        int num = SkDCubic::RootsReal(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_double(t4 + t3 + t2 + t1 + t0) ||
-                approximately_zero_when_compared_to(t4 + t3 + t2 + t1 + t0,  // 1 is one root
-                SkTMax(fabs(t4), SkTMax(fabs(t3), SkTMax(fabs(t2), SkTMax(fabs(t1), fabs(t0)))))));
-        // note that -C == A + B + D + E
-        int num = SkDCubic::RootsReal(t4, t4 + t3, -(t1 + t0), -t0, roots);
-        for (int i = 0; i < num; ++i) {
-            if (approximately_equal(roots[i], 1)) {
-                return num;
-            }
-        }
-        roots[num++] = 1;
-        return num;
-    }
-    return -1;
-}
-
-int SkQuarticRootsReal(int firstCubicRoot, const double A, const double B, const double C,
-        const double D, const double E, double s[4]) {
-    double  u, v;
-    /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
-    const double invA = 1 / A;
-    const double a = B * invA;
-    const double b = C * invA;
-    const double c = D * invA;
-    const double d = E * invA;
-    /*  substitute x = y - a/4 to eliminate cubic term:
-    x^4 + px^2 + qx + r = 0 */
-    const double a2 = a * a;
-    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;
-    double largest = SkTMax(fabs(p), fabs(q));
-    if (approximately_zero_when_compared_to(r, largest)) {
-    /* no absolute term: y(y^3 + py + q) = 0 */
-        num = SkDCubic::RootsReal(1, 0, p, q, s);
-        s[num++] = 0;
-    } else {
-        /* solve the resolvent cubic ... */
-        double cubicRoots[3];
-        int roots = SkDCubic::RootsReal(1, -p / 2, -r, r * p / 2 - q * q / 8, cubicRoots);
-        int index;
-        /* ... and take one real solution ... */
-        double z;
-        num = 0;
-        int num2 = 0;
-        for (index = firstCubicRoot; 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 = SkDQuad::RootsReal(1, q < 0 ? -v : v, z - u, s);
-            num2 = SkDQuad::RootsReal(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 (int i = 0; i < num - 1; ++i) {
-        for (int j = i + 1; j < num; ) {
-            if (AlmostDequalUlps(s[i], s[j])) {
-                if (j < --num) {
-                    s[j] = s[num];
-                }
-            } else {
-                ++j;
-            }
-        }
-    }
-    return num;
-}
diff --git a/src/pathops/SkQuarticRoot.h b/src/pathops/SkQuarticRoot.h
deleted file mode 100644
index 6ce0867..0000000
--- a/src/pathops/SkQuarticRoot.h
+++ /dev/null
@@ -1,16 +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 SkDQuarticRoot_DEFINED
-#define SkDQuarticRoot_DEFINED
-
-int SkReducedQuarticRoots(const double t4, const double t3, const double t2, const double t1,
-        const double t0, const bool oneHint, double s[4]);
-
-int SkQuarticRootsReal(int firstCubicRoot, const double A, const double B, const double C,
-        const double D, const double E, double s[4]);
-
-#endif
diff --git a/src/pathops/SkReduceOrder.cpp b/src/pathops/SkReduceOrder.cpp
index 6f06447..9c19bc5 100644
--- a/src/pathops/SkReduceOrder.cpp
+++ b/src/pathops/SkReduceOrder.cpp
@@ -271,7 +271,20 @@
     return SkPathOpsPointsToVerb(order - 1);
 }
 
+SkPath::Verb SkReduceOrder::Conic(const SkPoint a[3], SkScalar weight, SkPoint* reducePts) {
+    SkPath::Verb verb = SkReduceOrder::Quad(a, reducePts);
+    if (verb > SkPath::kLine_Verb && weight == 1) {
+        return SkPath::kQuad_Verb;
+    }
+    return verb == SkPath::kQuad_Verb ? SkPath::kConic_Verb : verb;
+}
+
 SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkPoint* reducePts) {
+    if (SkDPoint::ApproximatelyEqual(a[0], a[1]) && SkDPoint::ApproximatelyEqual(a[0], a[2])
+            && SkDPoint::ApproximatelyEqual(a[0], a[3])) {
+        reducePts[0] = a[0];
+        return SkPath::kMove_Verb;
+    }
     SkDCubic cubic;
     cubic.set(a);
     SkReduceOrder reducer;
diff --git a/src/pathops/SkReduceOrder.h b/src/pathops/SkReduceOrder.h
index 4ff9a1d..e9e4090 100644
--- a/src/pathops/SkReduceOrder.h
+++ b/src/pathops/SkReduceOrder.h
@@ -7,11 +7,9 @@
 #ifndef SkReduceOrder_DEFINED
 #define SkReduceOrder_DEFINED
 
-#include "SkPath.h"
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsLine.h"
 #include "SkPathOpsQuad.h"
-#include "SkTArray.h"
 
 union SkReduceOrder {
     enum Quadratics {
@@ -23,6 +21,7 @@
     int reduce(const SkDLine& line);
     int reduce(const SkDQuad& quad);
 
+    static SkPath::Verb Conic(const SkPoint pts[3], SkScalar weight, SkPoint* reducePts);
     static SkPath::Verb Cubic(const SkPoint pts[4], SkPoint* reducePts);
     static SkPath::Verb Quad(const SkPoint pts[3], SkPoint* reducePts);
 
diff --git a/src/pdf/SkJpegInfo.cpp b/src/pdf/SkJpegInfo.cpp
new file mode 100644
index 0000000..85cd325
--- /dev/null
+++ b/src/pdf/SkJpegInfo.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkData.h"
+#include "SkJpegInfo.h"
+
+namespace {
+class JpegSegment {
+public:
+    JpegSegment(const SkData* skdata)
+        : fData(static_cast<const char*>(skdata->data()))
+        , fSize(skdata->size())
+        , fOffset(0)
+        , fLength(0) {}
+    bool read() {
+        if (!this->readBigendianUint16(&fMarker)) {
+            return false;
+        }
+        if (JpegSegment::StandAloneMarker(fMarker)) {
+            fLength = 0;
+            fBuffer = NULL;
+            return true;
+        }
+        if (!this->readBigendianUint16(&fLength) || fLength < 2) {
+            return false;
+        }
+        fLength -= 2;  // Length includes itself for some reason.
+        if (fOffset + fLength > fSize) {
+            return false;  // Segment too long.
+        }
+        fBuffer = &fData[fOffset];
+        fOffset += fLength;
+        return true;
+    }
+
+    bool isSOF() {
+        return (fMarker & 0xFFF0) == 0xFFC0 && fMarker != 0xFFC4 &&
+               fMarker != 0xFFC8 && fMarker != 0xFFCC;
+    }
+    uint16_t marker() { return fMarker; }
+    uint16_t length() { return fLength; }
+    const char* data() { return fBuffer; }
+
+    static uint16_t GetBigendianUint16(const char* ptr) {
+        // "the most significant byte shall come first"
+        return (static_cast<uint8_t>(ptr[0]) << 8) |
+            static_cast<uint8_t>(ptr[1]);
+    }
+
+private:
+    const char* const fData;
+    const size_t fSize;
+    size_t fOffset;
+    const char* fBuffer;
+    uint16_t fMarker;
+    uint16_t fLength;
+
+    bool readBigendianUint16(uint16_t* value) {
+        if (fOffset + 2 > fSize) {
+            return false;
+        }
+        *value = JpegSegment::GetBigendianUint16(&fData[fOffset]);
+        fOffset += 2;
+        return true;
+    }
+    static bool StandAloneMarker(uint16_t marker) {
+        // RST[m] markers or SOI, EOI, TEM
+        return (marker & 0xFFF8) == 0xFFD0 || marker == 0xFFD8 ||
+               marker == 0xFFD9 || marker == 0xFF01;
+    }
+};
+}  // namespace
+
+bool SkIsJFIF(const SkData* skdata, SkJFIFInfo* info) {
+    static const uint16_t kSOI = 0xFFD8;
+    static const uint16_t kAPP0 = 0xFFE0;
+    JpegSegment segment(skdata);
+    if (!segment.read() || segment.marker() != kSOI) {
+        return false;  // not a JPEG
+    }
+    if (!segment.read() || segment.marker() != kAPP0) {
+        return false;  // not an APP0 segment
+    }
+    static const char kJfif[] = {'J', 'F', 'I', 'F', '\0'};
+    SkASSERT(segment.data());
+    if (SkToSizeT(segment.length()) < sizeof(kJfif) ||
+        0 != memcmp(segment.data(), kJfif, sizeof(kJfif))) {
+        return false;  // Not JFIF JPEG
+    }
+    do {
+        if (!segment.read()) {
+            return false;  // malformed JPEG
+        }
+    } while (!segment.isSOF());
+    if (segment.length() < 6) {
+        return false;  // SOF segment is short
+    }
+    if (8 != segment.data()[0]) {
+        return false;  // Only support 8-bit precision
+    }
+    int numberOfComponents = segment.data()[5];
+    if (numberOfComponents != 1 && numberOfComponents != 3) {
+        return false;  // Invalid JFIF
+    }
+    if (info) {
+        info->fHeight = JpegSegment::GetBigendianUint16(&segment.data()[1]);
+        info->fWidth = JpegSegment::GetBigendianUint16(&segment.data()[3]);
+        if (numberOfComponents == 3) {
+            info->fType = SkJFIFInfo::kYCbCr;
+        } else {
+            info->fType = SkJFIFInfo::kGrayscale;
+        }
+    }
+    return true;
+}
+
+
diff --git a/src/pdf/SkJpegInfo.h b/src/pdf/SkJpegInfo.h
new file mode 100644
index 0000000..1be4c0f
--- /dev/null
+++ b/src/pdf/SkJpegInfo.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkJpegInfo_DEFINED
+#define SkJpegInfo_DEFINED
+
+class SkData;
+
+struct SkJFIFInfo {
+    int fWidth;
+    int fHeight;
+    enum Type {
+        kGrayscale,
+        kYCbCr,
+    } fType;
+};
+
+/** Returns true iff the data seems to be a valid JFIF JPEG image.  
+    If so and if info is not NULL, populate info.
+
+    JPEG/JFIF References:
+        http://www.w3.org/Graphics/JPEG/itu-t81.pdf
+        http://www.w3.org/Graphics/JPEG/jfif3.pdf
+*/
+bool SkIsJFIF(const SkData* skdata, SkJFIFInfo* info);
+
+#endif  // SkJpegInfo_DEFINED
diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp
index 668f7de..4e044a2 100644
--- a/src/pdf/SkPDFBitmap.cpp
+++ b/src/pdf/SkPDFBitmap.cpp
@@ -6,10 +6,13 @@
  */
 
 #include "SkColorPriv.h"
+#include "SkData.h"
 #include "SkFlate.h"
+#include "SkImageGenerator.h"
+#include "SkJpegInfo.h"
 #include "SkPDFBitmap.h"
 #include "SkPDFCanon.h"
-#include "SkPDFCatalog.h"
+#include "SkPixelRef.h"
 #include "SkStream.h"
 #include "SkUnPreMultiply.h"
 
@@ -25,114 +28,214 @@
     stream->write(streamEnd, strlen(streamEnd));
 }
 
-static size_t pixel_count(const SkBitmap& bm) {
-    return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
-}
+////////////////////////////////////////////////////////////////////////////////
 
 // write a single byte to a stream n times.
 static void fill_stream(SkWStream* out, char value, size_t n) {
     char buffer[4096];
     memset(buffer, value, sizeof(buffer));
-    while (n) {
-        size_t k = SkTMin(n, sizeof(buffer));
-        out->write(buffer, k);
-        n -= k;
+    for (size_t i = 0; i < n / sizeof(buffer); ++i) {
+        out->write(buffer, sizeof(buffer));
     }
+    out->write(buffer, n % sizeof(buffer));
 }
 
-static SkPMColor get_pmcolor_neighbor_avg_color(const SkBitmap& bitmap,
-                                                int xOrig,
-                                                int yOrig) {
-    SkASSERT(kN32_SkColorType == bitmap.colorType());
-    SkASSERT(bitmap.getPixels());
-    uint8_t count = 0;
-    unsigned r = 0;
-    unsigned g = 0;
-    unsigned b = 0;
-    for (int y = yOrig - 1; y <= yOrig + 1; ++y) {
-        if (y < 0 || y >= bitmap.height()) {
-            continue;
-        }
-        uint32_t* src = bitmap.getAddr32(0, y);
-        for (int x = xOrig - 1; x <= xOrig + 1; ++x) {
-            if (x < 0 || x >= bitmap.width()) {
-                continue;
-            }
-            SkPMColor pmColor = src[x];
-            U8CPU alpha = SkGetPackedA32(pmColor);
-            if (alpha != SK_AlphaTRANSPARENT) {
-                uint32_t s = SkUnPreMultiply::GetScale(alpha);
-                r += SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
-                g += SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
-                b += SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
-                ++count;
-            }
+// unpremultiply and extract R, G, B components.
+static void pmcolor_to_rgb24(SkPMColor pmColor, uint8_t* rgb) {
+    uint32_t s = SkUnPreMultiply::GetScale(SkGetPackedA32(pmColor));
+    rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
+    rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
+    rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
+}
+
+/* It is necessary to average the color component of transparent
+   pixels with their surrounding neighbors since the PDF renderer may
+   separately re-sample the alpha and color channels when the image is
+   not displayed at its native resolution. Since an alpha of zero
+   gives no information about the color component, the pathological
+   case is a white image with sharp transparency bounds - the color
+   channel goes to black, and the should-be-transparent pixels are
+   rendered as grey because of the separate soft mask and color
+   resizing. e.g.: gm/bitmappremul.cpp */
+static void get_neighbor_avg_color(const SkBitmap& bm,
+                                   int xOrig,
+                                   int yOrig,
+                                   uint8_t rgb[3]) {
+    SkASSERT(kN32_SkColorType == bm.colorType());
+    unsigned a = 0, r = 0, g = 0, b = 0;
+    // Clamp the range to the edge of the bitmap.
+    int ymin = SkTMax(0, yOrig - 1);
+    int ymax = SkTMin(yOrig + 1, bm.height() - 1);
+    int xmin = SkTMax(0, xOrig - 1);
+    int xmax = SkTMin(xOrig + 1, bm.width() - 1);
+    for (int y = ymin; y <= ymax; ++y) {
+        SkPMColor* scanline = bm.getAddr32(0, y);
+        for (int x = xmin; x <= xmax; ++x) {
+            SkPMColor pmColor = scanline[x];
+            a += SkGetPackedA32(pmColor);
+            r += SkGetPackedR32(pmColor);
+            g += SkGetPackedG32(pmColor);
+            b += SkGetPackedB32(pmColor);
         }
     }
-    if (count == 0) {
-        return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0);
+    if (a > 0) {
+        rgb[0] = SkToU8(255 * r / a);
+        rgb[1] = SkToU8(255 * g / a);
+        rgb[2] = SkToU8(255 * b / a);
     } else {
-        return SkPackARGB32NoCheck(
-                SK_AlphaOPAQUE, r / count, g / count, b / count);
+        rgb[0] = rgb[1] = rgb[2] = 0;
     }
 }
 
-static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) {
-    SkASSERT(kN32_SkColorType == bm.colorType());
-    if (!bm.getPixels()) {
-        fill_stream(out, '\xFF', 3 * pixel_count(bm));
+static size_t pixel_count(const SkBitmap& bm) {
+    return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
+}
+
+static const SkBitmap& not4444(const SkBitmap& input, SkBitmap* copy) {
+    if (input.colorType() != kARGB_4444_SkColorType) {
+        return input;
+    }
+    // ARGB_4444 is rarely used, so we can do a wasteful tmp copy.
+    SkAssertResult(input.copyTo(copy, kN32_SkColorType));
+    copy->setImmutable();
+    return *copy;
+}
+
+static size_t pdf_color_component_count(SkColorType ct) {
+    switch (ct) {
+        case kN32_SkColorType:
+        case kRGB_565_SkColorType:
+        case kARGB_4444_SkColorType:
+            return 3;
+        case kAlpha_8_SkColorType:
+        case kIndex_8_SkColorType:
+        case kGray_8_SkColorType:
+            return 1;
+        case kUnknown_SkColorType:
+        default:
+            SkDEBUGFAIL("unexpected color type");
+            return 0;
+    }
+}
+
+static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) {
+    if (!bitmap.getPixels()) {
+        size_t size = pixel_count(bitmap) *
+                      pdf_color_component_count(bitmap.colorType());
+        fill_stream(out, '\x00', size);
         return;
     }
-    size_t scanlineLength = 3 * bm.width();
-    SkAutoTMalloc<uint8_t> scanline(scanlineLength);
-    for (int y = 0; y < bm.height(); ++y) {
-        uint8_t* dst = scanline.get();
-        const SkPMColor* src = bm.getAddr32(0, y);
-        for (int x = 0; x < bm.width(); ++x) {
-            SkPMColor color = *src++;
-            U8CPU alpha = SkGetPackedA32(color);
-            if (alpha != SK_AlphaTRANSPARENT) {
-                uint32_t s = SkUnPreMultiply::GetScale(alpha);
-                *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color));
-                *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color));
-                *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color));
-            } else {
-                /* It is necessary to average the color component of
-                   transparent pixels with their surrounding neighbors
-                   since the PDF renderer may separately re-sample the
-                   alpha and color channels when the image is not
-                   displayed at its native resolution. Since an alpha
-                   of zero gives no information about the color
-                   component, the pathological case is a white image
-                   with sharp transparency bounds - the color channel
-                   goes to black, and the should-be-transparent pixels
-                   are rendered as grey because of the separate soft
-                   mask and color resizing. e.g.: gm/bitmappremul.cpp */
-                color = get_pmcolor_neighbor_avg_color(bm, x, y);
-                *dst++ = SkGetPackedR32(color);
-                *dst++ = SkGetPackedG32(color);
-                *dst++ = SkGetPackedB32(color);
+    SkBitmap copy;
+    const SkBitmap& bm = not4444(bitmap, &copy);
+    SkAutoLockPixels autoLockPixels(bm);
+    switch (bm.colorType()) {
+        case kN32_SkColorType: {
+            SkASSERT(3 == pdf_color_component_count(bitmap.colorType()));
+            SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
+            for (int y = 0; y < bm.height(); ++y) {
+                const SkPMColor* src = bm.getAddr32(0, y);
+                uint8_t* dst = scanline.get();
+                for (int x = 0; x < bm.width(); ++x) {
+                    SkPMColor color = *src++;
+                    U8CPU alpha = SkGetPackedA32(color);
+                    if (alpha != SK_AlphaTRANSPARENT) {
+                        pmcolor_to_rgb24(color, dst);
+                    } else {
+                        get_neighbor_avg_color(bm, x, y, dst);
+                    }
+                    dst += 3;
+                }
+                out->write(scanline.get(), 3 * bm.width());
             }
+            return;
         }
-        out->write(scanline.get(), scanlineLength);
+        case kRGB_565_SkColorType: {
+            SkASSERT(3 == pdf_color_component_count(bitmap.colorType()));
+            SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
+            for (int y = 0; y < bm.height(); ++y) {
+                const uint16_t* src = bm.getAddr16(0, y);
+                uint8_t* dst = scanline.get();
+                for (int x = 0; x < bm.width(); ++x) {
+                    U16CPU color565 = *src++;
+                    *dst++ = SkPacked16ToR32(color565);
+                    *dst++ = SkPacked16ToG32(color565);
+                    *dst++ = SkPacked16ToB32(color565);
+                }
+                out->write(scanline.get(), 3 * bm.width());
+            }
+            return;
+        }
+        case kAlpha_8_SkColorType:
+            SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
+            fill_stream(out, '\x00', pixel_count(bm));
+            return;
+        case kGray_8_SkColorType:
+        case kIndex_8_SkColorType:
+            SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
+            // these two formats need no transformation to serialize.
+            for (int y = 0; y < bm.height(); ++y) {
+                out->write(bm.getAddr8(0, y), bm.width());
+            }
+            return;
+        case kUnknown_SkColorType:
+        case kARGB_4444_SkColorType:
+        default:
+            SkDEBUGFAIL("unexpected color type");
     }
 }
 
-static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) {
-    SkASSERT(kN32_SkColorType == bm.colorType());
-    if (!bm.getPixels()) {
-        fill_stream(out, '\xFF', pixel_count(bm));
+////////////////////////////////////////////////////////////////////////////////
+
+static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
+    if (!bitmap.getPixels()) {
+        fill_stream(out, '\xFF', pixel_count(bitmap));
         return;
     }
-    size_t scanlineLength = bm.width();
-    SkAutoTMalloc<uint8_t> scanline(scanlineLength);
-    for (int y = 0; y < bm.height(); ++y) {
-        uint8_t* dst = scanline.get();
-        const SkPMColor* src = bm.getAddr32(0, y);
-        for (int x = 0; x < bm.width(); ++x) {
-            *dst++ = SkGetPackedA32(*src++);
+    SkBitmap copy;
+    const SkBitmap& bm = not4444(bitmap, &copy);
+    SkAutoLockPixels autoLockPixels(bm);
+    switch (bm.colorType()) {
+        case kN32_SkColorType: {
+            SkAutoTMalloc<uint8_t> scanline(bm.width());
+            for (int y = 0; y < bm.height(); ++y) {
+                uint8_t* dst = scanline.get();
+                const SkPMColor* src = bm.getAddr32(0, y);
+                for (int x = 0; x < bm.width(); ++x) {
+                    *dst++ = SkGetPackedA32(*src++);
+                }
+                out->write(scanline.get(), bm.width());
+            }
+            return;
         }
-        out->write(scanline.get(), scanlineLength);
+        case kAlpha_8_SkColorType:
+            for (int y = 0; y < bm.height(); ++y) {
+                out->write(bm.getAddr8(0, y), bm.width());
+            }
+            return;
+        case kIndex_8_SkColorType: {
+            SkColorTable* ct = bm.getColorTable();
+            SkASSERT(ct);
+            SkAutoTMalloc<uint8_t> scanline(bm.width());
+            for (int y = 0; y < bm.height(); ++y) {
+                uint8_t* dst = scanline.get();
+                const uint8_t* src = bm.getAddr8(0, y);
+                for (int x = 0; x < bm.width(); ++x) {
+                    *dst++ = SkGetPackedA32((*ct)[*src++]);
+                }
+                out->write(scanline.get(), bm.width());
+            }
+            return;
+        }
+        case kRGB_565_SkColorType:
+        case kGray_8_SkColorType:
+            SkDEBUGFAIL("color type has no alpha");
+            return;
+        case kARGB_4444_SkColorType:
+            SkDEBUGFAIL("4444 color type should have been converted to N32");
+            return;
+        case kUnknown_SkColorType:
+        default:
+            SkDEBUGFAIL("unexpected color type");
     }
 }
 
@@ -144,173 +247,227 @@
 public:
     PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
     ~PDFAlphaBitmap() {}
-    void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
-    void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {}
+    void emitObject(SkWStream*,
+                    const SkPDFObjNumMap&,
+                    const SkPDFSubstituteMap&) override;
 
 private:
     const SkBitmap fBitmap;
-    void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
 };
 
-void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+void PDFAlphaBitmap::emitObject(SkWStream* stream,
+                                const SkPDFObjNumMap& objNumMap,
+                                const SkPDFSubstituteMap& substitutes) {
     SkAutoLockPixels autoLockPixels(fBitmap);
+    SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
+             fBitmap.getColorTable());
 
-#ifndef SK_NO_FLATE
     // Write to a temporary buffer to get the compressed length.
     SkDynamicMemoryWStream buffer;
     SkDeflateWStream deflateWStream(&buffer);
-    pmcolor_alpha_to_a8(fBitmap, &deflateWStream);
+    bitmap_alpha_to_a8(fBitmap, &deflateWStream);
     deflateWStream.finalize();  // call before detachAsStream().
     SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
 
-    this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
-    pdf_stream_begin(stream);
-    stream->writeStream(asset.get(), asset->getLength());
-    pdf_stream_end(stream);
-#else
-    this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false);
-    pdf_stream_begin(stream);
-    pmcolor_alpha_to_a8(fBitmap, stream);
-    pdf_stream_end(stream);
-#endif  // SK_NO_FLATE
-}
-
-void PDFAlphaBitmap::emitDict(SkWStream* stream,
-                              SkPDFCatalog* catalog,
-                              size_t length,
-                              bool deflate) const {
     SkPDFDict pdfDict("XObject");
     pdfDict.insertName("Subtype", "Image");
     pdfDict.insertInt("Width", fBitmap.width());
     pdfDict.insertInt("Height", fBitmap.height());
     pdfDict.insertName("ColorSpace", "DeviceGray");
     pdfDict.insertInt("BitsPerComponent", 8);
-    if (deflate) {
-        pdfDict.insertName("Filter", "FlateDecode");
-    }
-    pdfDict.insertInt("Length", length);
-    pdfDict.emitObject(stream, catalog);
+    pdfDict.insertName("Filter", "FlateDecode");
+    pdfDict.insertInt("Length", asset->getLength());
+    pdfDict.emitObject(stream, objNumMap, substitutes);
+
+    pdf_stream_begin(stream);
+    stream->writeStream(asset.get(), asset->getLength());
+    pdf_stream_end(stream);
 }
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet,
-                               SkPDFCatalog* catalog) const {
+namespace {
+class PDFDefaultBitmap : public SkPDFBitmap {
+public:
+    const SkAutoTUnref<SkPDFObject> fSMask;
+    void emitObject(SkWStream*,
+                    const SkPDFObjNumMap&,
+                    const SkPDFSubstituteMap&) override;
+    void addResources(SkPDFObjNumMap*,
+                      const SkPDFSubstituteMap&) const override;
+    PDFDefaultBitmap(const SkBitmap& bm, SkPDFObject* smask)
+        : SkPDFBitmap(bm), fSMask(smask) {}
+};
+}  // namespace
+
+void PDFDefaultBitmap::addResources(
+        SkPDFObjNumMap* catalog,
+        const SkPDFSubstituteMap& substitutes) const {
     if (fSMask.get()) {
-        resourceSet->add(fSMask.get());
+        SkPDFObject* obj = substitutes.getSubstitute(fSMask.get());
+        SkASSERT(obj);
+        if (catalog->addObject(obj)) {
+            obj->addResources(catalog, substitutes);
+        }
     }
 }
 
-void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    SkAutoLockPixels autoLockPixels(fBitmap);
+static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
+    SkPDFArray* result = SkNEW(SkPDFArray);
+    result->reserve(4);
+    result->appendName("Indexed");
+    result->appendName("DeviceRGB");
+    SkASSERT(table);
+    if (table->count() < 1) {
+        result->appendInt(0);
+        char shortTableArray[3] = {0, 0, 0};
+        SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
+        result->appendString(tableString);
+        return result;
+    }
+    result->appendInt(table->count() - 1);  // maximum color index.
 
-#ifndef SK_NO_FLATE
+    // Potentially, this could be represented in fewer bytes with a stream.
+    // Max size as a string is 1.5k.
+    char tableArray[256 * 3];
+    SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray));
+    uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray);
+    const SkPMColor* colors = table->readColors();
+    for (int i = 0; i < table->count(); i++) {
+        pmcolor_to_rgb24(colors[i], tablePtr);
+        tablePtr += 3;
+    }
+    SkString tableString(tableArray, 3 * table->count());
+    result->appendString(tableString);
+    return result;
+}
+
+void PDFDefaultBitmap::emitObject(SkWStream* stream,
+                                  const SkPDFObjNumMap& objNumMap,
+                                  const SkPDFSubstituteMap& substitutes) {
+    SkAutoLockPixels autoLockPixels(fBitmap);
+    SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
+             fBitmap.getColorTable());
+
     // Write to a temporary buffer to get the compressed length.
     SkDynamicMemoryWStream buffer;
     SkDeflateWStream deflateWStream(&buffer);
-    pmcolor_to_rgb24(fBitmap, &deflateWStream);
+    bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
     deflateWStream.finalize();  // call before detachAsStream().
     SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
 
-    this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
-    pdf_stream_begin(stream);
-    stream->writeStream(asset.get(), asset->getLength());
-    pdf_stream_end(stream);
-#else
-    this->emitDict(stream, catalog, 3 * pixel_count(fBitmap), /*deflate=*/false);
-    pdf_stream_begin(stream);
-    pmcolor_to_rgb24(fBitmap, stream);
-    pdf_stream_end(stream);
-    return;
-#endif  // SK_NO_FLATE
-}
-
-void SkPDFBitmap::emitDict(SkWStream* stream,
-                           SkPDFCatalog* catalog,
-                           size_t length,
-                           bool deflate) const {
     SkPDFDict pdfDict("XObject");
     pdfDict.insertName("Subtype", "Image");
     pdfDict.insertInt("Width", fBitmap.width());
     pdfDict.insertInt("Height", fBitmap.height());
-    pdfDict.insertName("ColorSpace", "DeviceRGB");
+    if (fBitmap.colorType() == kIndex_8_SkColorType) {
+        SkASSERT(1 == pdf_color_component_count(fBitmap.colorType()));
+        pdfDict.insertObject("ColorSpace",
+                             make_indexed_color_space(fBitmap.getColorTable()));
+    } else if (1 == pdf_color_component_count(fBitmap.colorType())) {
+        pdfDict.insertName("ColorSpace", "DeviceGray");
+    } else {
+        pdfDict.insertName("ColorSpace", "DeviceRGB");
+    }
     pdfDict.insertInt("BitsPerComponent", 8);
     if (fSMask) {
-        pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
+        pdfDict.insertObjRef("SMask", SkRef(fSMask.get()));
     }
-    if (deflate) {
-        pdfDict.insertName("Filter", "FlateDecode");
-    }
-    pdfDict.insertInt("Length", length);
-    pdfDict.emitObject(stream, catalog);
+    pdfDict.insertName("Filter", "FlateDecode");
+    pdfDict.insertInt("Length", asset->getLength());
+    pdfDict.emitObject(stream, objNumMap, substitutes);
+
+    pdf_stream_begin(stream);
+    stream->writeStream(asset.get(), asset->getLength());
+    pdf_stream_end(stream);
 }
 
-SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
-                         SkPDFObject* smask)
-    : fBitmap(bm), fSMask(smask) {}
-
-SkPDFBitmap::~SkPDFBitmap() {}
-
 ////////////////////////////////////////////////////////////////////////////////
-static bool is_transparent(const SkBitmap& bm) {
-    SkAutoLockPixels autoLockPixels(bm);
-    if (NULL == bm.getPixels()) {
-        return true;
+
+static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) {
+    if (bm.isImmutable()) {
+        return bm;
     }
-    SkASSERT(kN32_SkColorType == bm.colorType());
-    for (int y = 0; y < bm.height(); ++y) {
-        U8CPU alpha = 0;
-        const SkPMColor* src = bm.getAddr32(0, y);
-        for (int x = 0; x < bm.width(); ++x) {
-            alpha |= SkGetPackedA32(*src++);
-        }
-        if (alpha) {
-            return false;
-        }
-    }
-    return true;
+    bm.copyTo(copy);
+    copy->setImmutable();
+    return *copy;
 }
 
-SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon,
-                                 const SkBitmap& bitmap,
-                                 const SkIRect& subset) {
+namespace {
+/**
+ *  This PDFObject assumes that its constructor was handed YUV JFIF
+ *  Jpeg-encoded data that can be directly embedded into a PDF.
+ */
+class PDFJpegBitmap : public SkPDFBitmap {
+public:
+    SkAutoTUnref<SkData> fData;
+    bool fIsYUV;
+    PDFJpegBitmap(const SkBitmap& bm, SkData* data, bool isYUV)
+        : SkPDFBitmap(bm), fData(SkRef(data)), fIsYUV(isYUV) {}
+    void emitObject(SkWStream*,
+                    const SkPDFObjNumMap&,
+                    const SkPDFSubstituteMap&) override;
+};
+
+void PDFJpegBitmap::emitObject(SkWStream* stream,
+                               const SkPDFObjNumMap& objNumMap,
+                               const SkPDFSubstituteMap& substituteMap) {
+    SkPDFDict pdfDict("XObject");
+    pdfDict.insertName("Subtype", "Image");
+    pdfDict.insertInt("Width", fBitmap.width());
+    pdfDict.insertInt("Height", fBitmap.height());
+    if (fIsYUV) {
+        pdfDict.insertName("ColorSpace", "DeviceRGB");
+    } else {
+        pdfDict.insertName("ColorSpace", "DeviceGray");
+    }
+    pdfDict.insertInt("BitsPerComponent", 8);
+    pdfDict.insertName("Filter", "DCTDecode");
+    pdfDict.insertInt("ColorTransform", 0);
+    pdfDict.insertInt("Length", SkToInt(fData->size()));
+    pdfDict.emitObject(stream, objNumMap, substituteMap);
+    pdf_stream_begin(stream);
+    stream->write(fData->data(), fData->size());
+    pdf_stream_end(stream);
+}
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) {
     SkASSERT(canon);
-    if (kN32_SkColorType != bitmap.colorType()) {
-        // TODO(halcanary): support other colortypes.
+    if (!SkColorTypeIsValid(bitmap.colorType()) ||
+        kUnknown_SkColorType == bitmap.colorType()) {
         return NULL;
     }
-    SkBitmap bm;
-    // Should extractSubset be done by the SkPDFDevice?
-    if (!bitmap.extractSubset(&bm, subset)) {
-        return NULL;
-    }
+    SkBitmap copy;
+    const SkBitmap& bm = immutable_bitmap(bitmap, &copy);
     if (bm.drawsNothing()) {
         return NULL;
     }
-    if (!bm.isImmutable()) {
-        SkBitmap copy;
-        if (!bm.copyTo(&copy)) {
-            return NULL;
-        }
-        copy.setImmutable();
-        bm = copy;
+    if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) {
+        return SkRef(canonBitmap);
     }
 
-    SkPDFBitmap* pdfBitmap = canon->findBitmap(bm);
-    if (pdfBitmap) {
-        return SkRef(pdfBitmap);
+    if (bm.pixelRef() && bm.pixelRefOrigin().isZero() &&
+        bm.dimensions() == bm.pixelRef()->info().dimensions()) {
+        // Requires the bitmap to be backed by lazy pixels.
+        SkAutoTUnref<SkData> data(bm.pixelRef()->refEncodedData());
+        SkJFIFInfo info;
+        if (data && SkIsJFIF(data, &info)) {
+            bool yuv = info.fType == SkJFIFInfo::kYCbCr;
+            SkPDFBitmap* pdfBitmap = SkNEW_ARGS(PDFJpegBitmap, (bm, data, yuv));
+            canon->addBitmap(pdfBitmap);
+            return pdfBitmap;
+        }
     }
+
     SkPDFObject* smask = NULL;
     if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
-        if (is_transparent(bm)) {
-            return NULL;
-        }
-        // PDFAlphaBitmaps do not get directly canonicalized (they
-        // are refed by the SkPDFBitmap).
         smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
     }
-    pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
+    SkPDFBitmap* pdfBitmap = SkNEW_ARGS(PDFDefaultBitmap, (bm, smask));
     canon->addBitmap(pdfBitmap);
     return pdfBitmap;
 }
diff --git a/src/pdf/SkPDFBitmap.h b/src/pdf/SkPDFBitmap.h
index 45a8aa6..2c8653f 100644
--- a/src/pdf/SkPDFBitmap.h
+++ b/src/pdf/SkPDFBitmap.h
@@ -17,33 +17,24 @@
  * It is designed to use a minimal amout of memory, aside from refing
  * the bitmap's pixels, and its emitObject() does not cache any data.
  *
- * As of now, it only supports 8888 bitmaps (the most common case).
+ * If !bitmap.isImmutable(), then a copy of the bitmap must be made;
+ * there is no way around this.
  *
  * The SkPDFBitmap::Create function will check the canon for duplicates.
  */
 class SkPDFBitmap : public SkPDFObject {
 public:
     // Returns NULL on unsupported bitmap;
-    // TODO(halcanary): support other bitmap colortypes and replace
-    // SkPDFImage.
-    static SkPDFBitmap* Create(SkPDFCanon*,
-                               const SkBitmap&,
-                               const SkIRect& subset);
-    ~SkPDFBitmap();
-    void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
-    void addResources(SkTSet<SkPDFObject*>* resourceSet,
-                      SkPDFCatalog* catalog) const SK_OVERRIDE;
+    static SkPDFBitmap* Create(SkPDFCanon*, const SkBitmap&);
     bool equals(const SkBitmap& other) const {
         return fBitmap.getGenerationID() == other.getGenerationID() &&
                fBitmap.pixelRefOrigin() == other.pixelRefOrigin() &&
                fBitmap.dimensions() == other.dimensions();
     }
 
-private:
+protected:
     const SkBitmap fBitmap;
-    const SkAutoTUnref<SkPDFObject> fSMask;
-    SkPDFBitmap(const SkBitmap&, SkPDFObject*);
-    void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
+    SkPDFBitmap(const SkBitmap& bm) : fBitmap(bm) {}
 };
 
 #endif  // SkPDFBitmap_DEFINED
diff --git a/src/pdf/SkPDFCanon.cpp b/src/pdf/SkPDFCanon.cpp
index eda2550..b6187cb 100644
--- a/src/pdf/SkPDFCanon.cpp
+++ b/src/pdf/SkPDFCanon.cpp
@@ -8,7 +8,6 @@
 #include "SkPDFBitmap.h"
 #include "SkPDFCanon.h"
 #include "SkPDFFont.h"
-#include "SkPDFGraphicState.h"
 #include "SkPDFShader.h"
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -24,7 +23,7 @@
     fAlphaShaderRecords.reset();
     fImageShaderRecords.unrefAll();
     fImageShaderRecords.reset();
-    fGraphicStateRecords.unrefAll();
+    fGraphicStateRecords.foreach ([](WrapGS w) { w.fPtr->unref(); });
     fGraphicStateRecords.reset();
     fBitmapRecords.unrefAll();
     fBitmapRecords.reset();
@@ -107,12 +106,17 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-SkPDFGraphicState* SkPDFCanon::findGraphicState(const SkPaint& paint) const {
-    return find_item(fGraphicStateRecords, paint);
+const SkPDFGraphicState* SkPDFCanon::findGraphicState(
+        const SkPDFGraphicState& key) const {
+    const WrapGS* ptr = fGraphicStateRecords.find(WrapGS(&key));
+    return ptr ? ptr->fPtr : NULL;
 }
 
-void SkPDFCanon::addGraphicState(SkPDFGraphicState* state) {
-    fGraphicStateRecords.push(SkRef(state));
+void SkPDFCanon::addGraphicState(const SkPDFGraphicState* state) {
+    SkASSERT(state);
+    WrapGS w(SkRef(state));
+    SkASSERT(!fGraphicStateRecords.contains(w));
+    fGraphicStateRecords.add(w);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/pdf/SkPDFCanon.h b/src/pdf/SkPDFCanon.h
index d0bef27..5198e7b 100644
--- a/src/pdf/SkPDFCanon.h
+++ b/src/pdf/SkPDFCanon.h
@@ -7,12 +7,13 @@
 #ifndef SkPDFCanon_DEFINED
 #define SkPDFCanon_DEFINED
 
+#include "SkPDFGraphicState.h"
 #include "SkPDFShader.h"
 #include "SkTDArray.h"
+#include "SkTHash.h"
 
 class SkBitmap;
 class SkPDFFont;
-class SkPDFGraphicState;
 class SkPDFBitmap;
 class SkPaint;
 
@@ -56,8 +57,8 @@
     SkPDFImageShader* findImageShader(const SkPDFShader::State&) const;
     void addImageShader(SkPDFImageShader*);
 
-    SkPDFGraphicState* findGraphicState(const SkPaint&) const;
-    void addGraphicState(SkPDFGraphicState*);
+    const SkPDFGraphicState* findGraphicState(const SkPDFGraphicState&) const;
+    void addGraphicState(const SkPDFGraphicState*);
 
     SkPDFBitmap* findBitmap(const SkBitmap&) const;
     void addBitmap(SkPDFBitmap*);
@@ -76,7 +77,20 @@
 
     SkTDArray<SkPDFImageShader*> fImageShaderRecords;
 
-    SkTDArray<SkPDFGraphicState*> fGraphicStateRecords;
+    struct WrapGS {
+        explicit WrapGS(const SkPDFGraphicState* ptr = NULL) : fPtr(ptr) {}
+        const SkPDFGraphicState* fPtr;
+        bool operator==(const WrapGS& rhs) const {
+            SkASSERT(fPtr);
+            SkASSERT(rhs.fPtr);
+            return *fPtr == *rhs.fPtr;
+        }
+        static uint32_t Hash(const WrapGS& w) {
+            SkASSERT(w.fPtr);
+            return w.fPtr->hash();
+        }
+    };
+    SkTHashSet<WrapGS, WrapGS::Hash> fGraphicStateRecords;
 
     SkTDArray<SkPDFBitmap*> fBitmapRecords;
 };
diff --git a/src/pdf/SkPDFCatalog.cpp b/src/pdf/SkPDFCatalog.cpp
deleted file mode 100644
index 4add164..0000000
--- a/src/pdf/SkPDFCatalog.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include "SkPDFCatalog.h"
-#include "SkPDFTypes.h"
-#include "SkStream.h"
-#include "SkTypes.h"
-
-SkPDFCatalog::SkPDFCatalog()
-    : fFirstPageCount(0)
-    , fNextObjNum(1)
-    , fNextFirstPageObjNum(0) {}
-
-SkPDFCatalog::~SkPDFCatalog() {
-    fSubstituteResourcesRemaining.safeUnrefAll();
-    fSubstituteResourcesFirstPage.safeUnrefAll();
-}
-
-SkPDFObject* SkPDFCatalog::addObject(SkPDFObject* obj, bool onFirstPage) {
-    if (findObjectIndex(obj) != -1) {  // object already added
-        return obj;
-    }
-    SkASSERT(fNextFirstPageObjNum == 0);
-    if (onFirstPage) {
-        fFirstPageCount++;
-    }
-
-    Rec newEntry(obj, onFirstPage);
-    fCatalog.append(1, &newEntry);
-    return obj;
-}
-
-void SkPDFCatalog::setFileOffset(SkPDFObject* obj, off_t offset) {
-    int objIndex = assignObjNum(obj) - 1;
-    SkASSERT(fCatalog[objIndex].fObjNumAssigned);
-    SkASSERT(fCatalog[objIndex].fFileOffset == 0);
-    fCatalog[objIndex].fFileOffset = offset;
-}
-
-int32_t SkPDFCatalog::getObjectNumber(SkPDFObject* obj) {
-    return (int32_t)assignObjNum(obj);
-}
-
-int SkPDFCatalog::findObjectIndex(SkPDFObject* obj) {
-    for (int i = 0; i < fCatalog.count(); i++) {
-        if (fCatalog[i].fObject == obj) {
-            return i;
-        }
-    }
-    // If it's not in the main array, check if it's a substitute object.
-    for (int i = 0; i < fSubstituteMap.count(); ++i) {
-        if (fSubstituteMap[i].fSubstitute == obj) {
-            return findObjectIndex(fSubstituteMap[i].fOriginal);
-        }
-    }
-    Rec newEntry(obj, false);
-    fCatalog.append(1, &newEntry);
-    return fCatalog.count() - 1;
-}
-
-int SkPDFCatalog::assignObjNum(SkPDFObject* obj) {
-    int pos = findObjectIndex(obj);
-    // If this assert fails, it means you probably forgot to add an object
-    // to the resource list.
-    SkASSERT(pos >= 0);
-    uint32_t currentIndex = pos;
-    if (fCatalog[currentIndex].fObjNumAssigned) {
-        return currentIndex + 1;
-    }
-
-    // First assignment.
-    if (fNextFirstPageObjNum == 0) {
-        fNextFirstPageObjNum = fCatalog.count() - fFirstPageCount + 1;
-    }
-
-    uint32_t objNum;
-    if (fCatalog[currentIndex].fOnFirstPage) {
-        objNum = fNextFirstPageObjNum;
-        fNextFirstPageObjNum++;
-    } else {
-        objNum = fNextObjNum;
-        fNextObjNum++;
-    }
-
-    // When we assign an object an object number, we put it in that array
-    // offset (minus 1 because object number 0 is reserved).
-    SkASSERT(!fCatalog[objNum - 1].fObjNumAssigned);
-    if (objNum - 1 != currentIndex) {
-        SkTSwap(fCatalog[objNum - 1], fCatalog[currentIndex]);
-    }
-    fCatalog[objNum - 1].fObjNumAssigned = true;
-    return objNum;
-}
-
-int32_t SkPDFCatalog::emitXrefTable(SkWStream* stream, bool firstPage) {
-    int first = -1;
-    int last = fCatalog.count() - 1;
-    // TODO(vandebo): Support linearized format.
-    // int last = fCatalog.count() - fFirstPageCount - 1;
-    // if (firstPage) {
-    //     first = fCatalog.count() - fFirstPageCount;
-    //     last = fCatalog.count() - 1;
-    // }
-
-    stream->writeText("xref\n");
-    stream->writeDecAsText(first + 1);
-    stream->writeText(" ");
-    stream->writeDecAsText(last - first + 1);
-    stream->writeText("\n");
-
-    if (first == -1) {
-        stream->writeText("0000000000 65535 f \n");
-        first++;
-    }
-    for (int i = first; i <= last; i++) {
-        // For 32 bits platforms, the maximum offset has to fit within off_t
-        // which is a 32 bits signed integer on these platforms.
-        SkDEBUGCODE(static const off_t kMaxOff = SK_MaxS32;)
-        SkASSERT(fCatalog[i].fFileOffset > 0);
-        SkASSERT(fCatalog[i].fFileOffset < kMaxOff);
-        stream->writeBigDecAsText(fCatalog[i].fFileOffset, 10);
-        stream->writeText(" 00000 n \n");
-    }
-
-    return fCatalog.count() + 1;
-}
-
-void SkPDFCatalog::setSubstitute(SkPDFObject* original,
-                                 SkPDFObject* substitute) {
-#if defined(SK_DEBUG)
-    // Sanity check: is the original already in substitute list?
-    for (int i = 0; i < fSubstituteMap.count(); ++i) {
-        if (original == fSubstituteMap[i].fSubstitute ||
-            original == fSubstituteMap[i].fOriginal) {
-            SkASSERT(false);
-            return;
-        }
-    }
-#endif
-    SubstituteMapping newMapping(original, substitute);
-    fSubstituteMap.append(1, &newMapping);
-}
-
-SkPDFObject* SkPDFCatalog::getSubstituteObject(SkPDFObject* object) {
-    for (int i = 0; i < fSubstituteMap.count(); ++i) {
-        if (object == fSubstituteMap[i].fOriginal) {
-            return fSubstituteMap[i].fSubstitute;
-        }
-    }
-    return object;
-}
-
diff --git a/src/pdf/SkPDFCatalog.h b/src/pdf/SkPDFCatalog.h
deleted file mode 100644
index 1bdac93..0000000
--- a/src/pdf/SkPDFCatalog.h
+++ /dev/null
@@ -1,108 +0,0 @@
-
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkPDFCatalog_DEFINED
-#define SkPDFCatalog_DEFINED
-
-#include <sys/types.h>
-
-#include "SkPDFTypes.h"
-#include "SkTDArray.h"
-
-/** \class SkPDFCatalog
-
-    The PDF catalog manages object numbers and file offsets.  It is used
-    to create the PDF cross reference table.
-*/
-class SkPDFCatalog {
-public:
-    /** Create a PDF catalog.
-     */
-    SkPDFCatalog();
-    ~SkPDFCatalog();
-
-    /** Add the passed object to the catalog.  Refs obj.
-     *  @param obj         The object to add.
-     *  @param onFirstPage Is the object on the first page.
-     *  @return The obj argument is returned.
-     */
-    SkPDFObject* addObject(SkPDFObject* obj, bool onFirstPage);
-
-    /** Inform the catalog of the object's position in the final stream.
-     *  The object should already have been added to the catalog.
-     *  @param obj         The object to add.
-     *  @param offset      The byte offset in the output stream of this object.
-     */
-    void setFileOffset(SkPDFObject* obj, off_t offset);
-
-    /** Get the object number for the passed object.
-     *  @param obj         The object of interest.
-     */
-    int32_t getObjectNumber(SkPDFObject* obj);
-
-    /** Output the cross reference table for objects in the catalog.
-     *  Returns the total number of objects.
-     *  @param stream      The writable output stream to send the output to.
-     *  @param firstPage   If true, include first page objects only, otherwise
-     *                     include all objects not on the first page.
-     */
-    int32_t emitXrefTable(SkWStream* stream, bool firstPage);
-
-    /** Set substitute object for the passed object.
-     */
-    void setSubstitute(SkPDFObject* original, SkPDFObject* substitute);
-
-    /** Find and return any substitute object set for the passed object. If
-     *  there is none, return the passed object.
-     */
-    SkPDFObject* getSubstituteObject(SkPDFObject* object);
-
-private:
-    struct Rec {
-        Rec(SkPDFObject* object, bool onFirstPage)
-            : fObject(object),
-              fFileOffset(0),
-              fObjNumAssigned(false),
-              fOnFirstPage(onFirstPage) {
-        }
-        SkPDFObject* fObject;
-        off_t fFileOffset;
-        bool fObjNumAssigned;
-        bool fOnFirstPage;
-    };
-
-    struct SubstituteMapping {
-        SubstituteMapping(SkPDFObject* original, SkPDFObject* substitute)
-            : fOriginal(original), fSubstitute(substitute) {
-        }
-        SkPDFObject* fOriginal;
-        SkPDFObject* fSubstitute;
-    };
-
-    // TODO(vandebo): Make this a hash if it's a performance problem.
-    SkTDArray<Rec> fCatalog;
-
-    // TODO(arthurhsu): Make this a hash if it's a performance problem.
-    SkTDArray<SubstituteMapping> fSubstituteMap;
-    SkTSet<SkPDFObject*> fSubstituteResourcesFirstPage;
-    SkTSet<SkPDFObject*> fSubstituteResourcesRemaining;
-
-    // Number of objects on the first page.
-    uint32_t fFirstPageCount;
-    // Next object number to assign (on page > 1).
-    uint32_t fNextObjNum;
-    // Next object number to assign on the first page.
-    uint32_t fNextFirstPageObjNum;
-
-    int findObjectIndex(SkPDFObject* obj);
-
-    int assignObjNum(SkPDFObject* obj);
-};
-
-#endif
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 99f3ce1..3b0d6f7 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -8,7 +8,6 @@
 #include "SkPDFDevice.h"
 
 #include "SkAnnotation.h"
-#include "SkBitmapDevice.h"
 #include "SkColor.h"
 #include "SkClipStack.h"
 #include "SkData.h"
@@ -17,10 +16,10 @@
 #include "SkPaint.h"
 #include "SkPath.h"
 #include "SkPathOps.h"
+#include "SkPDFBitmap.h"
 #include "SkPDFFont.h"
 #include "SkPDFFormXObject.h"
 #include "SkPDFGraphicState.h"
-#include "SkPDFImage.h"
 #include "SkPDFResourceDict.h"
 #include "SkPDFShader.h"
 #include "SkPDFStream.h"
@@ -33,7 +32,6 @@
 #include "SkTextFormatParams.h"
 #include "SkTemplates.h"
 #include "SkTypefacePriv.h"
-#include "SkTSet.h"
 
 #define DPI_FOR_RASTER_SCALE_ONE 72
 
@@ -41,15 +39,12 @@
 
 static void emit_pdf_color(SkColor color, SkWStream* result) {
     SkASSERT(SkColorGetA(color) == 0xFF);  // We handle alpha elsewhere.
-    SkScalar colorMax = SkIntToScalar(0xFF);
-    SkPDFScalar::Append(
-            SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), result);
+    SkScalar colorScale = SkScalarInvert(0xFF);
+    SkPDFUtils::AppendScalar(SkColorGetR(color) * colorScale, result);
     result->writeText(" ");
-    SkPDFScalar::Append(
-            SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), result);
+    SkPDFUtils::AppendScalar(SkColorGetG(color) * colorScale, result);
     result->writeText(" ");
-    SkPDFScalar::Append(
-            SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), result);
+    SkPDFUtils::AppendScalar(SkColorGetB(color) * colorScale, result);
     result->writeText(" ");
 }
 
@@ -166,11 +161,11 @@
     // Flip the text about the x-axis to account for origin swap and include
     // the passed parameters.
     content->writeText("1 0 ");
-    SkPDFScalar::Append(0 - textSkewX, content);
+    SkPDFUtils::AppendScalar(0 - textSkewX, content);
     content->writeText(" -1 ");
-    SkPDFScalar::Append(x, content);
+    SkPDFUtils::AppendScalar(x, content);
     content->writeText(" ");
-    SkPDFScalar::Append(y, content);
+    SkPDFUtils::AppendScalar(y, content);
     content->writeText(" Tm\n");
 }
 
@@ -329,7 +324,6 @@
     }
 }
 
-#ifdef SK_PDF_USE_PATHOPS
 /* Calculate an inverted path's equivalent non-inverted path, given the
  * canvas bounds.
  * outPath may alias with invPath (since this is supported by PathOps).
@@ -344,6 +338,7 @@
     return Op(clipPath, invPath, kIntersect_PathOp, outPath);
 }
 
+#ifdef SK_PDF_USE_PATHOPS_CLIPPING
 // Sanity check the numerical values of the SkRegion ops and PathOps ops
 // enums so region_op_to_pathops_op can do a straight passthrough cast.
 // If these are failing, it may be necessary to make region_op_to_pathops_op
@@ -441,7 +436,7 @@
     SkMatrix transform;
     transform.setTranslate(translation.fX, translation.fY);
 
-#ifdef SK_PDF_USE_PATHOPS
+#ifdef SK_PDF_USE_PATHOPS_CLIPPING
     SkPath clipPath;
     if (get_clip_stack_path(transform, clipStack, clipRegion, &clipPath)) {
         emit_clip(&clipPath, NULL, fContentStream);
@@ -549,7 +544,7 @@
         if (state.fTextScaleX != currentEntry()->fTextScaleX) {
             SkScalar pdfScale = SkScalarMul(state.fTextScaleX,
                                             SkIntToScalar(100));
-            SkPDFScalar::Append(pdfScale, fContentStream);
+            SkPDFUtils::AppendScalar(pdfScale, fContentStream);
             fContentStream->writeText(" Tz\n");
             currentEntry()->fTextScaleX = state.fTextScaleX;
         }
@@ -566,14 +561,21 @@
     }
 }
 
-SkBaseDevice* SkPDFDevice::onCreateCompatibleDevice(const CreateInfo& cinfo) {
+static bool not_supported_for_layers(const SkPaint& layerPaint) {
     // PDF does not support image filters, so render them on CPU.
     // Note that this rendering is done at "screen" resolution (100dpi), not
     // printer resolution.
     // FIXME: It may be possible to express some filters natively using PDF
     // to improve quality and file size (http://skbug.com/3043)
-    if (kImageFilter_Usage == cinfo.fUsage) {
-        return SkBitmapDevice::Create(cinfo.fInfo);
+
+    // TODO: should we return true if there is a colorfilter?
+    return layerPaint.getImageFilter() != NULL;
+}
+
+SkBaseDevice* SkPDFDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) {
+    if (cinfo.fForImageFilter ||
+        (layerPaint && not_supported_for_layers(*layerPaint))) {
+        return NULL;
     }
     SkISize size = SkISize::Make(cinfo.fInfo.width(), cinfo.fInfo.height());
     return SkPDFDevice::Create(size, fRasterDpi, fCanon);
@@ -700,7 +702,6 @@
     , fContentSize(pageSize)
     , fExistingClipRegion(SkIRect::MakeSize(pageSize))
     , fAnnotations(NULL)
-    , fResourceDict(NULL)
     , fLastContentEntry(NULL)
     , fLastMarginContentEntry(NULL)
     , fDrawingArea(kContent_DrawingArea)
@@ -730,7 +731,6 @@
 
 void SkPDFDevice::init() {
     fAnnotations = NULL;
-    fResourceDict = NULL;
     fContentEntries.free();
     fLastContentEntry = NULL;
     fMarginContentEntries.free();
@@ -747,7 +747,6 @@
     fFontResources.unrefAll();
     fShaderResources.unrefAll();
     SkSafeUnref(fAnnotations);
-    SkSafeUnref(fResourceDict);
     fNamedDestinations.deleteAll();
 
     if (clearFontUsage) {
@@ -948,11 +947,9 @@
         return;
     }
 
-#ifdef SK_PDF_USE_PATHOPS
     if (handleInversePath(d, origPath, paint, pathIsMutable, prePathMatrix)) {
         return;
     }
-#endif
 
     if (handleRectAnnotation(pathPtr->getBounds(), matrix, paint)) {
         return;
@@ -1049,6 +1046,41 @@
                              paint);
 }
 
+//  Create a PDF string. Maximum length (in bytes) is 65,535.
+//  @param input     A string value.
+//  @param len       The length of the input array.
+//  @param wideChars True iff the upper byte in each uint16_t is
+//                   significant and should be encoded and not
+//                   discarded.  If true, the upper byte is encoded
+//                   first.  Otherwise, we assert the upper byte is
+//                   zero.
+static SkString format_wide_string(const uint16_t* input,
+                                   size_t len,
+                                   bool wideChars) {
+    if (wideChars) {
+        SkASSERT(2 * len < 65535);
+        static const char gHex[] = "0123456789ABCDEF";
+        SkString result(4 * len + 2);
+        result[0] = '<';
+        for (size_t i = 0; i < len; i++) {
+            result[4 * i + 1] = gHex[(input[i] >> 12) & 0xF];
+            result[4 * i + 2] = gHex[(input[i] >>  8) & 0xF];
+            result[4 * i + 3] = gHex[(input[i] >>  4) & 0xF];
+            result[4 * i + 4] = gHex[(input[i]      ) & 0xF];
+        }
+        result[4 * len + 1] = '>';
+        return result;
+    } else {
+        SkASSERT(len <= 65535);
+        SkString tmp(len);
+        for (size_t i = 0; i < len; i++) {
+            SkASSERT(0 == input[i] >> 8);
+            tmp[i] = static_cast<uint8_t>(input[i]);
+        }
+        return SkPDFUtils::FormatString(tmp.c_str(), tmp.size());
+    }
+}
+
 void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
                            SkScalar x, SkScalar y, const SkPaint& paint) {
     NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
@@ -1088,8 +1120,8 @@
                 font,  glyphIDsCopy.begin() + consumedGlyphCount,
                 availableGlyphs);
         SkString encodedString =
-            SkPDFString::FormatString(glyphIDsCopy.begin() + consumedGlyphCount,
-                                      availableGlyphs, font->multiByteGlyphs());
+                format_wide_string(glyphIDsCopy.begin() + consumedGlyphCount,
+                                   availableGlyphs, font->multiByteGlyphs());
         content.entry()->fContent.writeText(encodedString.c_str());
         consumedGlyphCount += availableGlyphs;
         content.entry()->fContent.writeText(" Tj\n");
@@ -1142,7 +1174,7 @@
         align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y);
         set_text_transform(x, y, textPaint.getTextSkewX(), &content.entry()->fContent);
         SkString encodedString =
-            SkPDFString::FormatString(&encodedValue, 1, font->multiByteGlyphs());
+                format_wide_string(&encodedValue, 1, font->multiByteGlyphs());
         content.entry()->fContent.writeText(encodedString.c_str());
         content.entry()->fContent.writeText(" Tj\n");
     }
@@ -1244,44 +1276,17 @@
     fDrawingArea = drawingArea;
 }
 
-SkPDFResourceDict* SkPDFDevice::getResourceDict() {
-    if (NULL == fResourceDict) {
-        fResourceDict = SkNEW(SkPDFResourceDict);
-
-        if (fGraphicStateResources.count()) {
-            for (int i = 0; i < fGraphicStateResources.count(); i++) {
-                fResourceDict->insertResourceAsReference(
-                        SkPDFResourceDict::kExtGState_ResourceType,
-                        i, fGraphicStateResources[i]);
-            }
-        }
-
-        if (fXObjectResources.count()) {
-            for (int i = 0; i < fXObjectResources.count(); i++) {
-                fResourceDict->insertResourceAsReference(
-                        SkPDFResourceDict::kXObject_ResourceType,
-                        i, fXObjectResources[i]);
-            }
-        }
-
-        if (fFontResources.count()) {
-            for (int i = 0; i < fFontResources.count(); i++) {
-                fResourceDict->insertResourceAsReference(
-                        SkPDFResourceDict::kFont_ResourceType,
-                        i, fFontResources[i]);
-            }
-        }
-
-        if (fShaderResources.count()) {
-            SkAutoTUnref<SkPDFDict> patterns(new SkPDFDict());
-            for (int i = 0; i < fShaderResources.count(); i++) {
-                fResourceDict->insertResourceAsReference(
-                        SkPDFResourceDict::kPattern_ResourceType,
-                        i, fShaderResources[i]);
-            }
-        }
+SkPDFDict* SkPDFDevice::createResourceDict() const {
+    SkTDArray<SkPDFObject*> fonts;
+    fonts.setReserve(fFontResources.count());
+    for (SkPDFFont* font : fFontResources) {
+        fonts.push(font);
     }
-    return fResourceDict;
+    return SkPDFResourceDict::Create(
+            &fGraphicStateResources,
+            &fShaderResources,
+            &fXObjectResources,
+            &fonts);
 }
 
 const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const {
@@ -1290,15 +1295,14 @@
 
 SkPDFArray* SkPDFDevice::copyMediaBox() const {
     // should this be a singleton?
-    SkAutoTUnref<SkPDFInt> zero(SkNEW_ARGS(SkPDFInt, (0)));
 
-    SkPDFArray* mediaBox = SkNEW(SkPDFArray);
+    SkAutoTUnref<SkPDFArray> mediaBox(SkNEW(SkPDFArray));
     mediaBox->reserve(4);
-    mediaBox->append(zero.get());
-    mediaBox->append(zero.get());
+    mediaBox->appendInt(0);
+    mediaBox->appendInt(0);
     mediaBox->appendInt(fPageSize.fWidth);
     mediaBox->appendInt(fPageSize.fHeight);
-    return mediaBox;
+    return mediaBox.detach();
 }
 
 SkStreamAsset* SkPDFDevice::content() const {
@@ -1352,7 +1356,6 @@
     SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), out);
 }
 
-#ifdef SK_PDF_USE_PATHOPS
 /* Draws an inverse filled path by using Path Ops to compute the positive
  * inverse using the current clip as the inverse bounds.
  * Return true if this was an inverse path and was properly handled,
@@ -1418,7 +1421,6 @@
     drawPath(d, modifiedPath, noInversePaint, prePathMatrix, true);
     return true;
 }
-#endif
 
 bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
                                        const SkPaint& p) {
@@ -1458,26 +1460,30 @@
     return false;
 }
 
-SkPDFDict* SkPDFDevice::createLinkAnnotation(const SkRect& r,
-                                             const SkMatrix& matrix) {
-    SkMatrix transform = matrix;
-    transform.postConcat(fInitialTransform);
-    SkRect translatedRect;
-    transform.mapRect(&translatedRect, r);
-
+void SkPDFDevice::addAnnotation(SkPDFDict* annotation) {
     if (NULL == fAnnotations) {
         fAnnotations = SkNEW(SkPDFArray);
     }
-    SkPDFDict* annotation(SkNEW_ARGS(SkPDFDict, ("Annot")));
+    fAnnotations->appendObject(annotation);
+}
+
+static SkPDFDict* create_link_annotation(const SkRect& r,
+                                         const SkMatrix& initialTransform,
+                                         const SkMatrix& matrix) {
+    SkMatrix transform = matrix;
+    transform.postConcat(initialTransform);
+    SkRect translatedRect;
+    transform.mapRect(&translatedRect, r);
+
+    SkAutoTUnref<SkPDFDict> annotation(SkNEW_ARGS(SkPDFDict, ("Annot")));
     annotation->insertName("Subtype", "Link");
-    fAnnotations->append(annotation);
 
     SkAutoTUnref<SkPDFArray> border(SkNEW(SkPDFArray));
     border->reserve(3);
     border->appendInt(0);  // Horizontal corner radius.
     border->appendInt(0);  // Vertical corner radius.
     border->appendInt(0);  // Width, 0 = no border.
-    annotation->insert("Border", border.get());
+    annotation->insertObject("Border", border.detach());
 
     SkAutoTUnref<SkPDFArray> rect(SkNEW(SkPDFArray));
     rect->reserve(4);
@@ -1485,29 +1491,33 @@
     rect->appendScalar(translatedRect.fTop);
     rect->appendScalar(translatedRect.fRight);
     rect->appendScalar(translatedRect.fBottom);
-    annotation->insert("Rect", rect.get());
+    annotation->insertObject("Rect", rect.detach());
 
-    return annotation;
+    return annotation.detach();
 }
 
 void SkPDFDevice::handleLinkToURL(SkData* urlData, const SkRect& r,
                                   const SkMatrix& matrix) {
-    SkAutoTUnref<SkPDFDict> annotation(createLinkAnnotation(r, matrix));
+    SkAutoTUnref<SkPDFDict> annotation(
+            create_link_annotation(r, fInitialTransform, matrix));
 
     SkString url(static_cast<const char *>(urlData->data()),
                  urlData->size() - 1);
     SkAutoTUnref<SkPDFDict> action(SkNEW_ARGS(SkPDFDict, ("Action")));
     action->insertName("S", "URI");
-    action->insert("URI", SkNEW_ARGS(SkPDFString, (url)))->unref();
-    annotation->insert("A", action.get());
+    action->insertString("URI", url);
+    annotation->insertObject("A", action.detach());
+    this->addAnnotation(annotation.detach());
 }
 
 void SkPDFDevice::handleLinkToNamedDest(SkData* nameData, const SkRect& r,
                                         const SkMatrix& matrix) {
-    SkAutoTUnref<SkPDFDict> annotation(createLinkAnnotation(r, matrix));
+    SkAutoTUnref<SkPDFDict> annotation(
+            create_link_annotation(r, fInitialTransform, matrix));
     SkString name(static_cast<const char *>(nameData->data()),
                   nameData->size() - 1);
-    annotation->insert("Dest", SkNEW_ARGS(SkPDFName, (name)))->unref();
+    annotation->insertName("Dest", name);
+    this->addAnnotation(annotation.detach());
 }
 
 struct NamedDestination {
@@ -1515,9 +1525,7 @@
     SkPoint point;
 
     NamedDestination(const SkData* nameData, const SkPoint& point)
-        : nameData(nameData), point(point) {
-        nameData->ref();
-    }
+        : nameData(SkRef(nameData)), point(point) {}
 
     ~NamedDestination() {
         nameData->unref();
@@ -1534,19 +1542,19 @@
         SkNEW_ARGS(NamedDestination, (nameData, translatedPoint)));
 }
 
-void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) {
+void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) const {
     int nDest = fNamedDestinations.count();
     for (int i = 0; i < nDest; i++) {
         NamedDestination* dest = fNamedDestinations[i];
         SkAutoTUnref<SkPDFArray> pdfDest(SkNEW(SkPDFArray));
         pdfDest->reserve(5);
-        pdfDest->append(SkNEW_ARGS(SkPDFObjRef, (page)))->unref();
+        pdfDest->appendObjRef(SkRef(page));
         pdfDest->appendName("XYZ");
         pdfDest->appendScalar(dest->point.x());
         pdfDest->appendScalar(dest->point.y());
         pdfDest->appendInt(0);  // Leave zoom unchanged
-        dict->insert(static_cast<const char *>(dest->nameData->data()),
-                     pdfDest);
+        SkString name(static_cast<const char*>(dest->nameData->data()));
+        dict->insertObject(name, pdfDest.detach());
     }
 }
 
@@ -1570,8 +1578,7 @@
         return;
     }
 
-    SkAutoTUnref<SkPDFGraphicState> sMaskGS(
-        SkPDFGraphicState::GetSMaskGraphicState(
+    SkAutoTUnref<SkPDFObject> sMaskGS(SkPDFGraphicState::GetSMaskGraphicState(
             mask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode));
 
     SkMatrix identity;
@@ -1942,7 +1949,7 @@
     }
 }
 
-int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) {
+int SkPDFDevice::addGraphicStateResource(SkPDFObject* gs) {
     // Assumes that gs has been canonicalized (so we can directly compare
     // pointers).
     int result = fGraphicStateResources.find(gs);
@@ -1978,7 +1985,7 @@
                 SkPDFResourceDict::kFont_ResourceType,
                 fontIndex).c_str());
         contentEntry->fContent.writeText(" ");
-        SkPDFScalar::Append(paint.getTextSize(), &contentEntry->fContent);
+        SkPDFUtils::AppendScalar(paint.getTextSize(), &contentEntry->fContent);
         contentEntry->fContent.writeText(" Tf\n");
         contentEntry->fState.fFont = fFontResources[fontIndex];
     }
@@ -2120,7 +2127,7 @@
     if (content.needShape()) {
         SkPath shape;
         shape.addRect(SkRect::MakeWH(SkIntToScalar(subset.width()),
-                                     SkIntToScalar( subset.height())));
+                                     SkIntToScalar(subset.height())));
         shape.transform(matrix);
         content.setShape(shape);
     }
@@ -2128,8 +2135,12 @@
         return;
     }
 
-    SkAutoTUnref<SkPDFObject> image(
-            SkPDFCreateImageObject(fCanon, *bitmap, subset));
+    SkBitmap subsetBitmap;
+    // Should extractSubset be done by the SkPDFDevice?
+    if (!bitmap->extractSubset(&subsetBitmap, subset)) {
+        return;
+    }
+    SkAutoTUnref<SkPDFObject> image(SkPDFBitmap::Create(fCanon, subsetBitmap));
     if (!image) {
         return;
     }
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index 8a88314..18be58d 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -31,11 +31,9 @@
 class SkPDFGlyphSetMap;
 class SkPDFGraphicState;
 class SkPDFObject;
-class SkPDFResourceDict;
 class SkPDFShader;
 class SkPDFStream;
 class SkRRect;
-template <typename T> class SkTSet;
 
 // Private classes.
 struct ContentEntry;
@@ -85,40 +83,40 @@
      and are handling any looping from the paint, and any effects from the
      DrawFilter.
      */
-    void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE;
+    void drawPaint(const SkDraw&, const SkPaint& paint) override;
     void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
                     size_t count, const SkPoint[],
-                    const SkPaint& paint) SK_OVERRIDE;
-    void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint) SK_OVERRIDE;
-    void drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) SK_OVERRIDE;
-    void drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& paint) SK_OVERRIDE;
+                    const SkPaint& paint) override;
+    void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint) override;
+    void drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) override;
+    void drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& paint) override;
     void drawPath(const SkDraw&, const SkPath& origpath,
                   const SkPaint& paint, const SkMatrix* prePathMatrix,
-                  bool pathIsMutable) SK_OVERRIDE;
+                  bool pathIsMutable) override;
     void drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
                         const SkRect* src, const SkRect& dst,
                         const SkPaint& paint,
-                        SkCanvas::DrawBitmapRectFlags flags) SK_OVERRIDE;
+                        SkCanvas::DrawBitmapRectFlags flags) override;
     void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
-                    const SkMatrix& matrix, const SkPaint&) SK_OVERRIDE;
+                    const SkMatrix& matrix, const SkPaint&) override;
     void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y,
-                    const SkPaint& paint) SK_OVERRIDE;
+                    const SkPaint& paint) override;
     void drawText(const SkDraw&, const void* text, size_t len,
-                  SkScalar x, SkScalar y, const SkPaint&) SK_OVERRIDE;
+                  SkScalar x, SkScalar y, const SkPaint&) override;
     void drawPosText(const SkDraw&, const void* text, size_t len,
                      const SkScalar pos[], int scalarsPerPos,
-                     const SkPoint& offset, const SkPaint&) SK_OVERRIDE;
+                     const SkPoint& offset, const SkPaint&) override;
     void drawVertices(const SkDraw&, SkCanvas::VertexMode,
                       int vertexCount, const SkPoint verts[],
                       const SkPoint texs[], const SkColor colors[],
                       SkXfermode* xmode, const uint16_t indices[],
-                      int indexCount, const SkPaint& paint) SK_OVERRIDE;
+                      int indexCount, const SkPaint& paint) override;
     void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
-                    const SkPaint&) SK_OVERRIDE;
+                    const SkPaint&) override;
 
-    void onAttachToCanvas(SkCanvas* canvas) SK_OVERRIDE;
-    void onDetachFromCanvas() SK_OVERRIDE;
-    SkImageInfo imageInfo() const SK_OVERRIDE;
+    void onAttachToCanvas(SkCanvas* canvas) override;
+    void onDetachFromCanvas() override;
+    SkImageInfo imageInfo() const override;
 
     enum DrawingArea {
         kContent_DrawingArea,  // Drawing area for the page content.
@@ -138,9 +136,9 @@
 
     // PDF specific methods.
 
-    /** Returns the resource dictionary for this device.
+    /** Create the resource dictionary for this device.
      */
-    SkPDFResourceDict* getResourceDict();
+    SkPDFDict* createResourceDict() const;
 
     /** Get the fonts used on this device.
      */
@@ -150,7 +148,7 @@
      *  @param dict  Dictionary to add destinations to.
      *  @param page  The PDF object representing the page for this device.
      */
-    void appendDestinations(SkPDFDict* dict, SkPDFObject* page);
+    void appendDestinations(SkPDFDict* dict, SkPDFObject* page) const;
 
     /** Returns a copy of the media box for this device. The caller is required
      *  to unref() this when it is finished.
@@ -180,12 +178,16 @@
         return *(fFontGlyphUsage.get());
     }
 
+#ifdef SK_DEBUG
+    SkPDFCanon* getCanon() const { return fCanon; }
+#endif  // SK_DEBUG
+
 protected:
-    const SkBitmap& onAccessBitmap() SK_OVERRIDE {
+    const SkBitmap& onAccessBitmap() override {
         return fLegacyBitmap;
     }
 
-    SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) SK_OVERRIDE;
+    SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) override;
 
 private:
     // TODO(vandebo): push most of SkPDFDevice's state into a core object in
@@ -198,10 +200,9 @@
     SkClipStack fExistingClipStack;
     SkRegion fExistingClipRegion;
     SkPDFArray* fAnnotations;
-    SkPDFResourceDict* fResourceDict;
     SkTDArray<NamedDestination*> fNamedDestinations;
 
-    SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
+    SkTDArray<SkPDFObject*> fGraphicStateResources;
     SkTDArray<SkPDFObject*> fXObjectResources;
     SkTDArray<SkPDFFont*> fFontResources;
     SkTDArray<SkPDFObject*> fShaderResources;
@@ -235,8 +236,7 @@
     ContentEntry* getLastContentEntry();
     void setLastContentEntry(ContentEntry* contentEntry);
 
-    // override from SkBaseDevice
-    SkBaseDevice* onCreateCompatibleDevice(const CreateInfo&) SK_OVERRIDE;
+    SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
 
     void init();
     void cleanUp(bool clearFontUsage);
@@ -270,7 +270,7 @@
                                             const SkPaint& paint,
                                             bool hasText,
                                             GraphicStateEntry* entry);
-    int addGraphicStateResource(SkPDFGraphicState* gs);
+    int addGraphicStateResource(SkPDFObject* gs);
     int addXObjectResource(SkPDFObject* xObject);
 
     void updateFont(const SkPaint& paint, uint16_t glyphID,
@@ -290,16 +290,14 @@
      */
     void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const;
 
-#ifdef SK_PDF_USE_PATHOPS
     bool handleInversePath(const SkDraw& d, const SkPath& origPath,
                            const SkPaint& paint, bool pathIsMutable,
                            const SkMatrix* prePathMatrix = NULL);
-#endif
     bool handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
                               const SkPaint& paint);
     bool handlePointAnnotation(const SkPoint* points, size_t count,
                                const SkMatrix& matrix, const SkPaint& paint);
-    SkPDFDict* createLinkAnnotation(const SkRect& r, const SkMatrix& matrix);
+    void addAnnotation(SkPDFDict*);
     void handleLinkToURL(SkData* urlData, const SkRect& r,
                          const SkMatrix& matrix);
     void handleLinkToNamedDest(SkData* nameData, const SkRect& r,
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
deleted file mode 100644
index 8280551..0000000
--- a/src/pdf/SkPDFDocument.cpp
+++ /dev/null
@@ -1,311 +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 "SkPDFCatalog.h"
-#include "SkPDFDevice.h"
-#include "SkPDFDocument.h"
-#include "SkPDFFont.h"
-#include "SkPDFPage.h"
-#include "SkPDFTypes.h"
-#include "SkStream.h"
-#include "SkTSet.h"
-
-static void addResourcesToCatalog(bool firstPage,
-                                  SkTSet<SkPDFObject*>* resourceSet,
-                                  SkPDFCatalog* catalog) {
-    for (int i = 0; i < resourceSet->count(); i++) {
-        catalog->addObject((*resourceSet)[i], firstPage);
-    }
-}
-
-static void perform_font_subsetting(SkPDFCatalog* catalog,
-                                    const SkTDArray<SkPDFPage*>& pages,
-                                    SkTDArray<SkPDFObject*>* substitutes) {
-    SkASSERT(catalog);
-    SkASSERT(substitutes);
-
-    SkPDFGlyphSetMap usage;
-    for (int i = 0; i < pages.count(); ++i) {
-        usage.merge(pages[i]->getFontGlyphUsage());
-    }
-    SkPDFGlyphSetMap::F2BIter iterator(usage);
-    const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
-    while (entry) {
-        SkPDFFont* subsetFont =
-            entry->fFont->getFontSubset(entry->fGlyphSet);
-        if (subsetFont) {
-            catalog->setSubstitute(entry->fFont, subsetFont);
-            substitutes->push(subsetFont);  // Transfer ownership to substitutes
-        }
-        entry = iterator.next();
-    }
-}
-
-SkPDFDocument::SkPDFDocument()
-        : fXRefFileOffset(0),
-          fTrailerDict(NULL) {
-    fCatalog.reset(SkNEW(SkPDFCatalog));
-    fDocCatalog = SkNEW_ARGS(SkPDFDict, ("Catalog"));
-    fCatalog->addObject(fDocCatalog, true);
-    fFirstPageResources = NULL;
-    fOtherPageResources = NULL;
-}
-
-SkPDFDocument::~SkPDFDocument() {
-    fPages.safeUnrefAll();
-
-    // The page tree has both child and parent pointers, so it creates a
-    // reference cycle.  We must clear that cycle to properly reclaim memory.
-    for (int i = 0; i < fPageTree.count(); i++) {
-        fPageTree[i]->clear();
-    }
-    fPageTree.safeUnrefAll();
-
-    if (fFirstPageResources) {
-        fFirstPageResources->safeUnrefAll();
-    }
-    if (fOtherPageResources) {
-        fOtherPageResources->safeUnrefAll();
-    }
-
-    fSubstitutes.safeUnrefAll();
-
-    fDocCatalog->unref();
-    SkSafeUnref(fTrailerDict);
-    SkDELETE(fFirstPageResources);
-    SkDELETE(fOtherPageResources);
-}
-
-bool SkPDFDocument::emitPDF(SkWStream* stream) {
-    if (fPages.isEmpty()) {
-        return false;
-    }
-    for (int i = 0; i < fPages.count(); i++) {
-        if (fPages[i] == NULL) {
-            return false;
-        }
-    }
-
-    fFirstPageResources = SkNEW(SkTSet<SkPDFObject*>);
-    fOtherPageResources = SkNEW(SkTSet<SkPDFObject*>);
-
-    // We haven't emitted the document before if fPageTree is empty.
-    if (fPageTree.isEmpty()) {
-        SkPDFDict* pageTreeRoot;
-        SkPDFPage::GeneratePageTree(fPages, fCatalog.get(), &fPageTree,
-                                    &pageTreeRoot);
-        fDocCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref();
-
-        /* TODO(vandebo): output intent
-        SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
-        outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref();
-        outputIntent->insert("OutputConditionIdentifier",
-                             new SkPDFString("sRGB"))->unref();
-        SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray;
-        intentArray->append(outputIntent.get());
-        fDocCatalog->insert("OutputIntent", intentArray.get());
-        */
-
-        SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict));
-
-        bool firstPage = true;
-        /* The references returned in newResources are transfered to
-         * fFirstPageResources or fOtherPageResources depending on firstPage and
-         * knownResources doesn't have a reference but just relies on the other
-         * two sets to maintain a reference.
-         */
-        SkTSet<SkPDFObject*> knownResources;
-
-        // mergeInto returns the number of duplicates.
-        // If there are duplicates, there is a bug and we mess ref counting.
-        SkDEBUGCODE(int duplicates =) knownResources.mergeInto(*fFirstPageResources);
-        SkASSERT(duplicates == 0);
-
-        for (int i = 0; i < fPages.count(); i++) {
-            if (i == 1) {
-                firstPage = false;
-                SkDEBUGCODE(duplicates =) knownResources.mergeInto(*fOtherPageResources);
-            }
-            SkTSet<SkPDFObject*> newResources;
-            fPages[i]->finalizePage(
-                fCatalog.get(), firstPage, knownResources, &newResources);
-            addResourcesToCatalog(firstPage, &newResources, fCatalog.get());
-            if (firstPage) {
-                SkDEBUGCODE(duplicates =) fFirstPageResources->mergeInto(newResources);
-            } else {
-                SkDEBUGCODE(duplicates =) fOtherPageResources->mergeInto(newResources);
-            }
-            SkASSERT(duplicates == 0);
-
-            SkDEBUGCODE(duplicates =) knownResources.mergeInto(newResources);
-            SkASSERT(duplicates == 0);
-
-            fPages[i]->appendDestinations(dests);
-        }
-
-        if (dests->size() > 0) {
-            SkPDFDict* raw_dests = dests.get();
-            fFirstPageResources->add(dests.detach());  // Transfer ownership.
-            fCatalog->addObject(raw_dests, true /* onFirstPage */);
-            fDocCatalog->insert("Dests", SkNEW_ARGS(SkPDFObjRef, (raw_dests)))->unref();
-        }
-
-        // Build font subsetting info before proceeding.
-        perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes);
-    }
-
-    SkTSet<SkPDFObject*> resourceSet;
-    if (resourceSet.add(fDocCatalog)) {
-        fDocCatalog->addResources(&resourceSet, fCatalog);
-    }
-    off_t baseOffset = SkToOffT(stream->bytesWritten());
-    emitHeader(stream);
-    for (int i = 0; i < resourceSet.count(); ++i) {
-        SkPDFObject* object = resourceSet[i];
-        fCatalog->setFileOffset(object,
-                                SkToOffT(stream->bytesWritten()) - baseOffset);
-        SkASSERT(object == fCatalog->getSubstituteObject(object));
-        stream->writeDecAsText(fCatalog->getObjectNumber(object));
-        stream->writeText(" 0 obj\n");  // Generation number is always 0.
-        object->emitObject(stream, fCatalog);
-        stream->writeText("\nendobj\n");
-    }
-    fXRefFileOffset = SkToOffT(stream->bytesWritten()) - baseOffset;
-    int64_t objCount = fCatalog->emitXrefTable(stream, fPages.count() > 1);
-    emitFooter(stream, objCount);
-    return true;
-}
-
-// TODO(halcanary): remove this method, since it is unused.
-bool SkPDFDocument::setPage(int pageNumber, SkPDFDevice* pdfDevice) {
-    if (!fPageTree.isEmpty()) {
-        return false;
-    }
-
-    pageNumber--;
-    SkASSERT(pageNumber >= 0);
-
-    if (pageNumber >= fPages.count()) {
-        int oldSize = fPages.count();
-        fPages.setCount(pageNumber + 1);
-        for (int i = oldSize; i <= pageNumber; i++) {
-            fPages[i] = NULL;
-        }
-    }
-
-    SkPDFPage* page = new SkPDFPage(pdfDevice);
-    SkSafeUnref(fPages[pageNumber]);
-    fPages[pageNumber] = page;  // Reference from new passed to fPages.
-    return true;
-}
-
-bool SkPDFDocument::appendPage(SkPDFDevice* pdfDevice) {
-    if (!fPageTree.isEmpty()) {
-        return false;
-    }
-
-    SkPDFPage* page = new SkPDFPage(pdfDevice);
-    fPages.push(page);  // Reference from new passed to fPages.
-    return true;
-}
-
-// Deprecated.
-// TODO(halcanary): remove
-void SkPDFDocument::getCountOfFontTypes(
-        int counts[SkAdvancedTypefaceMetrics::kOther_Font + 2]) const {
-    sk_bzero(counts, sizeof(int) *
-                     (SkAdvancedTypefaceMetrics::kOther_Font + 2));
-    SkTDArray<SkFontID> seenFonts;
-    int notEmbeddable = 0;
-
-    for (int pageNumber = 0; pageNumber < fPages.count(); pageNumber++) {
-        const SkTDArray<SkPDFFont*>& fontResources =
-                fPages[pageNumber]->getFontResources();
-        for (int font = 0; font < fontResources.count(); font++) {
-            SkFontID fontID = fontResources[font]->typeface()->uniqueID();
-            if (seenFonts.find(fontID) == -1) {
-                counts[fontResources[font]->getType()]++;
-                seenFonts.push(fontID);
-                if (!fontResources[font]->canEmbed()) {
-                    notEmbeddable++;
-                }
-            }
-        }
-    }
-    counts[SkAdvancedTypefaceMetrics::kOther_Font + 1] = notEmbeddable;
-}
-
-// TODO(halcanary): expose notEmbeddableCount in SkDocument
-void SkPDFDocument::getCountOfFontTypes(
-        int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
-        int* notSubsettableCount,
-        int* notEmbeddableCount) const {
-    sk_bzero(counts, sizeof(int) *
-                     (SkAdvancedTypefaceMetrics::kOther_Font + 1));
-    SkTDArray<SkFontID> seenFonts;
-    int notSubsettable = 0;
-    int notEmbeddable = 0;
-
-    for (int pageNumber = 0; pageNumber < fPages.count(); pageNumber++) {
-        const SkTDArray<SkPDFFont*>& fontResources =
-                fPages[pageNumber]->getFontResources();
-        for (int font = 0; font < fontResources.count(); font++) {
-            SkFontID fontID = fontResources[font]->typeface()->uniqueID();
-            if (seenFonts.find(fontID) == -1) {
-                counts[fontResources[font]->getType()]++;
-                seenFonts.push(fontID);
-                if (!fontResources[font]->canSubset()) {
-                    notSubsettable++;
-                }
-                if (!fontResources[font]->canEmbed()) {
-                    notEmbeddable++;
-                }
-            }
-        }
-    }
-    if (notSubsettableCount) {
-        *notSubsettableCount = notSubsettable;
-
-    }
-    if (notEmbeddableCount) {
-        *notEmbeddableCount = notEmbeddable;
-    }
-}
-
-void SkPDFDocument::emitHeader(SkWStream* stream) {
-    stream->writeText("%PDF-1.4\n%");
-    // The PDF spec recommends including a comment with four bytes, all
-    // with their high bits set.  This is "Skia" with the high bits set.
-    stream->write32(0xD3EBE9E1);
-    stream->writeText("\n");
-}
-
-//TODO(halcanary): remove this function
-size_t SkPDFDocument::headerSize() {
-    SkDynamicMemoryWStream buffer;
-    emitHeader(&buffer);
-    return buffer.getOffset();
-}
-
-void SkPDFDocument::emitFooter(SkWStream* stream, int64_t objCount) {
-    if (NULL == fTrailerDict) {
-        fTrailerDict = SkNEW(SkPDFDict);
-
-        // TODO(vandebo): Linearized format will take a Prev entry too.
-        // TODO(vandebo): PDF/A requires an ID entry.
-        fTrailerDict->insertInt("Size", int(objCount));
-        fTrailerDict->insert("Root", new SkPDFObjRef(fDocCatalog))->unref();
-    }
-
-    stream->writeText("trailer\n");
-    fTrailerDict->emitObject(stream, fCatalog.get());
-    stream->writeText("\nstartxref\n");
-    stream->writeBigDecAsText(fXRefFileOffset);
-    stream->writeText("\n%%EOF");
-}
diff --git a/src/pdf/SkPDFDocument.h b/src/pdf/SkPDFDocument.h
deleted file mode 100644
index 01290d0..0000000
--- a/src/pdf/SkPDFDocument.h
+++ /dev/null
@@ -1,102 +0,0 @@
-
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkPDFDocument_DEFINED
-#define SkPDFDocument_DEFINED
-
-#include "SkAdvancedTypefaceMetrics.h"
-#include "SkRefCnt.h"
-#include "SkTDArray.h"
-#include "SkTemplates.h"
-
-class SkPDFCatalog;
-class SkPDFDevice;
-class SkPDFDict;
-class SkPDFPage;
-class SkPDFObject;
-class SkWStream;
-template <typename T> class SkTSet;
-
-/** \class SkPDFDocument
-
-    A SkPDFDocument assembles pages together and generates the final PDF file.
-*/
-class SkPDFDocument {
-public:
-    SkPDFDocument();
-    ~SkPDFDocument();
-
-    /** Output the PDF to the passed stream.  It is an error to call this (it
-     *  will return false and not modify stream) if no pages have been added
-     *  or there are pages missing (i.e. page 1 and 3 have been added, but not
-     *  page 2).
-     *
-     *  @param stream    The writable output stream to send the PDF to.
-     */
-    bool emitPDF(SkWStream* stream);
-
-    /** Sets the specific page to the passed PDF device. If the specified
-     *  page is already set, this overrides it. Returns true if successful.
-     *  Will fail if the document has already been emitted.
-     *
-     *  @param pageNumber The position to add the passed device (1 based).
-     *  @param pdfDevice  The page to add to this document.
-     */
-    bool setPage(int pageNumber, SkPDFDevice* pdfDevice);
-
-    /** Append the passed pdf device to the document as a new page.  Returns
-     *  true if successful.  Will fail if the document has already been emitted.
-     *
-     *  @param pdfDevice The page to add to this document.
-     */
-    bool appendPage(SkPDFDevice* pdfDevice);
-
-    /** Get the count of unique font types used in the document.
-     * DEPRECATED.
-     */
-    void getCountOfFontTypes(
-        int counts[SkAdvancedTypefaceMetrics::kOther_Font + 2]) const;
-
-    /** Get the count of unique font types used in the document.
-     */
-    void getCountOfFontTypes(
-        int counts[SkAdvancedTypefaceMetrics::kOther_Font + 1],
-        int* notSubsettableCount,
-        int* notEmbedddableCount) const;
-
-private:
-    SkAutoTDelete<SkPDFCatalog> fCatalog;
-    int64_t fXRefFileOffset;
-
-    SkTDArray<SkPDFPage*> fPages;
-    SkTDArray<SkPDFDict*> fPageTree;
-    SkPDFDict* fDocCatalog;
-    SkTSet<SkPDFObject*>* fFirstPageResources;
-    SkTSet<SkPDFObject*>* fOtherPageResources;
-    SkTDArray<SkPDFObject*> fSubstitutes;
-
-    SkPDFDict* fTrailerDict;
-
-    /** Output the PDF header to the passed stream.
-     *  @param stream    The writable output stream to send the header to.
-     */
-    void emitHeader(SkWStream* stream);
-
-    /** Get the size of the header.
-     */
-    size_t headerSize();
-
-    /** Output the PDF footer to the passed stream.
-     *  @param stream    The writable output stream to send the footer to.
-     *  @param objCount  The number of objects in the PDF.
-     */
-    void emitFooter(SkWStream* stream, int64_t objCount);
-};
-
-#endif
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index d36a1d7..0afdb9b 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -10,7 +10,6 @@
 #include "SkData.h"
 #include "SkGlyphCache.h"
 #include "SkPaint.h"
-#include "SkPDFCatalog.h"
 #include "SkPDFCanon.h"
 #include "SkPDFDevice.h"
 #include "SkPDFFont.h"
@@ -287,7 +286,7 @@
 void setGlyphWidthAndBoundingBox(SkScalar width, SkIRect box,
                                  SkWStream* content) {
     // Specify width and bounding box for the glyph.
-    SkPDFScalar::Append(width, content);
+    SkPDFUtils::AppendScalar(width, content);
     content->writeText(" 0 ");
     content->writeDecAsText(box.fLeft);
     content->writeText(" ");
@@ -345,7 +344,7 @@
                     appendAdvance(advanceInfo->fAdvance[j], emSize,
                                   advanceArray.get());
                 result->appendInt(advanceInfo->fStartId);
-                result->append(advanceArray.get());
+                result->appendObject(advanceArray.detach());
                 break;
             }
             case SkAdvancedTypefaceMetrics::WidthRange::kRun: {
@@ -830,13 +829,13 @@
             return SkRef(relatedFont);
         }
     } else {
-        SkAdvancedTypefaceMetrics::PerGlyphInfo info;
-        info = SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo;
-        info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
-                  info, SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo);
+        SkTypeface::PerGlyphInfo info;
+        info = SkTypeface::kGlyphNames_PerGlyphInfo;
+        info = SkTBitOr<SkTypeface::PerGlyphInfo>(
+                  info, SkTypeface::kToUnicode_PerGlyphInfo);
 #if !defined (SK_SFNTLY_SUBSETTER)
-        info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
-                  info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo);
+        info = SkTBitOr<SkTypeface::PerGlyphInfo>(
+                  info, SkTypeface::kHAdvance_PerGlyphInfo);
 #endif
         fontMetrics.reset(
             typeface->getAdvancedTypefaceMetrics(info, NULL, 0));
@@ -844,8 +843,8 @@
         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);
+            info = SkTBitOr<SkTypeface::PerGlyphInfo>(
+                      info, SkTypeface::kHAdvance_PerGlyphInfo);
             fontMetrics.reset(
                 typeface->getAdvancedTypefaceMetrics(info, NULL, 0));
         }
@@ -961,8 +960,8 @@
     fDescriptor->insertScalar("CapHeight",
             scaleFromFontUnits(fFontInfo->fCapHeight, emSize));
     fDescriptor->insertInt("ItalicAngle", fFontInfo->fItalicAngle);
-    fDescriptor->insert("FontBBox", makeFontBBox(fFontInfo->fBBox,
-                                                 fFontInfo->fEmSize))->unref();
+    fDescriptor->insertObject(
+            "FontBBox", makeFontBBox(fFontInfo->fBBox, fFontInfo->fEmSize));
 
     if (defaultWidth > 0) {
         fDescriptor->insertScalar("MissingWidth",
@@ -983,11 +982,12 @@
     if (fFontInfo == NULL || fFontInfo->fGlyphToUnicode.begin() == NULL) {
         return;
     }
-    SkAutoTUnref<SkPDFStream> pdfCmap(
-        generate_tounicode_cmap(fFontInfo->fGlyphToUnicode, subset,
-                                multiByteGlyphs(), firstGlyphID(),
-                                lastGlyphID()));
-    insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref();
+    this->insertObjRef("ToUnicode",
+                       generate_tounicode_cmap(fFontInfo->fGlyphToUnicode,
+                                               subset,
+                                               multiByteGlyphs(),
+                                               firstGlyphID(),
+                                               lastGlyphID()));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1016,9 +1016,11 @@
 }
 
 #ifdef SK_DEBUG
-void SkPDFType0Font::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+void SkPDFType0Font::emitObject(SkWStream* stream,
+                                const SkPDFObjNumMap& objNumMap,
+                                const SkPDFSubstituteMap& substitutes) {
     SkASSERT(fPopulated);
-    return INHERITED::emitObject(stream, catalog);
+    return INHERITED::emitObject(stream, objNumMap, substitutes);
 }
 #endif
 
@@ -1030,8 +1032,8 @@
     SkAutoTUnref<SkPDFCIDFont> newCIDFont(
             new SkPDFCIDFont(fontInfo(), typeface(), subset));
     SkAutoTUnref<SkPDFArray> descendantFonts(new SkPDFArray());
-    descendantFonts->append(new SkPDFObjRef(newCIDFont.get()))->unref();
-    insert("DescendantFonts", descendantFonts.get());
+    descendantFonts->appendObjRef(newCIDFont.detach());
+    this->insertObject("DescendantFonts", descendantFonts.detach());
 
     populateToUnicodeTable(subset);
 
@@ -1056,11 +1058,12 @@
                                      const SkTDArray<uint32_t>* subset) {
     SkAutoTUnref<SkPDFDict> descriptor(new SkPDFDict("FontDescriptor"));
     setFontDescriptor(descriptor.get());
-    insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
     if (!addCommonFontDescriptorEntries(defaultWidth)) {
+        this->insertObjRef("FontDescriptor", descriptor.detach());
         return false;
     }
     if (!canEmbed()) {
+        this->insertObjRef("FontDescriptor", descriptor.detach());
         return true;
     }
 
@@ -1086,8 +1089,7 @@
             SkASSERT(fontStream.get());
 
             fontStream->insertInt("Length1", fontSize);
-            descriptor->insert("FontFile2",
-                               new SkPDFObjRef(fontStream.get()))->unref();
+            descriptor->insertObjRef("FontFile2", fontStream.detach());
             break;
         }
         case SkAdvancedTypefaceMetrics::kCFF_Font:
@@ -1102,13 +1104,13 @@
             } else {
                 fontStream->insertName("Subtype", "CIDFontType0c");
             }
-            descriptor->insert("FontFile3",
-                                new SkPDFObjRef(fontStream.get()))->unref();
+            descriptor->insertObjRef("FontFile3", fontStream.detach());
             break;
         }
         default:
             SkASSERT(false);
     }
+    this->insertObjRef("FontDescriptor", descriptor.detach());
     return true;
 }
 
@@ -1125,10 +1127,10 @@
             subset->exportTo(&glyphIDs);
         }
 
-        SkAdvancedTypefaceMetrics::PerGlyphInfo info;
-        info = SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo;
-        info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
-                  info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo);
+        SkTypeface::PerGlyphInfo info;
+        info = SkTypeface::kGlyphNames_PerGlyphInfo;
+        info = SkTBitOr<SkTypeface::PerGlyphInfo>(
+                  info, SkTypeface::kHAdvance_PerGlyphInfo);
         uint32_t* glyphs = (glyphIDs.count() == 0) ? NULL : glyphIDs.begin();
         uint32_t glyphsCount = glyphs ? glyphIDs.count() : 0;
         SkAutoTUnref<const SkAdvancedTypefaceMetrics> fontMetrics(
@@ -1152,10 +1154,10 @@
     }
 
     SkAutoTUnref<SkPDFDict> sysInfo(new SkPDFDict);
-    sysInfo->insert("Registry", new SkPDFString("Adobe"))->unref();
-    sysInfo->insert("Ordering", new SkPDFString("Identity"))->unref();
+    sysInfo->insertString("Registry", "Adobe");
+    sysInfo->insertString("Ordering", "Identity");
     sysInfo->insertInt("Supplement", 0);
-    insert("CIDSystemInfo", sysInfo.get());
+    this->insertObject("CIDSystemInfo", sysInfo.detach());
 
     if (fontInfo()->fGlyphWidths.get()) {
         int16_t defaultWidth = 0;
@@ -1164,10 +1166,11 @@
                                fontInfo()->fEmSize, &appendWidth,
                                &defaultWidth));
         if (widths->size())
-            insert("W", widths.get());
+            this->insertObject("W", widths.detach());
         if (defaultWidth != 0) {
-            insertScalar("DW", scaleFromFontUnits(defaultWidth,
-                                                  fontInfo()->fEmSize));
+            this->insertScalar(
+                    "DW",
+                    scaleFromFontUnits(defaultWidth, fontInfo()->fEmSize));
         }
     }
     if (fontInfo()->fVerticalMetrics.get()) {
@@ -1180,13 +1183,14 @@
                                fontInfo()->fEmSize, &appendVerticalAdvance,
                                &defaultAdvance));
         if (advances->size())
-            insert("W2", advances.get());
+            this->insertObject("W2", advances.detach());
         if (defaultAdvance.fVerticalAdvance ||
                 defaultAdvance.fOriginXDisp ||
                 defaultAdvance.fOriginYDisp) {
-            insert("DW2", appendVerticalAdvance(defaultAdvance,
-                                                fontInfo()->fEmSize,
-                                                new SkPDFArray))->unref();
+            this->insertObject("DW2",
+                               appendVerticalAdvance(defaultAdvance,
+                                                     fontInfo()->fEmSize,
+                                                     new SkPDFArray));
         }
     }
 
@@ -1208,9 +1212,8 @@
 SkPDFType1Font::~SkPDFType1Font() {}
 
 bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) {
-    if (getFontDescriptor() != NULL) {
-        SkPDFDict* descriptor = getFontDescriptor();
-        insert("FontDescriptor", new SkPDFObjRef(descriptor))->unref();
+    if (SkPDFDict* descriptor = getFontDescriptor()) {
+        this->insertObjRef("FontDescriptor", SkRef(descriptor));
         return true;
     }
 
@@ -1232,11 +1235,10 @@
         fontStream->insertInt("Length1", header);
         fontStream->insertInt("Length2", data);
         fontStream->insertInt("Length3", trailer);
-        descriptor->insert("FontFile",
-                           new SkPDFObjRef(fontStream.get()))->unref();
+        descriptor->insertObjRef("FontFile", fontStream.detach());
     }
 
-    insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
+    this->insertObjRef("FontDescriptor", descriptor.detach());
 
     return addCommonFontDescriptorEntries(defaultWidth);
 }
@@ -1276,18 +1278,17 @@
 
     addWidthInfoFromRange(defaultWidth, widthRangeEntry);
 
-    SkAutoTUnref<SkPDFDict> encoding(new SkPDFDict("Encoding"));
-    insert("Encoding", encoding.get());
 
     SkAutoTUnref<SkPDFArray> encDiffs(new SkPDFArray);
-    encoding->insert("Differences", encDiffs.get());
-
     encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2);
     encDiffs->appendInt(1);
     for (int gID = firstGlyphID(); gID <= lastGlyphID(); gID++) {
         encDiffs->appendName(fontInfo()->fGlyphNames->get()[gID].c_str());
     }
 
+    SkAutoTUnref<SkPDFDict> encoding(new SkPDFDict("Encoding"));
+    encoding->insertObject("Differences", encDiffs.detach());
+    this->insertObject("Encoding", encoding.detach());
     return true;
 }
 
@@ -1315,9 +1316,9 @@
     } else {
         appendWidth(defaultWidth, 1000, widthArray.get());
     }
-    insertInt("FirstChar", firstChar);
-    insertInt("LastChar", firstChar + widthArray->size() - 1);
-    insert("Widths", widthArray.get());
+    this->insertInt("FirstChar", firstChar);
+    this->insertInt("LastChar", firstChar + widthArray->size() - 1);
+    this->insertObject("Widths", widthArray.detach());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1350,16 +1351,12 @@
     // Flip about the x-axis and scale by 1/1000.
     SkMatrix fontMatrix;
     fontMatrix.setScale(SkScalarInvert(1000), -SkScalarInvert(1000));
-    insert("FontMatrix", SkPDFUtils::MatrixToArray(fontMatrix))->unref();
+    this->insertObject("FontMatrix", SkPDFUtils::MatrixToArray(fontMatrix));
 
     SkAutoTUnref<SkPDFDict> charProcs(new SkPDFDict);
-    insert("CharProcs", charProcs.get());
-
     SkAutoTUnref<SkPDFDict> encoding(new SkPDFDict("Encoding"));
-    insert("Encoding", encoding.get());
 
     SkAutoTUnref<SkPDFArray> encDiffs(new SkPDFArray);
-    encoding->insert("Differences", encDiffs.get());
     encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2);
     encDiffs->appendInt(1);
 
@@ -1389,17 +1386,20 @@
         SkAutoTDelete<SkMemoryStream> glyphStream(new SkMemoryStream());
         glyphStream->setData(content.copyToData())->unref();
 
-        SkAutoTUnref<SkPDFStream> glyphDescription(
-            new SkPDFStream(glyphStream.get()));
-        charProcs->insert(characterName.c_str(),
-                          new SkPDFObjRef(glyphDescription.get()))->unref();
+        charProcs->insertObjRef(characterName,
+                                new SkPDFStream(glyphStream.get()));
     }
 
-    insert("FontBBox", makeFontBBox(bbox, 1000))->unref();
-    insertInt("FirstChar", 1);
-    insertInt("LastChar", lastGlyphID() - firstGlyphID() + 1);
-    insert("Widths", widthArray.get());
-    insertName("CIDToGIDMap", "Identity");
+    encoding->insertObject("Differences", encDiffs.detach());
+
+    this->insertObject("CharProcs", charProcs.detach());
+    this->insertObject("Encoding", encoding.detach());
+
+    this->insertObject("FontBBox", makeFontBBox(bbox, 1000));
+    this->insertInt("FirstChar", 1);
+    this->insertInt("LastChar", lastGlyphID() - firstGlyphID() + 1);
+    this->insertObject("Widths", widthArray.detach());
+    this->insertName("CIDToGIDMap", "Identity");
 
     populateToUnicodeTable(NULL);
     return true;
diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h
index c286fa4..6f8a923 100644
--- a/src/pdf/SkPDFFont.h
+++ b/src/pdf/SkPDFFont.h
@@ -18,7 +18,7 @@
 
 class SkPaint;
 class SkPDFCanon;
-class SkPDFCatalog;
+class SkPDFObjNumMap;
 class SkPDFFont;
 
 class SkPDFGlyphSet : SkNoncopyable {
diff --git a/src/pdf/SkPDFFontImpl.h b/src/pdf/SkPDFFontImpl.h
index a0689a3..cc7a4a1 100644
--- a/src/pdf/SkPDFFontImpl.h
+++ b/src/pdf/SkPDFFontImpl.h
@@ -18,7 +18,9 @@
     virtual bool multiByteGlyphs() const { return true; }
     virtual SkPDFFont* getFontSubset(const SkPDFGlyphSet* usage);
 #ifdef SK_DEBUG
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog);
+    virtual void emitObject(SkWStream*,
+                            const SkPDFObjNumMap&,
+                            const SkPDFSubstituteMap&);
 #endif
 
 private:
diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp
index 19570f3..07f42fa 100644
--- a/src/pdf/SkPDFFormXObject.cpp
+++ b/src/pdf/SkPDFFormXObject.cpp
@@ -10,9 +10,7 @@
 #include "SkPDFFormXObject.h"
 
 #include "SkMatrix.h"
-#include "SkPDFCatalog.h"
 #include "SkPDFDevice.h"
-#include "SkPDFResourceDict.h"
 #include "SkPDFUtils.h"
 #include "SkStream.h"
 #include "SkTypes.h"
@@ -21,13 +19,13 @@
     // We don't want to keep around device because we'd have two copies
     // of content, so reference or copy everything we need (content and
     // resources).
-    SkPDFResourceDict* resourceDict = device->getResourceDict();
+    SkAutoTUnref<SkPDFDict> resourceDict(device->createResourceDict());
 
     SkAutoTDelete<SkStreamAsset> content(device->content());
     this->setData(content.get());
 
     SkAutoTUnref<SkPDFArray> bboxArray(device->copyMediaBox());
-    init(NULL, resourceDict, bboxArray);
+    this->init(NULL, resourceDict.get(), bboxArray);
 
     // We invert the initial transform and apply that to the xobject so that
     // it doesn't get applied twice. We can't just undo it because it's
@@ -39,7 +37,7 @@
             SkASSERT(false);
             inverse.reset();
         }
-        insert("Matrix", SkPDFUtils::MatrixToArray(inverse))->unref();
+        this->insertObject("Matrix", SkPDFUtils::MatrixToArray(inverse));
     }
 }
 
@@ -47,7 +45,7 @@
  * Creates a FormXObject from a content stream and associated resources.
  */
 SkPDFFormXObject::SkPDFFormXObject(SkStream* content, SkRect bbox,
-                                   SkPDFResourceDict* resourceDict) {
+                                   SkPDFDict* resourceDict) {
     setData(content);
 
     SkAutoTUnref<SkPDFArray> bboxArray(SkPDFUtils::RectToArray(bbox));
@@ -60,10 +58,10 @@
  */
 void SkPDFFormXObject::init(const char* colorSpace,
                             SkPDFDict* resourceDict, SkPDFArray* bbox) {
-    insertName("Type", "XObject");
-    insertName("Subtype", "Form");
-    insert("Resources", resourceDict);
-    insert("BBox", bbox);
+    this->insertName("Type", "XObject");
+    this->insertName("Subtype", "Form");
+    this->insertObject("Resources", SkRef(resourceDict));
+    this->insertObject("BBox", SkRef(bbox));
 
     // Right now SkPDFFormXObject is only used for saveLayer, which implies
     // isolated blending.  Do this conditionally if that changes.
@@ -73,8 +71,8 @@
     if (colorSpace != NULL) {
         group->insertName("CS", colorSpace);
     }
-    group->insert("I", new SkPDFBool(true))->unref();  // Isolated.
-    insert("Group", group.get());
+    group->insertBool("I", true);  // Isolated.
+    this->insertObject("Group", group.detach());
 }
 
 SkPDFFormXObject::~SkPDFFormXObject() {}
diff --git a/src/pdf/SkPDFFormXObject.h b/src/pdf/SkPDFFormXObject.h
index 11fa2d5..4f903f6 100644
--- a/src/pdf/SkPDFFormXObject.h
+++ b/src/pdf/SkPDFFormXObject.h
@@ -14,12 +14,11 @@
 #include "SkPDFTypes.h"
 #include "SkRect.h"
 #include "SkRefCnt.h"
-#include "SkPDFResourceDict.h"
 #include "SkString.h"
 
 class SkMatrix;
 class SkPDFDevice;
-class SkPDFCatalog;
+class SkPDFObjNumMap;
 
 /** \class SkPDFFormXObject
 
@@ -44,7 +43,7 @@
      */
     explicit SkPDFFormXObject(SkStream* content,
                               SkRect bbox,
-                              SkPDFResourceDict* resourceDict);
+                              SkPDFDict* resourceDict);
     virtual ~SkPDFFormXObject();
 
 private:
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index 09545d1..6cf6645 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -70,64 +70,58 @@
     return NULL;
 }
 
-static bool equivalent(const SkPaint& a, const SkPaint& b) {
-    // We're only interested in some fields of the SkPaint, so we have
-    // a custom equality function.
-    if (SkColorGetA(a.getColor()) != SkColorGetA(b.getColor()) ||
-        a.getStrokeCap() != b.getStrokeCap() ||
-        a.getStrokeJoin() != b.getStrokeJoin() ||
-        a.getStrokeWidth() != b.getStrokeWidth() ||
-        a.getStrokeMiter() != b.getStrokeMiter()) {
-        return false;
+// If a SkXfermode is unsupported in PDF, this function returns
+// SrcOver, otherwise, it returns that Xfermode as a Mode.
+static SkXfermode::Mode mode_for_pdf(const SkXfermode* xfermode) {
+    SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
+    if (xfermode) {
+        xfermode->asMode(&mode);
     }
-
-    SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
-    SkXfermode* aXfermode = a.getXfermode();
-    if (aXfermode) {
-        aXfermode->asMode(&aXfermodeName);
+    switch (mode) {
+        case SkXfermode::kSrcOver_Mode:
+        case SkXfermode::kMultiply_Mode:
+        case SkXfermode::kScreen_Mode:
+        case SkXfermode::kOverlay_Mode:
+        case SkXfermode::kDarken_Mode:
+        case SkXfermode::kLighten_Mode:
+        case SkXfermode::kColorDodge_Mode:
+        case SkXfermode::kColorBurn_Mode:
+        case SkXfermode::kHardLight_Mode:
+        case SkXfermode::kSoftLight_Mode:
+        case SkXfermode::kDifference_Mode:
+        case SkXfermode::kExclusion_Mode:
+        case SkXfermode::kHue_Mode:
+        case SkXfermode::kSaturation_Mode:
+        case SkXfermode::kColor_Mode:
+        case SkXfermode::kLuminosity_Mode:
+            // Mode is suppported and handled by pdf graphics state.
+            return mode;
+        default:
+            return SkXfermode::kSrcOver_Mode;  // Default mode.
     }
-    if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
-        as_blend_mode(aXfermodeName) == NULL) {
-        aXfermodeName = SkXfermode::kSrcOver_Mode;
-    }
-    const char* aXfermodeString = as_blend_mode(aXfermodeName);
-    SkASSERT(aXfermodeString != NULL);
-
-    SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
-    SkXfermode* bXfermode = b.getXfermode();
-    if (bXfermode) {
-        bXfermode->asMode(&bXfermodeName);
-    }
-    if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
-        as_blend_mode(bXfermodeName) == NULL) {
-        bXfermodeName = SkXfermode::kSrcOver_Mode;
-    }
-    const char* bXfermodeString = as_blend_mode(bXfermodeName);
-    SkASSERT(bXfermodeString != NULL);
-
-    return strcmp(aXfermodeString, bXfermodeString) == 0;
 }
 
-bool SkPDFGraphicState::equals(const SkPaint& paint) const {
-    return equivalent(paint, fPaint);
-}
-
-SkPDFGraphicState::~SkPDFGraphicState() {}
-
-void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    populateDict();
-    SkPDFDict::emitObject(stream, catalog);
-}
+SkPDFGraphicState::SkPDFGraphicState(const SkPaint& p)
+    : fStrokeWidth(p.getStrokeWidth())
+    , fStrokeMiter(p.getStrokeMiter())
+    , fAlpha(p.getAlpha())
+    , fStrokeCap(SkToU8(p.getStrokeCap()))
+    , fStrokeJoin(SkToU8(p.getStrokeJoin()))
+    , fMode(SkToU8(mode_for_pdf(p.getXfermode()))) {}
 
 // static
 SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
         SkPDFCanon* canon, const SkPaint& paint) {
     SkASSERT(canon);
-    SkPDFGraphicState* pdfGraphicState = canon->findGraphicState(paint);
-    if (pdfGraphicState) {
-        return SkRef(pdfGraphicState);
+    SkPDFGraphicState key(paint);
+    if (const SkPDFGraphicState* canonGS = canon->findGraphicState(key)) {
+        // The returned SkPDFGraphicState must be made non-const,
+        // since the emitObject() interface is non-const.  But We
+        // promise that there is no way to mutate this object from
+        // here on out.
+        return SkRef(const_cast<SkPDFGraphicState*>(canonGS));
     }
-    pdfGraphicState = new SkPDFGraphicState(paint);
+    SkPDFGraphicState* pdfGraphicState = new SkPDFGraphicState(paint);
     canon->addGraphicState(pdfGraphicState);
     return pdfGraphicState;
 }
@@ -149,20 +143,23 @@
     SkPDFStream* invertFunction = SkNEW_ARGS(
             SkPDFStream, (psInvertStream.get()));
     invertFunction->insertInt("FunctionType", 4);
-    invertFunction->insert("Domain", domainAndRange.get());
-    invertFunction->insert("Range", domainAndRange.get());
+    invertFunction->insertObject("Domain", SkRef(domainAndRange.get()));
+    invertFunction->insertObject("Range", domainAndRange.detach());
     return invertFunction;
 }
 
 template <typename T> void unref(T* ptr) { ptr->unref(); }
 }  // namespace
 
-SK_DECLARE_STATIC_LAZY_PTR(SkPDFObject, invertFunction,
-                           create_invert_function, unref<SkPDFObject>);
+SK_DECLARE_STATIC_LAZY_PTR(SkPDFObject,
+                           invertFunction,
+                           create_invert_function,
+                           unref<SkPDFObject>);
 
 // static
-SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState(
-        SkPDFFormXObject* sMask, bool invert, SkPDFSMaskMode sMaskMode) {
+SkPDFDict* SkPDFGraphicState::GetSMaskGraphicState(SkPDFFormXObject* sMask,
+                                                   bool invert,
+                                                   SkPDFSMaskMode sMaskMode) {
     // The practical chances of using the same mask more than once are unlikely
     // enough that it's not worth canonicalizing.
     SkAutoTUnref<SkPDFDict> sMaskDict(new SkPDFDict("Mask"));
@@ -171,82 +168,64 @@
     } else if (sMaskMode == kLuminosity_SMaskMode) {
         sMaskDict->insertName("S", "Luminosity");
     }
-    sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
-
-    SkPDFGraphicState* result = new SkPDFGraphicState;
-    result->fPopulated = true;
-    result->insertName("Type", "ExtGState");
-    result->insert("SMask", sMaskDict.get());
-
+    sMaskDict->insertObjRef("G", SkRef(sMask));
     if (invert) {
-        sMaskDict->insert("TR", new SkPDFObjRef(invertFunction.get()))->unref();
+        sMaskDict->insertObjRef("TR", SkRef(invertFunction.get()));
     }
 
+    SkPDFDict* result = new SkPDFDict("ExtGState");
+    result->insertObject("SMask", sMaskDict.detach());
     return result;
 }
 
-SkPDFGraphicState* SkPDFGraphicState::CreateNoSMaskGraphicState() {
-    SkPDFGraphicState* noSMaskGS = SkNEW(SkPDFGraphicState);
-    noSMaskGS->fPopulated = true;
-    noSMaskGS->insertName("Type", "ExtGState");
+namespace {
+SkPDFDict* create_no_smask_graphic_state() {
+    SkPDFDict* noSMaskGS = new SkPDFDict("ExtGState");
     noSMaskGS->insertName("SMask", "None");
     return noSMaskGS;
 }
-
-SK_DECLARE_STATIC_LAZY_PTR(
-        SkPDFGraphicState, noSMaskGraphicState,
-        SkPDFGraphicState::CreateNoSMaskGraphicState, unref<SkPDFGraphicState>);
+} // namespace
+SK_DECLARE_STATIC_LAZY_PTR(SkPDFDict,
+                           noSMaskGraphicState,
+                           create_no_smask_graphic_state,
+                           unref<SkPDFDict>);
 
 // static
-SkPDFGraphicState* SkPDFGraphicState::GetNoSMaskGraphicState() {
+SkPDFDict* SkPDFGraphicState::GetNoSMaskGraphicState() {
     return SkRef(noSMaskGraphicState.get());
 }
 
-SkPDFGraphicState::SkPDFGraphicState()
-    : fPopulated(false) {}
+void SkPDFGraphicState::emitObject(SkWStream* stream,
+                                   const SkPDFObjNumMap& objNumMap,
+                                   const SkPDFSubstituteMap& substitutes) {
+    SkAutoTUnref<SkPDFDict> dict(SkNEW_ARGS(SkPDFDict, ("ExtGState")));
+    dict->insertName("Type", "ExtGState");
 
-SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
-    : fPaint(paint), fPopulated(false) {}
+    SkScalar alpha = SkIntToScalar(fAlpha) / 0xFF;
+    dict->insertScalar("CA", alpha);
+    dict->insertScalar("ca", alpha);
 
-// populateDict and operator== have to stay in sync with each other.
-void SkPDFGraphicState::populateDict() {
-    if (!fPopulated) {
-        fPopulated = true;
-        insertName("Type", "ExtGState");
+    SkPaint::Cap strokeCap = (SkPaint::Cap)fStrokeCap;
+    SkPaint::Join strokeJoin = (SkPaint::Join)fStrokeJoin;
+    SkXfermode::Mode xferMode = (SkXfermode::Mode)fMode;
 
-        SkAutoTUnref<SkPDFScalar> alpha(
-            new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF)));
-        insert("CA", alpha.get());
-        insert("ca", alpha.get());
+    SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
+    SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
+    SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
+    SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
+    SkASSERT(strokeCap >= 0 && strokeCap <= 2);
+    dict->insertInt("LC", strokeCap);
 
-        SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
-        SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
-        SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
-        SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
-        SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
-        insertInt("LC", fPaint.getStrokeCap());
+    SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
+    SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
+    SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
+    SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
+    SkASSERT(strokeJoin >= 0 && strokeJoin <= 2);
+    dict->insertInt("LJ", strokeJoin);
 
-        SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
-        SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
-        SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
-        SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
-        SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
-        insertInt("LJ", fPaint.getStrokeJoin());
-
-        insertScalar("LW", fPaint.getStrokeWidth());
-        insertScalar("ML", fPaint.getStrokeMiter());
-        insert("SA", new SkPDFBool(true))->unref();  // Auto stroke adjustment.
-
-        SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
-        // If asMode fails, default to kSrcOver_Mode.
-        if (fPaint.getXfermode())
-            fPaint.getXfermode()->asMode(&xfermode);
-        // If we don't support the mode, just use kSrcOver_Mode.
-        if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
-            as_blend_mode(xfermode) == NULL) {
-            xfermode = SkXfermode::kSrcOver_Mode;
-            NOT_IMPLEMENTED("unsupported xfermode", false);
-        }
-        insertName("BM", as_blend_mode(xfermode));
-    }
+    dict->insertScalar("LW", fStrokeWidth);
+    dict->insertScalar("ML", fStrokeMiter);
+    dict->insertBool("SA", true);  // SA = Auto stroke adjustment.
+    dict->insertName("BM", as_blend_mode(xferMode));
+    dict->emitObject(stream, objNumMap, substitutes);
 }
diff --git a/src/pdf/SkPDFGraphicState.h b/src/pdf/SkPDFGraphicState.h
index dc41fc6..dddf439 100644
--- a/src/pdf/SkPDFGraphicState.h
+++ b/src/pdf/SkPDFGraphicState.h
@@ -13,19 +13,17 @@
 #include "SkPaint.h"
 #include "SkPDFTypes.h"
 #include "SkTemplates.h"
+#include "SkChecksum.h"
 
+class SkPDFCanon;
 class SkPDFFormXObject;
 
 /** \class SkPDFGraphicState
     SkPaint objects roughly correspond to graphic state dictionaries that can
     be installed. So that a given dictionary is only output to the pdf file
-    once, we want to canonicalize them. Static methods in this class manage
-    a weakly referenced set of SkPDFGraphicState objects: when the last
-    reference to a SkPDFGraphicState is removed, it removes itself from the
-    static set of objects.
-
+    once, we want to canonicalize them.
 */
-class SkPDFGraphicState : public SkPDFDict {
+class SkPDFGraphicState : public SkPDFObject {
     SK_DECLARE_INST_COUNT(SkPDFGraphicState)
 public:
     enum SkPDFSMaskMode {
@@ -33,11 +31,11 @@
         kLuminosity_SMaskMode
     };
 
-    virtual ~SkPDFGraphicState();
-
     // Override emitObject so that we can populate the dictionary on
     // demand.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog);
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes);
 
     /** Get the graphic state for the passed SkPaint. The reference count of
      *  the object is incremented and it is the caller's responsibility to
@@ -55,32 +53,37 @@
      *  @param sMask     The form xobject to use as a soft mask.
      *  @param invert    Indicates if the alpha of the sMask should be inverted.
      *  @param sMaskMode Whether to use alpha or luminosity for the sMask.
+     *
+     *  These are not de-duped.
      */
-    static SkPDFGraphicState* GetSMaskGraphicState(SkPDFFormXObject* sMask,
-                                                   bool invert,
-                                                   SkPDFSMaskMode sMaskMode);
+    static SkPDFDict* GetSMaskGraphicState(SkPDFFormXObject* sMask,
+                                           bool invert,
+                                           SkPDFSMaskMode sMaskMode);
 
     /** Get a graphic state that only unsets the soft mask. The reference
      *  count of the object is incremented and it is the caller's responsibility
      *  to unreference it when done. This is needed to accommodate the weak
      *  reference pattern used when the returned object is new and has no
      *  other references.
+     *
+     *  The returned object is a singleton.
      */
-    static SkPDFGraphicState* GetNoSMaskGraphicState();
+    static SkPDFDict* GetNoSMaskGraphicState();
 
-    bool equals(const SkPaint&) const;
-
-    // Only public for SK_DECLARE_STATIC_LAZY_PTR
-    static SkPDFGraphicState* CreateNoSMaskGraphicState();
+    bool operator==(const SkPDFGraphicState& rhs) const {
+        return 0 == memcmp(&fStrokeWidth, &rhs.fStrokeWidth, 12);
+    }
+    uint32_t hash() const { return SkChecksum::Murmur3(&fStrokeWidth, 12); }
 
 private:
-    const SkPaint fPaint;
-    bool fPopulated;
+    const SkScalar fStrokeWidth;
+    const SkScalar fStrokeMiter;
+    const uint8_t fAlpha;
+    const uint8_t fStrokeCap;   // SkPaint::Cap
+    const uint8_t fStrokeJoin;  // SkPaint::Join
+    const uint8_t fMode;        // SkXfermode::Mode
 
-    SkPDFGraphicState();
-    SkPDFGraphicState(const SkPaint& paint);
-
-    void populateDict();
+    SkPDFGraphicState(const SkPaint&);
 
     typedef SkPDFDict INHERITED;
 };
diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp
deleted file mode 100644
index 666a008..0000000
--- a/src/pdf/SkPDFImage.cpp
+++ /dev/null
@@ -1,706 +0,0 @@
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkPDFImage.h"
-
-#include "SkBitmap.h"
-#include "SkColor.h"
-#include "SkColorPriv.h"
-#include "SkData.h"
-#include "SkFlate.h"
-#include "SkPDFBitmap.h"
-#include "SkPDFCatalog.h"
-#include "SkPixelRef.h"
-#include "SkRect.h"
-#include "SkStream.h"
-#include "SkString.h"
-#include "SkUnPreMultiply.h"
-
-static size_t get_uncompressed_size(const SkBitmap& bitmap,
-                                    const SkIRect& srcRect) {
-    switch (bitmap.colorType()) {
-        case kIndex_8_SkColorType:
-            return srcRect.width() * srcRect.height();
-        case kARGB_4444_SkColorType:
-            return ((srcRect.width() * 3 + 1) / 2) * srcRect.height();
-        case kRGB_565_SkColorType:
-            return srcRect.width() * 3 * srcRect.height();
-        case kRGBA_8888_SkColorType:
-        case kBGRA_8888_SkColorType:
-            return srcRect.width() * 3 * srcRect.height();
-        case kAlpha_8_SkColorType:
-            return 1;
-        default:
-            SkASSERT(false);
-            return 0;
-    }
-}
-
-static SkStream* extract_index8_image(const SkBitmap& bitmap,
-                                      const SkIRect& srcRect) {
-    const int rowBytes = srcRect.width();
-    SkStream* stream = SkNEW_ARGS(SkMemoryStream,
-                                  (get_uncompressed_size(bitmap, srcRect)));
-    uint8_t* dst = (uint8_t*)stream->getMemoryBase();
-
-    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-        memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes);
-        dst += rowBytes;
-    }
-    return stream;
-}
-
-static SkStream* extract_argb4444_data(const SkBitmap& bitmap,
-                                       const SkIRect& srcRect,
-                                       bool extractAlpha,
-                                       bool* isOpaque,
-                                       bool* isTransparent) {
-    SkStream* stream;
-    uint8_t* dst = NULL;
-    if (extractAlpha) {
-        const int alphaRowBytes = (srcRect.width() + 1) / 2;
-        stream = SkNEW_ARGS(SkMemoryStream,
-                            (alphaRowBytes * srcRect.height()));
-    } else {
-        stream = SkNEW_ARGS(SkMemoryStream,
-                            (get_uncompressed_size(bitmap, srcRect)));
-    }
-    dst = (uint8_t*)stream->getMemoryBase();
-
-    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-        uint16_t* src = bitmap.getAddr16(0, y);
-        int x;
-        for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) {
-            if (extractAlpha) {
-                dst[0] = (SkGetPackedA4444(src[x]) << 4) |
-                    SkGetPackedA4444(src[x + 1]);
-                *isOpaque &= dst[0] == SK_AlphaOPAQUE;
-                *isTransparent &= dst[0] == SK_AlphaTRANSPARENT;
-                dst++;
-            } else {
-                dst[0] = (SkGetPackedR4444(src[x]) << 4) |
-                    SkGetPackedG4444(src[x]);
-                dst[1] = (SkGetPackedB4444(src[x]) << 4) |
-                    SkGetPackedR4444(src[x + 1]);
-                dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) |
-                    SkGetPackedB4444(src[x + 1]);
-                dst += 3;
-            }
-        }
-        if (srcRect.width() & 1) {
-            if (extractAlpha) {
-                dst[0] = (SkGetPackedA4444(src[x]) << 4);
-                *isOpaque &= dst[0] == (SK_AlphaOPAQUE & 0xF0);
-                *isTransparent &= dst[0] == (SK_AlphaTRANSPARENT & 0xF0);
-                dst++;
-
-            } else {
-                dst[0] = (SkGetPackedR4444(src[x]) << 4) |
-                    SkGetPackedG4444(src[x]);
-                dst[1] = (SkGetPackedB4444(src[x]) << 4);
-                dst += 2;
-            }
-        }
-    }
-    return stream;
-}
-
-static SkStream* extract_rgb565_image(const SkBitmap& bitmap,
-                                      const SkIRect& srcRect) {
-    SkStream* stream = SkNEW_ARGS(SkMemoryStream,
-                                  (get_uncompressed_size(bitmap,
-                                                     srcRect)));
-    uint8_t* dst = (uint8_t*)stream->getMemoryBase();
-    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-        uint16_t* src = bitmap.getAddr16(0, y);
-        for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
-            dst[0] = SkGetPackedR16(src[x]);
-            dst[1] = SkGetPackedG16(src[x]);
-            dst[2] = SkGetPackedB16(src[x]);
-            dst += 3;
-        }
-    }
-    return stream;
-}
-
-static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap,
-                                                int xOrig,
-                                                int yOrig);
-
-static SkStream* extract_argb8888_data(const SkBitmap& bitmap,
-                                       const SkIRect& srcRect,
-                                       bool extractAlpha,
-                                       bool* isOpaque,
-                                       bool* isTransparent) {
-    size_t streamSize = extractAlpha ? srcRect.width() * srcRect.height()
-                                     : get_uncompressed_size(bitmap, srcRect);
-    SkStream* stream = SkNEW_ARGS(SkMemoryStream, (streamSize));
-    uint8_t* dst = (uint8_t*)stream->getMemoryBase();
-
-    const SkUnPreMultiply::Scale* scaleTable = SkUnPreMultiply::GetScaleTable();
-
-    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-        uint32_t* src = bitmap.getAddr32(0, y);
-        for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
-            SkPMColor c = src[x];
-            U8CPU alpha = SkGetPackedA32(c);
-            if (extractAlpha) {
-                *isOpaque &= alpha == SK_AlphaOPAQUE;
-                *isTransparent &= alpha == SK_AlphaTRANSPARENT;
-                *dst++ = alpha;
-            } else {
-                if (SK_AlphaTRANSPARENT == alpha) {
-                    // It is necessary to average the color component of
-                    // transparent pixels with their surrounding neighbors
-                    // since the PDF renderer may separately re-sample the
-                    // alpha and color channels when the image is not
-                    // displayed at its native resolution. Since an alpha of
-                    // zero gives no information about the color component,
-                    // the pathological case is a white image with sharp
-                    // transparency bounds - the color channel goes to black,
-                    // and the should-be-transparent pixels are rendered
-                    // as grey because of the separate soft mask and color
-                    // resizing.
-                    c = get_argb8888_neighbor_avg_color(bitmap, x, y);
-                    *dst++ = SkGetPackedR32(c);
-                    *dst++ = SkGetPackedG32(c);
-                    *dst++ = SkGetPackedB32(c);
-                } else {
-                    SkUnPreMultiply::Scale s = scaleTable[alpha];
-                    *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
-                    *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(c));
-                    *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
-                }
-            }
-        }
-    }
-    SkASSERT(dst == streamSize + (uint8_t*)stream->getMemoryBase());
-    return stream;
-}
-
-static SkStream* extract_a8_alpha(const SkBitmap& bitmap,
-                                  const SkIRect& srcRect,
-                                  bool* isOpaque,
-                                  bool* isTransparent) {
-    const int alphaRowBytes = srcRect.width();
-    SkStream* stream = SkNEW_ARGS(SkMemoryStream,
-                                  (alphaRowBytes * srcRect.height()));
-    uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase();
-
-    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-        uint8_t* src = bitmap.getAddr8(0, y);
-        for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
-            alphaDst[0] = src[x];
-            *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE;
-            *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT;
-            alphaDst++;
-        }
-    }
-    return stream;
-}
-
-static SkStream* create_black_image() {
-    SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1));
-    ((uint8_t*)stream->getMemoryBase())[0] = 0;
-    return stream;
-}
-
-/**
- * Extract either the color or image data from a SkBitmap into a SkStream.
- * @param bitmap        Bitmap to extract data from.
- * @param srcRect       Region in the bitmap to extract.
- * @param extractAlpha  Set to true to extract the alpha data or false to
- *                      extract the color data.
- * @param isTransparent Pointer to a bool to output whether the alpha is
- *                      completely transparent. May be NULL. Only valid when
- *                      extractAlpha == true.
- * @return              Unencoded image data, or NULL if either data was not
- *                      available or alpha data was requested but the image was
- *                      entirely transparent or opaque.
- */
-static SkStream* extract_image_data(const SkBitmap& bitmap,
-                                    const SkIRect& srcRect,
-                                    bool extractAlpha, bool* isTransparent) {
-    SkColorType colorType = bitmap.colorType();
-    if (extractAlpha && (kIndex_8_SkColorType == colorType ||
-                         kRGB_565_SkColorType == colorType)) {
-        if (isTransparent != NULL) {
-            *isTransparent = false;
-        }
-        return NULL;
-    }
-
-    SkAutoLockPixels lock(bitmap);
-    if (NULL == bitmap.getPixels()) {
-        return NULL;
-    }
-
-    bool isOpaque = true;
-    bool transparent = extractAlpha;
-    SkAutoTDelete<SkStream> stream;
-
-    switch (colorType) {
-        case kIndex_8_SkColorType:
-            if (!extractAlpha) {
-                stream.reset(extract_index8_image(bitmap, srcRect));
-            }
-            break;
-        case kARGB_4444_SkColorType:
-            stream.reset(extract_argb4444_data(bitmap, srcRect, extractAlpha,
-                                               &isOpaque, &transparent));
-            break;
-        case kRGB_565_SkColorType:
-            if (!extractAlpha) {
-                stream.reset(extract_rgb565_image(bitmap, srcRect));
-            }
-            break;
-        case kN32_SkColorType:
-            stream.reset(extract_argb8888_data(bitmap, srcRect, extractAlpha,
-                                               &isOpaque, &transparent));
-            break;
-        case kAlpha_8_SkColorType:
-            if (!extractAlpha) {
-                stream.reset(create_black_image());
-            } else {
-                stream.reset(extract_a8_alpha(bitmap, srcRect,
-                                              &isOpaque, &transparent));
-            }
-            break;
-        default:
-            SkASSERT(false);
-    }
-
-    if (isTransparent != NULL) {
-        *isTransparent = transparent;
-    }
-    if (extractAlpha && (transparent || isOpaque)) {
-        return NULL;
-    }
-    return stream.detach();
-}
-
-static SkPDFArray* make_indexed_color_space(SkColorTable* table) {
-    SkPDFArray* result = new SkPDFArray();
-    result->reserve(4);
-    result->appendName("Indexed");
-    result->appendName("DeviceRGB");
-    result->appendInt(table->count() - 1);
-
-    // Potentially, this could be represented in fewer bytes with a stream.
-    // Max size as a string is 1.5k.
-    SkString index;
-    for (int i = 0; i < table->count(); i++) {
-        char buf[3];
-        SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
-        buf[0] = SkGetPackedR32(color);
-        buf[1] = SkGetPackedG32(color);
-        buf[2] = SkGetPackedB32(color);
-        index.append(buf, 3);
-    }
-    result->append(new SkPDFString(index))->unref();
-    return result;
-}
-
-/**
- * Removes the alpha component of an ARGB color (including unpremultiply) while
- * keeping the output in the same format as the input.
- */
-static uint32_t remove_alpha_argb8888(uint32_t pmColor) {
-    SkColor color = SkUnPreMultiply::PMColorToColor(pmColor);
-    return SkPackARGB32NoCheck(SK_AlphaOPAQUE,
-                               SkColorGetR(color),
-                               SkColorGetG(color),
-                               SkColorGetB(color));
-}
-
-static uint16_t remove_alpha_argb4444(uint16_t pmColor) {
-    return SkPixel32ToPixel4444(
-            remove_alpha_argb8888(SkPixel4444ToPixel32(pmColor)));
-}
-
-static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap,
-                                                int xOrig, int yOrig) {
-    uint8_t count = 0;
-    uint16_t r = 0;
-    uint16_t g = 0;
-    uint16_t b = 0;
-
-    for (int y = yOrig - 1; y <= yOrig + 1; y++) {
-        if (y < 0 || y >= bitmap.height()) {
-            continue;
-        }
-        uint32_t* src = bitmap.getAddr32(0, y);
-        for (int x = xOrig - 1; x <= xOrig + 1; x++) {
-            if (x < 0 || x >= bitmap.width()) {
-                continue;
-            }
-            if (SkGetPackedA32(src[x]) != SK_AlphaTRANSPARENT) {
-                uint32_t color = remove_alpha_argb8888(src[x]);
-                r += SkGetPackedR32(color);
-                g += SkGetPackedG32(color);
-                b += SkGetPackedB32(color);
-                count++;
-            }
-        }
-    }
-
-    if (count == 0) {
-        return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0);
-    } else {
-        return SkPackARGB32NoCheck(SK_AlphaOPAQUE,
-                                   r / count, g / count, b / count);
-    }
-}
-
-static uint16_t get_argb4444_neighbor_avg_color(const SkBitmap& bitmap,
-                                                int xOrig, int yOrig) {
-    uint8_t count = 0;
-    uint8_t r = 0;
-    uint8_t g = 0;
-    uint8_t b = 0;
-
-    for (int y = yOrig - 1; y <= yOrig + 1; y++) {
-        if (y < 0 || y >= bitmap.height()) {
-            continue;
-        }
-        uint16_t* src = bitmap.getAddr16(0, y);
-        for (int x = xOrig - 1; x <= xOrig + 1; x++) {
-            if (x < 0 || x >= bitmap.width()) {
-                continue;
-            }
-            if ((SkGetPackedA4444(src[x]) & 0x0F) != SK_AlphaTRANSPARENT) {
-                uint16_t color = remove_alpha_argb4444(src[x]);
-                r += SkGetPackedR4444(color);
-                g += SkGetPackedG4444(color);
-                b += SkGetPackedB4444(color);
-                count++;
-            }
-        }
-    }
-
-    if (count == 0) {
-        return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, 0, 0, 0);
-    } else {
-        return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F,
-                                   r / count, g / count, b / count);
-    }
-}
-
-static SkBitmap unpremultiply_bitmap(const SkBitmap& bitmap,
-                                     const SkIRect& srcRect) {
-    SkBitmap outBitmap;
-    outBitmap.allocPixels(bitmap.info().makeWH(srcRect.width(), srcRect.height()));
-    int dstRow = 0;
-
-    SkAutoLockPixels outBitmapPixelLock(outBitmap);
-    SkAutoLockPixels bitmapPixelLock(bitmap);
-    switch (bitmap.colorType()) {
-        case kARGB_4444_SkColorType: {
-            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-                uint16_t* dst = outBitmap.getAddr16(0, dstRow);
-                uint16_t* src = bitmap.getAddr16(0, y);
-                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
-                    uint8_t a = SkGetPackedA4444(src[x]);
-                    // It is necessary to average the color component of
-                    // transparent pixels with their surrounding neighbors
-                    // since the PDF renderer may separately re-sample the
-                    // alpha and color channels when the image is not
-                    // displayed at its native resolution. Since an alpha of
-                    // zero gives no information about the color component,
-                    // the pathological case is a white image with sharp
-                    // transparency bounds - the color channel goes to black,
-                    // and the should-be-transparent pixels are rendered
-                    // as grey because of the separate soft mask and color
-                    // resizing.
-                    if (a == (SK_AlphaTRANSPARENT & 0x0F)) {
-                        *dst = get_argb4444_neighbor_avg_color(bitmap, x, y);
-                    } else {
-                        *dst = remove_alpha_argb4444(src[x]);
-                    }
-                    dst++;
-                }
-                dstRow++;
-            }
-            break;
-        }
-        case kN32_SkColorType: {
-            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-                uint32_t* dst = outBitmap.getAddr32(0, dstRow);
-                uint32_t* src = bitmap.getAddr32(0, y);
-                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
-                    uint8_t a = SkGetPackedA32(src[x]);
-                    if (a == SK_AlphaTRANSPARENT) {
-                        *dst = get_argb8888_neighbor_avg_color(bitmap, x, y);
-                    } else {
-                        *dst = remove_alpha_argb8888(src[x]);
-                    }
-                    dst++;
-                }
-                dstRow++;
-            }
-            break;
-        }
-        default:
-            SkASSERT(false);
-    }
-
-    outBitmap.setImmutable();
-
-    return outBitmap;
-}
-
-// static
-SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
-                                    const SkIRect& srcRect) {
-    if (bitmap.colorType() == kUnknown_SkColorType) {
-        return NULL;
-    }
-
-    bool isTransparent = false;
-    SkAutoTDelete<SkStream> alphaData;
-    if (!bitmap.isOpaque()) {
-        // Note that isOpaque is not guaranteed to return false for bitmaps
-        // with alpha support but a completely opaque alpha channel,
-        // so alphaData may still be NULL if we have a completely opaque
-        // (or transparent) bitmap.
-        alphaData.reset(
-                extract_image_data(bitmap, srcRect, true, &isTransparent));
-    }
-    if (isTransparent) {
-        return NULL;
-    }
-
-    SkPDFImage* image;
-    SkColorType colorType = bitmap.colorType();
-    if (alphaData.get() != NULL && (kN32_SkColorType == colorType ||
-                                    kARGB_4444_SkColorType == colorType)) {
-        if (kN32_SkColorType == colorType) {
-            image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false,
-                                            SkIRect::MakeWH(srcRect.width(),
-                                                            srcRect.height())));
-        } else {
-            SkBitmap unpremulBitmap = unpremultiply_bitmap(bitmap, srcRect);
-            image = SkNEW_ARGS(SkPDFImage, (NULL, unpremulBitmap, false,
-                                            SkIRect::MakeWH(srcRect.width(),
-                                                            srcRect.height())));
-        }
-    } else {
-        image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, srcRect));
-    }
-    if (alphaData.get() != NULL) {
-        SkAutoTUnref<SkPDFImage> mask(
-                SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap, true, srcRect)));
-        image->insert("SMask", new SkPDFObjRef(mask))->unref();
-    }
-    return image;
-}
-
-SkPDFImage::~SkPDFImage() {}
-
-SkPDFImage::SkPDFImage(SkStream* stream,
-                       const SkBitmap& bitmap,
-                       bool isAlpha,
-                       const SkIRect& srcRect)
-    : fIsAlpha(isAlpha),
-      fSrcRect(srcRect) {
-
-    if (bitmap.isImmutable()) {
-        fBitmap = bitmap;
-    } else {
-        bitmap.deepCopyTo(&fBitmap);
-        fBitmap.setImmutable();
-    }
-
-    if (stream != NULL) {
-        this->setData(stream);
-        fStreamValid = true;
-    } else {
-        fStreamValid = false;
-    }
-
-    SkColorType colorType = fBitmap.colorType();
-
-    insertName("Type", "XObject");
-    insertName("Subtype", "Image");
-
-    bool alphaOnly = (kAlpha_8_SkColorType == colorType);
-
-    if (!isAlpha && alphaOnly) {
-        // For alpha only images, we stretch a single pixel of black for
-        // the color/shape part.
-        SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1));
-        insert("Width", one.get());
-        insert("Height", one.get());
-    } else {
-        insertInt("Width", fSrcRect.width());
-        insertInt("Height", fSrcRect.height());
-    }
-
-    if (isAlpha || alphaOnly) {
-        insertName("ColorSpace", "DeviceGray");
-    } else if (kIndex_8_SkColorType == colorType) {
-        SkAutoLockPixels alp(fBitmap);
-        insert("ColorSpace",
-               make_indexed_color_space(fBitmap.getColorTable()))->unref();
-    } else {
-        insertName("ColorSpace", "DeviceRGB");
-    }
-
-    int bitsPerComp = 8;
-    if (kARGB_4444_SkColorType == colorType) {
-        bitsPerComp = 4;
-    }
-    insertInt("BitsPerComponent", bitsPerComp);
-
-    if (kRGB_565_SkColorType == colorType) {
-        SkASSERT(!isAlpha);
-        SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0));
-        SkAutoTUnref<SkPDFScalar> scale5Val(
-                new SkPDFScalar(8.2258f));  // 255/2^5-1
-        SkAutoTUnref<SkPDFScalar> scale6Val(
-                new SkPDFScalar(4.0476f));  // 255/2^6-1
-        SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray());
-        decodeValue->reserve(6);
-        decodeValue->append(zeroVal.get());
-        decodeValue->append(scale5Val.get());
-        decodeValue->append(zeroVal.get());
-        decodeValue->append(scale6Val.get());
-        decodeValue->append(zeroVal.get());
-        decodeValue->append(scale5Val.get());
-        insert("Decode", decodeValue.get());
-    }
-}
-
-SkPDFImage::SkPDFImage(SkPDFImage& pdfImage)
-    : SkPDFStream(pdfImage),
-      fBitmap(pdfImage.fBitmap),
-      fIsAlpha(pdfImage.fIsAlpha),
-      fSrcRect(pdfImage.fSrcRect),
-      fStreamValid(pdfImage.fStreamValid) {
-    // Nothing to do here - the image params are already copied in SkPDFStream's
-    // constructor, and the bitmap will be regenerated and encoded in
-    // populate.
-}
-
-bool SkPDFImage::populate(SkPDFCatalog* catalog) {
-    if (getState() == kUnused_State) {
-        // Initializing image data for the first time.
-        // Fallback method
-        if (!fStreamValid) {
-            SkAutoTDelete<SkStream> stream(
-                    extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL));
-            this->setData(stream);
-            fStreamValid = true;
-        }
-        return INHERITED::populate(catalog);
-    }
-#ifndef SK_NO_FLATE
-    else if (getState() == kNoCompression_State) {
-        // Compression has not been requested when the stream was first created,
-        // but the new catalog wants it compressed.
-        if (!getSubstitute()) {
-            SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this));
-            setSubstitute(substitute);
-            catalog->setSubstitute(this, substitute);
-        }
-        return false;
-    }
-#endif  // SK_NO_FLATE
-    return true;
-}
-
-#if 0  // reenable when we can figure out the JPEG colorspace
-namespace {
-/**
- *  This PDFObject assumes that its constructor was handed
- *  Jpeg-encoded data that can be directly embedded into a PDF.
- */
-class PDFJPEGImage : public SkPDFObject {
-    SkAutoTUnref<SkData> fData;
-    int fWidth;
-    int fHeight;
-public:
-    PDFJPEGImage(SkData* data, int width, int height)
-        : fData(SkRef(data)), fWidth(width), fHeight(height) {}
-    virtual void emitObject(
-            SkWStream* stream,
-            SkPDFCatalog* catalog, bool indirect) SK_OVERRIDE {
-        if (indirect) {
-            this->emitIndirectObject(stream, catalog);
-            return;
-        }
-        SkASSERT(fData.get());
-        const char kPrefaceFormat[] =
-            "<<"
-            "/Type /XObject\n"
-            "/Subtype /Image\n"
-            "/Width %d\n"
-            "/Height %d\n"
-            "/ColorSpace /DeviceRGB\n"  // or DeviceGray
-            "/BitsPerComponent 8\n"
-            "/Filter /DCTDecode\n"
-            "/ColorTransform 0\n"
-            "/Length " SK_SIZE_T_SPECIFIER "\n"
-            ">> stream\n";
-        SkString preface(
-                SkStringPrintf(kPrefaceFormat, fWidth, fHeight, fData->size()));
-        const char kPostface[] = "\nendstream";
-        stream->write(preface.c_str(), preface.size());
-        stream->write(fData->data(), fData->size());
-        stream->write(kPostface, sizeof(kPostface));
-    }
-};
-
-/**
- *  If the bitmap is not subsetted, return its encoded data, if
- *  availible.
- */
-static inline SkData* ref_encoded_data(const SkBitmap& bm) {
-    if ((NULL == bm.pixelRef())
-        || !bm.pixelRefOrigin().isZero()
-        || (bm.info().dimensions() != bm.pixelRef()->info().dimensions())) {
-        return NULL;
-    }
-    return bm.pixelRef()->refEncodedData();
-}
-
-/*
- *  This functions may give false negatives but no false positives.
- */
-static bool is_jfif_jpeg(SkData* data) {
-    if (!data || (data->size() < 11)) {
-        return false;
-    }
-    const uint8_t bytesZeroToThree[] = {0xFF, 0xD8, 0xFF, 0xE0};
-    const uint8_t bytesSixToTen[] = {'J', 'F', 'I', 'F', 0};
-    // 0   1   2   3   4   5   6   7   8   9   10
-    // FF  D8  FF  E0  ??  ??  'J' 'F' 'I' 'F' 00 ...
-    return ((0 == memcmp(data->bytes(), bytesZeroToThree,
-                         sizeof(bytesZeroToThree)))
-            && (0 == memcmp(data->bytes() + 6, bytesSixToTen,
-                            sizeof(bytesSixToTen))));
-}
-}  // namespace
-#endif
-
-SkPDFObject* SkPDFCreateImageObject(SkPDFCanon* canon,
-                                    const SkBitmap& bitmap,
-                                    const SkIRect& subset) {
-    if (SkPDFObject* pdfBitmap = SkPDFBitmap::Create(canon, bitmap, subset)) {
-        return pdfBitmap;
-    }
-#if 0  // reenable when we can figure out the JPEG colorspace
-    if (SkIRect::MakeWH(bitmap.width(), bitmap.height()) == subset) {
-        SkAutoTUnref<SkData> encodedData(ref_encoded_data(bitmap));
-        if (is_jfif_jpeg(encodedData)) {
-            return SkNEW_ARGS(PDFJPEGImage,
-                              (encodedData, bitmap.width(), bitmap.height()));
-        }
-    }
-#endif
-    return SkPDFImage::CreateImage(bitmap, subset);
-}
diff --git a/src/pdf/SkPDFImage.h b/src/pdf/SkPDFImage.h
deleted file mode 100644
index 64be971..0000000
--- a/src/pdf/SkPDFImage.h
+++ /dev/null
@@ -1,91 +0,0 @@
-
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkPDFImage_DEFINED
-#define SkPDFImage_DEFINED
-
-#include "SkPicture.h"
-#include "SkPDFDevice.h"
-#include "SkPDFStream.h"
-#include "SkPDFTypes.h"
-#include "SkRefCnt.h"
-
-class SkBitmap;
-class SkData;
-class SkPDFCatalog;
-struct SkIRect;
-
-/**
- *  Return the mose efficient availible encoding of the given bitmap.
- */
-SkPDFObject* SkPDFCreateImageObject(SkPDFCanon* canon,
-                                    const SkBitmap&,
-                                    const SkIRect& subset);
-
-/** \class SkPDFImage
-
-    An image XObject.
-*/
-
-// We could play the same trick here as is done in SkPDFGraphicState, storing
-// a copy of the Bitmap object (not the pixels), the pixel generation number,
-// and settings used from the paint to canonicalize image objects.
-class SkPDFImage : public SkPDFStream {
-public:
-    /** Create a new Image XObject to represent the passed bitmap.
-     *  @param bitmap   The image to encode.
-     *  @param srcRect  The rectangle to cut out of bitmap.
-     *  @param paint    Used to calculate alpha, masks, etc.
-     *  @return  The image XObject or NUll if there is nothing to draw for
-     *           the given parameters.
-     */
-    static SkPDFImage* CreateImage(const SkBitmap& bitmap,
-                                   const SkIRect& srcRect);
-
-    virtual ~SkPDFImage();
-
-    bool isEmpty() {
-        return fSrcRect.isEmpty();
-    }
-
-private:
-    SkBitmap fBitmap;
-    bool fIsAlpha;
-    SkIRect fSrcRect;
-    bool fStreamValid;
-
-    /** Create a PDF image XObject. Entries for the image properties are
-     *  automatically added to the stream dictionary.
-     *  @param stream     The image stream. May be NULL. Otherwise, this
-     *                    (instead of the input bitmap) will be used as the
-     *                    PDF's content stream, possibly with lossless encoding.
-     *                    Will be duplicated, and left in indeterminate state.
-     *  @param bitmap     The image. If a stream is not given, its color data
-     *                    will be used as the image. If a stream is given, this
-     *                    is used for configuration only.
-     *  @param isAlpha    Whether or not this is the alpha of an image.
-     *  @param srcRect    The clipping applied to bitmap before generating
-     *                    imageData.
-     */
-    SkPDFImage(SkStream* stream, const SkBitmap& bitmap, bool isAlpha,
-               const SkIRect& srcRect);
-
-    /** Copy constructor, used to generate substitutes.
-     *  @param image      The SkPDFImage to copy.
-     */
-    SkPDFImage(SkPDFImage& pdfImage);
-
-    // Populate the stream dictionary.  This method returns false if
-    // fSubstitute should be used.
-    virtual bool populate(SkPDFCatalog* catalog);
-
-    typedef SkPDFStream INHERITED;
-};
-
-#endif
diff --git a/src/pdf/SkPDFPage.cpp b/src/pdf/SkPDFPage.cpp
deleted file mode 100644
index da0179a..0000000
--- a/src/pdf/SkPDFPage.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include "SkData.h"
-#include "SkPDFCatalog.h"
-#include "SkPDFDevice.h"
-#include "SkPDFPage.h"
-#include "SkPDFResourceDict.h"
-
-SkPDFPage::SkPDFPage(SkPDFDevice* content)
-    : SkPDFDict("Page"),
-      fDevice(content) {
-  SkSafeRef(content);
-}
-
-SkPDFPage::~SkPDFPage() {}
-
-void SkPDFPage::finalizePage(SkPDFCatalog* catalog, bool firstPage,
-                             const SkTSet<SkPDFObject*>& knownResourceObjects,
-                             SkTSet<SkPDFObject*>* newResourceObjects) {
-    if (fContentStream.get() == NULL) {
-        this->insert("Resources", fDevice->getResourceDict());
-        SkSafeUnref(this->insert("MediaBox", fDevice->copyMediaBox()));
-        SkPDFArray* annots = fDevice->getAnnotations();
-        if (annots && annots->size() > 0) {
-            insert("Annots", annots);
-        }
-
-        SkAutoTDelete<SkStreamAsset> content(fDevice->content());
-        fContentStream.reset(new SkPDFStream(content.get()));
-        insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref();
-    }
-    catalog->addObject(fContentStream.get(), firstPage);
-}
-
-// static
-void SkPDFPage::GeneratePageTree(const SkTDArray<SkPDFPage*>& pages,
-                                 SkPDFCatalog* catalog,
-                                 SkTDArray<SkPDFDict*>* pageTree,
-                                 SkPDFDict** rootNode) {
-    // PDF wants a tree describing all the pages in the document.  We arbitrary
-    // choose 8 (kNodeSize) as the number of allowed children.  The internal
-    // nodes have type "Pages" with an array of children, a parent pointer, and
-    // the number of leaves below the node as "Count."  The leaves are passed
-    // into the method, have type "Page" and need a parent pointer. This method
-    // builds the tree bottom up, skipping internal nodes that would have only
-    // one child.
-    static const int kNodeSize = 8;
-
-    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;
-    curNodes.setReserve(pages.count());
-    for (int i = 0; i < pages.count(); i++) {
-        SkSafeRef(pages[i]);
-        curNodes.push(pages[i]);
-    }
-
-    // nextRoundNodes passes its references to nodes on to curNodes.
-    SkTDArray<SkPDFDict*> nextRoundNodes;
-    nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
-
-    int treeCapacity = kNodeSize;
-    do {
-        for (int i = 0; i < curNodes.count(); ) {
-            if (i > 0 && i + 1 == curNodes.count()) {
-                nextRoundNodes.push(curNodes[i]);
-                break;
-            }
-
-            SkPDFDict* newNode = new SkPDFDict("Pages");
-            SkAutoTUnref<SkPDFObjRef> newNodeRef(new SkPDFObjRef(newNode));
-
-            SkAutoTUnref<SkPDFArray> kids(new SkPDFArray);
-            kids->reserve(kNodeSize);
-
-            int count = 0;
-            for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
-                curNodes[i]->insert(parentName.get(), newNodeRef.get());
-                kids->append(new SkPDFObjRef(curNodes[i]))->unref();
-
-                // TODO(vandebo): put the objects in strict access order.
-                // Probably doesn't matter because they are so small.
-                if (curNodes[i] != pages[0]) {
-                    pageTree->push(curNodes[i]);  // Transfer reference.
-                    catalog->addObject(curNodes[i], false);
-                } else {
-                    SkSafeUnref(curNodes[i]);
-                    catalog->addObject(curNodes[i], true);
-                }
-            }
-
-            // 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 (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.
-        }
-
-        curNodes = nextRoundNodes;
-        nextRoundNodes.rewind();
-        treeCapacity *= kNodeSize;
-    } while (curNodes.count() > 1);
-
-    pageTree->push(curNodes[0]);  // Transfer reference.
-    catalog->addObject(curNodes[0], false);
-    if (rootNode) {
-        *rootNode = curNodes[0];
-    }
-}
-
-const SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const {
-    return fDevice->getFontResources();
-}
-
-const SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const {
-    return fDevice->getFontGlyphUsage();
-}
-
-void SkPDFPage::appendDestinations(SkPDFDict* dict) {
-    fDevice->appendDestinations(dict, this);
-}
-
-SkPDFObject* SkPDFPage::getContentStream() const {
-    return fContentStream.get();
-}
diff --git a/src/pdf/SkPDFPage.h b/src/pdf/SkPDFPage.h
deleted file mode 100644
index 38cc5c4..0000000
--- a/src/pdf/SkPDFPage.h
+++ /dev/null
@@ -1,96 +0,0 @@
-
-/*
- * Copyright 2010 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkPDFPage_DEFINED
-#define SkPDFPage_DEFINED
-
-#include "SkPDFTypes.h"
-#include "SkPDFStream.h"
-#include "SkRefCnt.h"
-#include "SkTDArray.h"
-
-class SkPDFCatalog;
-class SkPDFDevice;
-class SkWStream;
-
-/** \class SkPDFPage
-
-    A SkPDFPage contains meta information about a page, is used in the page
-    tree and points to the content of the page.
-*/
-class SkPDFPage : public SkPDFDict {
-    SK_DECLARE_INST_COUNT(SkPDFPage)
-public:
-    /** Create a PDF page with the passed PDF device.  The device need not
-     *  have content on it yet.
-     *  @param content    The page content.
-     */
-    explicit SkPDFPage(SkPDFDevice* content);
-    ~SkPDFPage();
-
-    /** Before a page and its contents can be sized and emitted, it must
-     *  be finalized.  No changes to the PDFDevice will be honored after
-     *  finalizePage has been called.  This function adds the page content
-     *  to the passed catalog, so it must be called for each document
-     *  that the page is part of.
-     *  @param catalog         The catalog to add page content objects to.
-     *  @param firstPage       Indicate if this is the first page of a document.
-     *  @param newResourceObjects All the resource objects (recursively) used on
-     *                         the page are added to this array.  This gives
-     *                         the caller a chance to deduplicate resources
-     *                         across pages.
-     *  @param knownResourceObjects  The set of resources to be ignored.
-     */
-    void finalizePage(SkPDFCatalog* catalog, bool firstPage,
-                      const SkTSet<SkPDFObject*>& knownResourceObjects,
-                      SkTSet<SkPDFObject*>* newResourceObjects);
-
-    /** Add destinations for this page to the supplied dictionary.
-     *  @param dict       Dictionary to add destinations to.
-     */
-    void appendDestinations(SkPDFDict* dict);
-
-    /** Generate a page tree for the passed vector of pages.  New objects are
-     *  added to the catalog.  The pageTree vector is populated with all of
-     *  the 'Pages' dictionaries as well as the 'Page' objects.  Page trees
-     *  have both parent and children links, creating reference cycles, so
-     *  it must be torn down explicitly.  The first page is not added to
-     *  the pageTree dictionary array so the caller can handle it specially.
-     *  @param pages      The ordered vector of page objects.
-     *  @param catalog    The catalog to add new objects into.
-     *  @param pageTree   An output vector with all of the internal and leaf
-     *                    nodes of the pageTree.
-     *  @param rootNode   An output parameter set to the root node.
-     */
-    static void GeneratePageTree(const SkTDArray<SkPDFPage*>& pages,
-                                 SkPDFCatalog* catalog,
-                                 SkTDArray<SkPDFDict*>* pageTree,
-                                 SkPDFDict** rootNode);
-
-    /** Get the fonts used on this page.
-     */
-    const SkTDArray<SkPDFFont*>& getFontResources() const;
-
-    /** Returns a SkPDFGlyphSetMap which represents glyph usage of every font
-     *  that shows on this page.
-     */
-    const SkPDFGlyphSetMap& getFontGlyphUsage() const;
-
-    SkPDFObject* getContentStream() const;
-
-private:
-    // Multiple pages may reference the content.
-    SkAutoTUnref<SkPDFDevice> fDevice;
-
-    // Once the content is finalized, put it into a stream for output.
-    SkAutoTUnref<SkPDFStream> fContentStream;
-    typedef SkPDFDict INHERITED;
-};
-
-#endif
diff --git a/src/pdf/SkPDFResourceDict.cpp b/src/pdf/SkPDFResourceDict.cpp
index 40fc1d8..1f5bcea 100644
--- a/src/pdf/SkPDFResourceDict.cpp
+++ b/src/pdf/SkPDFResourceDict.cpp
@@ -46,58 +46,60 @@
 static const char* get_resource_type_name(
         SkPDFResourceDict::SkPDFResourceType type) {
     SkASSERT(type >= 0);
-    SkASSERT(type < SkPDFResourceDict::kResourceTypeCount);
+    SkASSERT(type < SK_ARRAY_COUNT(resource_type_names));
 
     return resource_type_names[type];
 }
 
-SkPDFResourceDict::SkPDFResourceDict() : SkPDFDict() {
-    const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
-    SkPDFArray* procSets = SkNEW(SkPDFArray());
-
-    procSets->reserve(SK_ARRAY_COUNT(procs));
-    for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++) {
-        procSets->appendName(procs[i]);
-    }
-    insert("ProcSets", procSets)->unref();
-
-    // Actual sub-dicts will be lazily added later
-    fTypes.setCount(kResourceTypeCount);
-    for (int i=0; i < kResourceTypeCount; i++) {
-        fTypes[i] = NULL;
-    }
-}
-
-SkPDFObject* SkPDFResourceDict::insertResourceAsReference(
-        SkPDFResourceType type, int key, SkPDFObject* value) {
-    SkAutoTUnref<SkPDFObjRef> ref(SkNEW_ARGS(SkPDFObjRef, (value)));
-    insertResource(type, key, ref);
-    fResources.add(value);
-
-    return value;
-}
-
 SkString SkPDFResourceDict::getResourceName(
-        SkPDFResourceType type, int key) {
+        SkPDFResourceDict::SkPDFResourceType type, int key) {
     SkString keyString;
     keyString.printf("%c%d", get_resource_type_prefix(type), key);
     return keyString;
 }
 
-SkPDFObject* SkPDFResourceDict::insertResource(
-        SkPDFResourceType type, int key, SkPDFObject* value) {
-    SkPDFDict* typeDict = fTypes[type];
-    if (NULL == typeDict) {
-        SkAutoTUnref<SkPDFDict> newDict(SkNEW(SkPDFDict()));
-        SkAutoTUnref<SkPDFName> typeName(
-                SkNEW_ARGS(SkPDFName, (get_resource_type_name(type))));
-        insert(typeName, newDict);  // ref counting handled here
-        fTypes[type] = newDict;
-        typeDict = newDict.get();
+static void add_subdict(
+        const SkTDArray<SkPDFObject*>& resourceList,
+        SkPDFResourceDict::SkPDFResourceType type,
+        SkPDFDict* dst) {
+    if (0 == resourceList.count()) {
+        return;
     }
+    SkAutoTUnref<SkPDFDict> resources(SkNEW(SkPDFDict));
+    for (int i = 0; i < resourceList.count(); i++) {
+        resources->insertObjRef(SkPDFResourceDict::getResourceName(type, i),
+                                SkRef(resourceList[i]));
+    }
+    dst->insertObject(get_resource_type_name(type), resources.detach());
+}
 
-    SkAutoTUnref<SkPDFName> keyName(
-            SkNEW_ARGS(SkPDFName, (getResourceName(type, key))));
-    typeDict->insert(keyName, value);
-    return value;
+SkPDFDict* SkPDFResourceDict::Create(
+        const SkTDArray<SkPDFObject*>* gStateResources,
+        const SkTDArray<SkPDFObject*>* patternResources,
+        const SkTDArray<SkPDFObject*>* xObjectResources,
+        const SkTDArray<SkPDFObject*>* fontResources) {
+    SkAutoTUnref<SkPDFDict> dict(SkNEW(SkPDFDict));
+    static const char kProcs[][7] = {
+        "PDF", "Text", "ImageB", "ImageC", "ImageI"};
+    SkAutoTUnref<SkPDFArray> procSets(SkNEW(SkPDFArray));
+
+    procSets->reserve(SK_ARRAY_COUNT(kProcs));
+    for (size_t i = 0; i < SK_ARRAY_COUNT(kProcs); i++) {
+        procSets->appendName(kProcs[i]);
+    }
+    dict->insertObject("ProcSets", procSets.detach());
+
+    if (gStateResources) {
+        add_subdict(*gStateResources, kExtGState_ResourceType, dict);
+    }
+    if (patternResources) {
+        add_subdict(*patternResources, kPattern_ResourceType, dict);
+    }
+    if (xObjectResources) {
+        add_subdict(*xObjectResources, kXObject_ResourceType, dict);
+    }
+    if (fontResources) {
+        add_subdict(*fontResources, kFont_ResourceType, dict);
+    }
+    return dict.detach();
 }
diff --git a/src/pdf/SkPDFResourceDict.h b/src/pdf/SkPDFResourceDict.h
index fa9bb44..2913779 100644
--- a/src/pdf/SkPDFResourceDict.h
+++ b/src/pdf/SkPDFResourceDict.h
@@ -10,7 +10,6 @@
 
 #include "SkPDFTypes.h"
 #include "SkTDArray.h"
-#include "SkTSet.h"
 #include "SkTypes.h"
 
 /** \class SkPDFResourceDict
@@ -19,11 +18,9 @@
     allows generation of a list of referenced SkPDFObjects inserted with
     insertResourceAsRef.
 */
-class SkPDFResourceDict : public SkPDFDict {
+class SkPDFResourceDict {
 public:
-    SK_DECLARE_INST_COUNT(SkPDFResourceDict)
-
-     enum SkPDFResourceType{
+    enum SkPDFResourceType {
         kExtGState_ResourceType,
         kPattern_ResourceType,
         kXObject_ResourceType,
@@ -31,28 +28,19 @@
         // These additional types are defined by the spec, but not
         // currently used by Skia: ColorSpace, Shading, Properties
         kResourceTypeCount
-     };
+    };
 
     /** Create a PDF resource dictionary.
      *  The full set of ProcSet entries is automatically created for backwards
      *  compatibility, as recommended by the PDF spec.
+     *
+     *  Any arguments can be NULL.
      */
-    SkPDFResourceDict();
-
-    /** Add the value SkPDFObject as a reference to the resource dictionary
-     *  with the give type and key.
-     *  The relevant sub-dicts will be automatically generated, and the
-     *  resource will be named by concatenating a type-specific prefix and
-     *  the input key.
-     *  This object will be part of the resource list when requested later.
-     *  @param type  The type of resource being entered, like
-     *    kPattern_ResourceType or kExtGState_ResourceType.
-     *  @param key   The resource key, should be unique within its type.
-     *  @param value The resource itself.
-     *  @return The value argument is returned.
-     */
-    SkPDFObject* insertResourceAsReference(SkPDFResourceType type, int key,
-                                           SkPDFObject* value);
+    static SkPDFDict* Create(
+        const SkTDArray<SkPDFObject*>* gStateResources,
+        const SkTDArray<SkPDFObject*>* patternResources,
+        const SkTDArray<SkPDFObject*>* xObjectResources,
+        const SkTDArray<SkPDFObject*>* fontResources);
 
     /**
      * Returns the name for the resource that will be generated by the resource
@@ -63,25 +51,6 @@
      *  @param key   The resource key, should be unique within its type.
      */
     static SkString getResourceName(SkPDFResourceType type, int key);
-
-private:
-    /** Add the value to the dictionary with the given key.  Refs value.
-     *  The relevant sub-dicts will be automatically generated, and the
-     *  resource will be named by concatenating a type-specific prefix and
-     *  the input key.
-     *  The object will NOT be part of the resource list when requested later.
-     *  @param type  The type of resource being entered.
-     *  @param key   The resource key, should be unique within its type.
-     *  @param value The resource itself.
-     *  @return The value argument is returned.
-     */
-    SkPDFObject* insertResource(SkPDFResourceType type, int key,
-                                SkPDFObject* value);
-
-    SkTSet<SkPDFObject*> fResources;
-
-    SkTDArray<SkPDFDict*> fTypes;
-    typedef SkPDFDict INHERITED;
 };
 
 #endif
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
index 0b66191..c627c14 100644
--- a/src/pdf/SkPDFShader.cpp
+++ b/src/pdf/SkPDFShader.cpp
@@ -11,7 +11,6 @@
 
 #include "SkData.h"
 #include "SkPDFCanon.h"
-#include "SkPDFCatalog.h"
 #include "SkPDFDevice.h"
 #include "SkPDFFormXObject.h"
 #include "SkPDFGraphicState.h"
@@ -20,7 +19,6 @@
 #include "SkScalar.h"
 #include "SkStream.h"
 #include "SkTemplates.h"
-#include "SkTSet.h"
 #include "SkTypes.h"
 
 static bool inverse_transform_bbox(const SkMatrix& matrix, SkRect* bbox) {
@@ -60,7 +58,7 @@
     // Figure out how to scale each color component.
     SkScalar multiplier[kColorComponents];
     for (int i = 0; i < kColorComponents; i++) {
-        multiplier[i] = SkScalarDiv(curColor[i] - prevColor[i], range);
+        multiplier[i] = (curColor[i] - prevColor[i]) / range;
     }
 
     // Calculate when we no longer need to keep a copy of the input parameter t.
@@ -292,58 +290,6 @@
     return function;
 }
 
-/* The math here is all based on the description in Two_Point_Radial_Gradient,
-   with one simplification, the coordinate space has been scaled so that
-   Dr = 1.  This means we don't need to scale the entire equation by 1/Dr^2.
- */
-static SkString twoPointRadialCode(const SkShader::GradientInfo& info,
-                                   const SkMatrix& perspectiveRemover) {
-    SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX;
-    SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY;
-    SkScalar sr = info.fRadius[0];
-    SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1;
-    bool posRoot = info.fRadius[1] > info.fRadius[0];
-
-    // We start with a stack of (x y), copy it and then consume one copy in
-    // order to calculate b and the other to calculate c.
-    SkString function("{");
-
-    function.append(apply_perspective_to_coordinates(perspectiveRemover));
-
-    function.append("2 copy ");
-
-    // Calculate -b and b^2.
-    function.appendScalar(dy);
-    function.append(" mul exch ");
-    function.appendScalar(dx);
-    function.append(" mul add ");
-    function.appendScalar(sr);
-    function.append(" sub 2 mul neg dup dup mul\n");
-
-    // Calculate c
-    function.append("4 2 roll dup mul exch dup mul add ");
-    function.appendScalar(SkScalarMul(sr, sr));
-    function.append(" sub\n");
-
-    // Calculate the determinate
-    function.appendScalar(SkScalarMul(SkIntToScalar(4), a));
-    function.append(" mul sub abs sqrt\n");
-
-    // And then the final value of t.
-    if (posRoot) {
-        function.append("sub ");
-    } else {
-        function.append("add ");
-    }
-    function.appendScalar(SkScalarMul(SkIntToScalar(2), a));
-    function.append(" div\n");
-
-    tileModeCode(info.fTileMode, &function);
-    gradientFunctionCode(info, &function);
-    function.append("}");
-    return function;
-}
-
 /* Conical gradient shader, based on the Canvas spec for radial gradients
    See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
  */
@@ -509,9 +455,7 @@
 SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
     : SkPDFDict("Pattern"), fShaderState(state) {}
 
-SkPDFFunctionShader::~SkPDFFunctionShader() {
-    fResources.unrefAll();
-}
+SkPDFFunctionShader::~SkPDFFunctionShader() {}
 
 bool SkPDFFunctionShader::equals(const SkPDFShader::State& state) const {
     return state == *fShaderState;
@@ -537,9 +481,7 @@
     return state == *fShaderState;
 }
 
-SkPDFImageShader::~SkPDFImageShader() {
-    fResources.unrefAll();
-}
+SkPDFImageShader::~SkPDFImageShader() {}
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -581,26 +523,24 @@
     return get_pdf_shader_by_state(canon, dpi, &state);
 }
 
-static SkPDFResourceDict* get_gradient_resource_dict(
+static SkPDFDict* get_gradient_resource_dict(
         SkPDFObject* functionShader,
         SkPDFObject* gState) {
-    SkPDFResourceDict* dict = new SkPDFResourceDict();
-
-    if (functionShader != NULL) {
-        dict->insertResourceAsReference(
-                SkPDFResourceDict::kPattern_ResourceType, 0, functionShader);
+    SkTDArray<SkPDFObject*> patterns;
+    if (functionShader) {
+        patterns.push(functionShader);
     }
-    if (gState != NULL) {
-        dict->insertResourceAsReference(
-                SkPDFResourceDict::kExtGState_ResourceType, 0, gState);
+    SkTDArray<SkPDFObject*> graphicStates;
+    if (gState) {
+        graphicStates.push(gState);
     }
-
-    return dict;
+    return SkPDFResourceDict::Create(&graphicStates, &patterns, NULL, NULL);
 }
 
 static void populate_tiling_pattern_dict(SkPDFDict* pattern,
-                                      SkRect& bbox, SkPDFDict* resources,
-                                      const SkMatrix& matrix) {
+                                         SkRect& bbox,
+                                         SkPDFDict* resources,
+                                         const SkMatrix& matrix) {
     const int kTiling_PatternType = 1;
     const int kColoredTilingPattern_PaintType = 1;
     const int kConstantSpacing_TilingType = 1;
@@ -609,12 +549,12 @@
     pattern->insertInt("PatternType", kTiling_PatternType);
     pattern->insertInt("PaintType", kColoredTilingPattern_PaintType);
     pattern->insertInt("TilingType", kConstantSpacing_TilingType);
-    pattern->insert("BBox", SkPDFUtils::RectToArray(bbox))->unref();
+    pattern->insertObject("BBox", SkPDFUtils::RectToArray(bbox));
     pattern->insertScalar("XStep", bbox.width());
     pattern->insertScalar("YStep", bbox.height());
-    pattern->insert("Resources", resources);
+    pattern->insertObject("Resources", SkRef(resources));
     if (!matrix.isIdentity()) {
-        pattern->insert("Matrix", SkPDFUtils::MatrixToArray(matrix))->unref();
+        pattern->insertObject("Matrix", SkPDFUtils::MatrixToArray(matrix));
     }
 }
 
@@ -640,7 +580,7 @@
  * Creates a ExtGState with the SMask set to the luminosityShader in
  * luminosity mode. The shader pattern extends to the bbox.
  */
-static SkPDFGraphicState* create_smask_graphic_state(
+static SkPDFObject* create_smask_graphic_state(
         SkPDFCanon* canon, SkScalar dpi, const SkPDFShader::State& state) {
     SkRect bbox;
     bbox.set(state.fBBox);
@@ -652,7 +592,7 @@
 
     SkAutoTDelete<SkStream> alphaStream(create_pattern_fill_content(-1, bbox));
 
-    SkAutoTUnref<SkPDFResourceDict>
+    SkAutoTUnref<SkPDFDict>
         resources(get_gradient_resource_dict(luminosityShader, NULL));
 
     SkAutoTUnref<SkPDFFormXObject> alphaMask(
@@ -673,31 +613,28 @@
 
     SkAutoTDelete<SkPDFShader::State> opaqueState(state.CreateOpaqueState());
 
-    SkPDFObject* colorShader =
-            get_pdf_shader_by_state(canon, dpi, &opaqueState);
+    SkAutoTUnref<SkPDFObject> colorShader(
+            get_pdf_shader_by_state(canon, dpi, &opaqueState));
     if (!colorShader) {
         return NULL;
     }
 
     // Create resource dict with alpha graphics state as G0 and
     // pattern shader as P0, then write content stream.
-    SkAutoTUnref<SkPDFGraphicState> alphaGs(
+    SkAutoTUnref<SkPDFObject> alphaGs(
             create_smask_graphic_state(canon, dpi, state));
 
     SkPDFAlphaFunctionShader* alphaFunctionShader =
             SkNEW_ARGS(SkPDFAlphaFunctionShader, (autoState->detach()));
 
-    alphaFunctionShader->fColorShader.reset(colorShader);
-
-    alphaFunctionShader->fResourceDict.reset(get_gradient_resource_dict(
-            alphaFunctionShader->fColorShader.get(), alphaGs.get()));
+    SkAutoTUnref<SkPDFDict> resourceDict(
+            get_gradient_resource_dict(colorShader.get(), alphaGs.get()));
 
     SkAutoTDelete<SkStream> colorStream(
             create_pattern_fill_content(0, bbox));
     alphaFunctionShader->setData(colorStream.get());
 
-    populate_tiling_pattern_dict(alphaFunctionShader, bbox,
-                                 alphaFunctionShader->fResourceDict.get(),
+    populate_tiling_pattern_dict(alphaFunctionShader, bbox, resourceDict.get(),
                                  SkMatrix::I());
     canon->addAlphaShader(alphaFunctionShader);
     return alphaFunctionShader;
@@ -766,8 +703,8 @@
             SkData::NewWithCopy(psCode.c_str(), psCode.size()));
     SkPDFStream* result = SkNEW_ARGS(SkPDFStream, (funcData.get()));
     result->insertInt("FunctionType", 4);
-    result->insert("Domain", domain);
-    result->insert("Range", rangeObject.get());
+    result->insertObject("Domain", SkRef(domain));
+    result->insertObject("Range", SkRef(rangeObject.get()));
     return result;
 }
 
@@ -793,17 +730,6 @@
             transformPoints[1].fX += info->fRadius[0];
             codeFunction = &radialCode;
             break;
-        case SkShader::kRadial2_GradientType: {
-            // Bail out if the radii are the same.
-            if (info->fRadius[0] == info->fRadius[1]) {
-                return NULL;
-            }
-            transformPoints[1] = transformPoints[0];
-            SkScalar dr = info->fRadius[1] - info->fRadius[0];
-            transformPoints[1].fX += dr;
-            codeFunction = &twoPointRadialCode;
-            break;
-        }
         case SkShader::kConical_GradientType: {
             transformPoints[1] = transformPoints[0];
             transformPoints[1].fX += SK_Scalar1;
@@ -863,7 +789,7 @@
     // state.fInfo
     // in translating from x, y coordinates to the t parameter. So, we have
     // to transform the points and radii according to the calculated matrix.
-    if (state.fType == SkShader::kRadial2_GradientType) {
+    if (state.fType == SkShader::kConical_GradientType) {
         SkShader::GradientInfo twoPointRadialInfo = *info;
         SkMatrix inverseMapperMatrix;
         if (!mapperMatrix.invert(&inverseMapperMatrix)) {
@@ -882,23 +808,19 @@
     SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict);
     pdfShader->insertInt("ShadingType", 1);
     pdfShader->insertName("ColorSpace", "DeviceRGB");
-    pdfShader->insert("Domain", domain.get());
+    pdfShader->insertObject("Domain", SkRef(domain.get()));
 
-    SkPDFStream* function = make_ps_function(functionCode, domain.get());
-    pdfShader->insert("Function", new SkPDFObjRef(function))->unref();
-
-    SkAutoTUnref<SkPDFArray> matrixArray(
-            SkPDFUtils::MatrixToArray(finalMatrix));
+    SkAutoTUnref<SkPDFStream> function(
+            make_ps_function(functionCode, domain.get()));
+    pdfShader->insertObjRef("Function", function.detach());
 
     SkPDFFunctionShader* pdfFunctionShader =
             SkNEW_ARGS(SkPDFFunctionShader, (autoState->detach()));
 
-    pdfFunctionShader->fResources.push(function);
-    // Pass ownership to resource list.
-
     pdfFunctionShader->insertInt("PatternType", 2);
-    pdfFunctionShader->insert("Matrix", matrixArray.get());
-    pdfFunctionShader->insert("Shading", pdfShader.get());
+    pdfFunctionShader->insertObject("Matrix",
+                                    SkPDFUtils::MatrixToArray(finalMatrix));
+    pdfFunctionShader->insertObject("Shading", pdfShader.detach());
 
     canon->addFunctionShader(pdfFunctionShader);
     return pdfFunctionShader;
@@ -1110,8 +1032,10 @@
             SkNEW_ARGS(SkPDFImageShader, (autoState->detach()));
     imageShader->setData(content.get());
 
+    SkAutoTUnref<SkPDFDict> resourceDict(
+            patternDevice->createResourceDict());
     populate_tiling_pattern_dict(imageShader, patternBBox,
-                                 patternDevice->getResourceDict(), finalMatrix);
+                                 resourceDict.get(), finalMatrix);
 
     imageShader->fShaderState->fImage.unlockPixels();
 
@@ -1156,7 +1080,6 @@
                     return false;
                 }
                 break;
-            case SkShader::kRadial2_GradientType:
             case SkShader::kConical_GradientType:
                 if (fInfo.fPoint[1] != b.fInfo.fPoint[1] ||
                         fInfo.fRadius[0] != b.fInfo.fRadius[0] ||
@@ -1209,7 +1132,7 @@
             static const SkScalar kMaxBitmapArea = 1024 * 1024;
             SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height();
             if (bitmapArea > kMaxBitmapArea) {
-                rasterScale *= SkScalarSqrt(SkScalarDiv(kMaxBitmapArea, bitmapArea));
+                rasterScale *= SkScalarSqrt(kMaxBitmapArea / bitmapArea);
             }
 
             SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()),
diff --git a/src/pdf/SkPDFShader.h b/src/pdf/SkPDFShader.h
index 4fdbc44..8ebe8a2 100644
--- a/src/pdf/SkPDFShader.h
+++ b/src/pdf/SkPDFShader.h
@@ -10,7 +10,6 @@
 #ifndef SkPDFShader_DEFINED
 #define SkPDFShader_DEFINED
 
-#include "SkPDFResourceDict.h"
 #include "SkPDFStream.h"
 #include "SkPDFTypes.h"
 
@@ -62,7 +61,6 @@
 
 private:
     SkAutoTDelete<const SkPDFShader::State> fShaderState;
-    SkTDArray<SkPDFObject*> fResources;
     SkPDFFunctionShader(SkPDFShader::State*);
     typedef SkPDFDict INHERITED;
 };
@@ -82,9 +80,8 @@
 
 private:
     SkAutoTDelete<const SkPDFShader::State> fShaderState;
-    SkAutoTUnref<SkPDFObject> fColorShader;
-    SkAutoTUnref<SkPDFResourceDict> fResourceDict;
     SkPDFAlphaFunctionShader(SkPDFShader::State*);
+    typedef SkPDFStream INHERITED;
 };
 
 class SkPDFImageShader : public SkPDFStream {
@@ -97,8 +94,8 @@
 
 private:
     SkAutoTDelete<const SkPDFShader::State> fShaderState;
-    SkTSet<SkPDFObject*> fResources;
     SkPDFImageShader(SkPDFShader::State*);
+    typedef SkPDFStream INHERITED;
 };
 
 #endif
diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp
index ae3fe4a..f77c85f 100644
--- a/src/pdf/SkPDFStream.cpp
+++ b/src/pdf/SkPDFStream.cpp
@@ -9,7 +9,6 @@
 
 #include "SkData.h"
 #include "SkFlate.h"
-#include "SkPDFCatalog.h"
 #include "SkPDFStream.h"
 #include "SkStream.h"
 #include "SkStreamPriv.h"
@@ -22,30 +21,28 @@
     this->setData(data);
 }
 
-SkPDFStream::SkPDFStream(const SkPDFStream& pdfStream)
-        : SkPDFDict(),
-          fState(kUnused_State) {
-    this->setData(pdfStream.fDataStream.get());
-    bool removeLength = true;
-    // Don't uncompress an already compressed stream, but we could.
-    if (pdfStream.fState == kCompressed_State) {
-        fState = kCompressed_State;
-        removeLength = false;
-    }
-    this->mergeFrom(pdfStream);
-    if (removeLength) {
-        this->remove("Length");
-    }
-}
-
 SkPDFStream::~SkPDFStream() {}
 
-void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    if (!this->populate(catalog)) {
-        return fSubstitute->emitObject(stream, catalog);
-    }
+void SkPDFStream::emitObject(SkWStream* stream,
+                             const SkPDFObjNumMap& objNumMap,
+                             const SkPDFSubstituteMap& substitutes) {
+    if (fState == kUnused_State) {
+        fState = kNoCompression_State;
+        SkDynamicMemoryWStream compressedData;
 
-    this->INHERITED::emitObject(stream, catalog);
+        SkAssertResult(
+                SkFlate::Deflate(fDataStream.get(), &compressedData));
+        SkAssertResult(fDataStream->rewind());
+        if (compressedData.getOffset() < this->dataSize()) {
+            SkAutoTDelete<SkStream> compressed(
+                    compressedData.detachAsStream());
+            this->setData(compressed.get());
+            this->insertName("Filter", "FlateDecode");
+        }
+        fState = kCompressed_State;
+        this->insertInt("Length", this->dataSize());
+    }
+    this->INHERITED::emitObject(stream, objNumMap, substitutes);
     stream->writeText(" stream\n");
     stream->writeStream(fDataStream.get(), fDataStream->getLength());
     SkAssertResult(fDataStream->rewind());
@@ -60,55 +57,14 @@
 }
 
 void SkPDFStream::setData(SkStream* stream) {
+    SkASSERT(stream);
     // Code assumes that the stream starts at the beginning and is rewindable.
-    if (stream) {
-        // SkStreamRewindableFromSkStream will try stream->duplicate().
-        fDataStream.reset(SkStreamRewindableFromSkStream(stream));
-        SkASSERT(fDataStream.get());
-    } else {
-        // Use an empty memory stream.
-        fDataStream.reset(SkNEW(SkMemoryStream));
-    }
+    // SkStreamRewindableFromSkStream will try stream->duplicate().
+    fDataStream.reset(SkStreamRewindableFromSkStream(stream));
+    SkASSERT(fDataStream.get());
 }
 
 size_t SkPDFStream::dataSize() const {
     SkASSERT(fDataStream->hasLength());
     return fDataStream->getLength();
 }
-
-bool SkPDFStream::populate(SkPDFCatalog* catalog) {
-#ifdef SK_NO_FLATE
-    if (fState == kUnused_State) {
-        fState = kNoCompression_State;
-        insertInt("Length", this->dataSize());
-    }
-    return true;
-
-#else  // !SK_NO_FLATE
-
-    if (fState == kUnused_State) {
-        fState = kNoCompression_State;
-        SkDynamicMemoryWStream compressedData;
-
-        SkAssertResult(
-                SkFlate::Deflate(fDataStream.get(), &compressedData));
-        SkAssertResult(fDataStream->rewind());
-        if (compressedData.getOffset() < this->dataSize()) {
-            SkAutoTDelete<SkStream> compressed(
-                    compressedData.detachAsStream());
-            this->setData(compressed.get());
-            insertName("Filter", "FlateDecode");
-        }
-        fState = kCompressed_State;
-        insertInt("Length", this->dataSize());
-    }
-    else if (fState == kNoCompression_State) {
-        if (!fSubstitute.get()) {
-            fSubstitute.reset(new SkPDFStream(*this));
-            catalog->setSubstitute(this, fSubstitute.get());
-        }
-        return false;
-    }
-    return true;
-#endif  // SK_NO_FLATE
-}
diff --git a/src/pdf/SkPDFStream.h b/src/pdf/SkPDFStream.h
index 90c6bca..e3a4ebf 100644
--- a/src/pdf/SkPDFStream.h
+++ b/src/pdf/SkPDFStream.h
@@ -15,7 +15,7 @@
 #include "SkStream.h"
 #include "SkTemplates.h"
 
-class SkPDFCatalog;
+class SkPDFObjNumMap;
 
 /** \class SkPDFStream
 
@@ -40,7 +40,9 @@
     virtual ~SkPDFStream();
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
+    void emitObject(SkWStream* stream,
+                    const SkPDFObjNumMap& objNumMap,
+                    const SkPDFSubstituteMap& substitutes) override;
 
 protected:
     enum State {
@@ -50,28 +52,11 @@
         kCompressed_State,     //!< The stream's already been compressed.
     };
 
-    /** Create a PDF stream with the same content and dictionary entries
-     *  as the passed one.
-     */
-    explicit SkPDFStream(const SkPDFStream& pdfStream);
-
     /* Create a PDF stream with no data.  The setData method must be called to
      * set the data.
      */
     SkPDFStream();
 
-    // Populate the stream dictionary.  This method returns false if
-    // fSubstitute should be used.
-    virtual bool populate(SkPDFCatalog* catalog);
-
-    void setSubstitute(SkPDFStream* stream) {
-        fSubstitute.reset(stream);
-    }
-
-    SkPDFStream* getSubstitute() const {
-        return fSubstitute.get();
-    }
-
     void setData(SkData* data);
     void setData(SkStream* stream);
 
@@ -81,16 +66,11 @@
         fState = state;
     }
 
-    State getState() const {
-        return fState;
-    }
-
 private:
     // Indicates what form (or if) the stream has been requested.
     State fState;
 
     SkAutoTDelete<SkStreamRewindable> fDataStream;
-    SkAutoTUnref<SkPDFStream> fSubstitute;
 
     typedef SkPDFDict INHERITED;
 };
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index 43cd684..2b2921d 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -6,9 +6,8 @@
  * found in the LICENSE file.
  */
 
-
-#include "SkPDFCatalog.h"
 #include "SkPDFTypes.h"
+#include "SkPDFUtils.h"
 #include "SkStream.h"
 
 #ifdef SK_BUILD_FOR_WIN
@@ -19,382 +18,472 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {
-    SkSafeRef(obj);
+SkString* pun(char* x) { return reinterpret_cast<SkString*>(x); }
+const SkString* pun(const char* x) {
+    return reinterpret_cast<const SkString*>(x);
 }
 
-SkPDFObjRef::~SkPDFObjRef() {}
+SkPDFUnion::SkPDFUnion(Type t) : fType(t) {}
 
-void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    stream->writeDecAsText(catalog->getObjectNumber(fObj.get()));
-    stream->writeText(" 0 R");  // Generation number is always 0.
-}
-
-void SkPDFObjRef::addResources(SkTSet<SkPDFObject*>* resourceSet,
-                               SkPDFCatalog* catalog) const {
-    SkPDFObject* obj = catalog->getSubstituteObject(fObj);
-    SkASSERT(obj);
-    if (resourceSet->add(obj)) {
-        obj->addResources(resourceSet, catalog);
+SkPDFUnion::~SkPDFUnion() {
+    switch (fType) {
+        case Type::kNameSkS:
+        case Type::kStringSkS:
+            pun(fSkString)->~SkString();
+            return;
+        case Type::kObjRef:
+        case Type::kObject:
+            SkSafeUnref(fObject);
+            return;
+        default:
+            return;
     }
 }
 
-////////////////////////////////////////////////////////////////////////////////
-
-SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {}
-SkPDFInt::~SkPDFInt() {}
-
-void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    stream->writeDecAsText(fValue);
+SkPDFUnion& SkPDFUnion::operator=(SkPDFUnion&& other) {
+    if (this != &other) {
+        this->~SkPDFUnion();
+        SkNEW_PLACEMENT_ARGS(this, SkPDFUnion, (other.move()));
+    }
+    return *this;
 }
 
-////////////////////////////////////////////////////////////////////////////////
+SkPDFUnion::SkPDFUnion(SkPDFUnion&& other) {
+    SkASSERT(this != &other);
+    memcpy(this, &other, sizeof(*this));
+    other.fType = Type::kDestroyed;
+}
 
-SkPDFBool::SkPDFBool(bool value) : fValue(value) {}
-SkPDFBool::~SkPDFBool() {}
-
-void SkPDFBool::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    if (fValue) {
-        stream->writeText("true");
-    } else {
-        stream->writeText("false");
+#if 0
+SkPDFUnion SkPDFUnion::copy() const {
+    SkPDFUnion u(fType);
+    memcpy(&u, this, sizeof(u));
+    switch (fType) {
+        case Type::kNameSkS:
+        case Type::kStringSkS:
+            SkNEW_PLACEMENT_ARGS(pun(u.fSkString), SkString,
+                                 (*pun(fSkString)));
+            return u.move();
+        case Type::kObjRef:
+        case Type::kObject:
+            SkRef(u.fObject);
+            return u.move();
+        default:
+            return u.move();
     }
 }
+SkPDFUnion& SkPDFUnion::operator=(const SkPDFUnion& other) {
+    return *this = other.copy();
+}
+SkPDFUnion::SkPDFUnion(const SkPDFUnion& other) {
+    *this = other.copy();
+}
+#endif
 
-////////////////////////////////////////////////////////////////////////////////
-
-SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
-SkPDFScalar::~SkPDFScalar() {}
-
-void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    Append(fValue, stream);
+bool SkPDFUnion::isName() const {
+    return Type::kName == fType || Type::kNameSkS == fType;
 }
 
-// static
-void SkPDFScalar::Append(SkScalar value, SkWStream* stream) {
-    // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and
-    // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31).
-    // When using floats that are outside the whole value range, we can use
-    // integers instead.
-
-#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
-    if (value > 32767 || value < -32767) {
-        stream->writeDecAsText(SkScalarRoundToInt(value));
-        return;
-    }
-
-    char buffer[SkStrAppendScalar_MaxSize];
-    char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value));
-    stream->write(buffer, end - buffer);
-    return;
-#endif  // !SK_ALLOW_LARGE_PDF_SCALARS
-
-#if defined(SK_ALLOW_LARGE_PDF_SCALARS)
-    // Floats have 24bits of significance, so anything outside that range is
-    // no more precise than an int. (Plus PDF doesn't support scientific
-    // notation, so this clamps to SK_Max/MinS32).
-    if (value > (1 << 24) || value < -(1 << 24)) {
-        stream->writeDecAsText(value);
-        return;
-    }
-    // Continue to enforce the PDF limits for small floats.
-    if (value < 1.0f/65536 && value > -1.0f/65536) {
-        stream->writeDecAsText(0);
-        return;
-    }
-    // SkStrAppendFloat might still use scientific notation, so use snprintf
-    // directly..
-    static const int kFloat_MaxSize = 19;
-    char buffer[kFloat_MaxSize];
-    int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value);
-    // %f always prints trailing 0s, so strip them.
-    for (; buffer[len - 1] == '0' && len > 0; len--) {
-        buffer[len - 1] = '\0';
-    }
-    if (buffer[len - 1] == '.') {
-        buffer[len - 1] = '\0';
-    }
-    stream->writeText(buffer);
-    return;
-#endif  // SK_ALLOW_LARGE_PDF_SCALARS
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-SkPDFString::SkPDFString(const char value[])
-    : fValue(FormatString(value, strlen(value))) {
-}
-
-SkPDFString::SkPDFString(const SkString& value)
-    : fValue(FormatString(value.c_str(), value.size())) {
-}
-
-SkPDFString::SkPDFString(const uint16_t* value, size_t len, bool wideChars)
-    : fValue(FormatString(value, len, wideChars)) {
-}
-
-SkPDFString::~SkPDFString() {}
-
-void SkPDFString::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    stream->write(fValue.c_str(), fValue.size());
-}
-
-// static
-SkString SkPDFString::FormatString(const char* input, size_t len) {
-    return DoFormatString(input, len, false, false);
-}
-
-SkString SkPDFString::FormatString(const uint16_t* input, size_t len,
-                                   bool wideChars) {
-    return DoFormatString(input, len, true, wideChars);
-}
-
-// static
-SkString SkPDFString::DoFormatString(const void* input, size_t len,
-                                     bool wideInput, bool wideOutput) {
-    SkASSERT(len <= kMaxLen);
-    const uint16_t* win = (const uint16_t*) input;
-    const char* cin = (const char*) input;
-
-    if (wideOutput) {
-        SkASSERT(wideInput);
-        SkString result;
-        result.append("<");
-        for (size_t i = 0; i < len; i++) {
-            result.appendHex(win[i], 4);
+#ifdef SK_DEBUG
+// Most names need no escaping.  Such names are handled as static
+// const strings.
+bool is_valid_name(const char* n) {
+    static const char kControlChars[] = "/%()<>[]{}";
+    while (*n) {
+        if (*n < '!' || *n > '~' || strchr(kControlChars, *n)) {
+            return false;
         }
-        result.append(">");
-        return result;
+        ++n;
     }
+    return true;
+}
+#endif  // SK_DEBUG
 
-    // 7-bit clean is a heuristic to decide what string format to use;
-    // a 7-bit clean string should require little escaping.
-    bool sevenBitClean = true;
-    for (size_t i = 0; i < len; i++) {
-        SkASSERT(!wideInput || !(win[i] & ~0xFF));
-        char val = wideInput ? win[i] : cin[i];
-        if (val > '~' || val < ' ') {
-            sevenBitClean = false;
-            break;
+// Given an arbitrary string, convert it to a valid name.
+static SkString escape_name(const char* name, size_t len) {
+    static const char kToEscape[] = "#/%()<>[]{}";
+    int count = 0;
+    const char* const end = &name[len];
+    for (const char* n = name; n != end; ++n) {
+        if (*n < '!' || *n > '~' || strchr(kToEscape, *n)) {
+            count += 2;
         }
+        ++count;
     }
-
-    SkString result;
-    if (sevenBitClean) {
-        result.append("(");
-        for (size_t i = 0; i < len; i++) {
-            SkASSERT(!wideInput || !(win[i] & ~0xFF));
-            char val = wideInput ? win[i] : cin[i];
-            if (val == '\\' || val == '(' || val == ')') {
-                result.append("\\");
-            }
-            result.append(&val, 1);
-        }
-        result.append(")");
-    } else {
-        result.append("<");
-        for (size_t i = 0; i < len; i++) {
-            SkASSERT(!wideInput || !(win[i] & ~0xFF));
-            unsigned char val = wideInput ? win[i] : cin[i];
-            result.appendHex(val, 2);
-        }
-        result.append(">");
-    }
-
-    return result;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-SkPDFName::SkPDFName(const char name[]) : fValue(FormatName(SkString(name))) {}
-SkPDFName::SkPDFName(const SkString& name) : fValue(FormatName(name)) {}
-SkPDFName::~SkPDFName() {}
-
-bool SkPDFName::operator==(const SkPDFName& b) const {
-    return fValue == b.fValue;
-}
-
-void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
-    stream->write(fValue.c_str(), fValue.size());
-}
-
-// static
-SkString SkPDFName::FormatName(const SkString& input) {
-    SkASSERT(input.size() <= kMaxLen);
-    // TODO(vandebo) If more escaping is needed, improve the linear scan.
-    static const char escaped[] = "#/%()<>[]{}";
-
-    SkString result("/");
-    for (size_t i = 0; i < input.size(); i++) {
-        if (input[i] & 0x80 || input[i] < '!' || strchr(escaped, input[i])) {
-            result.append("#");
-            // Mask with 0xFF to avoid sign extension. i.e. #FFFFFF81
-            result.appendHex(input[i] & 0xFF, 2);
+    SkString result(count);
+    char* s = result.writable_str();
+    static const char kHex[] = "0123456789ABCDEF";
+    for (const char* n = name; n != end; ++n) {
+        if (*n < '!' || *n > '~' || strchr(kToEscape, *n)) {
+            *s++ = '#';
+            *s++ = kHex[(*n >> 4) & 0xF];
+            *s++ = kHex[*n & 0xF];
         } else {
-            result.append(input.c_str() + i, 1);
+            *s++ = *n;
         }
     }
-
-    return result;
+    SkASSERT(&result.writable_str()[count] == s);  // don't over-write
+    return result;                                 // allocated space
 }
 
+static SkString escape_name(const SkString& name) {
+    return escape_name(name.c_str(), name.size());
+}
+
+static void write_string(SkWStream* o, const SkString& s) {
+    o->write(s.c_str(), s.size());
+}
+
+static SkString format_string(const SkString& s) {
+    return SkPDFUtils::FormatString(s.c_str(), s.size());
+}
+
+static SkString format_string(const char* s) {
+    return SkPDFUtils::FormatString(s, strlen(s));
+}
+
+void SkPDFUnion::emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) const {
+    switch (fType) {
+        case Type::kInt:
+            stream->writeDecAsText(fIntValue);
+            return;
+        case Type::kBool:
+            stream->writeText(fBoolValue ? "true" : "false");
+            return;
+        case Type::kScalar:
+            SkPDFUtils::AppendScalar(fScalarValue, stream);
+            return;
+        case Type::kName:
+            stream->writeText("/");
+            SkASSERT(is_valid_name(fStaticString));
+            stream->writeText(fStaticString);
+            return;
+        case Type::kString:
+            SkASSERT(fStaticString);
+            write_string(stream, format_string(fStaticString));
+            return;
+        case Type::kNameSkS:
+            stream->writeText("/");
+            write_string(stream, escape_name(*pun(fSkString)));
+            return;
+        case Type::kStringSkS:
+            write_string(stream, format_string(*pun(fSkString)));
+            return;
+        case Type::kObjRef:
+            stream->writeDecAsText(objNumMap.getObjectNumber(
+                    substitutes.getSubstitute(fObject)));
+            stream->writeText(" 0 R");  // Generation number is always 0.
+            return;
+        case Type::kObject:
+            fObject->emitObject(stream, objNumMap, substitutes);
+            return;
+        default:
+            SkDEBUGFAIL("SkPDFUnion::emitObject with bad type");
+    }
+}
+
+void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap,
+                              const SkPDFSubstituteMap& substituteMap) const {
+    switch (fType) {
+        case Type::kInt:
+        case Type::kBool:
+        case Type::kScalar:
+        case Type::kName:
+        case Type::kString:
+        case Type::kNameSkS:
+        case Type::kStringSkS:
+            return;  // These have no resources.
+        case Type::kObjRef: {
+            SkPDFObject* obj = substituteMap.getSubstitute(fObject);
+            if (objNumMap->addObject(obj)) {
+                obj->addResources(objNumMap, substituteMap);
+            }
+            return;
+        }
+        case Type::kObject:
+            fObject->addResources(objNumMap, substituteMap);
+            return;
+        default:
+            SkDEBUGFAIL("SkPDFUnion::addResources with bad type");
+    }
+}
+
+SkPDFUnion SkPDFUnion::Int(int32_t value) {
+    SkPDFUnion u(Type::kInt);
+    u.fIntValue = value;
+    return u.move();
+}
+
+SkPDFUnion SkPDFUnion::Bool(bool value) {
+    SkPDFUnion u(Type::kBool);
+    u.fBoolValue = value;
+    return u.move();
+}
+
+SkPDFUnion SkPDFUnion::Scalar(SkScalar value) {
+    SkPDFUnion u(Type::kScalar);
+    u.fScalarValue = value;
+    return u.move();
+}
+
+SkPDFUnion SkPDFUnion::Name(const char* value) {
+    SkPDFUnion u(Type::kName);
+    SkASSERT(value);
+    SkASSERT(is_valid_name(value));
+    u.fStaticString = value;
+    return u.move();
+}
+
+SkPDFUnion SkPDFUnion::String(const char* value) {
+    SkPDFUnion u(Type::kString);
+    SkASSERT(value);
+    u.fStaticString = value;
+    return u.move();
+}
+
+SkPDFUnion SkPDFUnion::Name(const SkString& s) {
+    SkPDFUnion u(Type::kNameSkS);
+    SkNEW_PLACEMENT_ARGS(pun(u.fSkString), SkString, (s));
+    return u.move();
+}
+
+SkPDFUnion SkPDFUnion::String(const SkString& s) {
+    SkPDFUnion u(Type::kStringSkS);
+    SkNEW_PLACEMENT_ARGS(pun(u.fSkString), SkString, (s));
+    return u.move();
+}
+
+SkPDFUnion SkPDFUnion::ObjRef(SkPDFObject* ptr) {
+    SkPDFUnion u(Type::kObjRef);
+    SkASSERT(ptr);
+    u.fObject = ptr;
+    return u.move();
+}
+
+SkPDFUnion SkPDFUnion::Object(SkPDFObject* ptr) {
+    SkPDFUnion u(Type::kObject);
+    SkASSERT(ptr);
+    u.fObject = ptr;
+    return u.move();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#if 0  // Enable if needed.
+void SkPDFAtom::emitObject(SkWStream* stream,
+                           const SkPDFObjNumMap& objNumMap,
+                           const SkPDFSubstituteMap& substitutes) {
+    fValue.emitObject(stream, objNumMap, substitutes);
+}
+void SkPDFAtom::addResources(SkPDFObjNumMap* map,
+                             const SkPDFSubstituteMap& substitutes) const {
+    fValue.addResources(map, substitutes);
+}
+#endif  // 0
+
 ////////////////////////////////////////////////////////////////////////////////
 
 SkPDFArray::SkPDFArray() {}
 SkPDFArray::~SkPDFArray() {
-    fValue.unrefAll();
+    for (SkPDFUnion& value : fValues) {
+        value.~SkPDFUnion();
+    }
+    fValues.reset();
 }
 
-void SkPDFArray::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+int SkPDFArray::size() const { return fValues.count(); }
+
+void SkPDFArray::reserve(int length) { fValues.setReserve(length); }
+
+void SkPDFArray::emitObject(SkWStream* stream,
+                             const SkPDFObjNumMap& objNumMap,
+                             const SkPDFSubstituteMap& substitutes) {
     stream->writeText("[");
-    for (int i = 0; i < fValue.count(); i++) {
-        catalog->getSubstituteObject(fValue[i])->emitObject(stream, catalog);
-        if (i + 1 < fValue.count()) {
+    for (int i = 0; i < fValues.count(); i++) {
+        fValues[i].emitObject(stream, objNumMap, substitutes);
+        if (i + 1 < fValues.count()) {
             stream->writeText(" ");
         }
     }
     stream->writeText("]");
 }
 
-void SkPDFArray::addResources(SkTSet<SkPDFObject*>* resourceSet,
-                              SkPDFCatalog* catalog) const {
-    for (int i = 0; i < fValue.count(); i++) {
-        catalog->getSubstituteObject(fValue[i])
-                ->addResources(resourceSet, catalog);
+void SkPDFArray::addResources(SkPDFObjNumMap* catalog,
+                              const SkPDFSubstituteMap& substitutes) const {
+    for (const SkPDFUnion& value : fValues) {
+        value.addResources(catalog, substitutes);
     }
 }
 
-void SkPDFArray::reserve(int length) {
-    SkASSERT(length <= kMaxLen);
-    fValue.setReserve(length);
-}
-
-SkPDFObject* SkPDFArray::setAt(int offset, SkPDFObject* value) {
-    SkASSERT(offset < fValue.count());
-    value->ref();
-    fValue[offset]->unref();
-    fValue[offset] = value;
-    return value;
-}
-
-SkPDFObject* SkPDFArray::append(SkPDFObject* value) {
-    SkASSERT(fValue.count() < kMaxLen);
-    value->ref();
-    fValue.push(value);
-    return value;
+void SkPDFArray::append(SkPDFUnion&& value) {
+    SkNEW_PLACEMENT_ARGS(fValues.append(), SkPDFUnion, (value.move()));
 }
 
 void SkPDFArray::appendInt(int32_t value) {
-    SkASSERT(fValue.count() < kMaxLen);
-    fValue.push(new SkPDFInt(value));
+    this->append(SkPDFUnion::Int(value));
+}
+
+void SkPDFArray::appendBool(bool value) {
+    this->append(SkPDFUnion::Bool(value));
 }
 
 void SkPDFArray::appendScalar(SkScalar value) {
-    SkASSERT(fValue.count() < kMaxLen);
-    fValue.push(new SkPDFScalar(value));
+    this->append(SkPDFUnion::Scalar(value));
 }
 
 void SkPDFArray::appendName(const char name[]) {
-    SkASSERT(fValue.count() < kMaxLen);
-    fValue.push(new SkPDFName(name));
+    this->append(SkPDFUnion::Name(SkString(name)));
+}
+
+void SkPDFArray::appendName(const SkString& name) {
+    this->append(SkPDFUnion::Name(name));
+}
+
+void SkPDFArray::appendString(const SkString& value) {
+    this->append(SkPDFUnion::String(value));
+}
+
+void SkPDFArray::appendString(const char value[]) {
+    this->append(SkPDFUnion::String(value));
+}
+
+void SkPDFArray::appendObject(SkPDFObject* value) {
+    this->append(SkPDFUnion::Object(value));
+}
+
+void SkPDFArray::appendObjRef(SkPDFObject* value) {
+    this->append(SkPDFUnion::ObjRef(value));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 SkPDFDict::SkPDFDict() {}
 
-SkPDFDict::SkPDFDict(const char type[]) {
-    insertName("Type", type);
-}
+SkPDFDict::~SkPDFDict() { this->clear(); }
 
-SkPDFDict::~SkPDFDict() {
-    clear();
-}
+SkPDFDict::SkPDFDict(const char type[]) { this->insertName("Type", type); }
 
-int SkPDFDict::size() const {
-    return fValue.count();
-}
-
-void SkPDFDict::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+void SkPDFDict::emitObject(SkWStream* stream,
+                           const SkPDFObjNumMap& objNumMap,
+                           const SkPDFSubstituteMap& substitutes) {
     stream->writeText("<<");
-    for (int i = 0; i < fValue.count(); i++) {
-        SkASSERT(fValue[i].key);
-        SkASSERT(fValue[i].value);
-        fValue[i].key->emitObject(stream, catalog);
+    for (int i = 0; i < fRecords.count(); i++) {
+        fRecords[i].fKey.emitObject(stream, objNumMap, substitutes);
         stream->writeText(" ");
-        catalog->getSubstituteObject(fValue[i].value)
-                ->emitObject(stream, catalog);
-        stream->writeText("\n");
+        fRecords[i].fValue.emitObject(stream, objNumMap, substitutes);
+        if (i + 1 < fRecords.count()) {
+            stream->writeText("\n");
+        }
     }
     stream->writeText(">>");
 }
 
-void SkPDFDict::addResources(SkTSet<SkPDFObject*>* resourceSet,
-                             SkPDFCatalog* catalog) const {
-    for (int i = 0; i < fValue.count(); i++) {
-        SkASSERT(fValue[i].key);
-        SkASSERT(fValue[i].value);
-        fValue[i].key->addResources(resourceSet, catalog);
-        catalog->getSubstituteObject(fValue[i].value)
-                ->addResources(resourceSet, catalog);
+void SkPDFDict::addResources(SkPDFObjNumMap* catalog,
+                             const SkPDFSubstituteMap& substitutes) const {
+    for (int i = 0; i < fRecords.count(); i++) {
+        fRecords[i].fKey.addResources(catalog, substitutes);
+        fRecords[i].fValue.addResources(catalog, substitutes);
     }
 }
 
-SkPDFObject*  SkPDFDict::append(SkPDFName* key, SkPDFObject* value) {
-    SkASSERT(key);
-    SkASSERT(value);
-    *(fValue.append()) = Rec(key, value);
-    return value;
+void SkPDFDict::set(SkPDFUnion&& name, SkPDFUnion&& value) {
+    Record* rec = fRecords.append();
+    SkASSERT(name.isName());
+    SkNEW_PLACEMENT_ARGS(&rec->fKey, SkPDFUnion, (name.move()));
+    SkNEW_PLACEMENT_ARGS(&rec->fValue, SkPDFUnion, (value.move()));
 }
 
-SkPDFObject* SkPDFDict::insert(SkPDFName* key, SkPDFObject* value) {
-    return this->append(SkRef(key), SkRef(value));
+int SkPDFDict::size() const { return fRecords.count(); }
+
+void SkPDFDict::insertObjRef(const char key[], SkPDFObject* value) {
+    this->set(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(value));
+}
+void SkPDFDict::insertObjRef(const SkString& key, SkPDFObject* value) {
+    this->set(SkPDFUnion::Name(key), SkPDFUnion::ObjRef(value));
 }
 
-SkPDFObject* SkPDFDict::insert(const char key[], SkPDFObject* value) {
-    return this->append(new SkPDFName(key), SkRef(value));
+void SkPDFDict::insertObject(const char key[], SkPDFObject* value) {
+    this->set(SkPDFUnion::Name(key), SkPDFUnion::Object(value));
+}
+void SkPDFDict::insertObject(const SkString& key, SkPDFObject* value) {
+    this->set(SkPDFUnion::Name(key), SkPDFUnion::Object(value));
+}
+
+void SkPDFDict::insertBool(const char key[], bool value) {
+    this->set(SkPDFUnion::Name(key), SkPDFUnion::Bool(value));
 }
 
 void SkPDFDict::insertInt(const char key[], int32_t value) {
-    (void)this->append(new SkPDFName(key), new SkPDFInt(value));
+    this->set(SkPDFUnion::Name(key), SkPDFUnion::Int(value));
+}
+
+void SkPDFDict::insertInt(const char key[], size_t value) {
+    this->insertInt(key, SkToS32(value));
 }
 
 void SkPDFDict::insertScalar(const char key[], SkScalar value) {
-    (void)this->append(new SkPDFName(key), new SkPDFScalar(value));
+    this->set(SkPDFUnion::Name(key), SkPDFUnion::Scalar(value));
 }
 
 void SkPDFDict::insertName(const char key[], const char name[]) {
-    (void)this->append(new SkPDFName(key), new SkPDFName(name));
+    this->set(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
+}
+
+void SkPDFDict::insertName(const char key[], const SkString& name) {
+    this->set(SkPDFUnion::Name(key), SkPDFUnion::Name(name));
+}
+
+void SkPDFDict::insertString(const char key[], const char value[]) {
+    this->set(SkPDFUnion::Name(key), SkPDFUnion::String(value));
+}
+
+void SkPDFDict::insertString(const char key[], const SkString& value) {
+    this->set(SkPDFUnion::Name(key), SkPDFUnion::String(value));
 }
 
 void SkPDFDict::clear() {
-    for (int i = 0; i < fValue.count(); i++) {
-        SkASSERT(fValue[i].key);
-        SkASSERT(fValue[i].value);
-        fValue[i].key->unref();
-        fValue[i].value->unref();
+    for (Record& rec : fRecords) {
+        rec.fKey.~SkPDFUnion();
+        rec.fValue.~SkPDFUnion();
     }
-    fValue.reset();
+    fRecords.reset();
 }
 
-void SkPDFDict::remove(const char key[]) {
-    SkASSERT(key);
-    SkPDFName name(key);
-    for (int i = 0; i < fValue.count(); i++) {
-        SkASSERT(fValue[i].key);
-        if (*(fValue[i].key) == name) {
-            fValue[i].key->unref();
-            SkASSERT(fValue[i].value);
-            fValue[i].value->unref();
-            fValue.removeShuffle(i);
-            return;
-        }
-    }
+////////////////////////////////////////////////////////////////////////////////
+
+SkPDFSubstituteMap::~SkPDFSubstituteMap() {
+    fSubstituteMap.foreach(
+            [](SkPDFObject*, SkPDFObject** v) { (*v)->unref(); });
 }
 
-void SkPDFDict::mergeFrom(const SkPDFDict& other) {
-    for (int i = 0; i < other.fValue.count(); i++) {
-        *(fValue.append()) =
-                Rec(SkRef(other.fValue[i].key), SkRef(other.fValue[i].value));
-    }
+void SkPDFSubstituteMap::setSubstitute(SkPDFObject* original,
+                                       SkPDFObject* substitute) {
+    SkASSERT(original != substitute);
+    SkASSERT(!fSubstituteMap.find(original));
+    fSubstituteMap.set(original, SkRef(substitute));
 }
+
+SkPDFObject* SkPDFSubstituteMap::getSubstitute(SkPDFObject* object) const {
+    SkPDFObject** found = fSubstituteMap.find(object);
+    return found ? *found : object;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool SkPDFObjNumMap::addObject(SkPDFObject* obj) {
+    if (fObjectNumbers.find(obj)) {
+        return false;
+    }
+    fObjectNumbers.set(obj, fObjectNumbers.count() + 1);
+    fObjects.push(obj);
+    return true;
+}
+
+int32_t SkPDFObjNumMap::getObjectNumber(SkPDFObject* obj) const {
+    int32_t* objectNumberFound = fObjectNumbers.find(obj);
+    SkASSERT(objectNumberFound);
+    return *objectNumberFound;
+}
+
diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h
index 7a72a38..35922f8 100644
--- a/src/pdf/SkPDFTypes.h
+++ b/src/pdf/SkPDFTypes.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2010 The Android Open Source Project
  *
@@ -14,23 +13,24 @@
 #include "SkScalar.h"
 #include "SkString.h"
 #include "SkTDArray.h"
-#include "SkTSet.h"
+#include "SkTHash.h"
 #include "SkTypes.h"
 
-class SkPDFCatalog;
-class SkWStream;
-
+class SkPDFObjNumMap;
 class SkPDFObject;
+class SkPDFSubstituteMap;
+class SkWStream;
 
 /** \class SkPDFObject
 
     A PDF Object is the base class for primitive elements in a PDF file.  A
     common subtype is used to ease the use of indirect object references,
     which are common in the PDF format.
+
 */
 class SkPDFObject : public SkRefCnt {
 public:
-    SK_DECLARE_INST_COUNT(SkPDFObject)
+    SK_DECLARE_INST_COUNT(SkPDFObject);
 
     /** Subclasses must implement this method to print the object to the
      *  PDF file.
@@ -38,185 +38,149 @@
      *  @param stream   The writable output stream to send the output to.
      */
     // TODO(halcanary): make this method const
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) = 0;
+    virtual void emitObject(SkWStream* stream,
+                            const SkPDFObjNumMap& objNumMap,
+                            const SkPDFSubstituteMap& substitutes) = 0;
 
     /**
-     *  Adds all transitive dependencies of this object to resourceSet.
-     *
-     *  @param catalog Implementations should respect the catalog's
-     *                 object substitution map.
+     *  Adds all transitive dependencies of this object to the
+     *  catalog.  Implementations should respect the catalog's object
+     *  substitution map.
      */
-    virtual void addResources(SkTSet<SkPDFObject*>* resourceSet,
-                              SkPDFCatalog* catalog) const {}
+    virtual void addResources(SkPDFObjNumMap* catalog,
+                              const SkPDFSubstituteMap& substitutes) const {}
 
 private:
     typedef SkRefCnt INHERITED;
 };
 
-/** \class SkPDFObjRef
+////////////////////////////////////////////////////////////////////////////////
 
-    An indirect reference to a PDF object.
-*/
-class SkPDFObjRef : public SkPDFObject {
+/**
+   A SkPDFUnion is a non-virtualized implementation of the
+   non-compound, non-specialized PDF Object types: Name, String,
+   Number, Boolean.
+ */
+class SkPDFUnion {
 public:
-    SK_DECLARE_INST_COUNT(SkPDFObjRef)
+    // u.move() is analogous to std::move(u). It returns an rvalue.
+    SkPDFUnion move() { return static_cast<SkPDFUnion&&>(*this); }
+    // Move contstructor and assignemnt operator destroy the argument
+    // and steal their references (if needed).
+    SkPDFUnion(SkPDFUnion&& other);
+    SkPDFUnion& operator=(SkPDFUnion&& other);
 
-    /** Create a reference to an existing SkPDFObject.
-     *  @param obj The object to reference.
-     */
-    explicit SkPDFObjRef(SkPDFObject* obj);
-    virtual ~SkPDFObjRef();
+    ~SkPDFUnion();
 
-    // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
-    virtual void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE;
+    /** The following nine functions are the standard way of creating
+        SkPDFUnion objects. */
+
+    static SkPDFUnion Int(int32_t);
+
+    static SkPDFUnion Int(size_t);
+
+    static SkPDFUnion Bool(bool);
+
+    static SkPDFUnion Scalar(SkScalar);
+
+    /** These two functions do NOT take ownership of ptr, and do NOT
+        copy the string.  Suitable for passing in static const
+        strings. For example:
+          SkPDFUnion n = SkPDFUnion::Name("Length");
+          SkPDFUnion u = SkPDFUnion::String("Identity"); */
+
+    /** SkPDFUnion::Name(const char*) assumes that the passed string
+        is already a valid name (that is: it has no control or
+        whitespace characters).  This will not copy the name. */
+    static SkPDFUnion Name(const char*);
+
+    /** SkPDFUnion::String will encode the passed string.  This will
+        not copy the name. */
+    static SkPDFUnion String(const char*);
+
+    /** SkPDFUnion::Name(const SkString&) does not assume that the
+        passed string is already a valid name and it will escape the
+        string. */
+    static SkPDFUnion Name(const SkString&);
+
+    /** SkPDFUnion::String will encode the passed string. */
+    static SkPDFUnion String(const SkString&);
+
+    /** This function DOES take ownership of the object. E.g.
+          SkAutoTUnref<SkPDFDict> dict(new SkPDFDict);
+          dict->insert(.....);
+          SkPDFUnion u = SkPDFUnion::Object(dict.detach()) */
+    static SkPDFUnion Object(SkPDFObject*);
+
+    /** This function DOES take ownership of the object. E.g.
+          SkAutoTUnref<SkPDFBitmap> image(
+                 SkPDFBitmap::Create(fCanon, bitmap));
+          SkPDFUnion u = SkPDFUnion::ObjRef(image.detach()) */
+    static SkPDFUnion ObjRef(SkPDFObject*);
+
+    /** These two non-virtual methods mirror SkPDFObject's
+        corresponding virtuals. */
+    void emitObject(SkWStream*,
+                    const SkPDFObjNumMap&,
+                    const SkPDFSubstituteMap&) const;
+    void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const;
+
+    bool isName() const;
 
 private:
-    SkAutoTUnref<SkPDFObject> fObj;
+    union {
+        int32_t fIntValue;
+        bool fBoolValue;
+        SkScalar fScalarValue;
+        const char* fStaticString;
+        char fSkString[sizeof(SkString)];
+        SkPDFObject* fObject;
+    };
+    enum class Type : char {
+        /** It is an error to call emitObject() or addResources() on an
+            kDestroyed object. */
+        kDestroyed = 0,
+        kInt,
+        kBool,
+        kScalar,
+        kName,
+        kString,
+        kNameSkS,
+        kStringSkS,
+        kObjRef,
+        kObject,
+    };
+    Type fType;
 
-    typedef SkPDFObject INHERITED;
+    SkPDFUnion(Type);
+    // We do not now need copy constructor and copy assignment, so we
+    // will disable this functionality.
+    SkPDFUnion& operator=(const SkPDFUnion&) = delete;
+    SkPDFUnion(const SkPDFUnion&) = delete;
 };
+SK_COMPILE_ASSERT(sizeof(SkString) == sizeof(void*), SkString_size);
 
-/** \class SkPDFInt
+////////////////////////////////////////////////////////////////////////////////
 
-    An integer object in a PDF.
-*/
-class SkPDFInt : public SkPDFObject {
+#if 0  // Enable if needed.
+/** This class is a SkPDFUnion with SkPDFObject virtuals attached.
+    The only use case of this is when a non-compound PDF object is
+    referenced indirectly. */
+class SkPDFAtom : public SkPDFObject {
 public:
-    SK_DECLARE_INST_COUNT(SkPDFInt)
-
-    /** Create a PDF integer (usually for indirect reference purposes).
-     *  @param value An integer value between 2^31 - 1 and -2^31.
-     */
-    explicit SkPDFInt(int32_t value);
-    virtual ~SkPDFInt();
-
-    // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
+    void emitObject(SkWStream* stream,
+                    const SkPDFObjNumMap& objNumMap,
+                    const SkPDFSubstituteMap& substitutes) final;
+    void addResources(SkPDFObjNumMap*, const SkPDFSubstituteMap&) const final;
+    SkPDFAtom(SkPDFUnion&& v) : fValue(v.move()) {}
 
 private:
-    int32_t fValue;
-
+    const SkPDFUnion fValue;
     typedef SkPDFObject INHERITED;
 };
+#endif  // 0
 
-/** \class SkPDFBool
-
-    An boolean value in a PDF.
-*/
-class SkPDFBool : public SkPDFObject {
-public:
-    SK_DECLARE_INST_COUNT(SkPDFBool)
-
-    /** Create a PDF boolean.
-     *  @param value true or false.
-     */
-    explicit SkPDFBool(bool value);
-    virtual ~SkPDFBool();
-
-    // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
-
-private:
-    bool fValue;
-
-    typedef SkPDFObject INHERITED;
-};
-
-/** \class SkPDFScalar
-
-    A real number object in a PDF.
-*/
-class SkPDFScalar : public SkPDFObject {
-public:
-    SK_DECLARE_INST_COUNT(SkPDFScalar)
-
-    /** Create a PDF real number.
-     *  @param value A real value.
-     */
-    explicit SkPDFScalar(SkScalar value);
-    virtual ~SkPDFScalar();
-
-    static void Append(SkScalar value, SkWStream* stream);
-
-    // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
-
-private:
-    SkScalar fValue;
-
-    typedef SkPDFObject INHERITED;
-};
-
-/** \class SkPDFString
-
-    A string object in a PDF.
-*/
-class SkPDFString : public SkPDFObject {
-public:
-    SK_DECLARE_INST_COUNT(SkPDFString)
-
-    /** Create a PDF string. Maximum length (in bytes) is 65,535.
-     *  @param value A string value.
-     */
-    explicit SkPDFString(const char value[]);
-    explicit SkPDFString(const SkString& value);
-
-    /** Create a PDF string. Maximum length (in bytes) is 65,535.
-     *  @param value     A string value.
-     *  @param len       The length of value.
-     *  @param wideChars Indicates if the top byte in value is significant and
-     *                   should be encoded (true) or not (false).
-     */
-    SkPDFString(const uint16_t* value, size_t len, bool wideChars);
-    virtual ~SkPDFString();
-
-    // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
-
-    static SkString FormatString(const char* input, size_t len);
-    static SkString FormatString(const uint16_t* input, size_t len,
-                                 bool wideChars);
-private:
-    static const size_t kMaxLen = 65535;
-
-    const SkString fValue;
-
-    static SkString DoFormatString(const void* input, size_t len,
-                                 bool wideInput, bool wideOutput);
-
-    typedef SkPDFObject INHERITED;
-};
-
-/** \class SkPDFName
-
-    A name object in a PDF.
-*/
-class SkPDFName : public SkPDFObject {
-public:
-    SK_DECLARE_INST_COUNT(SkPDFName)
-
-    /** Create a PDF name object. Maximum length is 127 bytes.
-     *  @param value The name.
-     */
-    explicit SkPDFName(const char name[]);
-    explicit SkPDFName(const SkString& name);
-    virtual ~SkPDFName();
-
-    bool operator==(const SkPDFName& b) const;
-
-    // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
-
-private:
-    static const size_t kMaxLen = 127;
-
-    const SkString fValue;
-
-    static SkString FormatName(const SkString& input);
-
-    typedef SkPDFObject INHERITED;
-};
+////////////////////////////////////////////////////////////////////////////////
 
 /** \class SkPDFArray
 
@@ -226,61 +190,46 @@
 public:
     SK_DECLARE_INST_COUNT(SkPDFArray)
 
+    static const int kMaxLen = 8191;
+
     /** Create a PDF array. Maximum length is 8191.
      */
     SkPDFArray();
     virtual ~SkPDFArray();
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
-    virtual void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE;
+    void emitObject(SkWStream* stream,
+                    const SkPDFObjNumMap& objNumMap,
+                    const SkPDFSubstituteMap& substitutes) override;
+    void addResources(SkPDFObjNumMap*,
+                      const SkPDFSubstituteMap&) const override;
 
     /** The size of the array.
      */
-    int size() { return fValue.count(); }
+    int size() const;
 
     /** Preallocate space for the given number of entries.
      *  @param length The number of array slots to preallocate.
      */
     void reserve(int length);
 
-    /** Returns the object at the given offset in the array.
-     *  @param index The index into the array to retrieve.
-     */
-    SkPDFObject* getAt(int index) { return fValue[index]; }
-
-    /** Set the object at the given offset in the array. Ref's value.
-     *  @param index The index into the array to set.
-     *  @param value The value to add to the array.
-     *  @return The value argument is returned.
-     */
-    SkPDFObject* setAt(int index, SkPDFObject* value);
-
-    /** Append the object to the end of the array and increments its ref count.
-     *  @param value The value to add to the array.
-     *  @return The value argument is returned.
-     */
-    SkPDFObject* append(SkPDFObject* value);
-
-    /** Creates a SkPDFInt object and appends it to the array.
+    /** Appends a value to the end of the array.
      *  @param value The value to add to the array.
      */
-    void appendInt(int32_t value);
-
-    /** Creates a SkPDFScalar object and appends it to the array.
-     *  @param value The value to add to the array.
-     */
-    void appendScalar(SkScalar value);
-
-    /** Creates a SkPDFName object and appends it to the array.
-     *  @param value The value to add to the array.
-     */
-    void appendName(const char name[]);
+    void appendInt(int32_t);
+    void appendBool(bool);
+    void appendScalar(SkScalar);
+    void appendName(const char[]);
+    void appendName(const SkString&);
+    void appendString(const char[]);
+    void appendString(const SkString&);
+    /** appendObject and appendObjRef take ownership of the passed object */
+    void appendObject(SkPDFObject*);
+    void appendObjRef(SkPDFObject*);
 
 private:
-    static const int kMaxLen = 8191;
-    SkTDArray<SkPDFObject*> fValue;
-
+    SkTDArray<SkPDFUnion> fValues;
+    void append(SkPDFUnion&& value);
     typedef SkPDFObject INHERITED;
 };
 
@@ -304,90 +253,109 @@
     virtual ~SkPDFDict();
 
     // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog) SK_OVERRIDE;
-    virtual void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE;
+    void emitObject(SkWStream* stream,
+                    const SkPDFObjNumMap& objNumMap,
+                    const SkPDFSubstituteMap& substitutes) override;
+    void addResources(SkPDFObjNumMap*,
+                      const SkPDFSubstituteMap&) const override;
 
     /** The size of the dictionary.
      */
     int size() const;
 
-    /** Add the value to the dictionary with the given key.  Refs value.
-     *  @param key   The key for this dictionary entry.
-     *  @param value The value for this dictionary entry.
-     *  @return The value argument is returned.
-     */
-    SkPDFObject* insert(SkPDFName* key, SkPDFObject* value);
-
-    /** Add the value to the dictionary with the given key.  Refs value.  The
-     *  method will create the SkPDFName object.
+    /** Add the value to the dictionary with the given key.  Takes
+     *  ownership of the object.
      *  @param key   The text of the key for this dictionary entry.
      *  @param value The value for this dictionary entry.
-     *  @return The value argument is returned.
      */
-    SkPDFObject* insert(const char key[], SkPDFObject* value);
+    void insertObject(const char key[], SkPDFObject* value);
+    void insertObject(const SkString& key, SkPDFObject* value);
+    void insertObjRef(const char key[], SkPDFObject* value);
+    void insertObjRef(const SkString& key, SkPDFObject* value);
 
-    /** Add the int to the dictionary with the given key.
+    /** Add the value to the dictionary with the given key.
      *  @param key   The text of the key for this dictionary entry.
-     *  @param value The int value for this dictionary entry.
+     *  @param value The value for this dictionary entry.
      */
+    void insertBool(const char key[], bool value);
     void insertInt(const char key[], int32_t value);
-
-    /**
-     *  Calls insertInt() but asserts in debug builds that the value can be represented
-     *  by an int32_t.
-     */
-    void insertInt(const char key[], size_t value) {
-        this->insertInt(key, SkToS32(value));
-    }
-
-    /** Add the scalar to the dictionary with the given key.
-     *  @param key   The text of the key for this dictionary entry.
-     *  @param value The scalar value for this dictionary entry.
-     */
+    void insertInt(const char key[], size_t value);
     void insertScalar(const char key[], SkScalar value);
-
-    /** Add the name to the dictionary with the given key.
-     *  @param key   The text of the key for this dictionary entry.
-     *  @param name  The name for this dictionary entry.
-     */
-    void insertName(const char key[], const char name[]);
-
-    /** Add the name to the dictionary with the given key.
-     *  @param key   The text of the key for this dictionary entry.
-     *  @param name  The name for this dictionary entry.
-     */
-    void insertName(const char key[], const SkString& name) {
-        this->insertName(key, name.c_str());
-    }
+    void insertName(const char key[], const char nameValue[]);
+    void insertName(const char key[], const SkString& nameValue);
+    void insertString(const char key[], const char value[]);
+    void insertString(const char key[], const SkString& value);
 
     /** Remove all entries from the dictionary.
      */
     void clear();
 
-protected:
-    /** Use to remove a single key from the dictionary.
-     */
-    void remove(const char key[]);
-
-    /** Insert references to all of the key-value pairs from the other
-     *  dictionary into this one.
-     */
-    void mergeFrom(const SkPDFDict& other);
-
 private:
-    struct Rec {
-        SkPDFName* key;
-        SkPDFObject* value;
-        Rec(SkPDFName* k, SkPDFObject* v) : key(k), value(v) {}
+    struct Record {
+        SkPDFUnion fKey;
+        SkPDFUnion fValue;
     };
-
+    SkTDArray<Record> fRecords;
     static const int kMaxLen = 4095;
 
-    SkTDArray<struct Rec> fValue;
-
-    SkPDFObject* append(SkPDFName* key, SkPDFObject* value);
+    void set(SkPDFUnion&& name, SkPDFUnion&& value);
 
     typedef SkPDFObject INHERITED;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
+/** \class SkPDFObjNumMap
+
+    The PDF Object Number Map manages object numbers.  It is used to
+    create the PDF cross reference table.
+*/
+class SkPDFObjNumMap : SkNoncopyable {
+public:
+    /** Add the passed object to the catalog.
+     *  @param obj         The object to add.
+     *  @return True iff the object was not already added to the catalog.
+     */
+    bool addObject(SkPDFObject* obj);
+
+    /** Get the object number for the passed object.
+     *  @param obj         The object of interest.
+     */
+    int32_t getObjectNumber(SkPDFObject* obj) const;
+
+    const SkTDArray<SkPDFObject*>& objects() const { return fObjects; }
+
+private:
+    SkTDArray<SkPDFObject*> fObjects;
+    SkTHashMap<SkPDFObject*, int32_t> fObjectNumbers;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+/** \class SkPDFSubstituteMap
+
+    The PDF Substitute Map manages substitute objects and owns the
+    substitutes.
+*/
+class SkPDFSubstituteMap : SkNoncopyable {
+public:
+    ~SkPDFSubstituteMap();
+    /** Set substitute object for the passed object.
+        Refs substitute.
+     */
+    void setSubstitute(SkPDFObject* original, SkPDFObject* substitute);
+
+    /** Find and return any substitute object set for the passed object. If
+     *  there is none, return the passed object.
+     */
+    SkPDFObject* getSubstitute(SkPDFObject* object) const;
+
+    SkPDFObject* operator()(SkPDFObject* o) const {
+        return this->getSubstitute(o);
+    }
+
+private:
+    SkTHashMap<SkPDFObject*, SkPDFObject*> fSubstituteMap;
+};
+
 #endif
diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp
index 0a5be3b..fc1bdbe 100644
--- a/src/pdf/SkPDFUtils.cpp
+++ b/src/pdf/SkPDFUtils.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -50,7 +49,7 @@
         SkMatrix::SetAffineIdentity(values);
     }
     for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
-        SkPDFScalar::Append(values[i], content);
+        SkPDFUtils::AppendScalar(values[i], content);
         content->writeText(" ");
     }
     content->writeText("cm\n");
@@ -58,17 +57,17 @@
 
 // static
 void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
-    SkPDFScalar::Append(x, content);
+    SkPDFUtils::AppendScalar(x, content);
     content->writeText(" ");
-    SkPDFScalar::Append(y, content);
+    SkPDFUtils::AppendScalar(y, content);
     content->writeText(" m\n");
 }
 
 // static
 void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
-    SkPDFScalar::Append(x, content);
+    SkPDFUtils::AppendScalar(x, content);
     content->writeText(" ");
-    SkPDFScalar::Append(y, content);
+    SkPDFUtils::AppendScalar(y, content);
     content->writeText(" l\n");
 }
 
@@ -77,20 +76,20 @@
                              SkScalar ctl2X, SkScalar ctl2Y,
                              SkScalar dstX, SkScalar dstY, SkWStream* content) {
     SkString cmd("y\n");
-    SkPDFScalar::Append(ctl1X, content);
+    SkPDFUtils::AppendScalar(ctl1X, content);
     content->writeText(" ");
-    SkPDFScalar::Append(ctl1Y, content);
+    SkPDFUtils::AppendScalar(ctl1Y, content);
     content->writeText(" ");
     if (ctl2X != dstX || ctl2Y != dstY) {
         cmd.set("c\n");
-        SkPDFScalar::Append(ctl2X, content);
+        SkPDFUtils::AppendScalar(ctl2X, content);
         content->writeText(" ");
-        SkPDFScalar::Append(ctl2Y, content);
+        SkPDFUtils::AppendScalar(ctl2Y, content);
         content->writeText(" ");
     }
-    SkPDFScalar::Append(dstX, content);
+    SkPDFUtils::AppendScalar(dstX, content);
     content->writeText(" ");
-    SkPDFScalar::Append(dstY, content);
+    SkPDFUtils::AppendScalar(dstY, content);
     content->writeText(" ");
     content->writeText(cmd.c_str());
 }
@@ -107,13 +106,13 @@
     // Skia has 0,0 at top left, pdf at bottom left.  Do the right thing.
     SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
 
-    SkPDFScalar::Append(rect.fLeft, content);
+    SkPDFUtils::AppendScalar(rect.fLeft, content);
     content->writeText(" ");
-    SkPDFScalar::Append(bottom, content);
+    SkPDFUtils::AppendScalar(bottom, content);
     content->writeText(" ");
-    SkPDFScalar::Append(rect.width(), content);
+    SkPDFUtils::AppendScalar(rect.width(), content);
     content->writeText(" ");
-    SkPDFScalar::Append(rect.height(), content);
+    SkPDFUtils::AppendScalar(rect.height(), content);
     content->writeText(" re\n");
 }
 
@@ -252,3 +251,95 @@
     content->writeText(resourceName.c_str());
     content->writeText(" scn\n");
 }
+
+void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) {
+    // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and
+    // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31).
+    // When using floats that are outside the whole value range, we can use
+    // integers instead.
+
+#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
+    if (value > 32767 || value < -32767) {
+        stream->writeDecAsText(SkScalarRoundToInt(value));
+        return;
+    }
+
+    char buffer[SkStrAppendScalar_MaxSize];
+    char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value));
+    stream->write(buffer, end - buffer);
+    return;
+#endif  // !SK_ALLOW_LARGE_PDF_SCALARS
+
+#if defined(SK_ALLOW_LARGE_PDF_SCALARS)
+    // Floats have 24bits of significance, so anything outside that range is
+    // no more precise than an int. (Plus PDF doesn't support scientific
+    // notation, so this clamps to SK_Max/MinS32).
+    if (value > (1 << 24) || value < -(1 << 24)) {
+        stream->writeDecAsText(value);
+        return;
+    }
+    // Continue to enforce the PDF limits for small floats.
+    if (value < 1.0f/65536 && value > -1.0f/65536) {
+        stream->writeDecAsText(0);
+        return;
+    }
+    // SkStrAppendFloat might still use scientific notation, so use snprintf
+    // directly..
+    static const int kFloat_MaxSize = 19;
+    char buffer[kFloat_MaxSize];
+    int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value);
+    // %f always prints trailing 0s, so strip them.
+    for (; buffer[len - 1] == '0' && len > 0; len--) {
+        buffer[len - 1] = '\0';
+    }
+    if (buffer[len - 1] == '.') {
+        buffer[len - 1] = '\0';
+    }
+    stream->writeText(buffer);
+    return;
+#endif  // SK_ALLOW_LARGE_PDF_SCALARS
+}
+
+SkString SkPDFUtils::FormatString(const char* cin, size_t len) {
+    SkDEBUGCODE(static const size_t kMaxLen = 65535;)
+    SkASSERT(len <= kMaxLen);
+
+    // 7-bit clean is a heuristic to decide what string format to use;
+    // a 7-bit clean string should require little escaping.
+    bool sevenBitClean = true;
+    size_t characterCount = 2 + len;
+    for (size_t i = 0; i < len; i++) {
+        if (cin[i] > '~' || cin[i] < ' ') {
+            sevenBitClean = false;
+            break;
+        }
+        if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
+            ++characterCount;
+        }
+    }
+    SkString result;
+    if (sevenBitClean) {
+        result.resize(characterCount);
+        char* str = result.writable_str();
+        *str++ = '(';
+        for (size_t i = 0; i < len; i++) {
+            if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
+                *str++ = '\\';
+            }
+            *str++ = cin[i];
+        }
+        *str++ = ')';
+    } else {
+        result.resize(2 * len + 2);
+        char* str = result.writable_str();
+        *str++ = '<';
+        for (size_t i = 0; i < len; i++) {
+            uint8_t c = static_cast<uint8_t>(cin[i]);
+            static const char gHex[] = "0123456789ABCDEF";
+            *str++ = gHex[(c >> 4) & 0xF];
+            *str++ = gHex[(c     ) & 0xF];
+        }
+        *str++ = '>';
+    }
+    return result;
+}
diff --git a/src/pdf/SkPDFUtils.h b/src/pdf/SkPDFUtils.h
index b30821d..38f300a 100644
--- a/src/pdf/SkPDFUtils.h
+++ b/src/pdf/SkPDFUtils.h
@@ -54,6 +54,8 @@
     static void DrawFormXObject(int objectIndex, SkWStream* content);
     static void ApplyGraphicState(int objectIndex, SkWStream* content);
     static void ApplyPattern(int objectIndex, SkWStream* content);
+    static void AppendScalar(SkScalar value, SkWStream* stream);
+    static SkString FormatString(const char* input, size_t len);
 };
 
 #endif
diff --git a/src/pdf/SkTSet.h b/src/pdf/SkTSet.h
deleted file mode 100644
index f57d30e..0000000
--- a/src/pdf/SkTSet.h
+++ /dev/null
@@ -1,356 +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 SkTSet_DEFINED
-#define SkTSet_DEFINED
-
-#include "SkTSort.h"
-#include "SkTDArray.h"
-#include "SkTypes.h"
-
-/** \class SkTSet<T>
-
-    The SkTSet template class defines a set. Elements are additionally
-    guaranteed to be sorted by their insertion order.
-    Main operations supported now are: add, merge, find and contains.
-
-    TSet<T> is mutable.
-*/
-
-// TODO: Add remove, intersect and difference operations.
-// TODO: Add bench tests.
-template <typename T> class SkTSet {
-public:
-    SkTSet() {
-        fSetArray = SkNEW(SkTDArray<T>);
-        fOrderedArray = SkNEW(SkTDArray<T>);
-    }
-
-    ~SkTSet() {
-        SkASSERT(fSetArray);
-        SkDELETE(fSetArray);
-        SkASSERT(fOrderedArray);
-        SkDELETE(fOrderedArray);
-    }
-
-    SkTSet(const SkTSet<T>& src) {
-        this->fSetArray = SkNEW_ARGS(SkTDArray<T>, (*src.fSetArray));
-        this->fOrderedArray = SkNEW_ARGS(SkTDArray<T>, (*src.fOrderedArray));
-#ifdef SK_DEBUG
-        validate();
-#endif
-    }
-
-    SkTSet<T>& operator=(const SkTSet<T>& src) {
-        *this->fSetArray = *src.fSetArray;
-        *this->fOrderedArray = *src.fOrderedArray;
-#ifdef SK_DEBUG
-        validate();
-#endif
-        return *this;
-    }
-
-    /** Merges src elements into this, and returns the number of duplicates
-     * found. Elements from src will retain their ordering and will be ordered
-     * after the elements currently in this set.
-     *
-     * Implementation note: this uses a 2-stage merge to obtain O(n log n) time.
-     * The first stage goes through src.fOrderedArray, checking if
-     * this->contains() is false before adding to this.fOrderedArray.
-     * The second stage does a standard sorted list merge on the fSetArrays.
-     */
-    int mergeInto(const SkTSet<T>& src) {
-        SkASSERT(fSetArray);
-        SkASSERT(fOrderedArray);
-
-        // Do fOrderedArray merge.
-        for (int i = 0; i < src.count(); ++i) {
-            if (!contains((*src.fOrderedArray)[i])) {
-                fOrderedArray->push((*src.fOrderedArray)[i]);
-            }
-        }
-
-        // Do fSetArray merge.
-        int duplicates = 0;
-
-        SkTDArray<T>* fArrayNew = new SkTDArray<T>();
-        fArrayNew->setReserve(fOrderedArray->count());
-        int i = 0;
-        int j = 0;
-
-        while (i < fSetArray->count() && j < src.count()) {
-            if ((*fSetArray)[i] < (*src.fSetArray)[j]) {
-                fArrayNew->push((*fSetArray)[i]);
-                i++;
-            } else if ((*fSetArray)[i] > (*src.fSetArray)[j]) {
-                fArrayNew->push((*src.fSetArray)[j]);
-                j++;
-            } else {
-                duplicates++;
-                j++; // Skip one of the duplicates.
-            }
-        }
-
-        while (i < fSetArray->count()) {
-            fArrayNew->push((*fSetArray)[i]);
-            i++;
-        }
-
-        while (j < src.count()) {
-            fArrayNew->push((*src.fSetArray)[j]);
-            j++;
-        }
-        SkDELETE(fSetArray);
-        fSetArray = fArrayNew;
-        fArrayNew = NULL;
-
-#ifdef SK_DEBUG
-        validate();
-#endif
-        return duplicates;
-    }
-
-    /** Adds a new element into set and returns false if the element is already
-     * in this set.
-    */
-    bool add(const T& elem) {
-        SkASSERT(fSetArray);
-        SkASSERT(fOrderedArray);
-
-        int pos = 0;
-        int i = find(elem, &pos);
-        if (i >= 0) {
-            return false;
-        }
-        *fSetArray->insert(pos) = elem;
-        fOrderedArray->push(elem);
-#ifdef SK_DEBUG
-        validate();
-#endif
-        return true;
-    }
-
-    /** Returns true if this set is empty.
-    */
-    bool isEmpty() const {
-        SkASSERT(fOrderedArray);
-        SkASSERT(fSetArray);
-        SkASSERT(fSetArray->isEmpty() == fOrderedArray->isEmpty());
-        return fOrderedArray->isEmpty();
-    }
-
-    /** Return the number of elements in the set.
-     */
-    int count() const {
-        SkASSERT(fOrderedArray);
-        SkASSERT(fSetArray);
-        SkASSERT(fSetArray->count() == fOrderedArray->count());
-        return fOrderedArray->count();
-    }
-
-    /** Return the number of bytes in the set: count * sizeof(T).
-     */
-    size_t bytes() const {
-        SkASSERT(fOrderedArray);
-        return fOrderedArray->bytes();
-    }
-
-    /** Return the beginning of a set iterator.
-     * Elements in the iterator will be sorted ascending.
-     */
-    const T*  begin() const {
-        SkASSERT(fOrderedArray);
-        return fOrderedArray->begin();
-    }
-
-    /** Return the end of a set iterator.
-     */
-    const T*  end() const {
-        SkASSERT(fOrderedArray);
-        return fOrderedArray->end();
-    }
-
-    const T&  operator[](int index) const {
-        SkASSERT(fOrderedArray);
-        return (*fOrderedArray)[index];
-    }
-
-    /** Resets the set (deletes memory and initiates an empty set).
-     */
-    void reset() {
-        SkASSERT(fSetArray);
-        SkASSERT(fOrderedArray);
-        fSetArray->reset();
-        fOrderedArray->reset();
-    }
-
-    /** Rewinds the set (preserves memory and initiates an empty set).
-     */
-    void rewind() {
-        SkASSERT(fSetArray);
-        SkASSERT(fOrderedArray);
-        fSetArray->rewind();
-        fOrderedArray->rewind();
-    }
-
-    /** Reserves memory for the set.
-     */
-    void setReserve(int reserve) {
-        SkASSERT(fSetArray);
-        SkASSERT(fOrderedArray);
-        fSetArray->setReserve(reserve);
-        fOrderedArray->setReserve(reserve);
-    }
-
-    /** Returns true if the array contains this element.
-     */
-    bool contains(const T& elem) const {
-        SkASSERT(fSetArray);
-        return (this->find(elem) >= 0);
-    }
-
-    /** Copies internal array to destination.
-     */
-    void copy(T* dst) const {
-        SkASSERT(fOrderedArray);
-        fOrderedArray->copyRange(dst, 0, fOrderedArray->count());
-    }
-
-    /** Returns a const reference to the internal vector.
-     */
-    const SkTDArray<T>& toArray() {
-        SkASSERT(fOrderedArray);
-        return *fOrderedArray;
-    }
-
-    /** Unref all elements in the set.
-     */
-    void unrefAll() {
-        SkASSERT(fSetArray);
-        SkASSERT(fOrderedArray);
-        fOrderedArray->unrefAll();
-        // Also reset the other array, as SkTDArray::unrefAll does an
-        // implcit reset
-        fSetArray->reset();
-    }
-
-    /** safeUnref all elements in the set.
-     */
-    void safeUnrefAll() {
-        SkASSERT(fSetArray);
-        SkASSERT(fOrderedArray);
-        fOrderedArray->safeUnrefAll();
-        // Also reset the other array, as SkTDArray::safeUnrefAll does an
-        // implcit reset
-        fSetArray->reset();
-    }
-
-#ifdef SK_DEBUG
-    void validate() const {
-        SkASSERT(fSetArray);
-        SkASSERT(fOrderedArray);
-        fSetArray->validate();
-        fOrderedArray->validate();
-        SkASSERT(isSorted() && !hasDuplicates() && arraysConsistent());
-    }
-
-    bool hasDuplicates() const {
-        for (int i = 0; i < fSetArray->count() - 1; ++i) {
-            if ((*fSetArray)[i] == (*fSetArray)[i + 1]) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    bool isSorted() const {
-        for (int i = 0; i < fSetArray->count() - 1; ++i) {
-            // Use only < operator
-            if (!((*fSetArray)[i] < (*fSetArray)[i + 1])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /** Checks if fSetArray is consistent with fOrderedArray
-     */
-    bool arraysConsistent() const {
-        if (fSetArray->count() != fOrderedArray->count()) {
-            return false;
-        }
-        if (fOrderedArray->count() == 0) {
-            return true;
-        }
-
-        // Copy and sort fOrderedArray, then compare to fSetArray.
-        // A O(n log n) algorithm is necessary as O(n^2) will choke some GMs.
-        SkAutoMalloc sortedArray(fOrderedArray->bytes());
-        T* sortedBase = reinterpret_cast<T*>(sortedArray.get());
-        int count = fOrderedArray->count();
-        fOrderedArray->copyRange(sortedBase, 0, count);
-
-        SkTQSort<T>(sortedBase, sortedBase + count - 1);
-
-        for (int i = 0; i < count; ++i) {
-            if (sortedBase[i] != (*fSetArray)[i]) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-#endif
-
-private:
-    SkTDArray<T>* fSetArray;        // Sorted by pointer address for fast
-                                    // lookup.
-    SkTDArray<T>* fOrderedArray;    // Sorted by insertion order for
-                                    // deterministic output.
-
-    /** Returns the index in fSetArray where an element was found.
-     * Returns -1 if the element was not found, and it fills *posToInsertSorted
-     * with the index of the place where elem should be inserted to preserve the
-     * internal array sorted.
-     * If element was found, *posToInsertSorted is undefined.
-     */
-    int find(const T& elem, int* posToInsertSorted = NULL) const {
-        SkASSERT(fSetArray);
-
-        if (fSetArray->count() == 0) {
-            if (posToInsertSorted) {
-                *posToInsertSorted = 0;
-            }
-            return -1;
-        }
-        int iMin = 0;
-        int iMax = fSetArray->count();
-
-        while (iMin < iMax - 1) {
-            int iMid = (iMin + iMax) / 2;
-            if (elem < (*fSetArray)[iMid]) {
-                iMax = iMid;
-            } else {
-                iMin = iMid;
-            }
-        }
-        if (elem == (*fSetArray)[iMin]) {
-            return iMin;
-        }
-        if (posToInsertSorted) {
-            if (elem < (*fSetArray)[iMin]) {
-                *posToInsertSorted = iMin;
-            } else {
-                *posToInsertSorted = iMin + 1;
-            }
-        }
-
-        return -1;
-    }
-};
-
-#endif
diff --git a/src/pipe/SkGPipePriv.h b/src/pipe/SkGPipePriv.h
index 736930b..c3919f6 100644
--- a/src/pipe/SkGPipePriv.h
+++ b/src/pipe/SkGPipePriv.h
@@ -43,6 +43,8 @@
     kDrawBitmapNine_DrawOp,
     kDrawBitmapRectToRect_DrawOp,
     kDrawDRRect_DrawOp,
+    kDrawImage_DrawOp,
+    kDrawImageRect_DrawOp,
     kDrawOval_DrawOp,
     kDrawPaint_DrawOp,
     kDrawPatch_DrawOp,
@@ -79,6 +81,7 @@
     // these are signals to playback, not drawing verbs
     kReportFlags_DrawOp,
     kShareBitmapHeap_DrawOp,
+    kShareImageHeap_DrawOp,
     kDone_DrawOp,
 };
 
@@ -141,6 +144,7 @@
     kDrawVertices_HasIndices_DrawOpFlag  = 1 << 2,
     kDrawVertices_HasXfermode_DrawOpFlag = 1 << 3,
 };
+// These are shared between drawbitmap and drawimage
 enum {
     kDrawBitmap_HasPaint_DrawOpFlag   = 1 << 0,
     // Specific to drawBitmapRect, but needs to be different from HasPaint,
@@ -213,6 +217,22 @@
             && !(flags & SkGPipeWriter::kSharedAddressSpace_Flag));
 }
 
+class SkImageHeap : public SkRefCnt {
+public:
+    SkImageHeap();
+    virtual ~SkImageHeap();
+
+    // slot must be "valid" -- 0 is never valid
+    const SkImage* get(int32_t slot) const;
+    // returns 0 if not found, else returns slot
+    int32_t find(const SkImage*) const;
+    // returns non-zero value for where the image was stored
+    int32_t insert(const SkImage*);
+
+private:
+    SkTDArray<const SkImage*> fArray;
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 
 enum PaintOps {
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
index 7684b72..917bb50 100644
--- a/src/pipe/SkGPipeRead.cpp
+++ b/src/pipe/SkGPipeRead.cpp
@@ -162,7 +162,7 @@
      * these SkBitmaps for bitmap shaders. Used only in cross process mode
      * without a shared heap.
      */
-    SkBitmap* getBitmap(int32_t index) const SK_OVERRIDE {
+    SkBitmap* getBitmap(int32_t index) const override {
         SkASSERT(shouldFlattenBitmaps(fFlags));
         return fBitmaps[index];
     }
@@ -170,7 +170,7 @@
     /**
      * Needed to be a non-abstract subclass of SkBitmapHeapReader.
      */
-    void releaseRef(int32_t) SK_OVERRIDE {}
+    void releaseRef(int32_t) override {}
 
     void setSharedHeap(SkBitmapHeap* heap) {
         SkASSERT(!shouldFlattenBitmaps(fFlags) || NULL == heap);
@@ -178,6 +178,10 @@
         this->updateReader();
     }
 
+    void setImageHeap(SkImageHeap* heap) {
+        fImageHeap.reset(SkRef(heap));
+    }
+
     /**
      * Access the shared heap. Only used in the case when bitmaps are not
      * flattened.
@@ -198,6 +202,10 @@
         return id ? fTypefaces[id - 1] : NULL;
     }
 
+    const SkImage* getImage(int32_t slot) const {
+        return fImageHeap->get(slot);
+    }
+
 private:
     void updateReader() {
         if (NULL == fReader) {
@@ -227,6 +235,7 @@
     bool                      fSilent;
     // Only used when sharing bitmaps with the writer.
     SkBitmapHeap*             fSharedHeap;
+    SkAutoTUnref<SkImageHeap> fImageHeap;
     unsigned                  fFlags;
 };
 
@@ -629,6 +638,35 @@
     }
 }
 
+static void drawImage_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, SkGPipeState* state) {
+    unsigned slot = DrawOp_unpackData(op32);
+    unsigned flags = DrawOp_unpackFlags(op32);
+    bool hasPaint = SkToBool(flags & kDrawBitmap_HasPaint_DrawOpFlag);
+    SkScalar x = reader->readScalar();
+    SkScalar y = reader->readScalar();
+    const SkImage* image = state->getImage(slot);
+    if (state->shouldDraw()) {
+        canvas->drawImage(image, x, y, hasPaint ? &state->paint() : NULL);
+    }
+}
+
+static void drawImageRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                             SkGPipeState* state) {
+    unsigned slot = DrawOp_unpackData(op32);
+    unsigned flags = DrawOp_unpackFlags(op32);
+    bool hasPaint = SkToBool(flags & kDrawBitmap_HasPaint_DrawOpFlag);
+    bool hasSrc = SkToBool(flags & kDrawBitmap_HasSrcRect_DrawOpFlag);
+    const SkRect* src = NULL;
+    if (hasSrc) {
+        src = skip<SkRect>(reader);
+    }
+    const SkRect* dst = skip<SkRect>(reader);
+    const SkImage* image = state->getImage(slot);
+    if (state->shouldDraw()) {
+        canvas->drawImageRect(image, src, *dst, hasPaint ? &state->paint() : NULL);
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static void drawPicture_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
@@ -682,7 +720,7 @@
             case kReset_PaintOp: p->reset(); break;
             case kFlags_PaintOp: p->setFlags(data); break;
             case kColor_PaintOp: p->setColor(reader->readU32()); break;
-            case kFilterLevel_PaintOp: p->setFilterLevel((SkPaint::FilterLevel)data); break;
+            case kFilterLevel_PaintOp: p->setFilterQuality((SkFilterQuality)data); break;
             case kStyle_PaintOp: p->setStyle((SkPaint::Style)data); break;
             case kJoin_PaintOp: p->setStrokeJoin((SkPaint::Join)data); break;
             case kCap_PaintOp: p->setStrokeCap((SkPaint::Cap)data); break;
@@ -774,10 +812,14 @@
 }
 
 static void shareBitmapHeap_rp(SkCanvas*, SkReader32* reader, uint32_t,
-                           SkGPipeState* state) {
+                               SkGPipeState* state) {
     state->setSharedHeap(static_cast<SkBitmapHeap*>(reader->readPtr()));
 }
 
+static void shareImageHeap_rp(SkCanvas*, SkReader32* reader, uint32_t, SkGPipeState* state) {
+    state->setImageHeap(static_cast<SkImageHeap*>(reader->readPtr()));
+}
+
 static void done_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState*) {}
 
 typedef void (*ReadProc)(SkCanvas*, SkReader32*, uint32_t op32, SkGPipeState*);
@@ -793,6 +835,8 @@
     drawBitmapNine_rp,
     drawBitmapRect_rp,
     drawDRRect_rp,
+    drawImage_rp,
+    drawImageRect_rp,
     drawOval_rp,
     drawPaint_rp,
     drawPatch_rp,
@@ -828,6 +872,7 @@
 
     reportFlags_rp,
     shareBitmapHeap_rp,
+    shareImageHeap_rp,
     done_rp
 };
 
diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp
index be687f7..2e73be8 100644
--- a/src/pipe/SkGPipeWrite.cpp
+++ b/src/pipe/SkGPipeWrite.cpp
@@ -86,9 +86,9 @@
         fPointers.freeAll();
     }
 
-    void* allocThrow(size_t bytes) SK_OVERRIDE;
+    void* allocThrow(size_t bytes) override;
 
-    void unalloc(void* ptr) SK_OVERRIDE;
+    void unalloc(void* ptr) override;
 
     void setBitmapStorage(SkBitmapHeap* heap) {
         this->setBitmapHeap(heap);
@@ -175,7 +175,7 @@
 
     ~BitmapShuttle();
 
-    bool insert(const SkBitmap& bitmap, int32_t slot) SK_OVERRIDE;
+    bool insert(const SkBitmap& bitmap, int32_t slot) override;
 
     /**
      *  Remove the SkGPipeCanvas used for insertion. After this, calls to
@@ -231,9 +231,9 @@
         return (NULL == fBitmapHeap) ? 0 : fBitmapHeap->bytesAllocated();
     }
 
-    void beginCommentGroup(const char* description) SK_OVERRIDE;
-    void addComment(const char* kywd, const char* value) SK_OVERRIDE;
-    void endCommentGroup() SK_OVERRIDE;
+    void beginCommentGroup(const char* description) override;
+    void addComment(const char* kywd, const char* value) override;
+    void endCommentGroup() override;
 
     /**
      * Flatten an SkBitmap to send to the reader, where it will be referenced
@@ -242,56 +242,53 @@
     bool shuttleBitmap(const SkBitmap&, int32_t slot);
 
 protected:
-    void willSave() SK_OVERRIDE;
-    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) SK_OVERRIDE;
-    void willRestore() SK_OVERRIDE;
+    void willSave() override;
+    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+    void willRestore() override;
 
-    void didConcat(const SkMatrix&) SK_OVERRIDE;
-    void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
+    void didConcat(const SkMatrix&) override;
+    void didSetMatrix(const SkMatrix&) override;
 
-    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
-    virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                            const SkPaint&) SK_OVERRIDE;
-    virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                               const SkPaint&) SK_OVERRIDE;
-    virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                                SkScalar constY, const SkPaint&) SK_OVERRIDE;
-    virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                                  const SkMatrix* matrix, const SkPaint&) SK_OVERRIDE;
-    virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                const SkPaint& paint) SK_OVERRIDE;
-    virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
-                             const SkPoint texCoords[4], SkXfermode* xmode,
-                             const SkPaint& paint) SK_OVERRIDE;
-    void onDrawPaint(const SkPaint&) SK_OVERRIDE;
-    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) SK_OVERRIDE;
-    void onDrawRect(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawOval(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawRRect(const SkRRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawPath(const SkPath&, const SkPaint&) SK_OVERRIDE;
-    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
+    void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+                    const SkPaint&) override;
+    void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+                       const SkPaint&) override;
+    void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+                        SkScalar constY, const SkPaint&) override;
+    void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+                          const SkMatrix* matrix, const SkPaint&) override;
+    void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+                        const SkPaint& paint) override;
+    void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+                     const SkPoint texCoords[4], SkXfermode* xmode,
+                     const SkPaint& paint) override;
+    void onDrawPaint(const SkPaint&) override;
+    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
+    void onDrawRect(const SkRect&, const SkPaint&) override;
+    void onDrawOval(const SkRect&, const SkPaint&) override;
+    void onDrawRRect(const SkRRect&, const SkPaint&) override;
+    void onDrawPath(const SkPath&, const SkPaint&) override;
+    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
-                          DrawBitmapRectFlags flags) SK_OVERRIDE;
-#if 0
-    // rely on decomposition into bitmap (for now)
-    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+                          DrawBitmapRectFlags flags) override;
+    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
-                         const SkPaint*) SK_OVERRIDE;
-#endif
+                         const SkPaint*) override;
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
-                          const SkPaint*) SK_OVERRIDE;
-    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) SK_OVERRIDE;
+                          const SkPaint*) override;
+    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
     void onDrawVertices(VertexMode vmode, int vertexCount,
                         const SkPoint vertices[], const SkPoint texs[],
                         const SkColor colors[], SkXfermode* xmode,
                         const uint16_t indices[], int indexCount,
-                        const SkPaint&) SK_OVERRIDE;
-    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRegion(const SkRegion&, SkRegion::Op) SK_OVERRIDE;
+                        const SkPaint&) override;
+    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRegion(const SkRegion&, SkRegion::Op) override;
 
-    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) SK_OVERRIDE;
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
 
 private:
     void recordTranslate(const SkMatrix&);
@@ -300,6 +297,7 @@
 
     SkNamedFactorySet* fFactorySet;
     SkBitmapHeap*      fBitmapHeap;
+    SkImageHeap*       fImageHeap;
     SkGPipeController* fController;
     SkWriter32&        fWriter;
     size_t             fBlockSize; // amount allocated for writer
@@ -348,8 +346,8 @@
 
     // Common code used by drawBitmap*. Behaves differently depending on the
     // type of SkBitmapHeap being used, which is determined by the flags used.
-    bool commonDrawBitmap(const SkBitmap& bm, DrawOps op, unsigned flags,
-                          size_t opBytesNeeded, const SkPaint* paint);
+    bool commonDrawBitmap(const SkBitmap&, DrawOps, unsigned flags, size_t bytes, const SkPaint*);
+    bool commonDrawImage(const SkImage*, DrawOps, unsigned flags, size_t bytes, const SkPaint*);
 
     SkPaint fPaint;
     void writePaint(const SkPaint&);
@@ -462,6 +460,13 @@
         }
     }
     fFlattenableHeap.setBitmapStorage(fBitmapHeap);
+
+    fImageHeap = SkNEW(SkImageHeap);
+    if (this->needOpBytes(sizeof(void*))) {
+        this->writeOp(kShareImageHeap_DrawOp);
+        fWriter.writePtr(static_cast<void*>(fImageHeap));
+    }
+
     this->doNotify();
 }
 
@@ -469,6 +474,7 @@
     this->finish(true);
     SkSafeUnref(fFactorySet);
     SkSafeUnref(fBitmapHeap);
+    SkSafeUnref(fImageHeap);
 }
 
 bool SkGPipeCanvas::needOpBytes(size_t needed) {
@@ -823,6 +829,53 @@
     }
 }
 
+bool SkGPipeCanvas::commonDrawImage(const SkImage* image, DrawOps op, unsigned flags,
+                                    size_t opBytesNeeded, const SkPaint* paint) {
+    if (fDone) {
+        return false;
+    }
+    
+    if (paint != NULL) {
+        flags |= kDrawBitmap_HasPaint_DrawOpFlag;
+        this->writePaint(*paint);
+    }
+    // This needs to run first so its calls to needOpBytes() and its writes
+    // don't interlace with the needOpBytes() and write below.
+    int32_t slot = fImageHeap->insert(image);
+    SkASSERT(slot != 0);
+    if (this->needOpBytes(opBytesNeeded)) {
+        this->writeOp(op, flags, slot);
+        return true;
+    }
+    return false;
+}
+
+void SkGPipeCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y,
+                                const SkPaint* paint) {
+    NOTIFY_SETUP(this);
+    if (this->commonDrawImage(image, kDrawImage_DrawOp, 0, sizeof(SkScalar) * 2, paint)) {
+        fWriter.writeScalar(x);
+        fWriter.writeScalar(y);
+    }
+}
+
+void SkGPipeCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                                    const SkPaint* paint) {
+    NOTIFY_SETUP(this);
+    unsigned flags = 0;
+    size_t opBytesNeeded = sizeof(SkRect);  // dst
+    if (src) {
+        flags |= kDrawBitmap_HasSrcRect_DrawOpFlag;
+        opBytesNeeded += sizeof(SkRect);    // src
+    }
+    if (this->commonDrawImage(image, kDrawImageRect_DrawOp, flags, opBytesNeeded, paint)) {
+        if (src) {
+            fWriter.writeRect(*src);
+        }
+        fWriter.writeRect(dst);
+    }
+}
+
 void SkGPipeCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
                                const SkPaint& paint) {
     if (byteLength) {
@@ -1136,9 +1189,9 @@
         *ptr++ = paint.getColor();
         base.setColor(paint.getColor());
     }
-    if (base.getFilterLevel() != paint.getFilterLevel()) {
-        *ptr++ = PaintOp_packOpData(kFilterLevel_PaintOp, paint.getFilterLevel());
-        base.setFilterLevel(paint.getFilterLevel());
+    if (base.getFilterQuality() != paint.getFilterQuality()) {
+        *ptr++ = PaintOp_packOpData(kFilterLevel_PaintOp, paint.getFilterQuality());
+        base.setFilterQuality(paint.getFilterQuality());
     }
     if (base.getStyle() != paint.getStyle()) {
         *ptr++ = PaintOp_packOpData(kStyle_PaintOp, paint.getStyle());
@@ -1337,3 +1390,34 @@
     fCanvas->unref();
     fCanvas = NULL;
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkImageHeap::SkImageHeap() {}
+
+SkImageHeap::~SkImageHeap() {
+    fArray.unrefAll();
+}
+
+const SkImage* SkImageHeap::get(int32_t slot) const {
+    SkASSERT(slot > 0);
+    return fArray[slot - 1];
+}
+
+int32_t SkImageHeap::find(const SkImage* img) const {
+    int index = fArray.find(img);
+    if (index >= 0) {
+        return index + 1;   // found
+    }
+    return 0;   // not found
+}
+
+int32_t SkImageHeap::insert(const SkImage* img) {
+    int32_t slot = this->find(img);
+    if (slot) {
+        return slot;
+    }
+    *fArray.append() = SkRef(img);
+    return fArray.count();  // slot is always index+1
+}
+
diff --git a/src/pipe/utils/SamplePipeControllers.h b/src/pipe/utils/SamplePipeControllers.h
index 3442c49..5e074c5 100644
--- a/src/pipe/utils/SamplePipeControllers.h
+++ b/src/pipe/utils/SamplePipeControllers.h
@@ -18,8 +18,8 @@
 public:
     PipeController(SkCanvas* target, SkPicture::InstallPixelRefProc proc = NULL);
     virtual ~PipeController();
-    void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE;
-    void notifyWritten(size_t bytes) SK_OVERRIDE;
+    void* requestBlock(size_t minRequest, size_t* actual) override;
+    void notifyWritten(size_t bytes) override;
 protected:
     const void* getData() { return (const char*) fBlock + fBytesWritten; }
     SkGPipeReader fReader;
@@ -37,8 +37,8 @@
     TiledPipeController(const SkBitmap&, SkPicture::InstallPixelRefProc proc = NULL,
                         const SkMatrix* initialMatrix = NULL);
     virtual ~TiledPipeController() {};
-    void notifyWritten(size_t bytes) SK_OVERRIDE;
-    int numberOfReaders() const SK_OVERRIDE { return NumberOfTiles; }
+    void notifyWritten(size_t bytes) override;
+    int numberOfReaders() const override { return NumberOfTiles; }
 private:
     enum {
         NumberOfTiles = 10
@@ -57,9 +57,9 @@
 class ThreadSafePipeController : public SkGPipeController {
 public:
     ThreadSafePipeController(int numberOfReaders);
-    void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE;
-    void notifyWritten(size_t bytes) SK_OVERRIDE;
-    int numberOfReaders() const SK_OVERRIDE { return fNumberOfReaders; }
+    void* requestBlock(size_t minRequest, size_t* actual) override;
+    void notifyWritten(size_t bytes) override;
+    int numberOfReaders() const override { return fNumberOfReaders; }
 
     /**
      * Play the stored drawing commands to the specified canvas. If SkGPipeWriter::startRecording
diff --git a/src/ports/SkFontConfigInterface_direct.cpp b/src/ports/SkFontConfigInterface_direct.cpp
index 0f2d882..239a790 100644
--- a/src/ports/SkFontConfigInterface_direct.cpp
+++ b/src/ports/SkFontConfigInterface_direct.cpp
@@ -111,14 +111,14 @@
                                  SkTypeface::Style requested,
                                  FontIdentity* outFontIdentifier,
                                  SkString* outFamilyName,
-                                 SkTypeface::Style* outStyle) SK_OVERRIDE;
-    SkStreamAsset* openStream(const FontIdentity&) SK_OVERRIDE;
+                                 SkTypeface::Style* outStyle) override;
+    SkStreamAsset* openStream(const FontIdentity&) override;
 
     // new APIs
-    SkDataTable* getFamilyNames() SK_OVERRIDE;
+    SkDataTable* getFamilyNames() override;
     virtual bool matchFamilySet(const char inFamilyName[],
                                 SkString* outFamilyName,
-                                SkTArray<FontIdentity>*) SK_OVERRIDE;
+                                SkTArray<FontIdentity>*) override;
 
 private:
     SkMutex mutex_;
@@ -229,6 +229,7 @@
         { GOTHIC, "MS Gothic" },
         { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
                   "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
+        { GOTHIC, "Noto Sans Mono CJK JP" },
         { GOTHIC, "IPAGothic" },
         { GOTHIC, "MotoyaG04GothicMono" },
 
diff --git a/src/ports/SkFontConfigParser_android.cpp b/src/ports/SkFontConfigParser_android.cpp
index eb2ed07..73e4c47 100644
--- a/src/ports/SkFontConfigParser_android.cpp
+++ b/src/ports/SkFontConfigParser_android.cpp
@@ -19,10 +19,6 @@
 #include <limits>
 #include <stdlib.h>
 
-// From Android version LMP onwards, all font files collapse into
-// /system/etc/fonts.xml. Instead of trying to detect which version
-// we're on, try to open fonts.xml; if that fails, fall back to the
-// older filename.
 #define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
 #define OLD_SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml"
 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
@@ -38,46 +34,81 @@
 #endif
 
 /**
- * This file contains TWO parsers: one for JB and earlier (system_fonts.xml /
- * fallback_fonts.xml), one for LMP and later (fonts.xml).
- * We start with the JB parser, and if we detect a <familyset> tag with
- * version 21 or higher we switch to the LMP parser.
+ * This file contains TWO 'familyset' handlers:
+ * One for JB and earlier which works with
+ *   /system/etc/system_fonts.xml
+ *   /system/etc/fallback_fonts.xml
+ *   /vendor/etc/fallback_fonts.xml
+ *   /system/etc/fallback_fonts-XX.xml
+ *   /vendor/etc/fallback_fonts-XX.xml
+ * and the other for LMP and later which works with
+ *   /system/etc/fonts.xml
+ *
+ * If the 'familyset' 'version' attribute is 21 or higher the LMP parser is used, otherwise the JB.
  */
 
-/** Used to track which tag is currently populating with data.
- *  Only nameset and fileset are of interest for now.
- */
-enum CurrentTag {
-    kNo_CurrentTag,
-    kNameSet_CurrentTag,
-    kFileSet_CurrentTag
+struct FamilyData;
+
+struct TagHandler {
+    /** Called at the start tag.
+     *  Called immediately after the parent tag retuns this handler from a call to 'tag'.
+     *  Allows setting up for handling the tag content and processing attributes.
+     *  If NULL, will not be called.
+     */
+    void (*start)(FamilyData* data, const char* tag, const char** attributes);
+
+    /** Called at the end tag.
+     *  Allows post-processing of any accumulated information.
+     *  This will be the last call made in relation to the current tag.
+     *  If NULL, will not be called.
+     */
+    void (*end)(FamilyData* data, const char* tag);
+
+    /** Called when a nested tag is encountered.
+     *  This is responsible for determining how to handle the tag.
+     *  If the tag is not recognized, return NULL to skip the tag.
+     *  If NULL, all nested tags will be skipped.
+     */
+    const TagHandler* (*tag)(FamilyData* data, const char* tag, const char** attributes);
+
+    /** The character handler for this tag.
+     *  This is only active for character data contained directly in this tag (not sub-tags).
+     *  The first parameter will be castable to a FamilyData*.
+     *  If NULL, any character data in this tag will be ignored.
+     */
+    XML_CharacterDataHandler chars;
 };
 
-/**
- * The FamilyData structure is passed around by the parser so that each handler
- * can read these variables that are relevant to the current parsing.
- */
+/** Represents the current parsing state. */
 struct FamilyData {
     FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families,
-               const SkString& basePath, bool isFallback)
+               const SkString& basePath, bool isFallback, const char* filename,
+               const TagHandler* topLevelHandler)
         : fParser(parser)
         , fFamilies(families)
         , fCurrentFamily(NULL)
         , fCurrentFontInfo(NULL)
-        , fCurrentTag(kNo_CurrentTag)
         , fVersion(0)
         , fBasePath(basePath)
         , fIsFallback(isFallback)
+        , fFilename(filename)
+        , fDepth(1)
+        , fSkip(0)
+        , fHandler(&topLevelHandler, 1)
     { };
 
     XML_Parser fParser;                       // The expat parser doing the work, owned by caller
     SkTDArray<FontFamily*>& fFamilies;        // The array to append families, owned by caller
     SkAutoTDelete<FontFamily> fCurrentFamily; // The family being created, owned by this
     FontFileInfo* fCurrentFontInfo;           // The fontInfo being created, owned by fCurrentFamily
-    CurrentTag fCurrentTag;                   // The kind of tag currently being parsed.
     int fVersion;                             // The version of the file parsed.
     const SkString& fBasePath;                // The current base path.
     const bool fIsFallback;                   // Indicates the file being parsed is a fallback file
+    const char* fFilename;                    // The name of the file currently being parsed.
+
+    int fDepth;                               // The current element depth of the parse.
+    int fSkip;                                // The depth to stop skipping, 0 if not skipping.
+    SkTDArray<const TagHandler*> fHandler;    // The stack of current tag handlers.
 };
 
 /** Parses a null terminated string into an integer type, checking for overflow.
@@ -113,56 +144,116 @@
 
 #define ATTS_NON_NULL(a, i) (a[i] != NULL && a[i+1] != NULL)
 
+#define SK_FONTCONFIGPARSER_PREFIX "[SkFontConfigParser] "
+
+#define SK_FONTCONFIGPARSER_WARNING(message, ...) SkDebugf( \
+    SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d: warning: " message "\n", \
+    self->fFilename, \
+    XML_GetCurrentLineNumber(self->fParser), \
+    XML_GetCurrentColumnNumber(self->fParser), \
+    ##__VA_ARGS__);
+
+static bool is_whitespace(char c) {
+    return c == ' ' || c == '\n'|| c == '\r' || c == '\t';
+}
+
+static void trim_string(SkString* s) {
+    char* str = s->writable_str();
+    const char* start = str;  // start is inclusive
+    const char* end = start + s->size();  // end is exclusive
+    while (is_whitespace(*start)) { ++start; }
+    if (start != end) {
+        --end;  // make end inclusive
+        while (is_whitespace(*end)) { --end; }
+        ++end;  // make end exclusive
+    }
+    size_t len = end - start;
+    memmove(str, start, len);
+    s->resize(len);
+}
+
 namespace lmpParser {
 
-static void family_element_handler(FontFamily* family, const char** attributes) {
-    // A non-fallback <family> tag must have a canonical name attribute.
-    // A fallback <family> tag has no name, and may have lang and variant
-    // attributes.
-    family->fIsFallbackFont = true;
-    for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
-        const char* name = attributes[i];
-        const char* value = attributes[i+1];
-        size_t nameLen = strlen(name);
-        size_t valueLen = strlen(value);
-        if (MEMEQ("name", name, nameLen)) {
-            SkAutoAsciiToLC tolc(value);
-            family->fNames.push_back().set(tolc.lc());
-            family->fIsFallbackFont = false;
-        } else if (MEMEQ("lang", name, nameLen)) {
-            family->fLanguage = SkLanguage(value, valueLen);
-        } else if (MEMEQ("variant", name, nameLen)) {
-            // Value should be either elegant or compact.
-            if (MEMEQ("elegant", value, valueLen)) {
-                family->fVariant = kElegant_FontVariant;
-            } else if (MEMEQ("compact", value, valueLen)) {
-                family->fVariant = kCompact_FontVariant;
+static const TagHandler fontHandler = {
+    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
+        // 'weight' (non-negative integer) [default 0]
+        // 'style' ("normal", "italic") [default "auto"]
+        // 'index' (non-negative integer) [default 0]
+        // The character data should be a filename.
+        FontFileInfo& file = self->fCurrentFamily->fFonts.push_back();
+        self->fCurrentFontInfo = &file;
+        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
+            const char* name = attributes[i];
+            const char* value = attributes[i+1];
+            size_t nameLen = strlen(name);
+            if (MEMEQ("weight", name, nameLen)) {
+                if (!parse_non_negative_integer(value, &file.fWeight)) {
+                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
+                }
+            } else if (MEMEQ("style", name, nameLen)) {
+                size_t valueLen = strlen(value);
+                if (MEMEQ("normal", value, valueLen)) {
+                    file.fStyle = FontFileInfo::Style::kNormal;
+                } else if (MEMEQ("italic", value, valueLen)) {
+                    file.fStyle = FontFileInfo::Style::kItalic;
+                }
+            } else if (MEMEQ("index", name, nameLen)) {
+                if (!parse_non_negative_integer(value, &file.fIndex)) {
+                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
+                }
             }
         }
+    },
+    /*end*/[](FamilyData* self, const char* tag) {
+        trim_string(&self->fCurrentFontInfo->fFileName);
+    },
+    /*tag*/NULL,
+    /*chars*/[](void* data, const char* s, int len) {
+        FamilyData* self = static_cast<FamilyData*>(data);
+        self->fCurrentFontInfo->fFileName.append(s, len);
     }
-}
+};
 
-static void XMLCALL font_file_name_handler(void* data, const char* s, int len) {
-    FamilyData* self = static_cast<FamilyData*>(data);
-    self->fCurrentFontInfo->fFileName.append(s, len);
-}
-
-static void font_element_handler(FamilyData* self, FontFileInfo* file, const char** attributes) {
-    // A <font> should have weight (integer) and style (normal, italic) attributes.
-    // NOTE: we ignore the style.
-    // The element should contain a filename.
-    for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
-        const char* name = attributes[i];
-        const char* value = attributes[i+1];
-        size_t nameLen = strlen(name);
-        if (MEMEQ("weight", name, nameLen)) {
-            if (!parse_non_negative_integer(value, &file->fWeight)) {
-                SkDebugf("---- Font weight %s (INVALID)", value);
+static const TagHandler familyHandler = {
+    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
+        // 'name' (string) [optional]
+        // 'lang' (string) [default ""]
+        // 'variant' ("elegant", "compact") [default "default"]
+        // If there is no name, this is a fallback only font.
+        FontFamily* family = new FontFamily(self->fBasePath, true);
+        self->fCurrentFamily.reset(family);
+        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
+            const char* name = attributes[i];
+            const char* value = attributes[i+1];
+            size_t nameLen = strlen(name);
+            size_t valueLen = strlen(value);
+            if (MEMEQ("name", name, nameLen)) {
+                SkAutoAsciiToLC tolc(value);
+                family->fNames.push_back().set(tolc.lc());
+                family->fIsFallbackFont = false;
+            } else if (MEMEQ("lang", name, nameLen)) {
+                family->fLanguage = SkLanguage(value, valueLen);
+            } else if (MEMEQ("variant", name, nameLen)) {
+                if (MEMEQ("elegant", value, valueLen)) {
+                    family->fVariant = kElegant_FontVariant;
+                } else if (MEMEQ("compact", value, valueLen)) {
+                    family->fVariant = kCompact_FontVariant;
+                }
             }
         }
-    }
-    XML_SetCharacterDataHandler(self->fParser, font_file_name_handler);
-}
+    },
+    /*end*/[](FamilyData* self, const char* tag) {
+        *self->fFamilies.append() = self->fCurrentFamily.detach();
+    },
+    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
+        size_t len = strlen(tag);
+        if (MEMEQ("font", tag, len)) {
+            return &fontHandler;
+        }
+        return NULL;
+    },
+    /*chars*/NULL,
+};
 
 static FontFamily* find_family(FamilyData* self, const SkString& familyName) {
     for (int i = 0; i < self->fFamilies.count(); i++) {
@@ -176,220 +267,280 @@
     return NULL;
 }
 
-static void alias_element_handler(FamilyData* self, const char** attributes) {
-    // An <alias> must have name and to attributes.
-    //   It may have weight (integer).
-    // If it *does not* have a weight, it is a variant name for a <family>.
-    // If it *does* have a weight, it names the <font>(s) of a specific weight
-    //   from a <family>.
+static const TagHandler aliasHandler = {
+    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
+        // 'name' (string) introduces a new family name.
+        // 'to' (string) specifies which (previous) family to alias
+        // 'weight' (non-negative integer) [optional]
+        // If it *does not* have a weight, 'name' is an alias for the entire 'to' family.
+        // If it *does* have a weight, 'name' is a new family consisting of
+        // the font(s) with 'weight' from the 'to' family.
 
-    SkString aliasName;
-    SkString to;
-    int weight = 0;
-    for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
-        const char* name = attributes[i];
-        const char* value = attributes[i+1];
-        size_t nameLen = strlen(name);
-        if (MEMEQ("name", name, nameLen)) {
-            SkAutoAsciiToLC tolc(value);
-            aliasName.set(tolc.lc());
-        } else if (MEMEQ("to", name, nameLen)) {
-            to.set(value);
-        } else if (MEMEQ("weight", name, nameLen)) {
-            if (!parse_non_negative_integer(value, &weight)) {
-                SkDebugf("---- Font weight %s (INVALID)", value);
+        SkString aliasName;
+        SkString to;
+        int weight = 0;
+        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
+            const char* name = attributes[i];
+            const char* value = attributes[i+1];
+            size_t nameLen = strlen(name);
+            if (MEMEQ("name", name, nameLen)) {
+                SkAutoAsciiToLC tolc(value);
+                aliasName.set(tolc.lc());
+            } else if (MEMEQ("to", name, nameLen)) {
+                to.set(value);
+            } else if (MEMEQ("weight", name, nameLen)) {
+                if (!parse_non_negative_integer(value, &weight)) {
+                    SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid weight", value);
+                }
             }
         }
-    }
 
-    // Assumes that the named family is already declared
-    FontFamily* targetFamily = find_family(self, to);
-    if (!targetFamily) {
-        SkDebugf("---- Font alias target %s (NOT FOUND)", to.c_str());
-        return;
-    }
-
-    if (weight) {
-        FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
-        family->fNames.push_back().set(aliasName);
-
-        for (int i = 0; i < targetFamily->fFonts.count(); i++) {
-            if (targetFamily->fFonts[i].fWeight == weight) {
-                family->fFonts.push_back(targetFamily->fFonts[i]);
-            }
+        // Assumes that the named family is already declared
+        FontFamily* targetFamily = find_family(self, to);
+        if (!targetFamily) {
+            SK_FONTCONFIGPARSER_WARNING("'%s' alias target not found", to.c_str());
+            return;
         }
-        *self->fFamilies.append() = family;
-    } else {
-        targetFamily->fNames.push_back().set(aliasName);
-    }
-}
 
-static void XMLCALL start_element_handler(void* data, const char* tag, const char** attributes) {
-    FamilyData* self = static_cast<FamilyData*>(data);
-    size_t len = strlen(tag);
-    if (MEMEQ("family", tag, len)) {
-        self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFallback));
-        family_element_handler(self->fCurrentFamily, attributes);
-    } else if (MEMEQ("font", tag, len)) {
-        FontFileInfo* file = &self->fCurrentFamily->fFonts.push_back();
-        self->fCurrentFontInfo = file;
-        font_element_handler(self, file, attributes);
-    } else if (MEMEQ("alias", tag, len)) {
-        alias_element_handler(self, attributes);
-    }
-}
+        if (weight) {
+            FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback);
+            family->fNames.push_back().set(aliasName);
 
-static void XMLCALL end_element_handler(void* data, const char* tag) {
-    FamilyData* self = static_cast<FamilyData*>(data);
-    size_t len = strlen(tag);
-    if (MEMEQ("family", tag, len)) {
-        *self->fFamilies.append() = self->fCurrentFamily.detach();
-    } else if (MEMEQ("font", tag, len)) {
-        XML_SetCharacterDataHandler(self->fParser, NULL);
-    }
-}
+            for (int i = 0; i < targetFamily->fFonts.count(); i++) {
+                if (targetFamily->fFonts[i].fWeight == weight) {
+                    family->fFonts.push_back(targetFamily->fFonts[i]);
+                }
+            }
+            *self->fFamilies.append() = family;
+        } else {
+            targetFamily->fNames.push_back().set(aliasName);
+        }
+    },
+    /*end*/NULL,
+    /*tag*/NULL,
+    /*chars*/NULL,
+};
+
+static const TagHandler familySetHandler = {
+    /*start*/[](FamilyData* self, const char* tag, const char** attributes) { },
+    /*end*/NULL,
+    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
+        size_t len = strlen(tag);
+        if (MEMEQ("family", tag, len)) {
+            return &familyHandler;
+        } else if (MEMEQ("alias", tag, len)) {
+            return &aliasHandler;
+        }
+        return NULL;
+    },
+    /*chars*/NULL,
+};
 
 } // lmpParser
 
 namespace jbParser {
 
-/**
- * Handler for arbitrary text. This is used to parse the text inside each name
- * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays.
- */
-static void XMLCALL text_handler(void* data, const char* s, int len) {
-    FamilyData* self = static_cast<FamilyData*>(data);
-    // Make sure we're in the right state to store this name information
-    if (self->fCurrentFamily.get()) {
-        switch (self->fCurrentTag) {
-        case kNameSet_CurrentTag: {
-            SkAutoAsciiToLC tolc(s, len);
-            self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
-            break;
-        }
-        case kFileSet_CurrentTag:
-            if (self->fCurrentFontInfo) {
-                self->fCurrentFontInfo->fFileName.append(s, len);
+static const TagHandler fileHandler = {
+    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
+        // 'variant' ("elegant", "compact") [default "default"]
+        // 'lang' (string) [default ""]
+        // 'index' (non-negative integer) [default 0]
+        // The character data should be a filename.
+        FontFamily& currentFamily = *self->fCurrentFamily.get();
+        FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
+        if (attributes) {
+            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
+                const char* name = attributes[i];
+                const char* value = attributes[i+1];
+                size_t nameLen = strlen(name);
+                size_t valueLen = strlen(value);
+                if (MEMEQ("variant", name, nameLen)) {
+                    const FontVariant prevVariant = currentFamily.fVariant;
+                    if (MEMEQ("elegant", value, valueLen)) {
+                        currentFamily.fVariant = kElegant_FontVariant;
+                    } else if (MEMEQ("compact", value, valueLen)) {
+                        currentFamily.fVariant = kCompact_FontVariant;
+                    }
+                    if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant != prevVariant) {
+                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected variant found\n"
+                            "Note: Every font file within a family must have identical variants.",
+                            value);
+                    }
+
+                } else if (MEMEQ("lang", name, nameLen)) {
+                    SkLanguage prevLang = currentFamily.fLanguage;
+                    currentFamily.fLanguage = SkLanguage(value, valueLen);
+                    if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) {
+                        SK_FONTCONFIGPARSER_WARNING("'%s' unexpected language found\n"
+                            "Note: Every font file within a family must have identical languages.",
+                            value);
+                    }
+
+                } else if (MEMEQ("index", name, nameLen)) {
+                    if (!parse_non_negative_integer(value, &newFileInfo.fIndex)) {
+                        SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid index", value);
+                    }
+                }
             }
-            break;
-        default:
-            // Noop - don't care about any text that's not in the Fonts or Names list
-            break;
         }
+        self->fCurrentFontInfo = &newFileInfo;
+    },
+    /*end*/NULL,
+    /*tag*/NULL,
+    /*chars*/[](void* data, const char* s, int len) {
+        FamilyData* self = static_cast<FamilyData*>(data);
+        self->fCurrentFontInfo->fFileName.append(s, len);
     }
-}
+};
 
-/**
- * Handler for font files. This processes the attributes for language and
- * variants then lets textHandler handle the actual file name
- */
-static void font_file_element_handler(FamilyData* self, const char** attributes) {
-    FontFamily& currentFamily = *self->fCurrentFamily.get();
-    FontFileInfo& newFileInfo = currentFamily.fFonts.push_back();
-    if (attributes) {
-        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
-            const char* attributeName = attributes[i];
-            const char* attributeValue = attributes[i+1];
-            size_t nameLength = strlen(attributeName);
-            size_t valueLength = strlen(attributeValue);
-            if (MEMEQ("variant", attributeName, nameLength)) {
-                const FontVariant prevVariant = currentFamily.fVariant;
-                if (MEMEQ("elegant", attributeValue, valueLength)) {
-                    currentFamily.fVariant = kElegant_FontVariant;
-                } else if (MEMEQ("compact", attributeValue, valueLength)) {
-                    currentFamily.fVariant = kCompact_FontVariant;
-                }
-                if (currentFamily.fFonts.count() > 1 && currentFamily.fVariant != prevVariant) {
-                    SkDebugf("Every font file within a family must have identical variants");
-                }
-
-            } else if (MEMEQ("lang", attributeName, nameLength)) {
-                SkLanguage prevLang = currentFamily.fLanguage;
-                currentFamily.fLanguage = SkLanguage(attributeValue, valueLength);
-                if (currentFamily.fFonts.count() > 1 && currentFamily.fLanguage != prevLang) {
-                    SkDebugf("Every font file within a family must have identical languages");
-                }
-
-            } else if (MEMEQ("index", attributeName, nameLength)) {
-                if (!parse_non_negative_integer(attributeValue, &newFileInfo.fIndex)) {
-                    SkDebugf("---- SystemFonts index=%s (INVALID)", attributeValue);
-                }
-            }
+static const TagHandler fileSetHandler = {
+    /*start*/NULL,
+    /*end*/NULL,
+    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
+        size_t len = strlen(tag);
+        if (MEMEQ("file", tag, len)) {
+            return &fileHandler;
         }
-    }
-    self->fCurrentFontInfo = &newFileInfo;
-    XML_SetCharacterDataHandler(self->fParser, text_handler);
-}
+        return NULL;
+    },
+    /*chars*/NULL,
+};
 
-/**
- * Handler for the start of a tag. The only tags we expect are familyset, family,
- * nameset, fileset, name, and file.
- */
-static void XMLCALL start_element_handler(void* data, const char* tag, const char** attributes) {
-    FamilyData* self = static_cast<FamilyData*>(data);
-    size_t len = strlen(tag);
-    if (MEMEQ("familyset", tag, len)) {
-        // The familyset tag has an optional "version" attribute with an integer value >= 0
-        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
-            size_t nameLen = strlen(attributes[i]);
-            if (!MEMEQ("version", attributes[i], nameLen)) continue;
-            const char* valueString = attributes[i+1];
-            int version;
-            if (parse_non_negative_integer(valueString, &version) && (version >= 21)) {
-                XML_SetElementHandler(self->fParser,
-                                      lmpParser::start_element_handler,
-                                      lmpParser::end_element_handler);
-                self->fVersion = version;
-            }
-        }
-    } else if (MEMEQ("family", tag, len)) {
-        self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFallback));
-        // The Family tag has an optional "order" attribute with an integer value >= 0
-        // If this attribute does not exist, the default value is -1
-        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
-            const char* valueString = attributes[i+1];
-            int value;
-            if (parse_non_negative_integer(valueString, &value)) {
-                self->fCurrentFamily->fOrder = value;
-            }
-        }
-    } else if (MEMEQ("nameset", tag, len)) {
-        self->fCurrentTag = kNameSet_CurrentTag;
-    } else if (MEMEQ("fileset", tag, len)) {
-        self->fCurrentTag = kFileSet_CurrentTag;
-    } else if (MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_CurrentTag) {
-        // If it's a Name, parse the text inside
+static const TagHandler nameHandler = {
+    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
+        // The character data should be a name for the font.
         self->fCurrentFamily->fNames.push_back();
-        XML_SetCharacterDataHandler(self->fParser, text_handler);
-    } else if (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_CurrentTag) {
-        // If it's a file, parse the attributes, then parse the text inside
-        font_file_element_handler(self, attributes);
+    },
+    /*end*/NULL,
+    /*tag*/NULL,
+    /*chars*/[](void* data, const char* s, int len) {
+        FamilyData* self = static_cast<FamilyData*>(data);
+        SkAutoAsciiToLC tolc(s, len);
+        self->fCurrentFamily->fNames.back().append(tolc.lc(), len);
     }
-}
+};
 
-/**
- * Handler for the end of tags. We only care about family, nameset, fileset,
- * name, and file.
- */
-static void XMLCALL end_element_handler(void* data, const char* tag) {
-    FamilyData* self = static_cast<FamilyData*>(data);
-    size_t len = strlen(tag);
-    if (MEMEQ("family", tag, len)) {
-        // Done parsing a Family - store the created currentFamily in the families array
+static const TagHandler nameSetHandler = {
+    /*start*/NULL,
+    /*end*/NULL,
+    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
+        size_t len = strlen(tag);
+        if (MEMEQ("name", tag, len)) {
+            return &nameHandler;
+        }
+        return NULL;
+    },
+    /*chars*/NULL,
+};
+
+static const TagHandler familyHandler = {
+    /*start*/[](FamilyData* self, const char* tag, const char** attributes) {
+        self->fCurrentFamily.reset(new FontFamily(self->fBasePath, self->fIsFallback));
+        // 'order' (non-negative integer) [default -1]
+        for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
+            const char* value = attributes[i+1];
+            parse_non_negative_integer(value, &self->fCurrentFamily->fOrder);
+        }
+    },
+    /*end*/[](FamilyData* self, const char* tag) {
         *self->fFamilies.append() = self->fCurrentFamily.detach();
-    } else if (MEMEQ("nameset", tag, len)) {
-        self->fCurrentTag = kNo_CurrentTag;
-    } else if (MEMEQ("fileset", tag, len)) {
-        self->fCurrentTag = kNo_CurrentTag;
-    } else if ((MEMEQ("name", tag, len) && self->fCurrentTag == kNameSet_CurrentTag) ||
-               (MEMEQ("file", tag, len) && self->fCurrentTag == kFileSet_CurrentTag)) {
-        // Disable the arbitrary text handler installed to load Name data
-        XML_SetCharacterDataHandler(self->fParser, NULL);
-    }
-}
+    },
+    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
+        size_t len = strlen(tag);
+        if (MEMEQ("nameset", tag, len)) {
+            return &nameSetHandler;
+        } else if (MEMEQ("fileset", tag, len)) {
+            return &fileSetHandler;
+        }
+        return NULL;
+    },
+    /*chars*/NULL,
+};
+
+static const TagHandler familySetHandler = {
+    /*start*/NULL,
+    /*end*/NULL,
+    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
+        size_t len = strlen(tag);
+        if (MEMEQ("family", tag, len)) {
+            return &familyHandler;
+        }
+        return NULL;
+    },
+    /*chars*/NULL,
+};
 
 } // namespace jbParser
 
+static const TagHandler topLevelHandler = {
+    /*start*/NULL,
+    /*end*/NULL,
+    /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
+        size_t len = strlen(tag);
+        if (MEMEQ("familyset", tag, len)) {
+            // 'version' (non-negative integer) [default 0]
+            for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
+                const char* name = attributes[i];
+                size_t nameLen = strlen(name);
+                if (MEMEQ("version", name, nameLen)) {
+                    const char* value = attributes[i+1];
+                    if (parse_non_negative_integer(value, &self->fVersion)) {
+                        if (self->fVersion >= 21) {
+                            return &lmpParser::familySetHandler;
+                        }
+                    }
+                }
+            }
+            return &jbParser::familySetHandler;
+        }
+        return NULL;
+    },
+    /*chars*/NULL,
+};
+
+static void XMLCALL start_element_handler(void *data, const char *tag, const char **attributes) {
+    FamilyData* self = static_cast<FamilyData*>(data);
+
+    if (!self->fSkip) {
+        const TagHandler* parent = self->fHandler.top();
+        const TagHandler* child = parent->tag ? parent->tag(self, tag, attributes) : NULL;
+        if (child) {
+            if (child->start) {
+                child->start(self, tag, attributes);
+            }
+            self->fHandler.push(child);
+            XML_SetCharacterDataHandler(self->fParser, child->chars);
+        } else {
+            SK_FONTCONFIGPARSER_WARNING("'%s' tag not recognized, skipping", tag);
+            XML_SetCharacterDataHandler(self->fParser, NULL);
+            self->fSkip = self->fDepth;
+        }
+    }
+
+    ++self->fDepth;
+}
+
+static void XMLCALL end_element_handler(void* data, const char* tag) {
+    FamilyData* self = static_cast<FamilyData*>(data);
+    --self->fDepth;
+
+    if (!self->fSkip) {
+        const TagHandler* child = self->fHandler.top();
+        if (child->end) {
+            child->end(self, tag);
+        }
+        self->fHandler.pop();
+        const TagHandler* parent = self->fHandler.top();
+        XML_SetCharacterDataHandler(self->fParser, parent->chars);
+    }
+
+    if (self->fSkip == self->fDepth) {
+        self->fSkip = 0;
+        const TagHandler* parent = self->fHandler.top();
+        XML_SetCharacterDataHandler(self->fParser, parent->chars);
+    }
+}
+
 static void XMLCALL xml_entity_decl_handler(void *data,
                                             const XML_Char *entityName,
                                             int is_parameter_entity,
@@ -401,7 +552,7 @@
                                             const XML_Char *notationName)
 {
     FamilyData* self = static_cast<FamilyData*>(data);
-    SkDebugf("Entity declaration %s found, stopping processing.", entityName);
+    SK_FONTCONFIGPARSER_WARNING("'%s' entity declaration found, stopping processing", entityName);
     XML_StopParser(self->fParser, XML_FALSE);
 }
 
@@ -426,25 +577,25 @@
     // 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.isValid()) {
-        SkDebugf("File %s could not be opened.\n", filename);
+        SkDebugf(SK_FONTCONFIGPARSER_PREFIX "'%s' could not be opened\n", filename);
         return -1;
     }
 
     SkAutoTCallVProc<remove_ptr<XML_Parser>::type, XML_ParserFree> parser(
         XML_ParserCreate_MM(NULL, &sk_XML_alloc, NULL));
     if (!parser) {
-        SkDebugf("Could not create XML parser.\n");
+        SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not create XML parser\n");
         return -1;
     }
 
-    FamilyData self(parser, families, basePath, isFallback);
+    FamilyData self(parser, families, basePath, isFallback, filename, &topLevelHandler);
     XML_SetUserData(parser, &self);
 
     // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340
     XML_SetEntityDeclHandler(parser, xml_entity_decl_handler);
 
     // Start parsing oldschool; switch these in flight if we detect a newer version of the file.
-    XML_SetElementHandler(parser, jbParser::start_element_handler, jbParser::end_element_handler);
+    XML_SetElementHandler(parser, start_element_handler, end_element_handler);
 
     // One would assume it would be faster to have a buffer on the stack and call XML_Parse.
     // But XML_Parse will call XML_GetBuffer anyway and memmove the passed buffer into it.
@@ -455,7 +606,7 @@
     while (!done) {
         void* buffer = XML_GetBuffer(parser, bufferSize);
         if (!buffer) {
-            SkDebugf("Could not buffer enough to continue.\n");
+            SkDebugf(SK_FONTCONFIGPARSER_PREFIX "could not buffer enough to continue\n");
             return -1;
         }
         size_t len = file.read(buffer, bufferSize);
@@ -465,10 +616,9 @@
             XML_Error error = XML_GetErrorCode(parser);
             int line = XML_GetCurrentLineNumber(parser);
             int column = XML_GetCurrentColumnNumber(parser);
-            int index = XML_GetCurrentByteIndex(parser);
             const XML_LChar* errorString = XML_ErrorString(error);
-            SkDebugf("Line: %d Column: %d (Offset: %d) Error %d: %s.\n",
-                          line,    column,      index,    error, errorString);
+            SkDebugf(SK_FONTCONFIGPARSER_PREFIX "%s:%d:%d error %d: %s.\n",
+                     filename, line, column, error, errorString);
             return -1;
         }
     }
diff --git a/src/ports/SkFontConfigParser_android.h b/src/ports/SkFontConfigParser_android.h
index 6dbf383..dd856bf 100644
--- a/src/ports/SkFontConfigParser_android.h
+++ b/src/ports/SkFontConfigParser_android.h
@@ -62,11 +62,12 @@
 
 // Must remain trivially movable (can be memmoved).
 struct FontFileInfo {
-    FontFileInfo() : fIndex(0), fWeight(0) { }
+    FontFileInfo() : fIndex(0), fWeight(0), fStyle(Style::kAuto) { }
 
     SkString fFileName;
     int fIndex;
     int fWeight;
+    enum class Style { kAuto, kNormal, kItalic } fStyle;
 };
 
 /**
diff --git a/src/ports/SkFontConfigTypeface.h b/src/ports/SkFontConfigTypeface.h
index 851bfb7..3672f4e 100644
--- a/src/ports/SkFontConfigTypeface.h
+++ b/src/ports/SkFontConfigTypeface.h
@@ -56,9 +56,9 @@
         // we default to empty fFamilyName and fIdentity
     }
 
-    void onGetFamilyName(SkString* familyName) const SK_OVERRIDE;
-    void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
+    void onGetFamilyName(SkString* familyName) const override;
+    void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override;
 
 private:
     typedef SkTypeface_FreeType INHERITED;
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index f462e1e..5c53b9d 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -193,14 +193,14 @@
     }
 
 protected:
-    unsigned generateGlyphCount() SK_OVERRIDE;
-    uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
-    void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
-    void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
-    void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
-    void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
-    void generateFontMetrics(SkPaint::FontMetrics*) SK_OVERRIDE;
-    SkUnichar generateGlyphToChar(uint16_t glyph) SK_OVERRIDE;
+    unsigned generateGlyphCount() override;
+    uint16_t generateCharToGlyph(SkUnichar uni) override;
+    void generateAdvance(SkGlyph* glyph) override;
+    void generateMetrics(SkGlyph* glyph) override;
+    void generateImage(const SkGlyph& glyph) override;
+    void generatePath(const SkGlyph& glyph, SkPath* path) override;
+    void generateFontMetrics(SkPaint::FontMetrics*) override;
+    SkUnichar generateGlyphToChar(uint16_t glyph) override;
 
 private:
     SkFaceRec*  fFaceRec;
@@ -460,7 +460,7 @@
 }
 
 SkAdvancedTypefaceMetrics* SkTypeface_FreeType::onGetAdvancedTypefaceMetrics(
-        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        PerGlyphInfo perGlyphInfo,
         const uint32_t* glyphIDs,
         uint32_t glyphIDsCount) const {
 #if defined(SK_BUILD_FOR_MAC)
@@ -587,10 +587,10 @@
                                     face->bbox.xMax, face->bbox.yMin);
 
     if (!FT_IS_SCALABLE(face)) {
-        perGlyphInfo = SkAdvancedTypefaceMetrics::kNo_PerGlyphInfo;
+        perGlyphInfo = kNo_PerGlyphInfo;
     }
 
-    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+    if (perGlyphInfo & kHAdvance_PerGlyphInfo) {
         if (FT_IS_FIXED_WIDTH(face)) {
             appendRange(&info->fGlyphWidths, 0);
             int16_t advance = face->max_advance_width;
@@ -624,12 +624,12 @@
         }
     }
 
-    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kVAdvance_PerGlyphInfo &&
+    if (perGlyphInfo & kVAdvance_PerGlyphInfo &&
             FT_HAS_VERTICAL(face)) {
         SkASSERT(false);  // Not implemented yet.
     }
 
-    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo &&
+    if (perGlyphInfo & kGlyphNames_PerGlyphInfo &&
             info->fType == SkAdvancedTypefaceMetrics::kType1_Font) {
         // Postscript fonts may contain more than 255 glyphs, so we end up
         // using multiple font descriptions with a glyph ordering.  Record
@@ -643,7 +643,7 @@
         }
     }
 
-    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo &&
+    if (perGlyphInfo & kToUnicode_PerGlyphInfo &&
            info->fType != SkAdvancedTypefaceMetrics::kType1_Font &&
            face->num_charmaps) {
         populate_glyph_to_unicode(face, &(info->fGlyphToUnicode));
@@ -1198,8 +1198,7 @@
     // This means do not try to scale embedded bitmaps; only scale bitmaps in bitmap only fonts.
     if (!FT_IS_SCALABLE(fFace) && fScaleY && fFace->size->metrics.y_ppem) {
         // NOTE: both dimensions are scaled by y_ppem. this is WAI.
-        scaleGlyphMetrics(*glyph, SkScalarDiv(SkFixedToScalar(fScaleY),
-                                              SkIntToScalar(fFace->size->metrics.y_ppem)));
+        scaleGlyphMetrics(*glyph, SkFixedToScalar(fScaleY) / fFace->size->metrics.y_ppem);
     }
 
 #ifdef ENABLE_GLYPH_SPEW
@@ -1315,9 +1314,20 @@
     SkScalar ascent, descent, leading, xmin, xmax, ymin, ymax;
     SkScalar underlineThickness, underlinePosition;
     if (face->face_flags & FT_FACE_FLAG_SCALABLE) { // scalable outline font
-        ascent = -SkIntToScalar(face->ascender) / upem;
-        descent = -SkIntToScalar(face->descender) / upem;
-        leading = SkIntToScalar(face->height + (face->descender - face->ascender)) / upem;
+        // FreeType will always use HHEA metrics if they're not zero.
+        // It completely ignores the OS/2 fsSelection::UseTypoMetrics bit.
+        // It also ignores the VDMX tables, which are also of interest here
+        // (and override everything else when they apply).
+        static const int kUseTypoMetricsMask = (1 << 7);
+        if (os2 && os2->version != 0xFFFF && (os2->fsSelection & kUseTypoMetricsMask)) {
+            ascent = -SkIntToScalar(os2->sTypoAscender) / upem;
+            descent = -SkIntToScalar(os2->sTypoDescender) / upem;
+            leading = SkIntToScalar(os2->sTypoLineGap) / upem;
+        } else {
+            ascent = -SkIntToScalar(face->ascender) / upem;
+            descent = -SkIntToScalar(face->descender) / upem;
+            leading = SkIntToScalar(face->height + (face->descender - face->ascender)) / upem;
+        }
         xmin = SkIntToScalar(face->bbox.xMin) / upem;
         xmax = SkIntToScalar(face->bbox.xMax) / upem;
         ymin = -SkIntToScalar(face->bbox.yMin) / upem;
@@ -1670,6 +1680,7 @@
             int const weight;
         } commonWeights [] = {
             // There are probably more common names, but these are known to exist.
+            { "all", SkFontStyle::kNormal_Weight }, // Multiple Masters usually default to normal.
             { "black", SkFontStyle::kBlack_Weight },
             { "bold", SkFontStyle::kBold_Weight },
             { "book", (SkFontStyle::kNormal_Weight + SkFontStyle::kLight_Weight)/2 },
diff --git a/src/ports/SkFontHost_FreeType_common.cpp b/src/ports/SkFontHost_FreeType_common.cpp
index a0863c1..5afe70a 100644
--- a/src/ports/SkFontHost_FreeType_common.cpp
+++ b/src/ports/SkFontHost_FreeType_common.cpp
@@ -463,7 +463,7 @@
             canvas.scale(SkIntToScalar(glyph.fWidth) / SkIntToScalar(face->glyph->bitmap.width),
                          SkIntToScalar(glyph.fHeight) / SkIntToScalar(face->glyph->bitmap.rows));
             SkPaint paint;
-            paint.setFilterLevel(SkPaint::kMedium_FilterLevel);
+            paint.setFilterQuality(kMedium_SkFilterQuality);
             canvas.drawBitmap(unscaledBitmap, 0, 0, &paint);
 
             // If the destination is BW or LCD, convert from A8.
diff --git a/src/ports/SkFontHost_FreeType_common.h b/src/ports/SkFontHost_FreeType_common.h
index 57aedd0..857d2c8 100644
--- a/src/ports/SkFontHost_FreeType_common.h
+++ b/src/ports/SkFontHost_FreeType_common.h
@@ -59,23 +59,22 @@
     {}
 
     virtual SkScalerContext* onCreateScalerContext(
-                                        const SkDescriptor*) const SK_OVERRIDE;
-    void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
-    virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                                SkAdvancedTypefaceMetrics::PerGlyphInfo,
-                                const uint32_t*, uint32_t) const SK_OVERRIDE;
-    int onGetUPEM() const SK_OVERRIDE;
+                                        const SkDescriptor*) const override;
+    void onFilterRec(SkScalerContextRec*) const override;
+    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
+                        PerGlyphInfo, const uint32_t*, uint32_t) const override;
+    int onGetUPEM() const override;
     virtual bool onGetKerningPairAdjustments(const uint16_t glyphs[], int count,
-                                       int32_t adjustments[]) const SK_OVERRIDE;
+                                       int32_t adjustments[]) const override;
     virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
-                                int glyphCount) const SK_OVERRIDE;
-    int onCountGlyphs() const SK_OVERRIDE;
+                                int glyphCount) const override;
+    int onCountGlyphs() const override;
 
-    LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE;
+    LocalizedStrings* onCreateFamilyNameIterator() const override;
 
-    int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
+    int onGetTableTags(SkFontTableTag tags[]) const override;
     virtual size_t onGetTableData(SkFontTableTag, size_t offset,
-                                  size_t length, void* data) const SK_OVERRIDE;
+                                  size_t length, void* data) const override;
 
 private:
     mutable int fGlyphCount;
diff --git a/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp
index 72508e9..da2c2d0 100644
--- a/src/ports/SkFontHost_linux.cpp
+++ b/src/ports/SkFontHost_linux.cpp
@@ -21,12 +21,6 @@
 
 #include <limits>
 
-#ifndef SK_FONT_FILE_PREFIX
-#    define SK_FONT_FILE_PREFIX "/usr/share/fonts/"
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
 /** The base SkTypeface implementation for the custom font manager. */
 class SkTypeface_Custom : public SkTypeface_FreeType {
 public:
@@ -38,16 +32,13 @@
 
     bool isSysFont() const { return fIsSysFont; }
 
-    virtual const char* getUniqueString() const = 0;
-
 protected:
-    void onGetFamilyName(SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(SkString* familyName) const override {
         *familyName = fFamilyName;
     }
 
-    void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const SK_OVERRIDE {
+    void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override {
         desc->setFamilyName(fFamilyName.c_str());
-        desc->setFontFileName(this->getUniqueString());
         desc->setFontIndex(fIndex);
         *isLocal = !this->isSysFont();
     }
@@ -69,10 +60,8 @@
 public:
     SkTypeface_Empty() : INHERITED(SkFontStyle(), false, true, SkString(), 0) {}
 
-    const char* getUniqueString() const SK_OVERRIDE { return NULL; }
-
 protected:
-    SkStreamAsset* onOpenStream(int*) const SK_OVERRIDE { return NULL; }
+    SkStreamAsset* onOpenStream(int*) const override { return NULL; }
 
 private:
     typedef SkTypeface_Custom INHERITED;
@@ -87,10 +76,8 @@
         , fStream(stream)
     { }
 
-    const char* getUniqueString() const SK_OVERRIDE { return NULL; }
-
 protected:
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override {
         *ttcIndex = this->getIndex();
         return fStream->duplicate();
     }
@@ -118,16 +105,8 @@
         , fStream(c_CustomTypefaceRetain ? SkStream::NewFromFile(fPath.c_str()) : NULL)
     { }
 
-    const char* getUniqueString() const SK_OVERRIDE {
-        const char* str = strrchr(fPath.c_str(), '/');
-        if (str) {
-            str += 1;   // skip the '/'
-        }
-        return str;
-    }
-
 protected:
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override {
         *ttcIndex = this->getIndex();
         if (fStream.get()) {
             return fStream->duplicate();
@@ -154,11 +133,16 @@
 public:
     explicit SkFontStyleSet_Custom(const SkString familyName) : fFamilyName(familyName) { }
 
-    int count() SK_OVERRIDE {
+    /** Should only be called during the inital build phase. */
+    void appendTypeface(SkTypeface_Custom* typeface) {
+        fStyles.push_back().reset(typeface);
+    }
+
+    int count() override {
         return fStyles.count();
     }
 
-    void getStyle(int index, SkFontStyle* style, SkString* name) SK_OVERRIDE {
+    void getStyle(int index, SkFontStyle* style, SkString* name) override {
         SkASSERT(index < fStyles.count());
         bool bold = fStyles[index]->isBold();
         bool italic = fStyles[index]->isItalic();
@@ -168,7 +152,7 @@
         name->reset();
     }
 
-    SkTypeface* createTypeface(int index) SK_OVERRIDE {
+    SkTypeface* createTypeface(int index) override {
         SkASSERT(index < fStyles.count());
         return SkRef(fStyles[index].get());
     }
@@ -181,7 +165,7 @@
         return score;
     }
 
-    SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
+    SkTypeface* matchStyle(const SkFontStyle& pattern) override {
         if (0 == fStyles.count()) {
             return NULL;
         }
@@ -206,14 +190,12 @@
         return SkRef(closest);
     }
 
+    SkString getFamilyName() { return fFamilyName; }
+
 private:
     SkTArray<SkAutoTUnref<SkTypeface_Custom>, true> fStyles;
     SkString fFamilyName;
 
-    void appendTypeface(SkTypeface_Custom* typeface) {
-        fStyles.push_back().reset(typeface);
-    }
-
     friend class SkFontMgr_Custom;
 };
 
@@ -226,50 +208,80 @@
  */
 class SkFontMgr_Custom : public SkFontMgr {
 public:
-    explicit SkFontMgr_Custom(const char* dir) {
-        this->load_system_fonts(dir);
+    typedef SkTArray<SkAutoTUnref<SkFontStyleSet_Custom>, true> Families;
+    class SystemFontLoader {
+    public:
+        virtual ~SystemFontLoader() { }
+        virtual void loadSystemFonts(const SkTypeface_FreeType::Scanner&, Families*) const = 0;
+    };
+    explicit SkFontMgr_Custom(const SystemFontLoader& loader) {
+        loader.loadSystemFonts(fScanner, &fFamilies);
+
+        // Try to pick a default font.
+        static const char* defaultNames[] = {
+            "Arial", "Verdana", "Times New Roman", "Droid Sans", NULL
+        };
+        for (size_t i = 0; i < SK_ARRAY_COUNT(defaultNames); ++i) {
+            SkFontStyleSet_Custom* set = this->onMatchFamily(defaultNames[i]);
+            if (NULL == set) {
+                continue;
+            }
+
+            SkTypeface* tf = set->matchStyle(SkFontStyle(SkFontStyle::kNormal_Weight,
+                                                         SkFontStyle::kNormal_Width,
+                                                         SkFontStyle::kUpright_Slant));
+            if (NULL == tf) {
+                continue;
+            }
+
+            fDefaultFamily = set;
+            break;
+        }
+        if (NULL == fDefaultFamily) {
+            fDefaultFamily = fFamilies[0];
+        }
     }
 
 protected:
-    int onCountFamilies() const SK_OVERRIDE {
+    int onCountFamilies() const override {
         return fFamilies.count();
     }
 
-    void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(int index, SkString* familyName) const override {
         SkASSERT(index < fFamilies.count());
-        familyName->set(fFamilies[index]->fFamilyName);
+        familyName->set(fFamilies[index]->getFamilyName());
     }
 
-    SkFontStyleSet_Custom* onCreateStyleSet(int index) const SK_OVERRIDE {
+    SkFontStyleSet_Custom* onCreateStyleSet(int index) const override {
         SkASSERT(index < fFamilies.count());
         return SkRef(fFamilies[index].get());
     }
 
-    SkFontStyleSet_Custom* onMatchFamily(const char familyName[]) const SK_OVERRIDE {
+    SkFontStyleSet_Custom* onMatchFamily(const char familyName[]) const override {
         for (int i = 0; i < fFamilies.count(); ++i) {
-            if (fFamilies[i]->fFamilyName.equals(familyName)) {
+            if (fFamilies[i]->getFamilyName().equals(familyName)) {
                 return SkRef(fFamilies[i].get());
             }
         }
         return NULL;
     }
 
-    virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
-                                           const SkFontStyle& fontStyle) const SK_OVERRIDE
+    SkTypeface* onMatchFamilyStyle(const char familyName[],
+                                   const SkFontStyle& fontStyle) const override
     {
         SkAutoTUnref<SkFontStyleSet> sset(this->matchFamily(familyName));
         return sset->matchStyle(fontStyle);
     }
 
-    virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
-                                                    const char* bcp47[], int bcp47Count,
-                                                    SkUnichar character) const SK_OVERRIDE
+    SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
+                                            const char* bcp47[], int bcp47Count,
+                                            SkUnichar character) const override
     {
         return NULL;
     }
 
-    virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
-                                         const SkFontStyle& fontStyle) const SK_OVERRIDE
+    SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
+                                 const SkFontStyle& fontStyle) const override
     {
         for (int i = 0; i < fFamilies.count(); ++i) {
             for (int j = 0; j < fFamilies[i]->fStyles.count(); ++j) {
@@ -281,11 +293,11 @@
         return NULL;
     }
 
-    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
         return this->createFromStream(new SkMemoryStream(data), ttcIndex);
     }
 
-    SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override {
         SkAutoTDelete<SkStreamAsset> stream(bareStream);
         if (NULL == stream || stream->getLength() <= 0) {
             return NULL;
@@ -302,14 +314,12 @@
         }
     }
 
-    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
         SkAutoTDelete<SkStreamAsset> stream(SkStream::NewFromFile(path));
         return stream.get() ? this->createFromStream(stream.detach(), ttcIndex) : NULL;
     }
 
-    virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
-                                               unsigned styleBits) const SK_OVERRIDE
-    {
+    SkTypeface* onLegacyCreateTypeface(const char familyName[], unsigned styleBits) const override {
         SkTypeface::Style oldStyle = (SkTypeface::Style)styleBits;
         SkFontStyle style = SkFontStyle(oldStyle & SkTypeface::kBold
                                                  ? SkFontStyle::kBold_Weight
@@ -325,15 +335,55 @@
         }
 
         if (NULL == tf) {
-            tf = gDefaultFamily->matchStyle(style);
+            tf = fDefaultFamily->matchStyle(style);
         }
 
         return SkSafeRef(tf);
     }
 
 private:
+    Families fFamilies;
+    SkFontStyleSet_Custom* fDefaultFamily;
+    SkTypeface_FreeType::Scanner fScanner;
+};
 
-    void load_directory_fonts(const SkString& directory, const char* suffix) {
+///////////////////////////////////////////////////////////////////////////////
+
+class DirectorySystemFontLoader : public SkFontMgr_Custom::SystemFontLoader {
+public:
+    DirectorySystemFontLoader(const char* dir) : fBaseDirectory(dir) { }
+
+    void loadSystemFonts(const SkTypeface_FreeType::Scanner& scanner,
+                         SkFontMgr_Custom::Families* families) const override
+    {
+        load_directory_fonts(scanner, fBaseDirectory, ".ttf", families);
+        load_directory_fonts(scanner, fBaseDirectory, ".ttc", families);
+        load_directory_fonts(scanner, fBaseDirectory, ".otf", families);
+        load_directory_fonts(scanner, fBaseDirectory, ".pfb", families);
+
+        if (families->empty()) {
+            SkFontStyleSet_Custom* family = new SkFontStyleSet_Custom(SkString());
+            families->push_back().reset(family);
+            family->appendTypeface(SkNEW(SkTypeface_Empty));
+        }
+    }
+
+private:
+    static SkFontStyleSet_Custom* find_family(SkFontMgr_Custom::Families& families,
+                                              const char familyName[])
+    {
+       for (int i = 0; i < families.count(); ++i) {
+            if (families[i]->getFamilyName().equals(familyName)) {
+                return families[i].get();
+            }
+        }
+        return NULL;
+    }
+
+    static void load_directory_fonts(const SkTypeface_FreeType::Scanner& scanner,
+                                     const SkString& directory, const char* suffix,
+                                     SkFontMgr_Custom::Families* families)
+    {
         SkOSFile::Iter iter(directory.c_str(), suffix);
         SkString name;
 
@@ -346,7 +396,7 @@
             }
 
             int numFaces;
-            if (!fScanner.recognizedFont(stream, &numFaces)) {
+            if (!scanner.recognizedFont(stream, &numFaces)) {
                 SkDebugf("---- failed to open <%s> as a font\n", filename.c_str());
                 continue;
             }
@@ -355,7 +405,7 @@
                 bool isFixedPitch;
                 SkString realname;
                 SkFontStyle style = SkFontStyle(); // avoid uninitialized warning
-                if (!fScanner.scanFont(stream, faceIndex, &realname, &style, &isFixedPitch)) {
+                if (!scanner.scanFont(stream, faceIndex, &realname, &style, &isFixedPitch)) {
                     SkDebugf("---- failed to open <%s> <%d> as a font\n",
                              filename.c_str(), faceIndex);
                     continue;
@@ -366,12 +416,13 @@
                                                     isFixedPitch,
                                                     true,  // system-font (cannot delete)
                                                     realname,
-                                                    filename.c_str(), 0));
+                                                    filename.c_str(),
+                                                    faceIndex));
 
-                SkFontStyleSet_Custom* addTo = this->onMatchFamily(realname.c_str());
+                SkFontStyleSet_Custom* addTo = find_family(*families, realname.c_str());
                 if (NULL == addTo) {
                     addTo = new SkFontStyleSet_Custom(realname);
-                    fFamilies.push_back().reset(addTo);
+                    families->push_back().reset(addTo);
                 }
                 addTo->appendTypeface(tf);
             }
@@ -383,56 +434,102 @@
                 continue;
             }
             SkString dirname(SkOSPath::Join(directory.c_str(), name.c_str()));
-            load_directory_fonts(dirname, suffix);
+            load_directory_fonts(scanner, dirname, suffix, families);
         }
     }
 
-    void load_system_fonts(const char* dir) {
-        SkString baseDirectory(dir);
-        load_directory_fonts(baseDirectory, ".ttf");
-        load_directory_fonts(baseDirectory, ".ttc");
-        load_directory_fonts(baseDirectory, ".otf");
-        load_directory_fonts(baseDirectory, ".pfb");
-
-        if (fFamilies.empty()) {
-            SkFontStyleSet_Custom* family = new SkFontStyleSet_Custom(SkString());
-            fFamilies.push_back().reset(family);
-            family->appendTypeface(SkNEW(SkTypeface_Empty));
-        }
-
-        // Try to pick a default font.
-        static const char* gDefaultNames[] = {
-            "Arial", "Verdana", "Times New Roman", "Droid Sans", NULL
-        };
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); ++i) {
-            SkFontStyleSet_Custom* set = this->onMatchFamily(gDefaultNames[i]);
-            if (NULL == set) {
-                continue;
-            }
-
-            SkTypeface* tf = set->matchStyle(SkFontStyle(SkFontStyle::kNormal_Weight,
-                                                         SkFontStyle::kNormal_Width,
-                                                         SkFontStyle::kUpright_Slant));
-            if (NULL == tf) {
-                continue;
-            }
-
-            gDefaultFamily = set;
-            gDefaultNormal = tf;
-            break;
-        }
-        if (NULL == gDefaultNormal) {
-            gDefaultFamily = fFamilies[0];
-            gDefaultNormal = gDefaultFamily->fStyles[0];
-        }
-    }
-
-    SkTArray<SkAutoTUnref<SkFontStyleSet_Custom>, true> fFamilies;
-    SkFontStyleSet_Custom* gDefaultFamily;
-    SkTypeface* gDefaultNormal;
-    SkTypeface_FreeType::Scanner fScanner;
+    SkString fBaseDirectory;
 };
 
+struct SkEmbeddedResource { const uint8_t* data; size_t size; };
+struct SkEmbeddedResourceHeader { const SkEmbeddedResource* entries; int count; };
+
+class EmbeddedSystemFontLoader : public SkFontMgr_Custom::SystemFontLoader {
+public:
+    EmbeddedSystemFontLoader(const SkEmbeddedResourceHeader* header) : fHeader(header) { }
+
+    void loadSystemFonts(const SkTypeface_FreeType::Scanner& scanner,
+                         SkFontMgr_Custom::Families* families) const override
+    {
+        for (int i = 0; i < fHeader->count; ++i) {
+            const SkEmbeddedResource& fontEntry = fHeader->entries[i];
+            load_embedded_font(scanner, fontEntry.data, fontEntry.size, i, families);
+        }
+
+        if (families->empty()) {
+            SkFontStyleSet_Custom* family = new SkFontStyleSet_Custom(SkString());
+            families->push_back().reset(family);
+            family->appendTypeface(SkNEW(SkTypeface_Empty));
+        }
+    }
+
+private:
+    static SkFontStyleSet_Custom* find_family(SkFontMgr_Custom::Families& families,
+                                              const char familyName[])
+    {
+       for (int i = 0; i < families.count(); ++i) {
+            if (families[i]->getFamilyName().equals(familyName)) {
+                return families[i].get();
+            }
+        }
+        return NULL;
+    }
+
+    static void load_embedded_font(const SkTypeface_FreeType::Scanner& scanner,
+                                   const uint8_t* data, size_t size, int index,
+                                   SkFontMgr_Custom::Families* families)
+    {
+        SkAutoTDelete<SkMemoryStream> stream(new SkMemoryStream(data, size, false));
+
+        int numFaces;
+        if (!scanner.recognizedFont(stream, &numFaces)) {
+            SkDebugf("---- failed to open <%d> as a font\n", index);
+            return;
+        }
+
+        for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
+            bool isFixedPitch;
+            SkString realname;
+            SkFontStyle style = SkFontStyle(); // avoid uninitialized warning
+            if (!scanner.scanFont(stream, faceIndex, &realname, &style, &isFixedPitch)) {
+                SkDebugf("---- failed to open <%d> <%d> as a font\n", index, faceIndex);
+                return;
+            }
+
+            SkTypeface_Custom* tf = SkNEW_ARGS(SkTypeface_Stream, (
+                                                style,
+                                                isFixedPitch,
+                                                true,  // system-font (cannot delete)
+                                                realname,
+                                                stream.detach(),
+                                                faceIndex));
+
+            SkFontStyleSet_Custom* addTo = find_family(*families, realname.c_str());
+            if (NULL == addTo) {
+                addTo = new SkFontStyleSet_Custom(realname);
+                families->push_back().reset(addTo);
+            }
+            addTo->appendTypeface(tf);
+        }
+    }
+
+    const SkEmbeddedResourceHeader* fHeader;
+};
+
+#ifdef SK_EMBEDDED_FONTS
+
+extern "C" const SkEmbeddedResourceHeader SK_EMBEDDED_FONTS;
 SkFontMgr* SkFontMgr::Factory() {
-    return new SkFontMgr_Custom(SK_FONT_FILE_PREFIX);
+    return new SkFontMgr_Custom(EmbeddedSystemFontLoader(&SK_EMBEDDED_FONTS));
 }
+
+#else
+
+#ifndef SK_FONT_FILE_PREFIX
+#    define SK_FONT_FILE_PREFIX "/usr/share/fonts/"
+#endif
+SkFontMgr* SkFontMgr::Factory() {
+    return new SkFontMgr_Custom(DirectorySystemFontLoader(SK_FONT_FILE_PREFIX));
+}
+
+#endif
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index 99766ed..3c7be46 100755
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -19,6 +19,7 @@
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 
+#include "SkAdvancedTypefaceMetrics.h"
 #include "SkCGUtils.h"
 #include "SkColorPriv.h"
 #include "SkDescriptor.h"
@@ -92,6 +93,12 @@
         }
     }
 
+    CFRef detach() {
+        CFRef self = fCFRef;
+        fCFRef = NULL;
+        return self;
+    }
+
     operator CFRef() const { return fCFRef; }
     CFRef get() const { return fCFRef; }
 
@@ -408,34 +415,6 @@
     return (SkTypeface::Style)style;
 }
 
-static SkFontID CTFontRef_to_SkFontID(CTFontRef fontRef) {
-    SkFontID id = 0;
-// CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to
-// bracket this to be Mac only.
-#ifdef SK_BUILD_FOR_MAC
-    ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
-    id = (SkFontID)ats;
-    if (id != 0) {
-        id &= 0x3FFFFFFF; // make top two bits 00
-        return id;
-    }
-#endif
-    // CTFontGetPlatformFont returns NULL if the font is local
-    // (e.g., was created by a CSS3 @font-face rule).
-    AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fontRef, NULL));
-    AutoCGTable<SkOTTableHead> headTable(cgFont);
-    if (headTable.fData) {
-        id = (SkFontID) headTable->checksumAdjustment;
-        id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01
-    }
-    // well-formed fonts have checksums, but as a last resort, use the pointer.
-    if (id == 0) {
-        id = (SkFontID) (uintptr_t) fontRef;
-        id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
-    }
-    return id;
-}
-
 #define WEIGHT_THRESHOLD    ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2)
 
 // kCTFontColorGlyphsTrait was added in the Mac 10.7 and iPhone 4.3 SDKs.
@@ -448,57 +427,63 @@
 
 class SkTypeface_Mac : public SkTypeface {
 public:
-    SkTypeface_Mac(const SkFontStyle& fs, SkFontID fontID, bool isFixedPitch,
+    SkTypeface_Mac(const SkFontStyle& fs, bool isFixedPitch,
                    CTFontRef fontRef, const char requestedName[], bool isLocalStream)
-        : SkTypeface(fs, fontID, isFixedPitch)
+        : SkTypeface(fs, SkTypefaceCache::NewFontID(), isFixedPitch)
         , fRequestedName(requestedName)
         , fFontRef(fontRef) // caller has already called CFRetain for us
+        , fHasColorGlyphs(SkToBool(CTFontGetSymbolicTraits(fFontRef) & SkCTFontColorGlyphsTrait))
         , fIsLocalStream(isLocalStream)
-        , fHasColorGlyphs(CTFontGetSymbolicTraits(fFontRef) & SkCTFontColorGlyphsTrait)
     {
         SkASSERT(fontRef);
     }
 
     SkString fRequestedName;
     AutoCFRelease<CTFontRef> fFontRef;
+    const bool fHasColorGlyphs;
 
 protected:
-    int onGetUPEM() const SK_OVERRIDE;
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
-    void onGetFamilyName(SkString* familyName) const SK_OVERRIDE;
-    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE;
-    int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
+    int onGetUPEM() const override;
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override;
+    void onGetFamilyName(SkString* familyName) const override;
+    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
+    int onGetTableTags(SkFontTableTag tags[]) const override;
     virtual size_t onGetTableData(SkFontTableTag, size_t offset,
-                                  size_t length, void* data) const SK_OVERRIDE;
-    SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
-    void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
-    void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
+                                  size_t length, void* data) const override;
+    SkScalerContext* onCreateScalerContext(const SkDescriptor*) const override;
+    void onFilterRec(SkScalerContextRec*) const override;
+    void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
     virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                                SkAdvancedTypefaceMetrics::PerGlyphInfo,
-                                const uint32_t*, uint32_t) const SK_OVERRIDE;
+                                PerGlyphInfo,
+                                const uint32_t*, uint32_t) const override;
     virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
-                                int glyphCount) const SK_OVERRIDE;
-    int onCountGlyphs() const SK_OVERRIDE;
+                                int glyphCount) const override;
+    int onCountGlyphs() const override;
 
 private:
     bool fIsLocalStream;
-    bool fHasColorGlyphs;
 
     typedef SkTypeface INHERITED;
 };
 
+/** Creates a typeface without searching the cache. Takes ownership of the CTFontRef. */
 static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[], bool isLocalStream) {
     SkASSERT(fontRef);
     bool isFixedPitch;
     SkFontStyle style = SkFontStyle(computeStyleBits(fontRef, &isFixedPitch));
-    SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
 
-    return new SkTypeface_Mac(style, fontID, isFixedPitch, fontRef, name, isLocalStream);
+    return new SkTypeface_Mac(style, isFixedPitch, fontRef, name, isLocalStream);
 }
 
-static SkTypeface* NewFromName(const char familyName[], const SkFontStyle& theStyle) {
-    CTFontRef ctFont = NULL;
+static bool find_by_CTFontRef(SkTypeface* cached, const SkFontStyle&, void* context) {
+    CTFontRef self = (CTFontRef)context;
+    CTFontRef other = ((SkTypeface_Mac*)cached)->fFontRef;
 
+    return CFEqual(self, other);
+}
+
+/** Creates a typeface from a name, searching the cache. */
+static SkTypeface* NewFromName(const char familyName[], const SkFontStyle& theStyle) {
     CTFontSymbolicTraits ctFontTraits = 0;
     if (theStyle.weight() >= SkFontStyle::kBold_Weight) {
         ctFontTraits |= kCTFontBoldTrait;
@@ -525,22 +510,32 @@
                                       &kCFTypeDictionaryKeyCallBacks,
                                       &kCFTypeDictionaryValueCallBacks));
 
-    // Create the font
-    if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
-        CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
-
-        CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
-        CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
-
-        AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
-                CTFontDescriptorCreateWithAttributes(cfAttributes));
-
-        if (ctFontDesc != NULL) {
-            ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
-        }
+    if (!cfFontName || !cfFontTraits || !cfAttributes || !cfTraits) {
+        return NULL;
     }
 
-    return ctFont ? NewFromFontRef(ctFont, familyName, false) : NULL;
+    CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
+
+    CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
+    CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
+
+    AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
+            CTFontDescriptorCreateWithAttributes(cfAttributes));
+    if (!ctFontDesc) {
+        return NULL;
+    }
+
+    AutoCFRelease<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL));
+    if (!ctFont) {
+        return NULL;
+    }
+
+    SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)ctFont.get());
+    if (!face) {
+        face = NewFromFontRef(ctFont.detach(), NULL, false);
+        SkTypefaceCache::Add(face, face->fontStyle());
+    }
+    return face;
 }
 
 SK_DECLARE_STATIC_MUTEX(gGetDefaultFaceMutex);
@@ -568,19 +563,12 @@
  *  not found, returns a new entry (after adding it to the cache).
  */
 SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
-    SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
-    SkTypeface* face = SkTypefaceCache::FindByID(fontID);
-    if (face) {
-        face->ref();
-    } else {
+    SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)fontRef);
+    if (!face) {
+        CFRetain(fontRef);
         face = NewFromFontRef(fontRef, NULL, false);
         SkTypefaceCache::Add(face, face->fontStyle());
-        // NewFromFontRef doesn't retain the parameter, but the typeface it
-        // creates does release it in its destructor, so we balance that with
-        // a retain call here.
-        CFRetain(fontRef);
     }
-    SkASSERT(face->getRefCnt() > 1);
     return face;
 }
 
@@ -630,13 +618,13 @@
     SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
 
 protected:
-    unsigned generateGlyphCount(void) SK_OVERRIDE;
-    uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
-    void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
-    void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
-    void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
-    void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
-    void generateFontMetrics(SkPaint::FontMetrics*) SK_OVERRIDE;
+    unsigned generateGlyphCount(void) override;
+    uint16_t generateCharToGlyph(SkUnichar uni) override;
+    void generateAdvance(SkGlyph* glyph) override;
+    void generateMetrics(SkGlyph* glyph) override;
+    void generateImage(const SkGlyph& glyph) override;
+    void generatePath(const SkGlyph& glyph, SkPath* path) override;
+    void generateFontMetrics(SkPaint::FontMetrics*) override;
 
 private:
     static void CTPathElement(void *info, const CGPathElement *element);
@@ -907,6 +895,21 @@
     // So always make the font transform identity and place the transform on the context.
     point = CGPointApplyAffineTransform(point, context.fInvTransform);
 
+    // Attempt to keep on the stack a hard reference to the font tables.
+    // This is an experiment to see if this affects crbug.com/413332 .
+    // When 10.6 headers are no longer supported, 'sbix' can be replaced with kCTFontTableSbix.
+    AutoCFRelease<CFDataRef> sbix;
+    if (static_cast<SkTypeface_Mac*>(context.getTypeface())->fHasColorGlyphs) {
+        sbix.reset(CGFontCopyTableForTag(context.fCGFont, 'sbix'));
+        // Attempt to read from the sbix table data to determine if the returned data is valid.
+        const UInt8* sbixData = CFDataGetBytePtr(sbix);
+        CFIndex sbixLength = CFDataGetLength(sbix);
+        if (sbixLength > 0 && *sbixData > 0x80) {
+            // We need to actually do something to avoid this being optimized away.
+            CFRetain(sbix);
+            CFRelease(sbix);
+        }
+    }
     ctFontDrawGlyphs(context.fCTUnrotatedFont, &glyphID, &point, 1, fCG);
 
     SkASSERT(rowBytesPtr);
@@ -1541,7 +1544,7 @@
 }
 
 SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
-        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        PerGlyphInfo perGlyphInfo,
         const uint32_t* glyphIDs,
         uint32_t glyphIDsCount) const {
 
@@ -1563,7 +1566,7 @@
     info->fFlags = SkAdvancedTypefaceMetrics::kEmpty_FontFlag;
     info->fStyle = 0;
 
-    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
+    if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
         populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
     }
 
@@ -1630,7 +1633,7 @@
         }
     }
 
-    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+    if (perGlyphInfo & kHAdvance_PerGlyphInfo) {
         if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
             skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
             info->fGlyphWidths->fAdvance.append(1, &min_width);
@@ -2068,24 +2071,16 @@
         return face;
     }
 
-    AutoCFRelease<CFDictionaryRef> fontFamilyNameDictionary(
-        CFDictionaryCreate(kCFAllocatorDefault,
-                           (const void**)&kCTFontFamilyNameAttribute, (const void**)&cfFamilyName,
-                           1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
-    AutoCFRelease<CTFontDescriptorRef> fontDescriptor(
-        CTFontDescriptorCreateWithAttributes(fontFamilyNameDictionary));
-    AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithFontDescriptor(fontDescriptor, 0, NULL));
-    CTFontRef ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, desc);
-    if (NULL == ctFont) {
+    AutoCFRelease<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, NULL));
+    if (!ctFont) {
         return NULL;
     }
 
     bool isFixedPitch;
     (void)computeStyleBits(ctFont, &isFixedPitch);
-    SkFontID fontID = CTFontRef_to_SkFontID(ctFont);
 
-    face = SkNEW_ARGS(SkTypeface_Mac, (cacheRequest.fStyle, fontID, isFixedPitch,
-                                       ctFont, skFamilyName.c_str(), false));
+    face = SkNEW_ARGS(SkTypeface_Mac, (cacheRequest.fStyle, isFixedPitch,
+                                       ctFont.detach(), skFamilyName.c_str(), false));
     SkTypefaceCache::Add(face, face->fontStyle());
     return face;
 }
@@ -2108,11 +2103,11 @@
         CFRelease(fFamilyName);
     }
 
-    int count() SK_OVERRIDE {
+    int count() override {
         return fCount;
     }
 
-    void getStyle(int index, SkFontStyle* style, SkString* name) SK_OVERRIDE {
+    void getStyle(int index, SkFontStyle* style, SkString* name) override {
         SkASSERT((unsigned)index < (unsigned)fCount);
         CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
         if (style) {
@@ -2125,14 +2120,14 @@
         }
     }
 
-    SkTypeface* createTypeface(int index) SK_OVERRIDE {
+    SkTypeface* createTypeface(int index) override {
         SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
         CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
 
         return createFromDesc(fFamilyName, desc);
     }
 
-    SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
+    SkTypeface* matchStyle(const SkFontStyle& pattern) override {
         if (0 == fCount) {
             return NULL;
         }
@@ -2196,11 +2191,11 @@
     }
 
 protected:
-    int onCountFamilies() const SK_OVERRIDE {
+    int onCountFamilies() const override {
         return fCount;
     }
 
-    void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(int index, SkString* familyName) const override {
         if ((unsigned)index < (unsigned)fCount) {
             CFStringToSkString(this->stringAt(index), familyName);
         } else {
@@ -2208,35 +2203,35 @@
         }
     }
 
-    SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
+    SkFontStyleSet* onCreateStyleSet(int index) const override {
         if ((unsigned)index >= (unsigned)fCount) {
             return NULL;
         }
         return CreateSet(this->stringAt(index));
     }
 
-    SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE {
+    SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
         AutoCFRelease<CFStringRef> cfName(make_CFString(familyName));
         return CreateSet(cfName);
     }
 
     virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
-                                           const SkFontStyle&) const SK_OVERRIDE {
+                                           const SkFontStyle&) const override {
         return NULL;
     }
 
     virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
                                                     const char* bcp47[], int bcp47Count,
-                                                    SkUnichar character) const SK_OVERRIDE {
+                                                    SkUnichar character) const override {
         return NULL;
     }
 
     virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
-                                         const SkFontStyle&) const SK_OVERRIDE {
+                                         const SkFontStyle&) const override {
         return NULL;
     }
 
-    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
         AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
         if (NULL == pr) {
             return NULL;
@@ -2244,7 +2239,7 @@
         return create_from_dataProvider(pr);
     }
 
-    SkTypeface* onCreateFromStream(SkStreamAsset* stream, int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromStream(SkStreamAsset* stream, int ttcIndex) const override {
         AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream));
         if (NULL == pr) {
             return NULL;
@@ -2252,7 +2247,7 @@
         return create_from_dataProvider(pr);
     }
 
-    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
         AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
         if (NULL == pr) {
             return NULL;
@@ -2261,7 +2256,7 @@
     }
 
     virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
-                                               unsigned styleBits) const SK_OVERRIDE {
+                                               unsigned styleBits) const override {
 
         SkFontStyle style = SkFontStyle((SkTypeface::Style)styleBits);
         if (familyName) {
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index 67927fd..bcbbe11 100755
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -254,22 +253,21 @@
     }
 
 protected:
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
-    SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
-    void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
-    virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                                SkAdvancedTypefaceMetrics::PerGlyphInfo,
-                                const uint32_t*, uint32_t) const SK_OVERRIDE;
-    void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override;
+    SkScalerContext* onCreateScalerContext(const SkDescriptor*) const override;
+    void onFilterRec(SkScalerContextRec*) const override;
+    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
+                                PerGlyphInfo, const uint32_t*, uint32_t) const override;
+    void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
     virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
-                                uint16_t glyphs[], int glyphCount) const SK_OVERRIDE;
-    int onCountGlyphs() const SK_OVERRIDE;
-    int onGetUPEM() const SK_OVERRIDE;
-    void onGetFamilyName(SkString* familyName) const SK_OVERRIDE;
-    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE;
-    int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
+                                uint16_t glyphs[], int glyphCount) const override;
+    int onCountGlyphs() const override;
+    int onGetUPEM() const override;
+    void onGetFamilyName(SkString* familyName) const override;
+    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
+    int onGetTableTags(SkFontTableTag tags[]) const override;
     virtual size_t onGetTableData(SkFontTableTag, size_t offset,
-                                  size_t length, void* data) const SK_OVERRIDE;
+                                  size_t length, void* data) const override;
 };
 
 class FontMemResourceTypeface : public LogFontTypeface {
@@ -282,7 +280,7 @@
     }
 
 protected:
-    void weak_dispose() const SK_OVERRIDE {
+    void weak_dispose() const override {
         RemoveFontMemResourceEx(fFontMemResource);
         //SkTypefaceCache::Remove(this);
         INHERITED::weak_dispose();
@@ -540,13 +538,13 @@
     bool isValid() const;
 
 protected:
-    unsigned generateGlyphCount() SK_OVERRIDE;
-    uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
-    void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
-    void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
-    void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
-    void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
-    void generateFontMetrics(SkPaint::FontMetrics*) SK_OVERRIDE;
+    unsigned generateGlyphCount() override;
+    uint16_t generateCharToGlyph(SkUnichar uni) override;
+    void generateAdvance(SkGlyph* glyph) override;
+    void generateMetrics(SkGlyph* glyph) override;
+    void generateImage(const SkGlyph& glyph) override;
+    void generatePath(const SkGlyph& glyph, SkPath* path) override;
+    void generateFontMetrics(SkPaint::FontMetrics*) override;
 
 private:
     DWORD getGDIGlyphPath(const SkGlyph& glyph, UINT flags,
@@ -616,8 +614,8 @@
     SetGraphicsMode(fDDC, GM_ADVANCED);
     SetBkMode(fDDC, TRANSPARENT);
 
-    // When GDI hinting, remove the entire Y scale to prevent 'subpixel' metrics.
-    // When not hinting, remove only the gdiTextSize scale which will be applied by GDI.
+    // When GDI hinting, remove the entire Y scale from sA and GsA. (Prevents 'linear' metrics.)
+    // When not hinting, remove only the integer Y scale from sA and GsA. (Applied by GDI.)
     SkScalerContextRec::PreMatrixScale scaleConstraints =
         (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting)
                    ? SkScalerContextRec::kVerticalInteger_PreMatrixScale
@@ -633,7 +631,19 @@
     fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX));
     fGsA.eM22 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleY));
 
-    SkScalar gdiTextSize = scale.fY;
+    // When not hinting, scale was computed with kVerticalInteger, so is already an integer.
+    // The sA and GsA transforms will be used to create 'linear' metrics.
+
+    // When hinting, scale was computed with kVertical, stating that our port can handle
+    // non-integer scales. This is done so that sA and GsA are computed without any 'residual'
+    // scale in them, preventing 'linear' metrics. However, GDI cannot actually handle non-integer
+    // scales so we need to round in this case. This is fine, since all of the scale has been
+    // removed from sA and GsA, so GDI will be handling the scale completely.
+    SkScalar gdiTextSize = SkScalarRoundToScalar(scale.fY);
+
+    // GDI will not accept a size of zero, so round the range [0, 1] to 1.
+    // If the size was non-zero, the scale factors will also be non-zero and 1px tall text is drawn.
+    // If the size actually was zero, the scale factors will also be zero, so GDI will draw nothing.
     if (gdiTextSize == 0) {
         gdiTextSize = SK_Scalar1;
     }
@@ -1721,7 +1731,7 @@
 }
 
 SkAdvancedTypefaceMetrics* LogFontTypeface::onGetAdvancedTypefaceMetrics(
-        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        PerGlyphInfo perGlyphInfo,
         const uint32_t* glyphIDs,
         uint32_t glyphIDsCount) const {
     LOGFONT lf = fLogFont;
@@ -1770,7 +1780,7 @@
                 SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag);
     }
 
-    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
+    if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
         populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
     }
 
@@ -1829,7 +1839,7 @@
         }
     }
 
-    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+    if (perGlyphInfo & kHAdvance_PerGlyphInfo) {
         if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
             appendRange(&info->fGlyphWidths, 0);
             info->fGlyphWidths->fAdvance.append(1, &min_width);
@@ -2389,11 +2399,11 @@
         ::DeleteDC(hdc);
     }
 
-    int count() SK_OVERRIDE {
+    int count() override {
         return fArray.count();
     }
 
-    void getStyle(int index, SkFontStyle* fs, SkString* styleName) SK_OVERRIDE {
+    void getStyle(int index, SkFontStyle* fs, SkString* styleName) override {
         if (fs) {
             *fs = get_style(fArray[index].elfLogFont);
         }
@@ -2410,11 +2420,11 @@
         }
     }
 
-    SkTypeface* createTypeface(int index) SK_OVERRIDE {
+    SkTypeface* createTypeface(int index) override {
         return SkCreateTypefaceFromLOGFONT(fArray[index].elfLogFont);
     }
 
-    SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
+    SkTypeface* matchStyle(const SkFontStyle& pattern) override {
         // todo:
         return SkCreateTypefaceFromLOGFONT(fArray[0].elfLogFont);
     }
@@ -2436,21 +2446,21 @@
     }
 
 protected:
-    int onCountFamilies() const SK_OVERRIDE {
+    int onCountFamilies() const override {
         return fLogFontArray.count();
     }
 
-    void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(int index, SkString* familyName) const override {
         SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
         tchar_to_skstring(fLogFontArray[index].elfLogFont.lfFaceName, familyName);
     }
 
-    SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
+    SkFontStyleSet* onCreateStyleSet(int index) const override {
         SkASSERT((unsigned)index < (unsigned)fLogFontArray.count());
         return SkNEW_ARGS(SkFontStyleSetGDI, (fLogFontArray[index].elfLogFont.lfFaceName));
     }
 
-    SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE {
+    SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
         if (NULL == familyName) {
             familyName = "";    // do we need this check???
         }
@@ -2460,7 +2470,7 @@
     }
 
     virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
-                                           const SkFontStyle& fontstyle) const SK_OVERRIDE {
+                                           const SkFontStyle& fontstyle) const override {
         // could be in base impl
         SkAutoTUnref<SkFontStyleSet> sset(this->matchFamily(familyName));
         return sset->matchStyle(fontstyle);
@@ -2468,35 +2478,35 @@
 
     virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
                                                     const char* bcp47[], int bcp47Count,
-                                                    SkUnichar character) const SK_OVERRIDE {
+                                                    SkUnichar character) const override {
         return NULL;
     }
 
     virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
-                                         const SkFontStyle& fontstyle) const SK_OVERRIDE {
+                                         const SkFontStyle& fontstyle) const override {
         // could be in base impl
         SkString familyName;
         ((LogFontTypeface*)familyMember)->getFamilyName(&familyName);
         return this->matchFamilyStyle(familyName.c_str(), fontstyle);
     }
 
-    SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override {
         SkAutoTDelete<SkStreamAsset> stream(bareStream);
         return create_from_stream(stream);
     }
 
-    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
         // could be in base impl
         return this->createFromStream(SkNEW_ARGS(SkMemoryStream, (data)));
     }
 
-    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
         // could be in base impl
         return this->createFromStream(SkStream::NewFromFile(path));
     }
 
     virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
-                                               unsigned styleBits) const SK_OVERRIDE {
+                                               unsigned styleBits) const override {
         LOGFONT lf;
         if (NULL == familyName) {
             lf = get_default_font();
diff --git a/src/ports/SkFontMgr_android.cpp b/src/ports/SkFontMgr_android.cpp
index 11f9449..ae1b663 100644
--- a/src/ports/SkFontMgr_android.cpp
+++ b/src/ports/SkFontMgr_android.cpp
@@ -36,7 +36,7 @@
         , fFamilyName(familyName) { }
 
 protected:
-    void onGetFamilyName(SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(SkString* familyName) const override {
         *familyName = fFamilyName;
     }
 
@@ -62,15 +62,14 @@
         , fVariantStyle(variantStyle) { }
 
     virtual void onGetFontDescriptor(SkFontDescriptor* desc,
-                                     bool* serialize) const SK_OVERRIDE {
+                                     bool* serialize) const override {
         SkASSERT(desc);
         SkASSERT(serialize);
         desc->setFamilyName(fFamilyName.c_str());
-        desc->setFontFileName(fPathName.c_str());
         desc->setFontIndex(fIndex);
         *serialize = false;
     }
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override {
         *ttcIndex = fIndex;
         return SkStream::NewFromFile(fPathName.c_str());
     }
@@ -93,15 +92,14 @@
         , fStream(stream) { }
 
     virtual void onGetFontDescriptor(SkFontDescriptor* desc,
-                                     bool* serialize) const SK_OVERRIDE {
+                                     bool* serialize) const override {
         SkASSERT(desc);
         SkASSERT(serialize);
         desc->setFamilyName(fFamilyName.c_str());
-        desc->setFontFileName(NULL);
         *serialize = true;
     }
 
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override {
         *ttcIndex = fIndex;
         return fStream->duplicate();
     }
@@ -145,9 +143,15 @@
                 continue;
             }
 
-            if (fontFile.fWeight != 0) {
-                style = SkFontStyle(fontFile.fWeight, style.width(), style.slant());
+            int weight = fontFile.fWeight != 0 ? fontFile.fWeight : style.weight();
+            SkFontStyle::Slant slant = style.slant();
+            switch (fontFile.fStyle) {
+                case FontFileInfo::Style::kAuto: slant = style.slant(); break;
+                case FontFileInfo::Style::kNormal: slant = SkFontStyle::kUpright_Slant; break;
+                case FontFileInfo::Style::kItalic: slant = SkFontStyle::kItalic_Slant; break;
+                default: SkASSERT(false); break;
             }
+            style = SkFontStyle(weight, style.width(), slant);
 
             const SkLanguage& lang = family.fLanguage;
             uint32_t variant = family.fVariant;
@@ -169,10 +173,10 @@
         }
     }
 
-    int count() SK_OVERRIDE {
+    int count() override {
         return fStyles.count();
     }
-    void getStyle(int index, SkFontStyle* style, SkString* name) SK_OVERRIDE {
+    void getStyle(int index, SkFontStyle* style, SkString* name) override {
         if (index < 0 || fStyles.count() <= index) {
             return;
         }
@@ -183,7 +187,7 @@
             name->reset();
         }
     }
-    SkTypeface_AndroidSystem* createTypeface(int index) SK_OVERRIDE {
+    SkTypeface_AndroidSystem* createTypeface(int index) override {
         if (index < 0 || fStyles.count() <= index) {
             return NULL;
         }
@@ -194,7 +198,7 @@
      *  TODO: consider replacing with SkStyleSet_Indirect::matchStyle();
      *  this simpler version using match_score() passes all our tests.
      */
-    SkTypeface_AndroidSystem* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
+    SkTypeface_AndroidSystem* matchStyle(const SkFontStyle& pattern) override {
         if (0 == fStyles.count()) {
             return NULL;
         }
@@ -269,11 +273,11 @@
     /** Returns not how many families we have, but how many unique names
      *  exist among the families.
      */
-    int onCountFamilies() const SK_OVERRIDE {
+    int onCountFamilies() const override {
         return fNameToFamilyMap.count();
     }
 
-    void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(int index, SkString* familyName) const override {
         if (index < 0 || fNameToFamilyMap.count() <= index) {
             familyName->reset();
             return;
@@ -281,14 +285,14 @@
         familyName->set(fNameToFamilyMap[index].name);
     }
 
-    SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
+    SkFontStyleSet* onCreateStyleSet(int index) const override {
         if (index < 0 || fNameToFamilyMap.count() <= index) {
             return NULL;
         }
         return SkRef(fNameToFamilyMap[index].styleSet);
     }
 
-    SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE {
+    SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
         if (!familyName) {
             return NULL;
         }
@@ -308,13 +312,13 @@
     }
 
     virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
-                                           const SkFontStyle& style) const SK_OVERRIDE {
+                                           const SkFontStyle& style) const override {
         SkAutoTUnref<SkFontStyleSet> sset(this->matchFamily(familyName));
         return sset->matchStyle(style);
     }
 
     virtual SkTypeface* onMatchFaceStyle(const SkTypeface* typeface,
-                                         const SkFontStyle& style) const SK_OVERRIDE {
+                                         const SkFontStyle& style) const override {
         for (int i = 0; i < fFontStyleSets.count(); ++i) {
             for (int j = 0; j < fFontStyleSets[i]->fStyles.count(); ++j) {
                 if (fFontStyleSets[i]->fStyles[j] == typeface) {
@@ -359,7 +363,7 @@
                                                     const SkFontStyle& style,
                                                     const char* bcp47[],
                                                     int bcp47Count,
-                                                    SkUnichar character) const SK_OVERRIDE
+                                                    SkUnichar character) const override
     {
         // The variant 'elegant' is 'not squashed', 'compact' is 'stays in ascent/descent'.
         // The variant 'default' means 'compact and elegant'.
@@ -393,16 +397,16 @@
         return NULL;
     }
 
-    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
         return this->createFromStream(new SkMemoryStream(data), ttcIndex);
     }
 
-    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
         SkAutoTDelete<SkStreamAsset> stream(SkStream::NewFromFile(path));
         return stream.get() ? this->createFromStream(stream.detach(), ttcIndex) : NULL;
     }
 
-    SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override {
         SkAutoTDelete<SkStreamAsset> stream(bareStream);
         bool isFixedPitch;
         SkFontStyle style;
@@ -416,7 +420,7 @@
 
 
     virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
-                                               unsigned styleBits) const SK_OVERRIDE {
+                                               unsigned styleBits) const override {
         SkFontStyle style = SkFontStyle(styleBits);
 
         if (familyName) {
diff --git a/src/ports/SkFontMgr_fontconfig.cpp b/src/ports/SkFontMgr_fontconfig.cpp
index a0e45fa..f03e5ac 100644
--- a/src/ports/SkFontMgr_fontconfig.cpp
+++ b/src/ports/SkFontMgr_fontconfig.cpp
@@ -382,16 +382,16 @@
         , fIndex(index)
     { };
 
-    void onGetFamilyName(SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(SkString* familyName) const override {
         familyName->reset();
     }
 
-    void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const SK_OVERRIDE {
+    void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
         desc->setFontIndex(fIndex);
         *serialize = true;
     }
 
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override {
         *ttcIndex = fIndex;
         return fStream->duplicate();
     }
@@ -411,21 +411,20 @@
     }
     mutable SkAutoFcPattern fPattern;
 
-    void onGetFamilyName(SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(SkString* familyName) const override {
         *familyName = get_string(fPattern, FC_FAMILY);
     }
 
-    void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const SK_OVERRIDE {
+    void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
         FCLocker lock;
         desc->setFamilyName(get_string(fPattern, FC_FAMILY));
         desc->setFullName(get_string(fPattern, FC_FULLNAME));
         desc->setPostscriptName(get_string(fPattern, FC_POSTSCRIPT_NAME));
-        desc->setFontFileName(get_string(fPattern, FC_FILE));
         desc->setFontIndex(get_int(fPattern, FC_INDEX, 0));
         *serialize = false;
     }
 
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE {
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override {
         FCLocker lock;
         *ttcIndex = get_int(fPattern, FC_INDEX, 0);
         return SkStream::NewFromFile(get_string(fPattern, FC_FILE));
@@ -469,9 +468,9 @@
             fFontSet.reset();
         }
 
-        int count() SK_OVERRIDE { return fFontSet->nfont; }
+        int count() override { return fFontSet->nfont; }
 
-        void getStyle(int index, SkFontStyle* style, SkString* styleName) SK_OVERRIDE {
+        void getStyle(int index, SkFontStyle* style, SkString* styleName) override {
             if (index < 0 || fFontSet->nfont <= index) {
                 return;
             }
@@ -485,14 +484,14 @@
             }
         }
 
-        SkTypeface* createTypeface(int index) SK_OVERRIDE {
+        SkTypeface* createTypeface(int index) override {
             FCLocker lock;
 
             FcPattern* match = fFontSet->fonts[index];
             return fFontMgr->createTypefaceFromFcPattern(match);
         }
 
-        SkTypeface* matchStyle(const SkFontStyle& style) SK_OVERRIDE {
+        SkTypeface* matchStyle(const SkFontStyle& style) override {
             FCLocker lock;
 
             SkAutoFcPattern pattern;
@@ -607,15 +606,15 @@
     }
 
 protected:
-    int onCountFamilies() const SK_OVERRIDE {
+    int onCountFamilies() const override {
         return fFamilyNames->count();
     }
 
-    void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE {
+    void onGetFamilyName(int index, SkString* familyName) const override {
         familyName->set(fFamilyNames->atStr(index));
     }
 
-    SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE {
+    SkFontStyleSet* onCreateStyleSet(int index) const override {
         return this->onMatchFamily(fFamilyNames->atStr(index));
     }
 
@@ -684,7 +683,7 @@
         return false;
     }
 
-    SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE {
+    SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
         FCLocker lock;
 
         SkAutoFcPattern pattern;
@@ -726,7 +725,7 @@
     }
 
     virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
-                                           const SkFontStyle& style) const SK_OVERRIDE
+                                           const SkFontStyle& style) const override
     {
         FCLocker lock;
 
@@ -767,12 +766,17 @@
                                                     const SkFontStyle& style,
                                                     const char* bcp47[],
                                                     int bcp47Count,
-                                                    SkUnichar character) const SK_OVERRIDE
+                                                    SkUnichar character) const override
     {
         FCLocker lock;
 
         SkAutoFcPattern pattern;
-        FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
+        if (familyName) {
+            FcValue familyNameValue;
+            familyNameValue.type = FcTypeString;
+            familyNameValue.u.s = reinterpret_cast<const FcChar8*>(familyName);
+            FcPatternAddWeak(pattern, FC_FAMILY, familyNameValue, FcFalse);
+        }
         fcpattern_from_skfontstyle(style, pattern);
 
         SkAutoFcCharSet charSet;
@@ -801,7 +805,7 @@
     }
 
     virtual SkTypeface* onMatchFaceStyle(const SkTypeface* typeface,
-                                         const SkFontStyle& style) const SK_OVERRIDE
+                                         const SkFontStyle& style) const override
     {
         //TODO: should the SkTypeface_fontconfig know its family?
         const SkTypeface_fontconfig* fcTypeface =
@@ -809,7 +813,7 @@
         return this->matchFamilyStyle(get_string(fcTypeface->fPattern, FC_FAMILY), style);
     }
 
-    SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override {
         SkAutoTDelete<SkStreamAsset> stream(bareStream);
         const size_t length = stream->getLength();
         if (length <= 0 || (1u << 30) < length) {
@@ -826,16 +830,16 @@
                                               static_cast<SkStreamAsset*>(stream.detach())));
     }
 
-    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
         return this->createFromStream(SkNEW_ARGS(SkMemoryStream, (data)), ttcIndex);
     }
 
-    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE {
+    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
         return this->createFromStream(SkStream::NewFromFile(path), ttcIndex);
     }
 
     virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
-                                               unsigned styleBits) const SK_OVERRIDE {
+                                               unsigned styleBits) const override {
         bool bold = styleBits & SkTypeface::kBold;
         bool italic = styleBits & SkTypeface::kItalic;
         SkFontStyle style = SkFontStyle(bold ? SkFontStyle::kBold_Weight
diff --git a/src/ports/SkFontMgr_win_dw.cpp b/src/ports/SkFontMgr_win_dw.cpp
index bff8593..7245f52 100644
--- a/src/ports/SkFontMgr_win_dw.cpp
+++ b/src/ports/SkFontMgr_win_dw.cpp
@@ -277,22 +277,22 @@
     }
 
 protected:
-    int onCountFamilies() const SK_OVERRIDE;
-    void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE;
-    SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE;
-    SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE;
+    int onCountFamilies() const override;
+    void onGetFamilyName(int index, SkString* familyName) const override;
+    SkFontStyleSet* onCreateStyleSet(int index) const override;
+    SkFontStyleSet* onMatchFamily(const char familyName[]) const override;
     virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
-                                           const SkFontStyle& fontstyle) const SK_OVERRIDE;
+                                           const SkFontStyle& fontstyle) const override;
     virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
                                                     const char* bcp47[], int bcp47Count,
-                                                    SkUnichar character) const SK_OVERRIDE;
+                                                    SkUnichar character) const override;
     virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
-                                         const SkFontStyle& fontstyle) const SK_OVERRIDE;
-    SkTypeface* onCreateFromStream(SkStreamAsset* stream, int ttcIndex) const SK_OVERRIDE;
-    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE;
-    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE;
+                                         const SkFontStyle& fontstyle) const override;
+    SkTypeface* onCreateFromStream(SkStreamAsset* stream, int ttcIndex) const override;
+    SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override;
+    SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override;
     virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
-                                               unsigned styleBits) const SK_OVERRIDE;
+                                               unsigned styleBits) const override;
 
 private:
     HRESULT getByFamilyName(const WCHAR familyName[], IDWriteFontFamily** fontFamily) const;
@@ -324,10 +324,10 @@
         , fFontFamily(SkRefComPtr(fontFamily))
     { }
 
-    int count() SK_OVERRIDE;
-    void getStyle(int index, SkFontStyle* fs, SkString* styleName) SK_OVERRIDE;
-    SkTypeface* createTypeface(int index) SK_OVERRIDE;
-    SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE;
+    int count() override;
+    void getStyle(int index, SkFontStyle* fs, SkString* styleName) override;
+    SkTypeface* createTypeface(int index) override;
+    SkTypeface* matchStyle(const SkFontStyle& pattern) override;
 
 private:
     SkAutoTUnref<const SkFontMgr_DirectWrite> fFontMgr;
@@ -519,7 +519,7 @@
         DWRITE_MEASURING_MODE measuringMode,
         DWRITE_GLYPH_RUN const* glyphRun,
         DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
-        IUnknown* clientDrawingEffect) SK_OVERRIDE
+        IUnknown* clientDrawingEffect) override
     {
         SkTScopedComPtr<IDWriteFont> font;
         HRM(fOuter->fFontCollection->GetFontFromFontFace(glyphRun->fontFace, &font),
@@ -547,7 +547,7 @@
         FLOAT baselineOriginX,
         FLOAT baselineOriginY,
         DWRITE_UNDERLINE const* underline,
-        IUnknown* clientDrawingEffect) SK_OVERRIDE
+        IUnknown* clientDrawingEffect) override
     { return E_NOTIMPL; }
 
     virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(
@@ -555,7 +555,7 @@
         FLOAT baselineOriginX,
         FLOAT baselineOriginY,
         DWRITE_STRIKETHROUGH const* strikethrough,
-        IUnknown* clientDrawingEffect) SK_OVERRIDE
+        IUnknown* clientDrawingEffect) override
     { return E_NOTIMPL; }
 
     virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(
@@ -565,13 +565,13 @@
         IDWriteInlineObject* inlineObject,
         BOOL isSideways,
         BOOL isRightToLeft,
-        IUnknown* clientDrawingEffect) SK_OVERRIDE
+        IUnknown* clientDrawingEffect) override
     { return E_NOTIMPL; }
 
     // IDWritePixelSnapping methods
     virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(
         void* clientDrawingContext,
-        BOOL* isDisabled) SK_OVERRIDE
+        BOOL* isDisabled) override
     {
         *isDisabled = FALSE;
         return S_OK;
@@ -579,7 +579,7 @@
 
     virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(
         void* clientDrawingContext,
-        DWRITE_MATRIX* transform) SK_OVERRIDE
+        DWRITE_MATRIX* transform) override
     {
         const DWRITE_MATRIX ident = { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 };
         *transform = ident;
@@ -588,18 +588,18 @@
 
     virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(
         void* clientDrawingContext,
-        FLOAT* pixelsPerDip) SK_OVERRIDE
+        FLOAT* pixelsPerDip) override
     {
         *pixelsPerDip = 1.0f;
         return S_OK;
     }
 
     // IUnknown methods
-    ULONG STDMETHODCALLTYPE AddRef() SK_OVERRIDE {
+    ULONG STDMETHODCALLTYPE AddRef() override {
         return InterlockedIncrement(&fRefCount);
     }
 
-    ULONG STDMETHODCALLTYPE Release() SK_OVERRIDE {
+    ULONG STDMETHODCALLTYPE Release() override {
         ULONG newCount = InterlockedDecrement(&fRefCount);
         if (0 == newCount) {
             delete this;
@@ -607,7 +607,7 @@
         return newCount;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE QueryInterface(IID const& riid, void** ppvObject) SK_OVERRIDE{
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(IID const& riid, void** ppvObject) override{
         if (__uuidof(IUnknown) == riid ||
             __uuidof(IDWritePixelSnapping) == riid ||
             __uuidof(IDWriteTextRenderer) == riid)
@@ -629,21 +629,6 @@
     SkTypeface* fResolvedTypeface;
 };
 
-static HRESULT getDefaultFontFamilyName(SkSMallocWCHAR* name) {
-    NONCLIENTMETRICSW metrics;
-    metrics.cbSize = sizeof(metrics);
-    if (0 == SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(metrics), &metrics, 0)) {
-        return E_UNEXPECTED;
-    }
-
-    size_t len = wcsnlen_s(metrics.lfMessageFont.lfFaceName, LF_FACESIZE) + 1;
-    if (0 != wcsncpy_s(name->reset(len), len, metrics.lfMessageFont.lfFaceName, _TRUNCATE)) {
-        return E_UNEXPECTED;
-    }
-
-    return S_OK;
-}
-
 class FontFallbackSource : public IDWriteTextAnalysisSource {
 public:
     FontFallbackSource(const WCHAR* string, UINT32 length, const WCHAR* locale,
@@ -660,7 +645,7 @@
     virtual HRESULT STDMETHODCALLTYPE GetTextAtPosition(
         UINT32 textPosition,
         WCHAR const** textString,
-        UINT32* textLength) SK_OVERRIDE
+        UINT32* textLength) override
     {
         if (fLength <= textPosition) {
             *textString = NULL;
@@ -675,7 +660,7 @@
     virtual HRESULT STDMETHODCALLTYPE GetTextBeforePosition(
         UINT32 textPosition,
         WCHAR const** textString,
-        UINT32* textLength) SK_OVERRIDE
+        UINT32* textLength) override
     {
         if (textPosition < 1 || fLength <= textPosition) {
             *textString = NULL;
@@ -687,7 +672,7 @@
         return S_OK;
     }
 
-    virtual DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() SK_OVERRIDE {
+    virtual DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() override {
         // TODO: this is also interesting.
         return DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
     }
@@ -695,7 +680,7 @@
     virtual HRESULT STDMETHODCALLTYPE GetLocaleName(
         UINT32 textPosition,
         UINT32* textLength,
-        WCHAR const** localeName) SK_OVERRIDE
+        WCHAR const** localeName) override
     {
         *localeName = fLocale;
         return S_OK;
@@ -704,18 +689,18 @@
     virtual HRESULT STDMETHODCALLTYPE GetNumberSubstitution(
         UINT32 textPosition,
         UINT32* textLength,
-        IDWriteNumberSubstitution** numberSubstitution) SK_OVERRIDE
+        IDWriteNumberSubstitution** numberSubstitution) override
     {
         *numberSubstitution = fNumberSubstitution;
         return S_OK;
     }
 
     // IUnknown methods
-    ULONG STDMETHODCALLTYPE AddRef() SK_OVERRIDE {
+    ULONG STDMETHODCALLTYPE AddRef() override {
         return InterlockedIncrement(&fRefCount);
     }
 
-    ULONG STDMETHODCALLTYPE Release() SK_OVERRIDE {
+    ULONG STDMETHODCALLTYPE Release() override {
         ULONG newCount = InterlockedDecrement(&fRefCount);
         if (0 == newCount) {
             delete this;
@@ -723,7 +708,7 @@
         return newCount;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE QueryInterface(IID const& riid, void** ppvObject) SK_OVERRIDE{
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(IID const& riid, void** ppvObject) override{
         if (__uuidof(IUnknown) == riid ||
             __uuidof(IDWriteTextAnalysisSource) == riid)
         {
@@ -750,11 +735,11 @@
 {
     const DWriteStyle dwStyle(style);
 
-    SkSMallocWCHAR dwFamilyName;
-    if (NULL == familyName) {
-        HRN(getDefaultFontFamilyName(&dwFamilyName));
-    } else {
-        HRN(sk_cstring_to_wchar(familyName, &dwFamilyName));
+    const WCHAR* dwFamilyName = NULL;
+    SkSMallocWCHAR dwFamilyNameLocal;
+    if (familyName) {
+        HRN(sk_cstring_to_wchar(familyName, &dwFamilyNameLocal));
+        dwFamilyName = dwFamilyNameLocal;
     }
 
     WCHAR str[16];
@@ -816,7 +801,7 @@
 #endif
 
     SkTScopedComPtr<IDWriteTextFormat> fallbackFormat;
-    HRNM(fFactory->CreateTextFormat(dwFamilyName,
+    HRNM(fFactory->CreateTextFormat(dwFamilyName ? dwFamilyName : L"",
                                     fFontCollection.get(),
                                     dwStyle.fWeight,
                                     dwStyle.fSlant,
diff --git a/src/ports/SkImageDecoder_WIC.cpp b/src/ports/SkImageDecoder_WIC.cpp
index 80fb8a3..55344a7 100644
--- a/src/ports/SkImageDecoder_WIC.cpp
+++ b/src/ports/SkImageDecoder_WIC.cpp
@@ -67,7 +67,7 @@
     bool decodeStream(SkStream* stream, SkBitmap* bm, WICModes wicMode, Format* format) const;
 
 protected:
-    Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
+    Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) override;
 };
 
 struct FormatConversion {
diff --git a/src/ports/SkImageGenerator_skia.cpp b/src/ports/SkImageGenerator_skia.cpp
index 6c27f45..1784cc2 100644
--- a/src/ports/SkImageGenerator_skia.cpp
+++ b/src/ports/SkImageGenerator_skia.cpp
@@ -21,7 +21,7 @@
     {}
 
 protected:
-    bool allocPixelRef(SkBitmap* bm, SkColorTable* ctable) SK_OVERRIDE {
+    bool allocPixelRef(SkBitmap* bm, SkColorTable* ctable) override {
         const SkImageInfo bmi = bm->info();
         if (bmi.width() != fInfo.width() || bmi.height() != fInfo.height() ||
             bmi.colorType() != fInfo.colorType())
@@ -39,21 +39,17 @@
 
 public:
     SkImageDecoderGenerator(const SkImageInfo& info, SkImageDecoder* decoder, SkData* data)
-        : fInfo(info), fDecoder(decoder), fData(SkRef(data))
+        : INHERITED(info), fInfo(info), fDecoder(decoder), fData(SkRef(data))
     {}
 
 protected:
-    SkData* onRefEncodedData() SK_OVERRIDE {
+    SkData* onRefEncodedData() override {
         return SkRef(fData.get());
     }
 
-    virtual bool onGetInfo(SkImageInfo* info) SK_OVERRIDE {
-        *info = fInfo;
-        return true;
-    }
-
     virtual Result onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-                               SkPMColor ctableEntries[], int* ctableCount) SK_OVERRIDE {
+                               const Options&,
+                               SkPMColor ctableEntries[], int* ctableCount) override {
         SkMemoryStream stream(fData->data(), fData->size(), false);
         SkAutoTUnref<BareMemoryAllocator> allocator(SkNEW_ARGS(BareMemoryAllocator,
                                                                (info, pixels, rowBytes)));
@@ -87,11 +83,13 @@
     }
 
     bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
-                         SkYUVColorSpace* colorSpace) SK_OVERRIDE {
+                         SkYUVColorSpace* colorSpace) override {
         SkMemoryStream stream(fData->data(), fData->size(), false);
         return fDecoder->decodeYUV8Planes(&stream, sizes, planes, rowBytes, colorSpace);
     }
-    
+
+private:
+    typedef SkImageGenerator INHERITED;
 };
 
 SkImageGenerator* SkImageGenerator::NewFromData(SkData* data) {
diff --git a/src/ports/SkRemotableFontMgr_win_dw.cpp b/src/ports/SkRemotableFontMgr_win_dw.cpp
index ad1efb7..8c6dd84 100644
--- a/src/ports/SkRemotableFontMgr_win_dw.cpp
+++ b/src/ports/SkRemotableFontMgr_win_dw.cpp
@@ -91,7 +91,7 @@
         memcpy(fLocaleName.get(), localeName, localeNameLength * sizeof(WCHAR));
     }
 
-    SkDataTable* getFamilyNames() const SK_OVERRIDE {
+    SkDataTable* getFamilyNames() const override {
         int count = fFontCollection->GetFontFamilyCount();
 
         SkDataTableBuilder names(1024);
@@ -158,7 +158,7 @@
         return S_OK;
     }
 
-    SkRemotableFontIdentitySet* getIndex(int familyIndex) const SK_OVERRIDE {
+    SkRemotableFontIdentitySet* getIndex(int familyIndex) const override {
         SkTScopedComPtr<IDWriteFontFamily> fontFamily;
         HRNM(fFontCollection->GetFontFamily(familyIndex, &fontFamily),
              "Could not get requested family.");
@@ -177,7 +177,7 @@
     }
 
     virtual SkFontIdentity matchIndexStyle(int familyIndex,
-                                           const SkFontStyle& pattern) const SK_OVERRIDE
+                                           const SkFontStyle& pattern) const override
     {
         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
 
@@ -216,7 +216,7 @@
         return S_OK;
     }
 
-    SkRemotableFontIdentitySet* matchName(const char familyName[]) const SK_OVERRIDE {
+    SkRemotableFontIdentitySet* matchName(const char familyName[]) const override {
         SkSMallocWCHAR dwFamilyName;
         if (NULL == familyName) {
             HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName),
@@ -239,7 +239,7 @@
     }
 
     virtual SkFontIdentity matchNameStyle(const char familyName[],
-                                          const SkFontStyle& style) const SK_OVERRIDE
+                                          const SkFontStyle& style) const override
     {
         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
 
@@ -279,7 +279,7 @@
             DWRITE_MEASURING_MODE measuringMode,
             DWRITE_GLYPH_RUN const* glyphRun,
             DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
-            IUnknown* clientDrawingEffect) SK_OVERRIDE
+            IUnknown* clientDrawingEffect) override
         {
             SkTScopedComPtr<IDWriteFont> font;
             HRM(fOuter->fFontCollection->GetFontFromFontFace(glyphRun->fontFace, &font),
@@ -303,7 +303,7 @@
             FLOAT baselineOriginX,
             FLOAT baselineOriginY,
             DWRITE_UNDERLINE const* underline,
-            IUnknown* clientDrawingEffect) SK_OVERRIDE
+            IUnknown* clientDrawingEffect) override
         { return E_NOTIMPL; }
 
         virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(
@@ -311,7 +311,7 @@
             FLOAT baselineOriginX,
             FLOAT baselineOriginY,
             DWRITE_STRIKETHROUGH const* strikethrough,
-            IUnknown* clientDrawingEffect) SK_OVERRIDE
+            IUnknown* clientDrawingEffect) override
         { return E_NOTIMPL; }
 
         virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(
@@ -321,13 +321,13 @@
             IDWriteInlineObject* inlineObject,
             BOOL isSideways,
             BOOL isRightToLeft,
-            IUnknown* clientDrawingEffect) SK_OVERRIDE
+            IUnknown* clientDrawingEffect) override
         { return E_NOTIMPL; }
 
         // IDWritePixelSnapping methods
         virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(
             void* clientDrawingContext,
-            BOOL* isDisabled) SK_OVERRIDE
+            BOOL* isDisabled) override
         {
             *isDisabled = FALSE;
             return S_OK;
@@ -335,7 +335,7 @@
 
         virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(
             void* clientDrawingContext,
-            DWRITE_MATRIX* transform) SK_OVERRIDE
+            DWRITE_MATRIX* transform) override
         {
             const DWRITE_MATRIX ident = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
             *transform = ident;
@@ -344,18 +344,18 @@
 
         virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(
             void* clientDrawingContext,
-            FLOAT* pixelsPerDip) SK_OVERRIDE
+            FLOAT* pixelsPerDip) override
         {
             *pixelsPerDip = 1.0f;
             return S_OK;
         }
 
         // IUnknown methods
-        ULONG STDMETHODCALLTYPE AddRef() SK_OVERRIDE {
+        ULONG STDMETHODCALLTYPE AddRef() override {
             return InterlockedIncrement(&fRefCount);
         }
 
-        ULONG STDMETHODCALLTYPE Release() SK_OVERRIDE {
+        ULONG STDMETHODCALLTYPE Release() override {
             ULONG newCount = InterlockedDecrement(&fRefCount);
             if (0 == newCount) {
                 delete this;
@@ -364,7 +364,7 @@
         }
 
         virtual HRESULT STDMETHODCALLTYPE QueryInterface(
-            IID const& riid, void** ppvObject) SK_OVERRIDE
+            IID const& riid, void** ppvObject) override
         {
             if (__uuidof(IUnknown) == riid ||
                 __uuidof(IDWritePixelSnapping) == riid ||
@@ -390,7 +390,7 @@
     virtual SkFontIdentity matchNameStyleCharacter(const char familyName[],
                                                    const SkFontStyle& pattern,
                                                    const char* bcp47[], int bcp47Count,
-                                                   SkUnichar character) const SK_OVERRIDE
+                                                   SkUnichar character) const override
     {
         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
 
@@ -452,7 +452,7 @@
         return fontFallbackRenderer->FallbackIdentity();
     }
 
-    SkStreamAsset* getData(int dataId) const SK_OVERRIDE {
+    SkStreamAsset* getData(int dataId) const override {
         SkAutoMutexAcquire ama(fDataIdCacheMutex);
         if (dataId >= fDataIdCache.count()) {
             return NULL;
diff --git a/src/ports/SkScalerContext_win_dw.cpp b/src/ports/SkScalerContext_win_dw.cpp
index 1e10e17..de527c8 100644
--- a/src/ports/SkScalerContext_win_dw.cpp
+++ b/src/ports/SkScalerContext_win_dw.cpp
@@ -591,7 +591,7 @@
     }
 }
 
-template<bool APPLY_PREBLEND>
+template<bool APPLY_PREBLEND, bool RGB>
 static void rgb_to_lcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
                          const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
     const size_t dstRB = glyph.rowBytes();
@@ -600,9 +600,16 @@
 
     for (U16CPU y = 0; y < glyph.fHeight; y++) {
         for (U16CPU i = 0; i < width; i++) {
-            U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
-            U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
-            U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
+            U8CPU r, g, b;
+            if (RGB) {
+                r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
+                g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
+                b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
+            } else {
+                b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
+                g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
+                r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
+            }
             dst[i] = SkPack888ToRGB16(r, g, b);
         }
         dst = (uint16_t*)((char*)dst + dstRB);
@@ -699,9 +706,17 @@
     } else {
         SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat);
         if (fPreBlend.isApplicable()) {
-            rgb_to_lcd16<true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
+                rgb_to_lcd16<true, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            } else {
+                rgb_to_lcd16<true, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            }
         } else {
-            rgb_to_lcd16<false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) {
+                rgb_to_lcd16<false, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            } else {
+                rgb_to_lcd16<false, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            }
         }
     }
 }
diff --git a/src/ports/SkScalerContext_win_dw.h b/src/ports/SkScalerContext_win_dw.h
index 5c13eab..abf2bc9 100644
--- a/src/ports/SkScalerContext_win_dw.h
+++ b/src/ports/SkScalerContext_win_dw.h
@@ -24,13 +24,13 @@
     virtual ~SkScalerContext_DW();
 
 protected:
-    unsigned generateGlyphCount() SK_OVERRIDE;
-    uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
-    void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
-    void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
-    void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
-    void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
-    void generateFontMetrics(SkPaint::FontMetrics*) SK_OVERRIDE;
+    unsigned generateGlyphCount() override;
+    uint16_t generateCharToGlyph(SkUnichar uni) override;
+    void generateAdvance(SkGlyph* glyph) override;
+    void generateMetrics(SkGlyph* glyph) override;
+    void generateImage(const SkGlyph& glyph) override;
+    void generatePath(const SkGlyph& glyph, SkPath* path) override;
+    void generateFontMetrics(SkPaint::FontMetrics*) override;
 
 private:
     const void* drawDWMask(const SkGlyph& glyph,
diff --git a/src/ports/SkTime_Unix.cpp b/src/ports/SkTime_Unix.cpp
index 6e305a1..2d5fa27 100644
--- a/src/ports/SkTime_Unix.cpp
+++ b/src/ports/SkTime_Unix.cpp
@@ -16,11 +16,15 @@
 {
     if (dt)
     {
+        tzset();  // initialize timezone variable;
         time_t m_time;
         time(&m_time);
         struct tm* tstruct;
         tstruct = localtime(&m_time);
+        int offset = tstruct->tm_isdst == 1 ? 60 : 0;
+
         // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/time.h.html
+        dt->fTimeZoneMinutes = SkToS16(offset - timezone / 60);
         dt->fYear       = tstruct->tm_year + 1900;
         dt->fMonth      = SkToU8(tstruct->tm_mon + 1);
         dt->fDayOfWeek  = SkToU8(tstruct->tm_wday);
diff --git a/src/ports/SkTime_win.cpp b/src/ports/SkTime_win.cpp
index bc0f8f0..19f4695 100644
--- a/src/ports/SkTime_win.cpp
+++ b/src/ports/SkTime_win.cpp
@@ -14,8 +14,22 @@
     if (dt)
     {
         SYSTEMTIME      st;
-        GetSystemTime(&st);
-
+        TIME_ZONE_INFORMATION timeZoneInfo;
+        int tz_bias;
+        GetLocalTime(&st);
+        // https://gist.github.com/wrl/8924636
+        switch (GetTimeZoneInformation(&timeZoneInfo)) {
+            case TIME_ZONE_ID_STANDARD:
+                tz_bias = -timeZoneInfo.Bias - timeZoneInfo.StandardBias;
+                break;
+            case TIME_ZONE_ID_DAYLIGHT:
+                tz_bias = -timeZoneInfo.Bias - timeZoneInfo.DaylightBias;
+                break;
+            default:
+                tz_bias = -timeZoneInfo.Bias;
+                break;
+        }
+        dt->fTimeZoneMinutes = SkToS16(tz_bias);
         dt->fYear       = st.wYear;
         dt->fMonth      = SkToU8(st.wMonth);
         dt->fDayOfWeek  = SkToU8(st.wDayOfWeek);
diff --git a/src/ports/SkTypeface_win_dw.cpp b/src/ports/SkTypeface_win_dw.cpp
index eb17201..cf45c16 100644
--- a/src/ports/SkTypeface_win_dw.cpp
+++ b/src/ports/SkTypeface_win_dw.cpp
@@ -137,7 +137,7 @@
         : fIndex(0), fStrings(strings)
     { }
 
-    bool next(SkTypeface::LocalizedString* localizedString) SK_OVERRIDE {
+    bool next(SkTypeface::LocalizedString* localizedString) override {
         if (fIndex >= fStrings->GetCount()) {
             return false;
         }
@@ -243,9 +243,7 @@
 }
 
 void DWriteFontTypeface::onFilterRec(SkScalerContext::Rec* rec) const {
-    if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
-        rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
-    {
+    if (rec->fFlags & SkScalerContext::kLCD_Vertical_Flag) {
         rec->fMaskFormat = SkMask::kA8_Format;
     }
 
@@ -253,7 +251,6 @@
                                   SkScalerContext::kDevKernText_Flag |
                                   SkScalerContext::kForceAutohinting_Flag |
                                   SkScalerContext::kEmbolden_Flag |
-                                  SkScalerContext::kLCD_BGROrder_Flag |
                                   SkScalerContext::kLCD_Vertical_Flag;
     rec->fFlags &= ~flagsWeDontSupport;
 
@@ -325,7 +322,7 @@
 }
 
 SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics(
-        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        PerGlyphInfo perGlyphInfo,
         const uint32_t* glyphIDs,
         uint32_t glyphIDsCount) const {
 
@@ -358,7 +355,7 @@
 
     hr = sk_wchar_to_skstring(familyName.get(), familyNameLen, &info->fFontName);
 
-    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
+    if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
         populate_glyph_to_unicode(fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode));
     }
 
@@ -369,7 +366,7 @@
     } else {
         info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
         info->fItalicAngle = 0;
-        info->fAscent = dwfm.ascent;;
+        info->fAscent = dwfm.ascent;
         info->fDescent = dwfm.descent;
         info->fStemV = 0;
         info->fCapHeight = dwfm.capHeight;
@@ -383,7 +380,7 @@
     AutoTDWriteTable<SkOTTableOS2> os2Table(fDWriteFontFace.get());
     if (!headTable.fExists || !postTable.fExists || !hheaTable.fExists || !os2Table.fExists) {
         info->fItalicAngle = 0;
-        info->fAscent = dwfm.ascent;;
+        info->fAscent = dwfm.ascent;
         info->fDescent = dwfm.descent;
         info->fStemV = 0;
         info->fCapHeight = dwfm.capHeight;
@@ -445,7 +442,7 @@
     }
     */
 
-    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+    if (perGlyphInfo & kHAdvance_PerGlyphInfo) {
         if (fixedWidth) {
             appendRange(&info->fGlyphWidths, 0);
             int16_t advance;
diff --git a/src/ports/SkTypeface_win_dw.h b/src/ports/SkTypeface_win_dw.h
index 9e824e5..71d928b 100644
--- a/src/ports/SkTypeface_win_dw.h
+++ b/src/ports/SkTypeface_win_dw.h
@@ -84,7 +84,7 @@
     }
 
 protected:
-    void weak_dispose() const SK_OVERRIDE {
+    void weak_dispose() const override {
         if (fDWriteFontCollectionLoader.get()) {
             HRV(fFactory->UnregisterFontCollectionLoader(fDWriteFontCollectionLoader.get()));
         }
@@ -96,22 +96,21 @@
         INHERITED::weak_dispose();
     }
 
-    SkStreamAsset* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
-    SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
-    void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
-    virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
-                                SkAdvancedTypefaceMetrics::PerGlyphInfo,
-                                const uint32_t*, uint32_t) const SK_OVERRIDE;
-    void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
+    SkStreamAsset* onOpenStream(int* ttcIndex) const override;
+    SkScalerContext* onCreateScalerContext(const SkDescriptor*) const override;
+    void onFilterRec(SkScalerContextRec*) const override;
+    SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
+                                PerGlyphInfo, const uint32_t*, uint32_t) const override;
+    void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
     virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
-                                uint16_t glyphs[], int glyphCount) const SK_OVERRIDE;
-    int onCountGlyphs() const SK_OVERRIDE;
-    int onGetUPEM() const SK_OVERRIDE;
-    void onGetFamilyName(SkString* familyName) const SK_OVERRIDE;
-    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE;
-    int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
+                                uint16_t glyphs[], int glyphCount) const override;
+    int onCountGlyphs() const override;
+    int onGetUPEM() const override;
+    void onGetFamilyName(SkString* familyName) const override;
+    SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
+    int onGetTableTags(SkFontTableTag tags[]) const override;
     virtual size_t onGetTableData(SkFontTableTag, size_t offset,
-                                  size_t length, void* data) const SK_OVERRIDE;
+                                  size_t length, void* data) const override;
 
 private:
     typedef SkTypeface INHERITED;
diff --git a/src/sfnt/SkOTUtils.h b/src/sfnt/SkOTUtils.h
index cbaf31c..e1db048 100644
--- a/src/sfnt/SkOTUtils.h
+++ b/src/sfnt/SkOTUtils.h
@@ -54,7 +54,7 @@
          */
         static LocalizedStrings_NameTable* CreateForFamilyNames(const SkTypeface& typeface);
 
-        bool next(SkTypeface::LocalizedString* localizedString) SK_OVERRIDE;
+        bool next(SkTypeface::LocalizedString* localizedString) override;
     private:
         static SkOTTableName::Record::NameID::Predefined::Value familyNameTypes[3];
 
@@ -72,7 +72,7 @@
             : fName(name), fLanguage(language), fHasNext(true)
         { }
 
-        bool next(SkTypeface::LocalizedString* localizedString) SK_OVERRIDE {
+        bool next(SkTypeface::LocalizedString* localizedString) override {
             localizedString->fString = fName;
             localizedString->fLanguage = fLanguage;
 
diff --git a/src/svg/SkSVGDevice.cpp b/src/svg/SkSVGDevice.cpp
index 45e616b..480dd92 100644
--- a/src/svg/SkSVGDevice.cpp
+++ b/src/svg/SkSVGDevice.cpp
@@ -103,12 +103,6 @@
     return tstr;
 }
 
-uint32_t hash_family_string(const SkString& family) {
-    // This is a lame hash function, but we don't really expect to see more than 1-2
-    // family names under normal circumstances.
-    return SkChecksum::Mix(SkToU32(family.size()));
-}
-
 struct Resources {
     Resources(const SkPaint& paint)
         : fPaintServer(svg_color(paint.getColor())) {}
@@ -538,7 +532,7 @@
     }
 
     SkString familyName;
-    SkTHashSet<SkString, hash_family_string> familySet;
+    SkTHashSet<SkString> familySet;
     SkAutoTUnref<const SkTypeface> tface(paint.getTypeface() ?
         SkRef(paint.getTypeface()) : SkTypeface::RefDefault());
 
@@ -609,10 +603,34 @@
                                           SkIntToScalar(this->height())));
 }
 
-void SkSVGDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
-                             const SkPoint[], const SkPaint& paint) {
-    // todo
-    SkDebugf("unsupported operation: drawPoints()\n");
+void SkSVGDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
+                             const SkPoint pts[], const SkPaint& paint) {
+    SkPath path;
+
+    switch (mode) {
+            // todo
+        case SkCanvas::kPoints_PointMode:
+            SkDebugf("unsupported operation: drawPoints(kPoints_PointMode)\n");
+            break;
+        case SkCanvas::kLines_PointMode:
+            count -= 1;
+            for (size_t i = 0; i < count; i += 2) {
+                path.rewind();
+                path.moveTo(pts[i]);
+                path.lineTo(pts[i+1]);
+                AutoElement elem("path", fWriter, fResourceBucket, draw, paint);
+                elem.addPathAttributes(path);
+            }
+            break;
+        case SkCanvas::kPolygon_PointMode:
+            if (count > 1) {
+                path.addPoly(pts, SkToInt(count), false);
+                path.moveTo(pts[0]);
+                AutoElement elem("path", fWriter, fResourceBucket, draw, paint);
+                elem.addPathAttributes(path);
+            }
+            break;
+    }
 }
 
 void SkSVGDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
@@ -628,9 +646,12 @@
     ellipse.addAttribute("ry", oval.height() / 2);
 }
 
-void SkSVGDevice::drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& paint) {
-    // todo
-    SkDebugf("unsupported operation: drawRRect()\n");
+void SkSVGDevice::drawRRect(const SkDraw& draw, const SkRRect& rr, const SkPaint& paint) {
+    SkPath path;
+    path.addRRect(rr);
+
+    AutoElement elem("path", fWriter, fResourceBucket, draw, paint);
+    elem.addPathAttributes(path);
 }
 
 void SkSVGDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint& paint,
diff --git a/src/svg/SkSVGDevice.h b/src/svg/SkSVGDevice.h
index 9146423..249cd65 100644
--- a/src/svg/SkSVGDevice.h
+++ b/src/svg/SkSVGDevice.h
@@ -16,46 +16,46 @@
 public:
     static SkBaseDevice* Create(const SkISize& size, SkXMLWriter* writer);
 
-    virtual SkImageInfo imageInfo() const SK_OVERRIDE;
+    SkImageInfo imageInfo() const override;
 
 protected:
-    virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE;
-    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
-                            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 drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& paint) SK_OVERRIDE;
-    virtual void drawPath(const SkDraw&, const SkPath& path,
-                          const SkPaint& paint,
-                          const SkMatrix* prePathMatrix = NULL,
-                          bool pathIsMutable = false) SK_OVERRIDE;
+    void drawPaint(const SkDraw&, const SkPaint& paint) override;
+    void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
+                    const SkPoint[], const SkPaint& paint) override;
+    void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint) override;
+    void drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) override;
+    void drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& paint) override;
+    void drawPath(const SkDraw&, const SkPath& path,
+                  const SkPaint& paint,
+                  const SkMatrix* prePathMatrix = NULL,
+                  bool pathIsMutable = false) override;
 
-    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
-                            const SkMatrix& matrix, const SkPaint& paint) SK_OVERRIDE;
-    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
-                            int x, int y, const SkPaint& paint) SK_OVERRIDE;
-    virtual void drawBitmapRect(const SkDraw&, const SkBitmap&,
-                                const SkRect* srcOrNull, const SkRect& dst,
-                                const SkPaint& paint,
-                                SkCanvas::DrawBitmapRectFlags flags) SK_OVERRIDE;
+    void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+                    const SkMatrix& matrix, const SkPaint& paint) override;
+    void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+                    int x, int y, const SkPaint& paint) override;
+    void drawBitmapRect(const SkDraw&, const SkBitmap&,
+                        const SkRect* srcOrNull, const SkRect& dst,
+                        const SkPaint& paint,
+                        SkCanvas::DrawBitmapRectFlags flags) override;
 
-    virtual void drawText(const SkDraw&, const void* text, size_t len,
-                          SkScalar x, SkScalar y, const SkPaint& paint) SK_OVERRIDE;
-    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
-                             const SkScalar pos[], int scalarsPerPos,
-                             const SkPoint& offset, const SkPaint& paint) SK_OVERRIDE;
-    virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
-                                const SkPath& path, const SkMatrix* matrix,
-                                const SkPaint& paint) SK_OVERRIDE;
-    virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount,
-                              const SkPoint verts[], const SkPoint texs[],
-                              const SkColor colors[], SkXfermode* xmode,
-                              const uint16_t indices[], int indexCount,
-                              const SkPaint& paint) SK_OVERRIDE;
+    void drawText(const SkDraw&, const void* text, size_t len,
+                  SkScalar x, SkScalar y, const SkPaint& paint) override;
+    void drawPosText(const SkDraw&, const void* text, size_t len,
+                     const SkScalar pos[], int scalarsPerPos,
+                     const SkPoint& offset, const SkPaint& paint) override;
+    void drawTextOnPath(const SkDraw&, const void* text, size_t len,
+                        const SkPath& path, const SkMatrix* matrix,
+                        const SkPaint& paint) override;
+    void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount,
+                      const SkPoint verts[], const SkPoint texs[],
+                      const SkColor colors[], SkXfermode* xmode,
+                      const uint16_t indices[], int indexCount,
+                      const SkPaint& paint) override;
 
-    virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
-                            const SkPaint&) SK_OVERRIDE;
-    virtual const SkBitmap& onAccessBitmap() SK_OVERRIDE;
+    void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
+                    const SkPaint&) override;
+    const SkBitmap& onAccessBitmap() override;
 
 private:
     SkSVGDevice(const SkISize& size, SkXMLWriter* writer);
diff --git a/src/svg/parser/SkSVGSVG.cpp b/src/svg/parser/SkSVGSVG.cpp
index fcce62d..61ccb3c 100644
--- a/src/svg/parser/SkSVGSVG.cpp
+++ b/src/svg/parser/SkSVGSVG.cpp
@@ -43,11 +43,8 @@
     if (strcmp(wSuffix, "pt") == 0)
         width = SkScalarMulDiv(width, SK_Scalar1 * 72, SK_Scalar1 * 96);
     SkParse::FindScalars(f_viewBox.c_str(), viewBox, 4);
-    SkRect box;
-    box.fLeft = SkScalarDiv(viewBox[0], width);
-    box.fTop = SkScalarDiv(viewBox[1], height);
-    box.fRight = SkScalarDiv(viewBox[2], width);
-    box.fBottom = SkScalarDiv(viewBox[3], height);
+    SkRect box = SkRect::MakeLTRB(viewBox[0] / width, viewBox[1] / height,
+                                  viewBox[2] / width, viewBox[3] / height);
     if (box.fLeft == 0 && box.fTop == 0 &&
         box.fRight == SK_Scalar1 && box.fBottom == SK_Scalar1)
             return;
diff --git a/src/utils/SkCanvasStack.h b/src/utils/SkCanvasStack.h
index 27bc6bc..6602110 100644
--- a/src/utils/SkCanvasStack.h
+++ b/src/utils/SkCanvasStack.h
@@ -18,7 +18,7 @@
     virtual ~SkCanvasStack();
 
     void pushCanvas(SkCanvas* canvas, const SkIPoint& origin);
-    void removeAll() SK_OVERRIDE;
+    void removeAll() override;
 
     /*
      * The following add/remove canvas methods are overrides from SkNWayCanvas
@@ -26,16 +26,16 @@
      * can share most of the other implementation of NWay we override those
      * methods to be no-ops.
      */
-    void addCanvas(SkCanvas*) SK_OVERRIDE { SkDEBUGFAIL("Invalid Op"); }
-    void removeCanvas(SkCanvas*) SK_OVERRIDE { SkDEBUGFAIL("Invalid Op"); }
+    void addCanvas(SkCanvas*) override { SkDEBUGFAIL("Invalid Op"); }
+    void removeCanvas(SkCanvas*) override { SkDEBUGFAIL("Invalid Op"); }
 
 protected:
-    void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
+    void didSetMatrix(const SkMatrix&) override;
 
-    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRegion(const SkRegion&, SkRegion::Op) SK_OVERRIDE;
+    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRegion(const SkRegion&, SkRegion::Op) override;
 
 private:
     void clipToZOrderedBounds();
diff --git a/src/utils/SkCanvasStateUtils.cpp b/src/utils/SkCanvasStateUtils.cpp
index 0a14802..f7a9447 100644
--- a/src/utils/SkCanvasStateUtils.cpp
+++ b/src/utils/SkCanvasStateUtils.cpp
@@ -141,15 +141,15 @@
     bool failed() { return fFailed; }
 
     // ClipVisitor
-    void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) SK_OVERRIDE {
+    void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) override {
         fFailed |= antialias;
     }
 
-    void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) SK_OVERRIDE {
+    void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) override {
         fFailed |= antialias;
     }
 
-    void clipPath(const SkPath&, SkRegion::Op, bool antialias) SK_OVERRIDE {
+    void clipPath(const SkPath&, SkRegion::Op, bool antialias) override {
         fFailed |= antialias;
     }
 
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index 8fe9f8a..7d0a8e8 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -32,12 +32,31 @@
     kSilent_PlaybackMode,
 };
 
-static bool should_draw_immediately(const SkBitmap* bitmap, const SkPaint* paint,
-                                    size_t bitmapSizeThreshold) {
+static uint64_t image_area(const SkImage* image) {
+    return sk_64_mul(image->width(), image->height());
+}
+
+// HACK -- see crbug.com/485243
+//
+// Work around case where Blink gives us an image, but will "mutate" it (by changing its contents
+// directly using webgl). Until that is fixed at the call-site, we treat gpu-backed-images as
+// mutable for now (at least for the purposes of deferred canvas)
+//
+static bool should_draw_gpu_image_immediately(const SkImage* image) {
+    return image->getTexture() != NULL;
+}
+
+static bool should_draw_immediately(const SkBitmap* bitmap, const SkImage* image,
+                                    const SkPaint* paint, size_t bitmapSizeThreshold) {
     if (bitmap && ((bitmap->getTexture() && !bitmap->isImmutable()) ||
-        (bitmap->getSize() > bitmapSizeThreshold))) {
+                   (bitmap->getSize() > bitmapSizeThreshold))) {
         return true;
     }
+    if (image) {
+        if (should_draw_gpu_image_immediately(image) || image_area(image) > bitmapSizeThreshold) {
+            return true;
+        }
+    }
     if (paint) {
         SkShader* shader = paint->getShader();
         // Here we detect the case where the shader is an SkBitmapProcShader
@@ -65,8 +84,8 @@
     DeferredPipeController();
     void setPlaybackCanvas(SkCanvas*);
     virtual ~DeferredPipeController();
-    void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE;
-    void notifyWritten(size_t bytes) SK_OVERRIDE;
+    void* requestBlock(size_t minRequest, size_t* actual) override;
+    void notifyWritten(size_t bytes) override;
     void playback(bool silent);
     bool hasPendingCommands() const { return fAllocator.totalUsed() != 0; }
     size_t storageAllocatedForRecording() const { return fAllocator.totalCapacity(); }
@@ -158,93 +177,95 @@
     void recordedDrawCommand();
     void setIsDrawingToLayer(bool value) {fIsDrawingToLayer = value;}
 
-    SkImageInfo imageInfo() const SK_OVERRIDE;
+    SkImageInfo imageInfo() const override;
 
-    GrRenderTarget* accessRenderTarget() SK_OVERRIDE;
+    GrRenderTarget* accessRenderTarget() override;
 
-    SkBaseDevice* onCreateCompatibleDevice(const CreateInfo&) SK_OVERRIDE;
+    SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override;
 
-    SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) SK_OVERRIDE;
+    SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) override;
 
 protected:
-    const SkBitmap& onAccessBitmap() SK_OVERRIDE;
-    bool onReadPixels(const SkImageInfo&, void*, size_t, int x, int y) SK_OVERRIDE;
-    bool onWritePixels(const SkImageInfo&, const void*, size_t, int x, int y) SK_OVERRIDE;
+    const SkBitmap& onAccessBitmap() override;
+    bool onReadPixels(const SkImageInfo&, void*, size_t, int x, int y) override;
+    bool onWritePixels(const SkImageInfo&, const void*, size_t, int x, int y) override;
 
     // None of the following drawing methods should ever get called on the
     // deferred device
-    void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE
+    void drawPaint(const SkDraw&, const SkPaint& paint) override
         {SkASSERT(0);}
-    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
-                            size_t count, const SkPoint[],
-                            const SkPaint& paint) SK_OVERRIDE
+    void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
+                    size_t count, const SkPoint[],
+                    const SkPaint& paint) override
         {SkASSERT(0);}
-    virtual void drawRect(const SkDraw&, const SkRect& r,
-                            const SkPaint& paint) SK_OVERRIDE
+    void drawRect(const SkDraw&, const SkRect& r,
+                  const SkPaint& paint) override
         {SkASSERT(0);}
-    void drawOval(const SkDraw&, const SkRect&, const SkPaint&) SK_OVERRIDE
+    void drawOval(const SkDraw&, const SkRect&, const SkPaint&) override
         {SkASSERT(0);}
-    virtual void drawRRect(const SkDraw&, const SkRRect& rr,
-                           const SkPaint& paint) SK_OVERRIDE
+    void drawRRect(const SkDraw&, const SkRRect& rr,
+                   const SkPaint& paint) override
     {SkASSERT(0);}
-    virtual void drawPath(const SkDraw&, const SkPath& path,
-                            const SkPaint& paint,
-                            const SkMatrix* prePathMatrix = NULL,
-                            bool pathIsMutable = false) SK_OVERRIDE
+    void drawPath(const SkDraw&, const SkPath& path,
+                  const SkPaint& paint,
+                  const SkMatrix* prePathMatrix = NULL,
+                  bool pathIsMutable = false) override
         {SkASSERT(0);}
-    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
-                            const SkMatrix& matrix, const SkPaint& paint) SK_OVERRIDE
+    void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+                    const SkMatrix& matrix, const SkPaint& paint) override
         {SkASSERT(0);}
-    virtual void drawBitmapRect(const SkDraw&, const SkBitmap&, const SkRect*,
-                                const SkRect&, const SkPaint&,
-                                SkCanvas::DrawBitmapRectFlags) SK_OVERRIDE
+    void drawBitmapRect(const SkDraw&, const SkBitmap&, const SkRect*,
+                        const SkRect&, const SkPaint&,
+                        SkCanvas::DrawBitmapRectFlags) override
         {SkASSERT(0);}
-    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
-                            int x, int y, const SkPaint& paint) SK_OVERRIDE
+    void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+                    int x, int y, const SkPaint& paint) override
         {SkASSERT(0);}
-    virtual void drawText(const SkDraw&, const void* text, size_t len,
-                            SkScalar x, SkScalar y, const SkPaint& paint) SK_OVERRIDE
+    void drawImage(const SkDraw&, const SkImage*, SkScalar, SkScalar, const SkPaint&) override
         {SkASSERT(0);}
-    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
-                             const SkScalar pos[], int scalarsPerPos,
-                             const SkPoint& offset, const SkPaint& paint) SK_OVERRIDE
+    void drawImageRect(const SkDraw&, const SkImage*, const SkRect*, const SkRect&,
+                       const SkPaint&) override
         {SkASSERT(0);}
-    virtual void drawTextOnPath(const SkDraw&, const void* text,
-                                size_t len, const SkPath& path,
-                                const SkMatrix* matrix,
-                                const SkPaint& paint) SK_OVERRIDE
+    void drawText(const SkDraw&, const void* text, size_t len,
+                  SkScalar x, SkScalar y, const SkPaint& paint) override
         {SkASSERT(0);}
-    virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode,
-                                int vertexCount, const SkPoint verts[],
-                                const SkPoint texs[], const SkColor colors[],
-                                SkXfermode* xmode, const uint16_t indices[],
-                                int indexCount, const SkPaint& paint) SK_OVERRIDE
+    void drawPosText(const SkDraw&, const void* text, size_t len,
+                     const SkScalar pos[], int scalarsPerPos,
+                     const SkPoint& offset, const SkPaint& paint) override
         {SkASSERT(0);}
-    virtual void drawPatch(const SkDraw&, const SkPoint cubics[12], const SkColor colors[4],
-                           const SkPoint texCoords[4], SkXfermode* xmode,
-                           const SkPaint& paint) SK_OVERRIDE
+    void drawTextOnPath(const SkDraw&, const void* text,
+                        size_t len, const SkPath& path,
+                        const SkMatrix* matrix,
+                        const SkPaint& paint) override
         {SkASSERT(0);}
-    virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
-                            const SkPaint&) SK_OVERRIDE
+    void drawVertices(const SkDraw&, SkCanvas::VertexMode,
+                      int vertexCount, const SkPoint verts[],
+                      const SkPoint texs[], const SkColor colors[],
+                      SkXfermode* xmode, const uint16_t indices[],
+                      int indexCount, const SkPaint& paint) override
+        {SkASSERT(0);}
+    void drawPatch(const SkDraw&, const SkPoint cubics[12], const SkColor colors[4],
+                   const SkPoint texCoords[4], SkXfermode* xmode,
+                   const SkPaint& paint) override
+        {SkASSERT(0);}
+    void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
+                    const SkPaint&) override
         {SkASSERT(0);}
 
-    void lockPixels() SK_OVERRIDE {}
-    void unlockPixels() SK_OVERRIDE {}
+    void lockPixels() override {}
+    void unlockPixels() override {}
 
-    bool allowImageFilter(const SkImageFilter*) SK_OVERRIDE {
+    bool canHandleImageFilter(const SkImageFilter*) override {
         return false;
     }
-    bool canHandleImageFilter(const SkImageFilter*) SK_OVERRIDE {
-        return false;
-    }
-    virtual bool filterImage(const SkImageFilter*, const SkBitmap&,
-                             const SkImageFilter::Context&, SkBitmap*, SkIPoint*) SK_OVERRIDE {
+    bool filterImage(const SkImageFilter*, const SkBitmap&,
+                     const SkImageFilter::Context&, SkBitmap*, SkIPoint*) override {
         return false;
     }
 
 private:
-    void flush() SK_OVERRIDE;
-    void replaceBitmapBackendForRasterSurface(const SkBitmap&) SK_OVERRIDE {}
+    void flush() override;
+    void replaceBitmapBackendForRasterSurface(const SkBitmap&) override {}
 
     void beginRecording();
     void init();
@@ -445,11 +466,11 @@
     SkASSERT(x + info.width() <= width());
     SkASSERT(y + info.height() <= height());
 
-    this->flushPendingCommands(kNormal_PlaybackMode);
-
     const SkImageInfo deviceInfo = this->imageInfo();
     if (info.width() == deviceInfo.width() && info.height() == deviceInfo.height()) {
         this->skipPendingCommands();
+    } else {
+        this->flushPendingCommands(kNormal_PlaybackMode);
     }
 
     this->prepareForImmediatePixelWrite();
@@ -461,16 +482,13 @@
     return immediateDevice()->accessBitmap(false);
 }
 
-SkBaseDevice* SkDeferredDevice::onCreateCompatibleDevice(const CreateInfo& cinfo) {
-    // Save layer usage not supported, and not required by SkDeferredCanvas.
-    SkASSERT(cinfo.fUsage != kSaveLayer_Usage);
-
+SkBaseDevice* SkDeferredDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) {
     // Create a compatible non-deferred device.
     // We do not create a deferred device because we know the new device
     // will not be used with a deferred canvas (there is no API for that).
     // And connecting a SkDeferredDevice to non-deferred canvas can result
     // in unpredictable behavior.
-    return immediateDevice()->onCreateCompatibleDevice(cinfo);
+    return immediateDevice()->onCreateDevice(cinfo, layerPaint);
 }
 
 SkSurface* SkDeferredDevice::newSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
@@ -487,11 +505,15 @@
 public:
     AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkBitmap* bitmap,
                               const SkPaint* paint) {
-        this->init(canvas, bitmap, paint);
+        this->init(canvas, bitmap, NULL, paint);
+    }
+    AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkImage* image,
+                              const SkPaint* paint) {
+        this->init(canvas, NULL, image, paint);
     }
 
     AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkPaint* paint) {
-        this->init(canvas, NULL, paint);
+        this->init(canvas, NULL, NULL, paint);
     }
 
     ~AutoImmediateDrawIfNeeded() {
@@ -500,9 +522,10 @@
         }
     }
 private:
-    void init(SkDeferredCanvas& canvas, const SkBitmap* bitmap, const SkPaint* paint) {
+    void init(SkDeferredCanvas& canvas, const SkBitmap* bitmap, const SkImage* image,
+              const SkPaint* paint) {
         if (canvas.isDeferredDrawing() &&
-            should_draw_immediately(bitmap, paint, canvas.getBitmapSizeThreshold())) {
+            should_draw_immediately(bitmap, image, paint, canvas.getBitmapSizeThreshold())) {
             canvas.setDeferredDrawing(false);
             fCanvas = &canvas;
         } else {
@@ -842,6 +865,34 @@
     this->recordedDrawCommand();
 }
 
+
+void SkDeferredCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y,
+                                   const SkPaint* paint) {
+    SkRect bounds = SkRect::MakeXYWH(x, y,
+                                     SkIntToScalar(image->width()), SkIntToScalar(image->height()));
+    if (fDeferredDrawing &&
+        this->isFullFrame(&bounds, paint) &&
+        isPaintOpaque(paint, image)) {
+        this->getDeferredDevice()->skipPendingCommands();
+    }
+    
+    AutoImmediateDrawIfNeeded autoDraw(*this, image, paint);
+    this->drawingCanvas()->drawImage(image, x, y, paint);
+    this->recordedDrawCommand();
+}
+void SkDeferredCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                                       const SkPaint* paint) {
+    if (fDeferredDrawing &&
+        this->isFullFrame(&dst, paint) &&
+        isPaintOpaque(paint, image)) {
+        this->getDeferredDevice()->skipPendingCommands();
+    }
+    
+    AutoImmediateDrawIfNeeded autoDraw(*this, image, paint);
+    this->drawingCanvas()->drawImageRect(image, src, dst, paint);
+    this->recordedDrawCommand();
+}
+
 void SkDeferredCanvas::onDrawBitmapNine(const SkBitmap& bitmap,
                                         const SkIRect& center, const SkRect& dst,
                                         const SkPaint* paint) {
diff --git a/src/utils/SkEventTracer.cpp b/src/utils/SkEventTracer.cpp
index ef2a055..d9f9258 100644
--- a/src/utils/SkEventTracer.cpp
+++ b/src/utils/SkEventTracer.cpp
@@ -5,11 +5,12 @@
  * found in the LICENSE file.
  */
 
+#include "SkAtomics.h"
 #include "SkEventTracer.h"
-#include "SkOnce.h"
+#include "SkLazyPtr.h"
 
-class SkDefaultEventTracer: public SkEventTracer {
-    virtual SkEventTracer::Handle
+class SkDefaultEventTracer : public SkEventTracer {
+    SkEventTracer::Handle
         addTraceEvent(char phase,
                       const uint8_t* categoryEnabledFlag,
                       const char* name,
@@ -18,42 +19,38 @@
                       const char** argNames,
                       const uint8_t* argTypes,
                       const uint64_t* argValues,
-                      uint8_t flags) SK_OVERRIDE { return 0; }
+                      uint8_t flags) override { return 0; }
 
-    virtual void
+    void
         updateTraceEventDuration(const uint8_t* categoryEnabledFlag,
                                  const char* name,
-                                 SkEventTracer::Handle handle) SK_OVERRIDE {};
+                                 SkEventTracer::Handle handle) override {}
 
-    const uint8_t* getCategoryGroupEnabled(const char* name) SK_OVERRIDE {
+    const uint8_t* getCategoryGroupEnabled(const char* name) override {
         static uint8_t no = 0;
         return &no;
-    };
-    virtual const char* getCategoryGroupName(
-      const uint8_t* categoryEnabledFlag) SK_OVERRIDE {
+    }
+    const char* getCategoryGroupName(
+      const uint8_t* categoryEnabledFlag) override {
         static const char* dummy = "dummy";
         return dummy;
-    };
+    }
 };
 
-SkEventTracer* SkEventTracer::gInstance;
+// We prefer gUserTracer if it's been set, otherwise we fall back on gDefaultTracer.
+static SkEventTracer* gUserTracer = nullptr;
+SK_DECLARE_STATIC_LAZY_PTR(SkDefaultEventTracer, gDefaultTracer);
 
-static void cleanup_tracer() {
-    // calling SetInstance will delete the existing instance.
-    SkEventTracer::SetInstance(NULL);
+void SkEventTracer::SetInstance(SkEventTracer* tracer) {
+    SkASSERT(nullptr == sk_atomic_load(&gUserTracer, sk_memory_order_acquire));
+    sk_atomic_store(&gUserTracer, tracer, sk_memory_order_release);
+    // An atomic load during process shutdown is probably overkill, but safe overkill.
+    atexit([](){ SkDELETE(sk_atomic_load(&gUserTracer, sk_memory_order_acquire)); });
 }
 
-static void intialize_default_tracer(SkEventTracer* current_instance) {
-    if (NULL == current_instance) {
-        SkEventTracer::SetInstance(SkNEW(SkDefaultEventTracer));
-    }
-    atexit(cleanup_tracer);
-}
-
-
-SK_DECLARE_STATIC_ONCE(once);
 SkEventTracer* SkEventTracer::GetInstance() {
-    SkOnce(&once, intialize_default_tracer, SkEventTracer::gInstance);
-    SkASSERT(SkEventTracer::gInstance);
-    return SkEventTracer::gInstance;
+    if (SkEventTracer* tracer = sk_atomic_load(&gUserTracer, sk_memory_order_acquire)) {
+        return tracer;
+    }
+    return gDefaultTracer.get();
 }
diff --git a/src/utils/SkFrontBufferedStream.cpp b/src/utils/SkFrontBufferedStream.cpp
index 562d376..beead37 100644
--- a/src/utils/SkFrontBufferedStream.cpp
+++ b/src/utils/SkFrontBufferedStream.cpp
@@ -14,21 +14,23 @@
     // Called by Create.
     FrontBufferedStream(SkStream*, size_t bufferSize);
 
-    size_t read(void* buffer, size_t size) SK_OVERRIDE;
+    size_t read(void* buffer, size_t size) override;
 
-    bool isAtEnd() const SK_OVERRIDE;
+    bool peek(void* buffer, size_t size) const override;
 
-    bool rewind() SK_OVERRIDE;
+    bool isAtEnd() const override;
 
-    bool hasPosition() const SK_OVERRIDE { return true; }
+    bool rewind() override;
 
-    size_t getPosition() const SK_OVERRIDE { return fOffset; }
+    bool hasPosition() const override { return true; }
 
-    bool hasLength() const SK_OVERRIDE { return fHasLength; }
+    size_t getPosition() const override { return fOffset; }
 
-    size_t getLength() const SK_OVERRIDE { return fLength; }
+    bool hasLength() const override { return fHasLength; }
 
-    SkStreamRewindable* duplicate() const SK_OVERRIDE { return NULL; }
+    size_t getLength() const override { return fLength; }
+
+    SkStreamRewindable* duplicate() const override { return NULL; }
 
 private:
     SkAutoTDelete<SkStream> fStream;
@@ -155,6 +157,20 @@
     return bytesReadDirectly;
 }
 
+bool FrontBufferedStream::peek(void* dst, size_t size) const {
+    // Keep track of the offset so we can return to it.
+    const size_t start = fOffset;
+    if (start + size > fBufferSize) {
+        // This stream is not able to buffer enough.
+        return false;
+    }
+    FrontBufferedStream* nonConstThis = const_cast<FrontBufferedStream*>(this);
+    SkDEBUGCODE(const size_t bytesRead =) nonConstThis->read(dst, size);
+    SkASSERT(bytesRead == size);
+    nonConstThis->fOffset = start;
+    return true;
+}
+
 size_t FrontBufferedStream::read(void* voidDst, size_t size) {
     // Cast voidDst to a char* for easy addition.
     char* dst = reinterpret_cast<char*>(voidDst);
diff --git a/src/utils/SkInterpolator.cpp b/src/utils/SkInterpolator.cpp
index 97574e4..03e7e4a 100644
--- a/src/utils/SkInterpolator.cpp
+++ b/src/utils/SkInterpolator.cpp
@@ -62,8 +62,7 @@
                                   SkMSec nextTime, const SkScalar blend[4]) {
     SkASSERT(time > prevTime && time < nextTime);
 
-    SkScalar t = SkScalarDiv((SkScalar)(time - prevTime),
-                             (SkScalar)(nextTime - prevTime));
+    SkScalar t = (SkScalar)(time - prevTime) / (SkScalar)(nextTime - prevTime);
     return blend ?
             SkUnitCubicInterp(t, blend[0], blend[1], blend[2], blend[3]) : t;
 }
diff --git a/src/utils/SkLua.cpp b/src/utils/SkLua.cpp
index ba79933..11d9b29 100644
--- a/src/utils/SkLua.cpp
+++ b/src/utils/SkLua.cpp
@@ -927,15 +927,15 @@
     return 1;
 }
 
-static int lpaint_getFilterLevel(lua_State* L) {
-    SkLua(L).pushU32(get_obj<SkPaint>(L, 1)->getFilterLevel());
+static int lpaint_getFilterQuality(lua_State* L) {
+    SkLua(L).pushU32(get_obj<SkPaint>(L, 1)->getFilterQuality());
     return 1;
 }
 
-static int lpaint_setFilterLevel(lua_State* L) {
+static int lpaint_setFilterQuality(lua_State* L) {
     int level = lua2int_def(L, 2, -1);
     if (level >= 0 && level <= 3) {
-        get_obj<SkPaint>(L, 1)->setFilterLevel((SkPaint::FilterLevel)level);
+        get_obj<SkPaint>(L, 1)->setFilterQuality((SkFilterQuality)level);
     }
     return 0;
 }
@@ -1131,8 +1131,8 @@
     { "setAntiAlias", lpaint_setAntiAlias },
     { "isDither", lpaint_isDither },
     { "setDither", lpaint_setDither },
-    { "getFilterLevel", lpaint_getFilterLevel },
-    { "setFilterLevel", lpaint_setFilterLevel },
+    { "getFilterQuality", lpaint_getFilterQuality },
+    { "setFilterQuality", lpaint_setFilterQuality },
     { "isUnderlineText", lpaint_isUnderlineText },
     { "isStrikeThruText", lpaint_isStrikeThruText },
     { "isFakeBoldText", lpaint_isFakeBoldText },
@@ -1495,10 +1495,10 @@
     return gStr[dir];
 }
 
-static int lpath_isNestedRects(lua_State* L) {
+static int lpath_isNestedFillRects(lua_State* L) {
     SkRect rects[2];
     SkPath::Direction dirs[2];
-    bool pred = get_obj<SkPath>(L, 1)->isNestedRects(rects, dirs);
+    bool pred = get_obj<SkPath>(L, 1)->isNestedFillRects(rects, dirs);
     int ret_count = 1;
     lua_pushboolean(L, pred);
     if (pred) {
@@ -1562,7 +1562,7 @@
     { "isConvex", lpath_isConvex },
     { "isEmpty", lpath_isEmpty },
     { "isRect", lpath_isRect },
-    { "isNestedRects", lpath_isNestedRects },
+    { "isNestedFillRects", lpath_isNestedFillRects },
     { "countPoints", lpath_countPoints },
     { "reset", lpath_reset },
     { "moveTo", lpath_moveTo },
diff --git a/src/utils/SkMD5.h b/src/utils/SkMD5.h
index 889338b..ed55793 100644
--- a/src/utils/SkMD5.h
+++ b/src/utils/SkMD5.h
@@ -24,18 +24,24 @@
     /** Processes input, adding it to the digest.
      *  Note that this treats the buffer as a series of uint8_t values.
      */
-    bool write(const void* buffer, size_t size) SK_OVERRIDE {
+    bool write(const void* buffer, size_t size) override {
         this->update(reinterpret_cast<const uint8_t*>(buffer), size);
         return true;
     }
 
-    size_t bytesWritten() const SK_OVERRIDE { return SkToSizeT(this->byteCount); }
+    size_t bytesWritten() const override { return SkToSizeT(this->byteCount); }
 
     /** 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];
+        bool operator ==(Digest const& other) const {
+            return 0 == memcmp(data, other.data, sizeof(data));
+        }
+        bool operator !=(Digest const& other) const {
+            return 0 != memcmp(data, other.data, sizeof(data));
+        }
     };
 
     /** Computes and returns the digest. */
diff --git a/src/utils/SkNinePatch.cpp b/src/utils/SkNinePatch.cpp
index 4e0d1d1..ce8d480 100644
--- a/src/utils/SkNinePatch.cpp
+++ b/src/utils/SkNinePatch.cpp
@@ -161,7 +161,7 @@
         if (bounds.width() >= fixed)
             stretchX = (bounds.width() - fixed) / stretchSize;
         else // reuse stretchX, but keep it negative as a signal
-            stretchX = SkScalarDiv(-bounds.width(), fixed);
+            stretchX = -bounds.width() / fixed;
     }
 
     if (numYStretch > 0) {
@@ -173,7 +173,7 @@
         if (bounds.height() >= fixed)
             stretchY = (bounds.height() - fixed) / stretchSize;
         else // reuse stretchX, but keep it negative as a signal
-            stretchY = SkScalarDiv(-bounds.height(), fixed);
+            stretchY = -bounds.height() / fixed;
     }
 
 #if 0
diff --git a/src/utils/SkPaintFilterCanvas.cpp b/src/utils/SkPaintFilterCanvas.cpp
new file mode 100644
index 0000000..1abebef
--- /dev/null
+++ b/src/utils/SkPaintFilterCanvas.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPaintFilterCanvas.h"
+
+#include "SkPaint.h"
+#include "SkTLazy.h"
+
+class SkPaintFilterCanvas::AutoPaintFilter {
+public:
+    AutoPaintFilter(const SkPaintFilterCanvas* canvas, Type type, const SkPaint* paint) {
+        if (paint) {
+            canvas->onFilterPaint(fLazyPaint.set(*paint), type);
+        }
+    }
+
+    AutoPaintFilter(const SkPaintFilterCanvas* canvas, Type type, const SkPaint& paint) {
+        canvas->onFilterPaint(fLazyPaint.set(paint), type);
+    }
+
+    const SkPaint* paint() const { return fLazyPaint.getMaybeNull(); }
+
+private:
+    SkTLazy<SkPaint> fLazyPaint;
+};
+
+SkPaintFilterCanvas::SkPaintFilterCanvas(int width, int height) : INHERITED(width, height) { }
+
+void SkPaintFilterCanvas::onDrawPaint(const SkPaint& paint) {
+    AutoPaintFilter apf(this, kPaint_Type, paint);
+    this->INHERITED::onDrawPaint(*apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                                       const SkPaint& paint) {
+    AutoPaintFilter apf(this, kPoint_Type, paint);
+    this->INHERITED::onDrawPoints(mode, count, pts, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
+    AutoPaintFilter apf(this, kRect_Type, paint);
+    this->INHERITED::onDrawRect(rect, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
+    AutoPaintFilter apf(this, kRRect_Type, paint);
+    this->INHERITED::onDrawRRect(rrect, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+                                       const SkPaint& paint) {
+    AutoPaintFilter apf(this, kDRRect_Type, paint);
+    this->INHERITED::onDrawDRRect(outer, inner, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawOval(const SkRect& rect, const SkPaint& paint) {
+    AutoPaintFilter apf(this, kOval_Type, paint);
+    this->INHERITED::onDrawOval(rect, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
+    AutoPaintFilter apf(this, kPath_Type, paint);
+    this->INHERITED::onDrawPath(path, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawBitmap(const SkBitmap& bm, SkScalar left, SkScalar top,
+                                       const SkPaint* paint) {
+    AutoPaintFilter apf(this, kBitmap_Type, paint);
+    this->INHERITED::onDrawBitmap(bm, left, top, apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst,
+                                           const SkPaint* paint, DrawBitmapRectFlags flags) {
+    AutoPaintFilter apf(this, kBitmap_Type, paint);
+    this->INHERITED::onDrawBitmapRect(bm, src, dst, apf.paint(), flags);
+}
+
+void SkPaintFilterCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
+                                      const SkPaint* paint) {
+    AutoPaintFilter apf(this, kBitmap_Type, paint);
+    this->INHERITED::onDrawImage(image, left, top, apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawImageRect(const SkImage* image, const SkRect* src,
+                                          const SkRect& dst, const SkPaint* paint) {
+    AutoPaintFilter apf(this, kBitmap_Type, paint);
+    this->INHERITED::onDrawImageRect(image, src, dst, apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center,
+                                           const SkRect& dst, const SkPaint* paint) {
+    AutoPaintFilter apf(this, kBitmap_Type, paint);
+    this->INHERITED::onDrawBitmapNine(bm, center, dst, apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawSprite(const SkBitmap& bm, int left, int top,
+                                       const SkPaint* paint) {
+    AutoPaintFilter apf(this, kBitmap_Type, paint);
+    this->INHERITED::onDrawSprite(bm, left, top, apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
+                                         const SkPoint vertices[], const SkPoint texs[],
+                                         const SkColor colors[], SkXfermode* xmode,
+                                         const uint16_t indices[], int indexCount,
+                                         const SkPaint& paint) {
+    AutoPaintFilter apf(this, kVertices_Type, paint);
+    this->INHERITED::onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode, indices,
+                                    indexCount, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawPatch(const SkPoint cubics[], const SkColor colors[],
+                                      const SkPoint texCoords[], SkXfermode* xmode,
+                                      const SkPaint& paint) {
+    AutoPaintFilter apf(this, kPatch_Type, paint);
+    this->INHERITED::onDrawPatch(cubics, colors, texCoords, xmode, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* m,
+                                        const SkPaint* paint) {
+    AutoPaintFilter apf(this, kPicture_Type, paint);
+    this->INHERITED::onDrawPicture(picture, m, apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+                                     const SkPaint& paint) {
+    AutoPaintFilter apf(this, kText_Type, paint);
+    this->INHERITED::onDrawText(text, byteLength, x, y, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+                                        const SkPaint& paint) {
+    AutoPaintFilter apf(this, kText_Type, paint);
+    this->INHERITED::onDrawPosText(text, byteLength, pos, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+                                         SkScalar constY, const SkPaint& paint) {
+    AutoPaintFilter apf(this, kText_Type, paint);
+    this->INHERITED::onDrawPosTextH(text, byteLength, xpos, constY, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+                                           const SkMatrix* matrix, const SkPaint& paint) {
+    AutoPaintFilter apf(this, kText_Type, paint);
+    this->INHERITED::onDrawTextOnPath(text, byteLength, path, matrix, *apf.paint());
+}
+
+void SkPaintFilterCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+                                         const SkPaint& paint) {
+    AutoPaintFilter apf(this, kTextBlob_Type, paint);
+    this->INHERITED::onDrawTextBlob(blob, x, y, *apf.paint());
+}
diff --git a/src/utils/SkPatchUtils.cpp b/src/utils/SkPatchUtils.cpp
index c9c526e..08fd438 100644
--- a/src/utils/SkPatchUtils.cpp
+++ b/src/utils/SkPatchUtils.cpp
@@ -48,12 +48,7 @@
         fPoints[2] = c;
         fPoints[3] = d;
         
-        SkScalar cx[4], cy[4];
-        SkGetCubicCoeff(fPoints, cx, cy);
-        fCoefs[0].set(cx[0], cy[0]);
-        fCoefs[1].set(cx[1], cy[1]);
-        fCoefs[2].set(cx[2], cy[2]);
-        fCoefs[3].set(cx[3], cy[3]);
+        SkCubicToCoeff(fPoints, fCoefs);
         
         this->restart(1);
     }
@@ -61,12 +56,7 @@
     explicit FwDCubicEvaluator(const SkPoint points[4])  {
         memcpy(fPoints, points, 4 * sizeof(SkPoint));
         
-        SkScalar cx[4], cy[4];
-        SkGetCubicCoeff(fPoints, cx, cy);
-        fCoefs[0].set(cx[0], cy[0]);
-        fCoefs[1].set(cx[1], cy[1]);
-        fCoefs[2].set(cx[2], cy[2]);
-        fCoefs[3].set(cx[3], cy[3]);
+        SkCubicToCoeff(fPoints, fCoefs);
         
         this->restart(1);
     }
diff --git a/src/utils/SkPathUtils.cpp b/src/utils/SkPathUtils.cpp
deleted file mode 100644
index fdca09a..0000000
--- a/src/utils/SkPathUtils.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- *  CAUTION: EXPERIMENTAL CODE
- *
- *  This code is not to be used and will not be supported
- *  if it fails on you. DO NOT USE!
- *
- */
-
-#include "SkPathUtils.h"
-
-#include "SkPath.h"
-#include "SkPathOps.h" // this can't be found, how do I link it?
-#include "SkRegion.h"
-
-typedef void (*line2path)(SkPath*, const char*, int, int);
-#define SQRT_2 1.41421356237f
-#define ON  0xFF000000 // black pixel
-#define OFF 0x00000000 // transparent pixel
-
-// assumes stride is in bytes
-/*
-static void FillRandomBits( int chars, char* bits ){
-    SkTime time;
-    SkRandom rand = SkRandom( time.GetMSecs() );
-
-    for (int i = 0; i < chars; ++i){
-        bits[i] = rand.nextU();
-    }
-}OA
-*/
-
-static int GetBit( const char* buffer, int x ) {
-    int byte = x >> 3;
-    int bit = x & 7;
-
-    return buffer[byte] & (128 >> bit);
-}
-
-/*
-static void Line2path_pixel(SkPath* path, const char* line,
-                            int lineIdx, int width) {
-    for (int i = 0; i < width; ++i) {
-        // simply makes every ON pixel into a rect path
-        if (GetBit(line,i)) {
-            path->addRect(SkRect::MakeXYWH(i, lineIdx, 1, 1),
-                          SkPath::kCW_Direction);
-        }
-    }
-}
-
-static void Line2path_pixelCircle(SkPath* path, const char* line,
-                                  int lineIdx, int width) {
-    for (int i = 0; i < width; ++i) {
-        // simply makes every ON pixel into a circle path
-        if (GetBit(line,i)) {
-            path->addCircle(i + SK_ScalarHalf,
-                            lineIdx + SK_ScalarHalf,
-                            SQRT_2 / 2.0f);
-        }
-    }
-}
-*/
-
-static void Line2path_span(SkPath* path, const char* line,
-                           int lineIdx, int width) {
-    bool inRun = 0;
-    int start = 1;
-
-    for (int i = 0; i < width; ++i) {
-        int curPixel = GetBit(line,i);
-
-        if ( (curPixel!=0) != inRun ) { // if transition
-            if (curPixel) { // if transition on
-                inRun = 1;
-                start = i; // mark beginning of span
-            }else { // if transition off add the span as a path
-                inRun = 0;
-                path->addRect(SkRect::MakeXYWH(SkIntToScalar(start), SkIntToScalar(lineIdx),
-                                               SkIntToScalar(i-start), SK_Scalar1),
-                              SkPath::kCW_Direction);
-            }
-        }
-    }
-
-    if (inRun==1) { // close any open spans
-        int end = 0;
-        if ( GetBit(line,width-1) ) ++end;
-        path->addRect(SkRect::MakeXYWH(SkIntToScalar(start), SkIntToScalar(lineIdx),
-                                       SkIntToScalar(width - 1 + end - start), SK_Scalar1),
-                      SkPath::kCW_Direction);
-    } else if ( GetBit(line, width - 1) ) { // if last pixel on add
-        path->addRect(SkRect::MakeXYWH(width - SK_Scalar1, SkIntToScalar(lineIdx),
-                                       SK_Scalar1, SK_Scalar1),
-                      SkPath::kCW_Direction);
-    }
-}
-
-void SkPathUtils::BitsToPath_Path(SkPath* path,
-                        const char* bitmap,
-                        int w, int h, int stride) {
-    // loop for every line in bitmap
-    for (int i = 0; i < h; ++i) {
-        // fn ptr handles each line separately
-        //l2p_fn(path, &bitmap[i*stride], i, w);
-        Line2path_span(path, &bitmap[i*stride], i, w);
-    }
-    Simplify(*path, path); // simplify resulting path.
-}
-
-void SkPathUtils::BitsToPath_Region(SkPath* path,
-                               const char* bitmap,
-                               int w, int h, int stride) {
-    SkRegion region;
-
-    // loop for each line
-    for (int y = 0; y < h; ++y){
-        bool inRun = 0;
-        int start = 1;
-        const char* line = &bitmap[y * stride];
-
-        // loop for each pixel
-        for (int i = 0; i < w; ++i) {
-            int curPixel = GetBit(line,i);
-
-            if ( (curPixel!=0) != inRun ) { // if transition
-                if (curPixel) { // if transition on
-                    inRun = 1;
-                    start = i; // mark beginning of span
-                }else { // if transition off add the span as a path
-                    inRun = 0;
-                    //add here
-                    region.op(SkIRect::MakeXYWH(start, y, i-start, 1),
-                              SkRegion::kUnion_Op );
-                }
-            }
-        }
-        if (inRun==1) { // close any open spans
-            int end = 0;
-            if ( GetBit(line,w-1) ) ++end;
-            // add the thing here
-            region.op(SkIRect::MakeXYWH(start, y, w-1-start+end, 1),
-                      SkRegion::kUnion_Op );
-
-        } else if ( GetBit(line,w-1) ) { // if last pixel on add rect
-            // add the thing here
-            region.op(SkIRect::MakeXYWH(w-1, y, 1, 1),
-                      SkRegion::kUnion_Op );
-        }
-    }
-    // convert region to path
-    region.getBoundaryPath(path);
-}
diff --git a/src/utils/SkPictureUtils.cpp b/src/utils/SkPictureUtils.cpp
index be7c431..a8a251c 100644
--- a/src/utils/SkPictureUtils.cpp
+++ b/src/utils/SkPictureUtils.cpp
@@ -12,13 +12,6 @@
 #include "SkRecord.h"
 #include "SkShader.h"
 
-struct MeasureRecords {
-    template <typename T> size_t operator()(const T& op) { return 0; }
-    size_t operator()(const SkRecords::DrawPicture& op) {
-        return SkPictureUtils::ApproximateBytesUsed(op.picture);
-    }
-};
-
 size_t SkPictureUtils::ApproximateBytesUsed(const SkPicture* pict) {
     size_t byteCount = sizeof(*pict);
 
@@ -26,10 +19,7 @@
     if (pict->fBBH.get()) {
         byteCount += pict->fBBH->bytesUsed();
     }
-    MeasureRecords visitor;
-    for (unsigned curOp = 0; curOp < pict->fRecord->count(); curOp++) {
-        byteCount += pict->fRecord->visit<size_t>(curOp, visitor);
-    }
+    byteCount += pict->fApproxBytesUsedBySubPictures;
 
     return byteCount;
 }
diff --git a/src/utils/SkSHA1.h b/src/utils/SkSHA1.h
index e7ede64..aa0867f 100644
--- a/src/utils/SkSHA1.h
+++ b/src/utils/SkSHA1.h
@@ -24,12 +24,12 @@
     /** Processes input, adding it to the digest.
      *  Note that this treats the buffer as a series of uint8_t values.
      */
-    bool write(const void* buffer, size_t size) SK_OVERRIDE {
+    bool write(const void* buffer, size_t size) override {
         update(reinterpret_cast<const uint8_t*>(buffer), size);
         return true;
     }
 
-    size_t bytesWritten() const SK_OVERRIDE { return SkToSizeT(this->byteCount); }
+    size_t bytesWritten() const override { return SkToSizeT(this->byteCount); }
 
     /** Processes input, adding it to the digest. Calling this after finish is undefined. */
     void update(const uint8_t* input, size_t length);
diff --git a/src/utils/SkTLogic.h b/src/utils/SkTLogic.h
index 2b5df0b..d188242 100644
--- a/src/utils/SkTLogic.h
+++ b/src/utils/SkTLogic.h
@@ -82,6 +82,7 @@
  *  SK_WHEN(!SkTrue, int) f(void* ptr) { return 2; }
  */
 #define SK_WHEN(cond_prefix, T) typename SkTEnableIf_c<cond_prefix::value, T>::type
+#define SK_WHEN_C(cond, T) typename SkTEnableIf_c<cond, T>::type
 
 // See http://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector
 #define SK_CREATE_MEMBER_DETECTOR(member)                                           \
diff --git a/src/utils/SkTextBox.cpp b/src/utils/SkTextBox.cpp
index 0bb7a6b..af05cad 100644
--- a/src/utils/SkTextBox.cpp
+++ b/src/utils/SkTextBox.cpp
@@ -244,9 +244,9 @@
     SkCanvas* fCanvas;
 public:
     CanvasVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
-    
-    virtual void operator()(const char text[], size_t length, SkScalar x, SkScalar y,
-                            const SkPaint& paint) SK_OVERRIDE {
+
+    void operator()(const char text[], size_t length, SkScalar x, SkScalar y,
+                    const SkPaint& paint) override {
         fCanvas->drawText(text, length, x, y, paint);
     }
 };
@@ -282,9 +282,9 @@
 class TextBlobVisitor : public SkTextBox::Visitor {
 public:
     SkTextBlobBuilder fBuilder;
-    
-    virtual void operator()(const char text[], size_t length, SkScalar x, SkScalar y,
-                            const SkPaint& paint) SK_OVERRIDE {
+
+    void operator()(const char text[], size_t length, SkScalar x, SkScalar y,
+                    const SkPaint& paint) override {
         SkPaint p(paint);
         p.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         const int count = paint.countText(text, length);
diff --git a/src/utils/SkTextureCompressor_Blitter.h b/src/utils/SkTextureCompressor_Blitter.h
index 186df53..73d8ee4 100644
--- a/src/utils/SkTextureCompressor_Blitter.h
+++ b/src/utils/SkTextureCompressor_Blitter.h
@@ -85,18 +85,18 @@
     virtual ~SkTCompressedAlphaBlitter() { this->flushRuns(); }
 
     // Blit a horizontal run of one or more pixels.
-    void blitH(int x, int y, int width) SK_OVERRIDE {
+    void blitH(int x, int y, int width) override {
         // This function is intended to be called from any standard RGB
         // buffer, so we should never encounter it. However, if some code
         // path does end up here, then this needs to be investigated.
         SkFAIL("Not implemented!");
     }
-    
+
     // Blit a horizontal run of antialiased pixels; runs[] is a *sparse*
     // zero-terminated run-length encoding of spans of constant alpha values.
-    virtual void blitAntiH(int x, int y,
-                           const SkAlpha antialias[],
-                           const int16_t runs[]) SK_OVERRIDE {
+    void blitAntiH(int x, int y,
+                   const SkAlpha antialias[],
+                   const int16_t runs[]) override {
         SkASSERT(0 == x);
 
         // Make sure that the new row to blit is either the first
@@ -141,7 +141,7 @@
     }
     
     // Blit a vertical run of pixels with a constant alpha value.
-    void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
+    void blitV(int x, int y, int height, SkAlpha alpha) override {
         // This function is currently not implemented. It is not explicitly
         // required by the contract, but if at some time a code path runs into
         // this function (which is entirely possible), it needs to be implemented.
@@ -163,7 +163,7 @@
 #ifdef SK_DEBUG
     bool fCalledOnceWithNonzeroY;
 #endif
-    void blitRect(int x, int y, int width, int height) SK_OVERRIDE {
+    void blitRect(int x, int y, int width, int height) override {
 
         // Assumptions:
         SkASSERT(0 == x);
@@ -278,8 +278,8 @@
     // Blit a rectangle with one alpha-blended column on the left,
     // width (zero or more) opaque pixels, and one alpha-blended column
     // on the right. The result will always be at least two pixels wide.
-    virtual void blitAntiRect(int x, int y, int width, int height,
-                              SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE {
+    void blitAntiRect(int x, int y, int width, int height,
+                      SkAlpha leftAlpha, SkAlpha rightAlpha) override {
         // This function is currently not implemented. It is not explicitly
         // required by the contract, but if at some time a code path runs into
         // this function (which is entirely possible), it needs to be implemented.
@@ -306,7 +306,7 @@
 #ifdef SK_DEBUG
     bool fBlitMaskCalled;
 #endif
-    void blitMask(const SkMask& mask, const SkIRect& clip) SK_OVERRIDE {
+    void blitMask(const SkMask& mask, const SkIRect& clip) override {
 
         // Assumptions:
         SkASSERT(!fBlitMaskCalled);
@@ -368,7 +368,7 @@
     // If the blitter just sets a single value for each pixel, return the
     // bitmap it draws into, and assign value. If not, return NULL and ignore
     // the value parameter.
-    const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE {
+    const SkBitmap* justAnOpaqueColor(uint32_t* value) override {
         return NULL;
     }
 
@@ -378,7 +378,7 @@
      * to preserve semantics if blitAntiH doesn't get called in too many
      * weird ways...
      */
-    int requestRowsPreserved() const SK_OVERRIDE { return BlockDim; }
+    int requestRowsPreserved() const override { return BlockDim; }
 
 private:
     static const int kPixelsPerBlock = BlockDim * BlockDim;
diff --git a/src/utils/android/SkAndroidSDKCanvas.cpp b/src/utils/android/SkAndroidSDKCanvas.cpp
new file mode 100644
index 0000000..06b960b
--- /dev/null
+++ b/src/utils/android/SkAndroidSDKCanvas.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkAndroidSDKCanvas.h"
+
+#include "SkColorFilter.h"
+#include "SkPathEffect.h"
+#include "SkShader.h"
+
+namespace {
+
+/** Discard SkShaders not exposed by the Android Java API. */
+
+void CheckShader(SkPaint* paint) {
+    SkShader* shader = paint->getShader();
+    if (!shader) {
+        return;
+    }
+
+    if (shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
+        return;
+    }
+    if (shader->asACompose(NULL)) {
+        return;
+    }
+    SkShader::GradientType gtype = shader->asAGradient(NULL);
+    if (gtype == SkShader::kLinear_GradientType ||
+        gtype == SkShader::kRadial_GradientType ||
+        gtype == SkShader::kSweep_GradientType) {
+        return;
+    }
+    paint->setShader(NULL);
+}
+
+void Filter(SkPaint* paint) {
+
+    uint32_t flags = paint->getFlags();
+    flags &= ~SkPaint::kLCDRenderText_Flag;
+    paint->setFlags(flags);
+
+    // Android doesn't support Xfermodes above kLighten_Mode
+    SkXfermode::Mode mode;
+    SkXfermode::AsMode(paint->getXfermode(), &mode);
+    if (mode > SkXfermode::kLighten_Mode) {
+        paint->setXfermode(NULL);
+    }
+
+    // Force bilinear scaling or none
+    if (paint->getFilterQuality() != kNone_SkFilterQuality) {
+        paint->setFilterQuality(kLow_SkFilterQuality);
+    }
+
+    CheckShader(paint);
+
+    // Android SDK only supports mode & matrix color filters
+    // (and, again, no modes above kLighten_Mode).
+    SkColorFilter* cf = paint->getColorFilter();
+    if (cf) {
+        SkColor color;
+        SkXfermode::Mode mode;
+        SkScalar srcColorMatrix[20];
+        bool isMode = cf->asColorMode(&color, &mode);
+        if (isMode && mode > SkXfermode::kLighten_Mode) {
+            paint->setColorFilter(
+                SkColorFilter::CreateModeFilter(color, SkXfermode::kSrcOver_Mode));
+        } else if (!isMode && !cf->asColorMatrix(srcColorMatrix)) {
+            paint->setColorFilter(NULL);
+        }
+    }
+
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+    SkPathEffect* pe = paint->getPathEffect();
+    if (pe && !pe->exposedInAndroidJavaAPI()) {
+        paint->setPathEffect(NULL);
+    }
+#endif
+
+    // TODO: Android doesn't support all the flags that can be passed to
+    // blur filters; we need plumbing to get them out.
+
+    paint->setImageFilter(NULL);
+    paint->setLooper(NULL);
+};
+
+}  // namespace
+
+#define FILTER(p)             \
+    SkPaint filteredPaint(p); \
+    Filter(&filteredPaint);
+
+#define FILTER_PTR(p)                          \
+    SkTLazy<SkPaint> lazyPaint;                \
+    SkPaint* filteredPaint = (SkPaint*) p;     \
+    if (p) {                                   \
+        filteredPaint = lazyPaint.set(*p);     \
+        Filter(filteredPaint);                 \
+    }
+
+
+SkAndroidSDKCanvas::SkAndroidSDKCanvas() : fProxyTarget(NULL) { }
+
+void SkAndroidSDKCanvas::reset(SkCanvas* newTarget) { fProxyTarget = newTarget; }
+
+void SkAndroidSDKCanvas::onDrawPaint(const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawPaint(filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawPoints(PointMode pMode,
+                                               size_t count,
+                                               const SkPoint pts[],
+                                               const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawPoints(pMode, count, pts, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawOval(const SkRect& r, const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawOval(r, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawRect(r, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawRRect(const SkRRect& r, const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawRRect(r, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawPath(path, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawBitmap(const SkBitmap& bitmap,
+                                               SkScalar left,
+                                               SkScalar top,
+                                               const SkPaint* paint) {
+    FILTER_PTR(paint);
+    fProxyTarget->drawBitmap(bitmap, left, top, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawBitmapRect(const SkBitmap& bitmap,
+                                                   const SkRect* src,
+                                                   const SkRect& dst,
+                                                   const SkPaint* paint,
+                                                   DrawBitmapRectFlags flags) {
+    FILTER_PTR(paint);
+    fProxyTarget->drawBitmapRectToRect(bitmap, src, dst, filteredPaint, flags);
+}
+void SkAndroidSDKCanvas::onDrawBitmapNine(const SkBitmap& bitmap,
+                                                   const SkIRect& center,
+                                                   const SkRect& dst,
+                                                   const SkPaint* paint) {
+    FILTER_PTR(paint);
+    fProxyTarget->drawBitmapNine(bitmap, center, dst, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawSprite(const SkBitmap& bitmap,
+                                               int left,
+                                               int top,
+                                               const SkPaint* paint) {
+    FILTER_PTR(paint);
+    fProxyTarget->drawSprite(bitmap, left, top, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawVertices(VertexMode vMode,
+                                                 int vertexCount,
+                                                 const SkPoint vertices[],
+                    const SkPoint texs[], const SkColor colors[], SkXfermode* xMode,
+                    const uint16_t indices[], int indexCount,
+                    const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawVertices(vMode, vertexCount, vertices, texs, colors,
+                               xMode, indices, indexCount, filteredPaint);
+}
+
+void SkAndroidSDKCanvas::onDrawDRRect(const SkRRect& outer,
+                                               const SkRRect& inner,
+                                               const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawDRRect(outer, inner, filteredPaint);
+}
+
+void SkAndroidSDKCanvas::onDrawText(const void* text,
+                                             size_t byteLength,
+                                             SkScalar x,
+                                             SkScalar y,
+                                             const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawText(text, byteLength, x, y, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawPosText(const void* text,
+                                                size_t byteLength,
+                                                const SkPoint pos[],
+                                                const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawPosText(text, byteLength, pos, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawPosTextH(const void* text,
+                                                 size_t byteLength,
+                                                 const SkScalar xpos[],
+                                                 SkScalar constY,
+                                                 const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawPosTextH(text, byteLength, xpos, constY, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawTextOnPath(const void* text,
+                                                   size_t byteLength,
+                                                   const SkPath& path,
+                                                   const SkMatrix* matrix,
+                                                   const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawTextOnPath(text, byteLength, path, matrix, filteredPaint);
+}
+void SkAndroidSDKCanvas::onDrawTextBlob(const SkTextBlob* blob,
+                                                 SkScalar x,
+                                                 SkScalar y,
+                                                 const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawTextBlob(blob, x, y, filteredPaint);
+}
+
+void SkAndroidSDKCanvas::onDrawPatch(const SkPoint cubics[12],
+                                              const SkColor colors[4],
+                                              const SkPoint texCoords[4],
+                                              SkXfermode* xmode,
+                                              const SkPaint& paint) {
+    FILTER(paint);
+    fProxyTarget->drawPatch(cubics, colors, texCoords, xmode, filteredPaint);
+}
+
+
+void SkAndroidSDKCanvas::onDrawImage(const SkImage* image,
+                                              SkScalar x,
+                                              SkScalar y,
+                                              const SkPaint* paint) {
+    FILTER_PTR(paint);
+    fProxyTarget->drawImage(image, x, y, filteredPaint);
+}
+
+void SkAndroidSDKCanvas::onDrawImageRect(const SkImage* image,
+                                                  const SkRect* in,
+                                                  const SkRect& out,
+                                                  const SkPaint* paint) {
+    FILTER_PTR(paint);
+    fProxyTarget->drawImageRect(image, in, out, filteredPaint);
+}
+
+void SkAndroidSDKCanvas::onDrawPicture(const SkPicture* picture,
+                                                const SkMatrix* matrix,
+                                                const SkPaint* paint) {
+    FILTER_PTR(paint);
+    fProxyTarget->drawPicture(picture, matrix, filteredPaint);
+}
+
+void SkAndroidSDKCanvas::onDrawDrawable(SkDrawable* drawable) {
+    fProxyTarget->drawDrawable(drawable);
+}
+
+SkISize SkAndroidSDKCanvas::getBaseLayerSize() const {
+    return fProxyTarget->getBaseLayerSize();
+}
+bool SkAndroidSDKCanvas::getClipBounds(SkRect* rect) const {
+    return fProxyTarget->getClipBounds(rect);
+}
+bool SkAndroidSDKCanvas::getClipDeviceBounds(SkIRect* rect) const {
+    return fProxyTarget->getClipDeviceBounds(rect);
+}
+
+bool SkAndroidSDKCanvas::isClipEmpty() const { return fProxyTarget->isClipEmpty(); }
+bool SkAndroidSDKCanvas::isClipRect() const { return fProxyTarget->isClipRect(); }
+
+SkSurface* SkAndroidSDKCanvas::onNewSurface(const SkImageInfo& info,
+                                                     const SkSurfaceProps& props) {
+    return fProxyTarget->newSurface(info, &props);
+}
+
+const void* SkAndroidSDKCanvas::onPeekPixels(SkImageInfo* info, size_t* data) {
+    return fProxyTarget->peekPixels(info, data);
+}
+
+void* SkAndroidSDKCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* data) {
+    return fProxyTarget->accessTopLayerPixels(info, data);
+}
+
+void SkAndroidSDKCanvas::willSave() {
+    fProxyTarget->save();
+}
+
+SkCanvas::SaveLayerStrategy SkAndroidSDKCanvas::willSaveLayer(const SkRect* rect,
+                                                              const SkPaint* paint,
+                                                              SaveFlags flags) {
+    fProxyTarget->saveLayer(rect, paint, flags);
+    return SkCanvas::kNoLayer_SaveLayerStrategy;
+}
+
+void SkAndroidSDKCanvas::willRestore() {
+    fProxyTarget->restore();
+}
+
+void SkAndroidSDKCanvas::didRestore() { }
+
+void SkAndroidSDKCanvas::didConcat(const SkMatrix& m) {
+    fProxyTarget->concat(m);
+}
+
+void SkAndroidSDKCanvas::didSetMatrix(const SkMatrix& m) {
+    fProxyTarget->setMatrix(m);
+}
+
+void SkAndroidSDKCanvas::onClipRect(const SkRect& rect,
+                                             SkRegion::Op op,
+                                             ClipEdgeStyle style) {
+    fProxyTarget->clipRect(rect, op, style);
+}
+
+void SkAndroidSDKCanvas::onClipRRect(const SkRRect& rrect,
+                                              SkRegion::Op op,
+                                              ClipEdgeStyle style) {
+    fProxyTarget->clipRRect(rrect, op, style);
+}
+
+void SkAndroidSDKCanvas::onClipPath(const SkPath& path,
+                                             SkRegion::Op op,
+                                             ClipEdgeStyle style) {
+    fProxyTarget->clipPath(path, op, style);
+}
+
+void SkAndroidSDKCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) {
+    fProxyTarget->clipRegion(region, op);
+}
+
+void SkAndroidSDKCanvas::onDiscard() { fProxyTarget->discard(); }
+
+
diff --git a/src/utils/android/SkAndroidSDKCanvas.h b/src/utils/android/SkAndroidSDKCanvas.h
new file mode 100644
index 0000000..cddd262
--- /dev/null
+++ b/src/utils/android/SkAndroidSDKCanvas.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkAndroidSDKCanvas_DEFINED
+#define SkAndroidSDKCanvas_DEFINED
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRect.h"
+
+/** SkDrawFilter is likely to be deprecated; this is a proxy
+    canvas that does the same thing: alter SkPaint fields.
+
+    onDraw*() functions may have their SkPaint modified, and are then
+    passed on to the same function on proxyTarget. THIS BREAKS CONSTNESS!
+
+    This still suffers one of the same architectural flaws as SkDrawFilter:
+    TextBlob paints are incomplete when filter is called.
+*/
+
+class SkAndroidSDKCanvas : public SkCanvas {
+public:
+    SkAndroidSDKCanvas();
+    void reset(SkCanvas* newTarget);
+
+protected:
+
+    // FILTERING
+
+    void onDrawPaint(const SkPaint& paint) override;
+    void onDrawPoints(PointMode pMode, size_t count, const SkPoint pts[],
+                      const SkPaint& paint) override;
+    void onDrawOval(const SkRect& r, const SkPaint& paint) override;
+    void onDrawRect(const SkRect& r, const SkPaint& paint) override;
+    void onDrawRRect(const SkRRect& r, const SkPaint& paint) override;
+    void onDrawPath(const SkPath& path, const SkPaint& paint) override;
+    void onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+                      const SkPaint* paint) override;
+    void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
+                          const SkPaint* paint, DrawBitmapRectFlags flags) override;
+    void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+                          const SkRect& dst, const SkPaint* paint) override;
+    void onDrawSprite(const SkBitmap& bitmap, int left, int top,
+                      const SkPaint* paint) override;
+    void onDrawVertices(VertexMode vMode, int vertexCount, const SkPoint vertices[],
+                        const SkPoint texs[], const SkColor colors[], SkXfermode* xMode,
+                        const uint16_t indices[], int indexCount,
+                        const SkPaint& paint) override;
+
+    void onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+                      const SkPaint& paint) override;
+
+    void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+                    const SkPaint& paint) override;
+    void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+                       const SkPaint& paint) override;
+    void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+                        SkScalar constY, const SkPaint& paint) override;
+    void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+                          const SkMatrix* matrix, const SkPaint& paint) override;
+    void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+                        const SkPaint& paint) override;
+
+    void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+                     const SkPoint texCoords[4], SkXfermode* xmode,
+                     const SkPaint& paint) override;
+
+    void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override;
+    void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*)
+        override;
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*);
+
+    // PASS THROUGH
+
+    void onDrawDrawable(SkDrawable*) override;
+    SkISize getBaseLayerSize() const override;
+    bool getClipBounds(SkRect*) const override;
+    bool getClipDeviceBounds(SkIRect*) const override;
+    bool isClipEmpty() const override;
+    bool isClipRect() const override;
+    SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
+    const void* onPeekPixels(SkImageInfo*, size_t*) override;
+    void* onAccessTopLayerPixels(SkImageInfo*, size_t*) override;
+    void willSave() override;
+    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+    void willRestore() override;
+    void didRestore() override;
+    void didConcat(const SkMatrix&) override;
+    void didSetMatrix(const SkMatrix&) override;
+    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRegion(const SkRegion&, SkRegion::Op) override;
+    void onDiscard() override;
+
+protected:
+    SkCanvas* fProxyTarget;
+};
+
+#endif  // SkAndroidSDKCanvas_DEFINED
+
diff --git a/src/utils/android/SkHwuiRenderer.cpp b/src/utils/android/SkHwuiRenderer.cpp
new file mode 100644
index 0000000..70b7e5b
--- /dev/null
+++ b/src/utils/android/SkHwuiRenderer.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkHwuiRenderer.h"
+
+#include "AnimationContext.h"
+#include "IContextFactory.h"
+#include "SkBitmap.h"
+#include "gui/BufferQueue.h"
+
+namespace {
+
+/**
+ * Helper class for setting up android::uirenderer::renderthread::RenderProxy.
+ */
+class ContextFactory : public android::uirenderer::IContextFactory {
+public:
+    android::uirenderer::AnimationContext* createAnimationContext
+        (android::uirenderer::renderthread::TimeLord& clock) override {
+        return new android::uirenderer::AnimationContext(clock);
+    }
+};
+
+}
+
+void SkHwuiRenderer::initialize(SkISize size) {
+    this->size = size;
+    android::BufferQueue::createBufferQueue(&this->producer, &this->consumer);
+    this->cpuConsumer = new android::CpuConsumer(this->consumer, 1);
+    this->cpuConsumer->setName(android::String8("SkiaBenchmarkClient"));
+    this->cpuConsumer->setDefaultBufferSize(size.width(), size.height());
+    this->androidSurface = new android::Surface(this->producer);
+    native_window_set_buffers_dimensions(this->androidSurface.get(),
+                                         size.width(), size.height());
+    native_window_set_buffers_format(this->androidSurface.get(),
+                                     android::PIXEL_FORMAT_RGBA_8888);
+    native_window_set_usage(this->androidSurface.get(), GRALLOC_USAGE_SW_READ_OFTEN |
+                                           GRALLOC_USAGE_SW_WRITE_NEVER |
+                                           GRALLOC_USAGE_HW_RENDER);
+    this->rootNode.reset(new android::uirenderer::RenderNode());
+    this->rootNode->incStrong(nullptr);
+    this->rootNode->mutateStagingProperties().setLeftTopRightBottom
+        (0, 0, size.width(), size.height());
+    this->rootNode->mutateStagingProperties().setClipToBounds(false);
+    this->rootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::GENERIC);
+    ContextFactory factory;
+    this->proxy.reset
+        (new android::uirenderer::renderthread::RenderProxy(false, this->rootNode, &factory));
+    this->proxy->loadSystemProperties();
+    this->proxy->initialize(this->androidSurface.get());
+    float lightX = size.width() / 2.0f;
+    android::uirenderer::Vector3 lightVector { lightX, -200.0f, 800.0f };
+    this->proxy->setup(size.width(), size.height(), 800.0f,
+                         255 * 0.075f, 255 * 0.15f);
+    this->proxy->setLightCenter(lightVector);
+    this->canvas.reset(new android::uirenderer::DisplayListCanvas());
+    this->canvas->setViewport(size.width(), size.height());
+}
+
+SkCanvas* SkHwuiRenderer::prepareToDraw() {
+    this->canvas->prepare();
+    this->canvas->clipRect(0, 0, this->size.width(), this->size.height(),
+                           SkRegion::Op::kReplace_Op);
+    return this->canvas->asSkCanvas();
+}
+
+void SkHwuiRenderer::finishDrawing() {
+    this->canvas->finish();
+    this->rootNode->setStagingDisplayList(this->canvas->finishRecording());
+    this->proxy->syncAndDrawFrame();
+    // Surprisingly, calling this->proxy->fence() here appears to make no difference to
+    // the timings we record.
+}
+
+bool SkHwuiRenderer::capturePixels(SkBitmap* bmp) {
+    SkImageInfo destinationConfig =
+        SkImageInfo::Make(this->size.width(), this->size.height(),
+                          kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+    bmp->allocPixels(destinationConfig);
+    sk_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED,
+                this->size.width() * this->size.height());
+
+    android::CpuConsumer::LockedBuffer nativeBuffer;
+    android::status_t retval = this->cpuConsumer->lockNextBuffer(&nativeBuffer);
+    if (retval == android::BAD_VALUE) {
+        SkDebugf("write_canvas_png() got no buffer; returning transparent");
+        // No buffer ready to read - commonly triggered by dm sending us
+        // a no-op source, or calling code that doesn't do anything on this
+        // backend.
+        bmp->eraseColor(SK_ColorTRANSPARENT);
+        return false;
+    } else if (retval) {
+        SkDebugf("Failed to lock buffer to read pixels: %d.", retval);
+        return false;
+    }
+
+    // Move the pixels into the destination SkBitmap
+
+    SK_ALWAYSBREAK(nativeBuffer.format == android::PIXEL_FORMAT_RGBA_8888 &&
+                   "Native buffer not RGBA!");
+    SkImageInfo nativeConfig =
+        SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height,
+                          kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+    // Android stride is in pixels, Skia stride is in bytes
+    SkBitmap nativeWrapper;
+    bool success =
+        nativeWrapper.installPixels(nativeConfig, nativeBuffer.data, nativeBuffer.stride * 4);
+    if (!success) {
+        SkDebugf("Failed to wrap HWUI buffer in a SkBitmap");
+        return false;
+    }
+
+    SK_ALWAYSBREAK(bmp->colorType() == kRGBA_8888_SkColorType &&
+                   "Destination buffer not RGBA!");
+    success =
+        nativeWrapper.readPixels(destinationConfig, bmp->getPixels(), bmp->rowBytes(), 0, 0);
+    if (!success) {
+        SkDebugf("Failed to extract pixels from HWUI buffer");
+        return false;
+    }
+
+    this->cpuConsumer->unlockBuffer(nativeBuffer);
+
+    return true;
+}
+
diff --git a/src/utils/android/SkHwuiRenderer.h b/src/utils/android/SkHwuiRenderer.h
new file mode 100644
index 0000000..0a2762a
--- /dev/null
+++ b/src/utils/android/SkHwuiRenderer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkHwuiRenderer_DEFINED
+#define SkHwuiRenderer_DEFINED
+
+#include "DisplayListCanvas.h"
+#include "RenderNode.h"
+#include "SkTypes.h"
+#include "gui/CpuConsumer.h"
+#include "gui/IGraphicBufferConsumer.h"
+#include "gui/IGraphicBufferProducer.h"
+#include "gui/Surface.h"
+#include "renderthread/RenderProxy.h"
+
+class SkBitmap;
+
+struct SkHwuiRenderer {
+    SkAutoTDelete<android::uirenderer::RenderNode> rootNode;
+    SkAutoTDelete<android::uirenderer::renderthread::RenderProxy> proxy;
+    SkAutoTDelete<android::uirenderer::DisplayListCanvas> canvas;
+    android::sp<android::IGraphicBufferProducer> producer;
+    android::sp<android::IGraphicBufferConsumer> consumer;
+    android::sp<android::CpuConsumer> cpuConsumer;
+    android::sp<android::Surface> androidSurface;
+    SkISize size;
+
+    void initialize(SkISize size);
+
+    /// Returns a canvas to draw into.
+    SkCanvas* prepareToDraw();
+
+    void finishDrawing();
+
+    bool capturePixels(SkBitmap* bmp);
+};
+
+#endif  // SkHwuiRenderer_DEFINED
diff --git a/src/utils/debugger/SkDebugCanvas.cpp b/src/utils/debugger/SkDebugCanvas.cpp
index 6474582..21272d7 100644
--- a/src/utils/debugger/SkDebugCanvas.cpp
+++ b/src/utils/debugger/SkDebugCanvas.cpp
@@ -10,19 +10,98 @@
 #include "SkColorPriv.h"
 #include "SkDebugCanvas.h"
 #include "SkDrawCommand.h"
-#include "SkDrawFilter.h"
 #include "SkDevice.h"
+#include "SkPaintFilterCanvas.h"
 #include "SkXfermode.h"
 
+namespace {
+
+class OverdrawXfermode : public SkXfermode {
+public:
+    SkPMColor xferColor(SkPMColor src, SkPMColor dst) const override {
+        // This table encodes the color progression of the overdraw visualization
+        static const SkPMColor gTable[] = {
+            SkPackARGB32(0x00, 0x00, 0x00, 0x00),
+            SkPackARGB32(0xFF, 128, 158, 255),
+            SkPackARGB32(0xFF, 170, 185, 212),
+            SkPackARGB32(0xFF, 213, 195, 170),
+            SkPackARGB32(0xFF, 255, 192, 127),
+            SkPackARGB32(0xFF, 255, 185, 85),
+            SkPackARGB32(0xFF, 255, 165, 42),
+            SkPackARGB32(0xFF, 255, 135, 0),
+            SkPackARGB32(0xFF, 255,  95, 0),
+            SkPackARGB32(0xFF, 255,  50, 0),
+            SkPackARGB32(0xFF, 255,  0, 0)
+        };
+
+
+        int idx;
+        if (SkColorGetR(dst) < 64) { // 0
+            idx = 0;
+        } else if (SkColorGetG(dst) < 25) { // 10
+            idx = 9;  // cap at 9 for upcoming increment
+        } else if ((SkColorGetB(dst)+21)/42 > 0) { // 1-6
+            idx = 7 - (SkColorGetB(dst)+21)/42;
+        } else { // 7-9
+            idx = 10 - (SkColorGetG(dst)+22)/45;
+        }
+        ++idx;
+        SkASSERT(idx < (int)SK_ARRAY_COUNT(gTable));
+
+        return gTable[idx];
+    }
+
+    Factory getFactory() const override { return NULL; }
+#ifndef SK_IGNORE_TO_STRING
+    void toString(SkString* str) const override { str->set("OverdrawXfermode"); }
+#endif
+};
+
+class DebugPaintFilterCanvas : public SkPaintFilterCanvas {
+public:
+    DebugPaintFilterCanvas(int width, int height, bool overdrawViz, bool overrideFilterQuality,
+                           SkFilterQuality quality)
+        : INHERITED(width, height)
+        , fOverdrawXfermode(overdrawViz ? SkNEW(OverdrawXfermode) : NULL)
+        , fOverrideFilterQuality(overrideFilterQuality)
+        , fFilterQuality(quality) { }
+
+protected:
+    void onFilterPaint(SkPaint* paint, Type) const override {
+        if (NULL != fOverdrawXfermode.get()) {
+            paint->setAntiAlias(false);
+            paint->setXfermode(fOverdrawXfermode.get());
+        }
+
+        if (fOverrideFilterQuality) {
+            paint->setFilterQuality(fFilterQuality);
+        }
+    }
+
+    void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
+        // We need to replay the picture onto this canvas in order to filter its internal paints.
+        this->SkCanvas::onDrawPicture(picture, matrix, paint);
+    }
+
+private:
+    SkAutoTUnref<SkXfermode> fOverdrawXfermode;
+
+    bool fOverrideFilterQuality;
+    SkFilterQuality fFilterQuality;
+
+    typedef SkPaintFilterCanvas INHERITED;
+};
+
+}
+
 SkDebugCanvas::SkDebugCanvas(int width, int height)
         : INHERITED(width, height)
         , fPicture(NULL)
         , fFilter(false)
         , fMegaVizMode(false)
         , fOverdrawViz(false)
-        , fOverdrawFilter(NULL)
-        , fOverrideTexFiltering(false)
-        , fTexOverrideFilter(NULL) {
+        , fOverrideFilterQuality(false)
+        , fFilterQuality(kNone_SkFilterQuality) {
     fUserMatrix.reset();
 
     // SkPicturePlayback uses the base-class' quickReject calls to cull clipped
@@ -46,12 +125,9 @@
 
 SkDebugCanvas::~SkDebugCanvas() {
     fCommandVector.deleteAll();
-    SkSafeUnref(fOverdrawFilter);
-    SkSafeUnref(fTexOverrideFilter);
 }
 
 void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
-    command->setOffset(this->getOpID());
     fCommandVector.push(command);
 }
 
@@ -88,112 +164,25 @@
     return layer;
 }
 
-class OverdrawXfermode : public SkXfermode {
-public:
-    SkPMColor xferColor(SkPMColor src, SkPMColor dst) const SK_OVERRIDE {
-        // This table encodes the color progression of the overdraw visualization
-        static const SkPMColor gTable[] = {
-            SkPackARGB32(0x00, 0x00, 0x00, 0x00),
-            SkPackARGB32(0xFF, 128, 158, 255),
-            SkPackARGB32(0xFF, 170, 185, 212),
-            SkPackARGB32(0xFF, 213, 195, 170),
-            SkPackARGB32(0xFF, 255, 192, 127),
-            SkPackARGB32(0xFF, 255, 185, 85),
-            SkPackARGB32(0xFF, 255, 165, 42),
-            SkPackARGB32(0xFF, 255, 135, 0),
-            SkPackARGB32(0xFF, 255,  95, 0),
-            SkPackARGB32(0xFF, 255,  50, 0),
-            SkPackARGB32(0xFF, 255,  0, 0)
-        };
- 
-
-        int idx;
-        if (SkColorGetR(dst) < 64) { // 0
-            idx = 0;
-        } else if (SkColorGetG(dst) < 25) { // 10
-            idx = 9;  // cap at 9 for upcoming increment
-        } else if ((SkColorGetB(dst)+21)/42 > 0) { // 1-6
-            idx = 7 - (SkColorGetB(dst)+21)/42;
-        } else { // 7-9
-            idx = 10 - (SkColorGetG(dst)+22)/45;
-        }
-        ++idx;
-        SkASSERT(idx < (int)SK_ARRAY_COUNT(gTable));
-
-        return gTable[idx];
-    }
-
-    Factory getFactory() const SK_OVERRIDE { return NULL; }
-#ifndef SK_IGNORE_TO_STRING
-    virtual void toString(SkString* str) const SK_OVERRIDE { str->set("OverdrawXfermode"); }
-#endif
-};
-
-class SkOverdrawFilter : public SkDrawFilter {
-public:
-    SkOverdrawFilter() {
-        fXferMode = SkNEW(OverdrawXfermode);
-    }
-
-    virtual ~SkOverdrawFilter() {
-        delete fXferMode;
-    }
-
-    bool filter(SkPaint* p, Type) SK_OVERRIDE {
-        p->setXfermode(fXferMode);
-        p->setAntiAlias(false);
-        return true;
-    }
-
-protected:
-    SkXfermode* fXferMode;
-
-private:
-    typedef SkDrawFilter INHERITED;
-};
-
-// SkTexOverrideFilter modifies every paint to use the specified
-// texture filtering mode
-class SkTexOverrideFilter : public SkDrawFilter {
-public:
-    SkTexOverrideFilter() : fFilterLevel(SkPaint::kNone_FilterLevel) {
-    }
-
-    void setFilterLevel(SkPaint::FilterLevel filterLevel) {
-        fFilterLevel = filterLevel;
-    }
-
-    bool filter(SkPaint* p, Type) SK_OVERRIDE {
-        p->setFilterLevel(fFilterLevel);
-        return true;
-    }
-
-protected:
-    SkPaint::FilterLevel fFilterLevel;
-
-private:
-    typedef SkDrawFilter INHERITED;
-};
-
 class SkDebugClipVisitor : public SkCanvas::ClipVisitor {
 public:
     SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
 
-    void clipRect(const SkRect& r, SkRegion::Op, bool doAA) SK_OVERRIDE {
+    void clipRect(const SkRect& r, SkRegion::Op, bool doAA) override {
         SkPaint p;
         p.setColor(SK_ColorRED);
         p.setStyle(SkPaint::kStroke_Style);
         p.setAntiAlias(doAA);
         fCanvas->drawRect(r, p);
     }
-    void clipRRect(const SkRRect& rr, SkRegion::Op, bool doAA) SK_OVERRIDE {
+    void clipRRect(const SkRRect& rr, SkRegion::Op, bool doAA) override {
         SkPaint p;
         p.setColor(SK_ColorGREEN);
         p.setStyle(SkPaint::kStroke_Style);
         p.setAntiAlias(doAA);
         fCanvas->drawRRect(rr, p);
     }
-    void clipPath(const SkPath& path, SkRegion::Op, bool doAA) SK_OVERRIDE {
+    void clipPath(const SkPath& path, SkRegion::Op, bool doAA) override {
         SkPaint p;
         p.setColor(SK_ColorBLUE);
         p.setStyle(SkPaint::kStroke_Style);
@@ -250,28 +239,9 @@
     }
     this->applyUserTransform(canvas);
 
-    // The setting of the draw filter has to go here (rather than in
-    // SkRasterWidget) due to the canvas restores this class performs.
-    // Since the draw filter is stored in the layer stack if we
-    // call setDrawFilter on anything but the root layer odd things happen.
-    if (fOverdrawViz) {
-        if (NULL == fOverdrawFilter) {
-            fOverdrawFilter = new SkOverdrawFilter;
-        }
-
-        if (fOverdrawFilter != canvas->getDrawFilter()) {
-            canvas->setDrawFilter(fOverdrawFilter);
-        }
-    } else if (fOverrideTexFiltering) {
-        if (NULL == fTexOverrideFilter) {
-            fTexOverrideFilter = new SkTexOverrideFilter;
-        }
-
-        if (fTexOverrideFilter != canvas->getDrawFilter()) {
-            canvas->setDrawFilter(fTexOverrideFilter);
-        }
-    } else {
-        canvas->setDrawFilter(NULL);
+    if (fPaintFilterCanvas) {
+        fPaintFilterCanvas->addCanvas(canvas);
+        canvas = fPaintFilterCanvas.get();
     }
 
     if (fMegaVizMode) {
@@ -342,6 +312,10 @@
     }
 
     canvas->restoreToCount(saveCount);
+
+    if (fPaintFilterCanvas) {
+        fPaintFilterCanvas->removeAll();
+    }
 }
 
 void SkDebugCanvas::deleteDrawCommandAt(int index) {
@@ -379,13 +353,37 @@
     return fCommandVector;
 }
 
-void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkPaint::FilterLevel level) {
-    if (NULL == fTexOverrideFilter) {
-        fTexOverrideFilter = new SkTexOverrideFilter;
+void SkDebugCanvas::updatePaintFilterCanvas() {
+    if (!fOverdrawViz && !fOverrideFilterQuality) {
+        fPaintFilterCanvas.reset(NULL);
+        return;
     }
 
-    fOverrideTexFiltering = overrideTexFiltering;
-    fTexOverrideFilter->setFilterLevel(level);
+    const SkImageInfo info = this->imageInfo();
+    fPaintFilterCanvas.reset(SkNEW_ARGS(DebugPaintFilterCanvas, (info.width(),
+                                                                 info.height(),
+                                                                 fOverdrawViz,
+                                                                 fOverrideFilterQuality,
+                                                                 fFilterQuality)));
+}
+
+void SkDebugCanvas::setOverdrawViz(bool overdrawViz) {
+    if (fOverdrawViz == overdrawViz) {
+        return;
+    }
+
+    fOverdrawViz = overdrawViz;
+    this->updatePaintFilterCanvas();
+}
+
+void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkFilterQuality quality) {
+    if (fOverrideFilterQuality == overrideTexFiltering && fFilterQuality == quality) {
+        return;
+    }
+
+    fOverrideFilterQuality = overrideTexFiltering;
+    fFilterQuality = quality;
+    this->updatePaintFilterCanvas();
 }
 
 void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
@@ -461,7 +459,9 @@
 void SkDebugCanvas::onDrawPicture(const SkPicture* picture,
                                   const SkMatrix* matrix,
                                   const SkPaint* paint) {
-    this->addDrawCommand(new SkDrawPictureCommand(picture, matrix, paint));
+    this->addDrawCommand(new SkBeginDrawPictureCommand(picture, matrix, paint));
+    this->INHERITED::onDrawPicture(picture, matrix, paint);
+    this->addDrawCommand(new SkEndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint)));
 }
 
 void SkDebugCanvas::onDrawPoints(PointMode mode, size_t count,
diff --git a/src/utils/debugger/SkDebugCanvas.h b/src/utils/debugger/SkDebugCanvas.h
index 97eb734..8cf16d2 100644
--- a/src/utils/debugger/SkDebugCanvas.h
+++ b/src/utils/debugger/SkDebugCanvas.h
@@ -17,7 +17,7 @@
 #include "SkTArray.h"
 #include "SkString.h"
 
-class SkTexOverrideFilter;
+class SkNWayCanvas;
 
 class SK_API SkDebugCanvas : public SkCanvas {
 public:
@@ -32,7 +32,7 @@
     /**
      * Enable or disable overdraw visualization
      */
-    void setOverdrawViz(bool overdrawViz) { fOverdrawViz = overdrawViz; }
+    void setOverdrawViz(bool overdrawViz);
     bool getOverdrawViz() const { return fOverdrawViz; }
 
     bool getAllowSimplifyClip() const { return fAllowSimplifyClip; }
@@ -42,7 +42,7 @@
     /**
      * Enable or disable texure filtering override
      */
-    void overrideTexFiltering(bool overrideTexFiltering, SkPaint::FilterLevel level);
+    void overrideTexFiltering(bool overrideTexFiltering, SkFilterQuality);
 
     /**
         Executes all draw calls to the canvas.
@@ -142,16 +142,16 @@
 // Inherited from SkCanvas
 ////////////////////////////////////////////////////////////////////////////////
 
-    void beginCommentGroup(const char* description) SK_OVERRIDE;
-    void addComment(const char* kywd, const char* value) SK_OVERRIDE;
-    void endCommentGroup() SK_OVERRIDE;
+    void beginCommentGroup(const char* description) override;
+    void addComment(const char* kywd, const char* value) override;
+    void endCommentGroup() override;
 
     static const int kVizImageHeight = 256;
     static const int kVizImageWidth = 256;
 
-    bool isClipEmpty() const SK_OVERRIDE { return false; }
-    bool isClipRect() const SK_OVERRIDE { return true; }
-    bool getClipBounds(SkRect* bounds) const SK_OVERRIDE {
+    bool isClipEmpty() const override { return false; }
+    bool isClipRect() const override { return true; }
+    bool getClipBounds(SkRect* bounds) const override {
         if (bounds) {
             bounds->setXYWH(0, 0,
                             SkIntToScalar(this->imageInfo().width()),
@@ -159,7 +159,7 @@
         }
         return true;
     }
-    bool getClipDeviceBounds(SkIRect* bounds) const SK_OVERRIDE {
+    bool getClipDeviceBounds(SkIRect* bounds) const override {
         if (bounds) {
             bounds->setLargest();
         }
@@ -167,54 +167,54 @@
     }
 
 protected:
-    void willSave() SK_OVERRIDE;
-    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) SK_OVERRIDE;
-    void willRestore() SK_OVERRIDE;
+    void willSave() override;
+    SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+    void willRestore() override;
 
-    void didConcat(const SkMatrix&) SK_OVERRIDE;
-    void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
+    void didConcat(const SkMatrix&) override;
+    void didSetMatrix(const SkMatrix&) override;
 
-    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
+    void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
     void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                    const SkPaint&) SK_OVERRIDE;
+                    const SkPaint&) override;
     void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                       const SkPaint&) SK_OVERRIDE;
+                       const SkPaint&) override;
     void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
-                        SkScalar constY, const SkPaint&) SK_OVERRIDE;
+                        SkScalar constY, const SkPaint&) override;
     void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
-                          const SkMatrix* matrix, const SkPaint&) SK_OVERRIDE;
+                          const SkMatrix* matrix, const SkPaint&) override;
     void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
-                        const SkPaint& paint) SK_OVERRIDE;
+                        const SkPaint& paint) override;
 
     void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
                      const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint);
-    void onDrawPaint(const SkPaint&) SK_OVERRIDE;
+    void onDrawPaint(const SkPaint&) override;
 
-    void onDrawRect(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawOval(const SkRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawRRect(const SkRRect&, const SkPaint&) SK_OVERRIDE;
-    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) SK_OVERRIDE;
+    void onDrawRect(const SkRect&, const SkPaint&) override;
+    void onDrawOval(const SkRect&, const SkPaint&) override;
+    void onDrawRRect(const SkRRect&, const SkPaint&) override;
+    void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
     void onDrawVertices(VertexMode vmode, int vertexCount,
                         const SkPoint vertices[], const SkPoint texs[],
                         const SkColor colors[], SkXfermode* xmode,
                         const uint16_t indices[], int indexCount,
-                        const SkPaint&) SK_OVERRIDE;
-    void onDrawPath(const SkPath&, const SkPaint&) SK_OVERRIDE;
-    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+                        const SkPaint&) override;
+    void onDrawPath(const SkPath&, const SkPaint&) override;
+    void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
-                          DrawBitmapRectFlags flags) SK_OVERRIDE;
-    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) SK_OVERRIDE;
+                          DrawBitmapRectFlags flags) override;
+    void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
-                         const SkPaint*) SK_OVERRIDE;
+                         const SkPaint*) override;
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
-                          const SkPaint*) SK_OVERRIDE;
-    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) SK_OVERRIDE;
-    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) SK_OVERRIDE;
-    void onClipRegion(const SkRegion& region, SkRegion::Op) SK_OVERRIDE;
+                          const SkPaint*) override;
+    void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
+    void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+    void onClipRegion(const SkRegion& region, SkRegion::Op) override;
 
-    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) SK_OVERRIDE;
+    void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
 
     void markActiveCommands(int index);
 
@@ -232,10 +232,10 @@
     SkPath fSaveDevPath;
 
     bool fOverdrawViz;
-    SkDrawFilter* fOverdrawFilter;
+    bool fOverrideFilterQuality;
+    SkFilterQuality fFilterQuality;
 
-    bool fOverrideTexFiltering;
-    SkTexOverrideFilter* fTexOverrideFilter;
+    SkAutoTUnref<SkNWayCanvas> fPaintFilterCanvas;
 
     /**
         The active saveLayer commands at a given point in the renderering.
@@ -255,15 +255,6 @@
      */
     void applyUserTransform(SkCanvas* canvas);
 
-    size_t getOpID() const {
-#if 0
-        if (fPicture) {
-            return fPicture->EXPERIMENTAL_curOpID();
-        }
-#endif
-        return 0;
-    }
-
     void resetClipStackData() { fClipStackData.reset(); fCalledAddStackData = false; }
 
     void addClipStackData(const SkPath& devPath, const SkPath& operand, SkRegion::Op elementOp);
@@ -274,6 +265,8 @@
     void outputPointsCommon(const SkPoint* pts, int count);
     void outputScalar(SkScalar num);
 
+    void updatePaintFilterCanvas();
+
     typedef SkCanvas INHERITED;
 };
 
diff --git a/src/utils/debugger/SkDrawCommand.cpp b/src/utils/debugger/SkDrawCommand.cpp
index 38f02d9..09b1889 100644
--- a/src/utils/debugger/SkDrawCommand.cpp
+++ b/src/utils/debugger/SkDrawCommand.cpp
@@ -16,7 +16,6 @@
 
 SkDrawCommand::SkDrawCommand(OpType type)
     : fOpType(type)
-    , fOffset(0)
     , fVisible(true) {
 }
 
@@ -27,6 +26,7 @@
 const char* SkDrawCommand::GetCommandString(OpType type) {
     switch (type) {
         case kBeginCommentGroup_OpType: return "BeginCommentGroup";
+        case kBeginDrawPicture_OpType: return "BeginDrawPicture";
         case kClipPath_OpType: return "ClipPath";
         case kClipRegion_OpType: return "ClipRegion";
         case kClipRect_OpType: return "ClipRect";
@@ -42,7 +42,6 @@
         case kDrawPaint_OpType: return "DrawPaint";
         case kDrawPatch_OpType: return "DrawPatch";
         case kDrawPath_OpType: return "DrawPath";
-        case kDrawPicture_OpType: return "DrawPicture";
         case kDrawPoints_OpType: return "DrawPoints";
         case kDrawPosText_OpType: return "DrawPosText";
         case kDrawPosTextH_OpType: return "DrawPosTextH";
@@ -54,6 +53,7 @@
         case kDrawTextOnPath_OpType: return "DrawTextOnPath";
         case kDrawVertices_OpType: return "DrawVertices";
         case kEndCommentGroup_OpType: return "EndCommentGroup";
+        case kEndDrawPicture_OpType: return "EndDrawPicture";
         case kRestore_OpType: return "Restore";
         case kSave_OpType: return "Save";
         case kSaveLayer_OpType: return "SaveLayer";
@@ -446,41 +446,48 @@
     return true;
 }
 
-SkDrawPictureCommand::SkDrawPictureCommand(const SkPicture* picture,
-                                           const SkMatrix* matrix,
-                                           const SkPaint* paint)
-    : INHERITED(kDrawPicture_OpType)
-    , fPicture(SkRef(picture))
-    , fMatrixPtr(NULL)
-    , fPaintPtr(NULL) {
+SkBeginDrawPictureCommand::SkBeginDrawPictureCommand(const SkPicture* picture,
+                                                     const SkMatrix* matrix,
+                                                     const SkPaint* paint)
+    : INHERITED(kBeginDrawPicture_OpType)
+    , fPicture(SkRef(picture)) {
+
+    SkString* str = new SkString;
+    str->appendf("SkPicture: L: %f T: %f R: %f B: %f",
+                 picture->cullRect().fLeft, picture->cullRect().fTop,
+                 picture->cullRect().fRight, picture->cullRect().fBottom);
+    fInfo.push(str);
 
     if (matrix) {
-        fMatrix = *matrix;
-        fMatrixPtr = &fMatrix;
-    }
-    if (paint) {
-        fPaint = *paint;
-        fPaintPtr = &fPaint;
-    }
-
-    SkString* temp = new SkString;
-    temp->appendf("SkPicture: L: %f T: %f R: %f B: %f",
-                  picture->cullRect().fLeft, picture->cullRect().fTop,
-                  picture->cullRect().fRight, picture->cullRect().fBottom);
-    fInfo.push(temp);
-    if (matrix) {
+        fMatrix.set(*matrix);
         fInfo.push(SkObjectParser::MatrixToString(*matrix));
     }
+
     if (paint) {
+        fPaint.set(*paint);
         fInfo.push(SkObjectParser::PaintToString(*paint));
     }
+
+}
+
+void SkBeginDrawPictureCommand::execute(SkCanvas* canvas) const {
+    if (fPaint.isValid()) {
+        SkRect bounds = fPicture->cullRect();
+        if (fMatrix.isValid()) {
+            fMatrix.get()->mapRect(&bounds);
+        }
+        canvas->saveLayer(&bounds, fPaint.get());
+    }
+
+    if (fMatrix.isValid()) {
+        if (!fPaint.isValid()) {
+            canvas->save();
+        }
+        canvas->concat(*fMatrix.get());
+    }
 }
 
-void SkDrawPictureCommand::execute(SkCanvas* canvas) const {
-    canvas->drawPicture(fPicture, fMatrixPtr, fPaintPtr);
-}
-
-bool SkDrawPictureCommand::render(SkCanvas* canvas) const {
+bool SkBeginDrawPictureCommand::render(SkCanvas* canvas) const {
     canvas->clear(0xFFFFFFFF);
     canvas->save();
 
@@ -493,6 +500,15 @@
     return true;
 }
 
+SkEndDrawPictureCommand::SkEndDrawPictureCommand(bool restore)
+    : INHERITED(kEndDrawPicture_OpType) , fRestore(restore) { }
+
+void SkEndDrawPictureCommand::execute(SkCanvas* canvas) const {
+    if (fRestore) {
+        canvas->restore();
+    }
+}
+
 SkDrawPointsCommand::SkDrawPointsCommand(SkCanvas::PointMode mode, size_t count,
                                          const SkPoint pts[], const SkPaint& paint)
     : INHERITED(kDrawPoints_OpType) {
diff --git a/src/utils/debugger/SkDrawCommand.h b/src/utils/debugger/SkDrawCommand.h
index bcc97ca..538dd23 100644
--- a/src/utils/debugger/SkDrawCommand.h
+++ b/src/utils/debugger/SkDrawCommand.h
@@ -16,6 +16,7 @@
 public:
     enum OpType {
         kBeginCommentGroup_OpType,
+        kBeginDrawPicture_OpType,
         kClipPath_OpType,
         kClipRegion_OpType,
         kClipRect_OpType,
@@ -31,7 +32,6 @@
         kDrawPaint_OpType,
         kDrawPatch_OpType,
         kDrawPath_OpType,
-        kDrawPicture_OpType,
         kDrawPoints_OpType,
         kDrawPosText_OpType,
         kDrawPosTextH_OpType,
@@ -43,6 +43,7 @@
         kDrawTextOnPath_OpType,
         kDrawVertices_OpType,
         kEndCommentGroup_OpType,
+        kEndDrawPicture_OpType,
         kRestore_OpType,
         kSave_OpType,
         kSaveLayer_OpType,
@@ -59,9 +60,6 @@
 
     virtual SkString toString() const;
 
-    void setOffset(size_t offset) { fOffset = offset; }
-    size_t offset() const { return fOffset; }
-
     virtual const char* toCString() const {
         return GetCommandString(fOpType);
     }
@@ -106,15 +104,14 @@
 
 private:
     OpType fOpType;
-    size_t fOffset;
     bool   fVisible;
 };
 
 class SkRestoreCommand : public SkDrawCommand {
 public:
     SkRestoreCommand();
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    Action action() const SK_OVERRIDE { return kPopLayer_Action; }
+    void execute(SkCanvas* canvas) const override;
+    Action action() const override { return kPopLayer_Action; }
 
 private:
     typedef SkDrawCommand INHERITED;
@@ -123,7 +120,7 @@
 class SkClearCommand : public SkDrawCommand {
 public:
     SkClearCommand(SkColor color);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
 private:
     SkColor fColor;
 
@@ -133,8 +130,8 @@
 class SkClipPathCommand : public SkDrawCommand {
 public:
     SkClipPathCommand(const SkPath& path, SkRegion::Op op, bool doAA);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 private:
     SkPath       fPath;
     SkRegion::Op fOp;
@@ -146,7 +143,7 @@
 class SkClipRegionCommand : public SkDrawCommand {
 public:
     SkClipRegionCommand(const SkRegion& region, SkRegion::Op op);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
 private:
     SkRegion     fRegion;
     SkRegion::Op fOp;
@@ -157,7 +154,7 @@
 class SkClipRectCommand : public SkDrawCommand {
 public:
     SkClipRectCommand(const SkRect& rect, SkRegion::Op op, bool doAA);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
 
     const SkRect& rect() const { return fRect; }
     SkRegion::Op op() const { return fOp; }
@@ -174,8 +171,8 @@
 class SkClipRRectCommand : public SkDrawCommand {
 public:
     SkClipRRectCommand(const SkRRect& rrect, SkRegion::Op op, bool doAA);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 
     const SkRRect& rrect() const { return fRRect; }
     SkRegion::Op op() const { return fOp; }
@@ -192,7 +189,7 @@
 class SkConcatCommand : public SkDrawCommand {
 public:
     SkConcatCommand(const SkMatrix& matrix);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
 private:
     SkMatrix fMatrix;
 
@@ -203,8 +200,8 @@
 public:
     SkDrawBitmapCommand(const SkBitmap& bitmap, SkScalar left, SkScalar top,
                         const SkPaint* paint);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 private:
     SkBitmap fBitmap;
     SkScalar fLeft;
@@ -219,8 +216,8 @@
 public:
     SkDrawBitmapNineCommand(const SkBitmap& bitmap, const SkIRect& center,
                             const SkRect& dst, const SkPaint* paint);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 private:
     SkBitmap fBitmap;
     SkIRect  fCenter;
@@ -236,8 +233,8 @@
     SkDrawBitmapRectCommand(const SkBitmap& bitmap, const SkRect* src,
                             const SkRect& dst, const SkPaint* paint,
                             SkCanvas::DrawBitmapRectFlags flags);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 
     const SkBitmap& bitmap() const { return fBitmap; }
 
@@ -273,7 +270,7 @@
 class SkBeginCommentGroupCommand : public SkDrawCommand {
 public:
     SkBeginCommentGroupCommand(const char* description);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE {
+    void execute(SkCanvas* canvas) const override {
         canvas->beginCommentGroup(fDescription.c_str());
     };
 private:
@@ -285,7 +282,7 @@
 class SkCommentCommand : public SkDrawCommand {
 public:
     SkCommentCommand(const char* kywd, const char* value);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE {
+    void execute(SkCanvas* canvas) const override {
         canvas->addComment(fKywd.c_str(), fValue.c_str());
     };
 private:
@@ -298,7 +295,7 @@
 class SkEndCommentGroupCommand : public SkDrawCommand {
 public:
     SkEndCommentGroupCommand();
-    void execute(SkCanvas* canvas) const SK_OVERRIDE {
+    void execute(SkCanvas* canvas) const override {
         canvas->endCommentGroup();
     };
 private:
@@ -308,8 +305,8 @@
 class SkDrawOvalCommand : public SkDrawCommand {
 public:
     SkDrawOvalCommand(const SkRect& oval, const SkPaint& paint);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 private:
     SkRect  fOval;
     SkPaint fPaint;
@@ -320,8 +317,8 @@
 class SkDrawPaintCommand : public SkDrawCommand {
 public:
     SkDrawPaintCommand(const SkPaint& paint);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 private:
     SkPaint fPaint;
 
@@ -331,8 +328,8 @@
 class SkDrawPathCommand : public SkDrawCommand {
 public:
     SkDrawPathCommand(const SkPath& path, const SkPaint& paint);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 
 private:
     SkPath   fPath;
@@ -341,18 +338,31 @@
     typedef SkDrawCommand INHERITED;
 };
 
-class SkDrawPictureCommand : public SkDrawCommand {
+class SkBeginDrawPictureCommand : public SkDrawCommand {
 public:
-    SkDrawPictureCommand(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    SkBeginDrawPictureCommand(const SkPicture* picture,
+                              const SkMatrix* matrix,
+                              const SkPaint* paint);
+
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 
 private:
     SkAutoTUnref<const SkPicture> fPicture;
-    SkMatrix                      fMatrix;
-    SkMatrix*                     fMatrixPtr;
-    SkPaint                       fPaint;
-    SkPaint*                      fPaintPtr;
+    SkTLazy<SkMatrix>             fMatrix;
+    SkTLazy<SkPaint>              fPaint;
+
+    typedef SkDrawCommand INHERITED;
+};
+
+class SkEndDrawPictureCommand : public SkDrawCommand {
+public:
+    SkEndDrawPictureCommand(bool restore);
+
+    void execute(SkCanvas* canvas) const override;
+
+private:
+    bool fRestore;
 
     typedef SkDrawCommand INHERITED;
 };
@@ -362,8 +372,8 @@
     SkDrawPointsCommand(SkCanvas::PointMode mode, size_t count, const SkPoint pts[],
                         const SkPaint& paint);
     virtual ~SkDrawPointsCommand() { delete [] fPts; }
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 private:
     SkCanvas::PointMode fMode;
     size_t              fCount;
@@ -378,7 +388,7 @@
     SkDrawTextCommand(const void* text, size_t byteLength, SkScalar x, SkScalar y,
                       const SkPaint& paint);
     virtual ~SkDrawTextCommand() { delete [] fText; }
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
 private:
     char*    fText;
     size_t   fByteLength;
@@ -394,7 +404,7 @@
     SkDrawPosTextCommand(const void* text, size_t byteLength, const SkPoint pos[],
                          const SkPaint& paint);
     virtual ~SkDrawPosTextCommand() { delete [] fPos; delete [] fText; }
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
 private:
     char*    fText;
     size_t   fByteLength;
@@ -409,7 +419,7 @@
     SkDrawTextOnPathCommand(const void* text, size_t byteLength, const SkPath& path,
                             const SkMatrix* matrix, const SkPaint& paint);
     virtual ~SkDrawTextOnPathCommand() { delete [] fText; }
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
 private:
     char*    fText;
     size_t   fByteLength;
@@ -425,7 +435,7 @@
     SkDrawPosTextHCommand(const void* text, size_t byteLength, const SkScalar xpos[],
                           SkScalar constY, const SkPaint& paint);
     virtual ~SkDrawPosTextHCommand() { delete [] fXpos; delete [] fText; }
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
 private:
     SkScalar* fXpos;
     char*     fText;
@@ -440,8 +450,8 @@
 public:
     SkDrawTextBlobCommand(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint);
 
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 
 private:
     SkAutoTUnref<const SkTextBlob> fBlob;
@@ -457,7 +467,7 @@
     SkDrawPatchCommand(const SkPoint cubics[12], const SkColor colors[4],
                        const SkPoint texCoords[4], SkXfermode* xmode,
                        const SkPaint& paint);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
 
 private:
     SkPoint fCubics[12];
@@ -473,7 +483,7 @@
 class SkDrawRectCommand : public SkDrawCommand {
 public:
     SkDrawRectCommand(const SkRect& rect, const SkPaint& paint);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
 
     const SkRect& rect() const   { return fRect; }
     const SkPaint& paint() const { return fPaint; }
@@ -487,8 +497,8 @@
 class SkDrawRRectCommand : public SkDrawCommand {
 public:
     SkDrawRRectCommand(const SkRRect& rrect, const SkPaint& paint);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 private:
     SkRRect fRRect;
     SkPaint fPaint;
@@ -500,8 +510,8 @@
 public:
     SkDrawDRRectCommand(const SkRRect& outer, const SkRRect& inner,
                         const SkPaint& paint);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 private:
     SkRRect fOuter;
     SkRRect fInner;
@@ -513,8 +523,8 @@
 class SkDrawSpriteCommand : public SkDrawCommand {
 public:
     SkDrawSpriteCommand(const SkBitmap& bitmap, int left, int top, const SkPaint* paint);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    bool render(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
+    bool render(SkCanvas* canvas) const override;
 private:
     SkBitmap fBitmap;
     int      fLeft;
@@ -533,7 +543,7 @@
                           const uint16_t indices[], int indexCount,
                           const SkPaint& paint);
     virtual ~SkDrawVerticesCommand();
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void execute(SkCanvas* canvas) const override;
 private:
     SkCanvas::VertexMode fVmode;
     int         fVertexCount;
@@ -551,8 +561,8 @@
 class SkSaveCommand : public SkDrawCommand {
 public:
     SkSaveCommand();
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    Action action() const SK_OVERRIDE { return kPushLayer_Action; }
+    void execute(SkCanvas* canvas) const override;
+    Action action() const override { return kPushLayer_Action; }
 private:
     typedef SkDrawCommand INHERITED;
 };
@@ -561,11 +571,11 @@
 public:
     SkSaveLayerCommand(const SkRect* bounds, const SkPaint* paint,
                        SkCanvas::SaveFlags flags);
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
-    void vizExecute(SkCanvas* canvas) const SK_OVERRIDE;
-    Action action() const SK_OVERRIDE{ return kPushLayer_Action; }
-    void setActive(bool active) SK_OVERRIDE { fActive = active; }
-    bool active() const SK_OVERRIDE { return fActive; }
+    void execute(SkCanvas* canvas) const override;
+    void vizExecute(SkCanvas* canvas) const override;
+    Action action() const override{ return kPushLayer_Action; }
+    void setActive(bool active) override { fActive = active; }
+    bool active() const override { return fActive; }
 
     const SkPaint* paint() const { return fPaintPtr; }
 
@@ -583,8 +593,8 @@
 class SkSetMatrixCommand : public SkDrawCommand {
 public:
     SkSetMatrixCommand(const SkMatrix& matrix);
-    void setUserMatrix(const SkMatrix&) SK_OVERRIDE;
-    void execute(SkCanvas* canvas) const SK_OVERRIDE;
+    void setUserMatrix(const SkMatrix&) override;
+    void execute(SkCanvas* canvas) const override;
 private:
     SkMatrix fUserMatrix;
     SkMatrix fMatrix;
diff --git a/src/utils/debugger/SkObjectParser.cpp b/src/utils/debugger/SkObjectParser.cpp
index 798db95..d4c8325 100644
--- a/src/utils/debugger/SkObjectParser.cpp
+++ b/src/utils/debugger/SkObjectParser.cpp
@@ -26,7 +26,7 @@
     mBitmap->appendS32(bitmap.height());
 
     const char* gColorTypeStrings[] = {
-        "None", "A8", "565", "4444", "RGBA", "BGRA", "Index8"
+        "None", "A8", "565", "4444", "RGBA", "BGRA", "Index8", "G8"
     };
     SkASSERT(kLastEnum_SkColorType + 1 == SK_ARRAY_COUNT(gColorTypeStrings));
 
diff --git a/src/utils/win/SkDWriteFontFileStream.h b/src/utils/win/SkDWriteFontFileStream.h
index ba16f70..e78b621 100644
--- a/src/utils/win/SkDWriteFontFileStream.h
+++ b/src/utils/win/SkDWriteFontFileStream.h
@@ -25,16 +25,16 @@
     explicit SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream);
     virtual ~SkDWriteFontFileStream();
 
-    size_t read(void* buffer, size_t size) SK_OVERRIDE;
-    bool isAtEnd() const SK_OVERRIDE;
-    bool rewind() SK_OVERRIDE;
-    SkDWriteFontFileStream* duplicate() const SK_OVERRIDE;
-    size_t getPosition() const SK_OVERRIDE;
-    bool seek(size_t position) SK_OVERRIDE;
-    bool move(long offset) SK_OVERRIDE;
-    SkDWriteFontFileStream* fork() const SK_OVERRIDE;
-    size_t getLength() const SK_OVERRIDE;
-    const void* getMemoryBase() SK_OVERRIDE;
+    size_t read(void* buffer, size_t size) override;
+    bool isAtEnd() const override;
+    bool rewind() override;
+    SkDWriteFontFileStream* duplicate() const override;
+    size_t getPosition() const override;
+    bool seek(size_t position) override;
+    bool move(long offset) override;
+    SkDWriteFontFileStream* fork() const override;
+    size_t getLength() const override;
+    const void* getMemoryBase() override;
 
 private:
     SkTScopedComPtr<IDWriteFontFileStream> fFontFileStream;
diff --git a/src/utils/win/SkDWriteGeometrySink.h b/src/utils/win/SkDWriteGeometrySink.h
index 703ac3d..417c7f0 100644
--- a/src/utils/win/SkDWriteGeometrySink.h
+++ b/src/utils/win/SkDWriteGeometrySink.h
@@ -28,17 +28,17 @@
     virtual ~SkDWriteGeometrySink();
 
 public:
-    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object) SK_OVERRIDE;
-    ULONG STDMETHODCALLTYPE AddRef(void) SK_OVERRIDE;
-    ULONG STDMETHODCALLTYPE Release(void) SK_OVERRIDE;
+    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object) override;
+    ULONG STDMETHODCALLTYPE AddRef(void) override;
+    ULONG STDMETHODCALLTYPE Release(void) override;
 
-    void STDMETHODCALLTYPE SetFillMode(D2D1_FILL_MODE fillMode) SK_OVERRIDE;
-    void STDMETHODCALLTYPE SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) SK_OVERRIDE;
-    void STDMETHODCALLTYPE BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) SK_OVERRIDE;
-    void STDMETHODCALLTYPE AddLines(const D2D1_POINT_2F *points, UINT pointsCount) SK_OVERRIDE;
-    void STDMETHODCALLTYPE AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) SK_OVERRIDE;
-    void STDMETHODCALLTYPE EndFigure(D2D1_FIGURE_END figureEnd) SK_OVERRIDE;
-    HRESULT STDMETHODCALLTYPE Close() SK_OVERRIDE;
+    void STDMETHODCALLTYPE SetFillMode(D2D1_FILL_MODE fillMode) override;
+    void STDMETHODCALLTYPE SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) override;
+    void STDMETHODCALLTYPE BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) override;
+    void STDMETHODCALLTYPE AddLines(const D2D1_POINT_2F *points, UINT pointsCount) override;
+    void STDMETHODCALLTYPE AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) override;
+    void STDMETHODCALLTYPE EndFigure(D2D1_FIGURE_END figureEnd) override;
+    HRESULT STDMETHODCALLTYPE Close() override;
 
     static HRESULT Create(SkPath* path, IDWriteGeometrySink** geometryToPath);
 };
diff --git a/src/views/SkWindow.cpp b/src/views/SkWindow.cpp
index 61eaa97..f222715 100644
--- a/src/views/SkWindow.cpp
+++ b/src/views/SkWindow.cpp
@@ -360,7 +360,7 @@
     GrGLint buffer;
     GR_GL_GetIntegerv(interface, GR_GL_FRAMEBUFFER_BINDING, &buffer);
     desc.fRenderTargetHandle = buffer;
-    return grContext->wrapBackendRenderTarget(desc);
+    return grContext->textureProvider()->wrapBackendRenderTarget(desc);
 }
 
 #endif
diff --git a/tests/Android.mk b/tests/Android.mk
deleted file mode 100644
index f928d6f..0000000
--- a/tests/Android.mk
+++ /dev/null
@@ -1,283 +0,0 @@
-
-###############################################################################
-#
-# THIS FILE IS AUTOGENERATED BY GYP_TO_ANDROID.PY. DO NOT EDIT.
-#
-###############################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_CFLAGS += \
-	-fPIC \
-	-Wno-c++11-extensions \
-	-Wno-unused-parameter \
-	-U_FORTIFY_SOURCE \
-	-D_FORTIFY_SOURCE=1
-
-LOCAL_CPPFLAGS := \
-	-Wno-invalid-offsetof
-
-LOCAL_SRC_FILES := \
-	skia_test.cpp \
-	Test.cpp \
-	PathOpsAngleTest.cpp \
-	PathOpsBoundsTest.cpp \
-	PathOpsCubicIntersectionTest.cpp \
-	PathOpsCubicIntersectionTestData.cpp \
-	PathOpsCubicLineIntersectionTest.cpp \
-	PathOpsCubicQuadIntersectionTest.cpp \
-	PathOpsCubicReduceOrderTest.cpp \
-	PathOpsCubicToQuadsTest.cpp \
-	PathOpsDCubicTest.cpp \
-	PathOpsDLineTest.cpp \
-	PathOpsDPointTest.cpp \
-	PathOpsDQuadTest.cpp \
-	PathOpsDRectTest.cpp \
-	PathOpsDTriangleTest.cpp \
-	PathOpsDVectorTest.cpp \
-	PathOpsExtendedTest.cpp \
-	PathOpsInverseTest.cpp \
-	PathOpsLineIntersectionTest.cpp \
-	PathOpsLineParametetersTest.cpp \
-	PathOpsOpCubicThreadedTest.cpp \
-	PathOpsOpRectThreadedTest.cpp \
-	PathOpsOpTest.cpp \
-	PathOpsQuadIntersectionTest.cpp \
-	PathOpsQuadIntersectionTestData.cpp \
-	PathOpsQuadLineIntersectionTest.cpp \
-	PathOpsQuadLineIntersectionThreadedTest.cpp \
-	PathOpsQuadParameterizationTest.cpp \
-	PathOpsQuadReduceOrderTest.cpp \
-	PathOpsSimplifyDegenerateThreadedTest.cpp \
-	PathOpsSimplifyFailTest.cpp \
-	PathOpsSimplifyQuadralateralsThreadedTest.cpp \
-	PathOpsSimplifyQuadThreadedTest.cpp \
-	PathOpsSimplifyRectThreadedTest.cpp \
-	PathOpsSimplifyTest.cpp \
-	PathOpsSimplifyTrianglesThreadedTest.cpp \
-	PathOpsSkpTest.cpp \
-	PathOpsTestCommon.cpp \
-	PathOpsThreadedCommon.cpp \
-	PathOpsTightBoundsTest.cpp \
-	AAClipTest.cpp \
-	ARGBImageEncoderTest.cpp \
-	AndroidPaintTest.cpp \
-	AnnotationTest.cpp \
-	AsADashTest.cpp \
-	AtomicTest.cpp \
-	BBoxHierarchyTest.cpp \
-	BitSetTest.cpp \
-	BitmapCopyTest.cpp \
-	BitmapGetColorTest.cpp \
-	BitmapHasherTest.cpp \
-	BitmapHeapTest.cpp \
-	BitmapTest.cpp \
-	BlendTest.cpp \
-	BlitRowTest.cpp \
-	BlurTest.cpp \
-	CachedDecodingPixelRefTest.cpp \
-	CanvasStateHelpers.cpp \
-	CanvasStateTest.cpp \
-	CanvasTest.cpp \
-	ChecksumTest.cpp \
-	ClampRangeTest.cpp \
-	ClipCacheTest.cpp \
-	ClipCubicTest.cpp \
-	ClipStackTest.cpp \
-	ClipperTest.cpp \
-	ColorFilterTest.cpp \
-	ColorPrivTest.cpp \
-	ColorTest.cpp \
-	DashPathEffectTest.cpp \
-	DataRefTest.cpp \
-	DeferredCanvasTest.cpp \
-	DequeTest.cpp \
-	DeviceLooperTest.cpp \
-	DiscardableMemoryPoolTest.cpp \
-	DiscardableMemoryTest.cpp \
-	DocumentTest.cpp \
-	DrawBitmapRectTest.cpp \
-	DrawPathTest.cpp \
-	DrawTextTest.cpp \
-	DynamicHashTest.cpp \
-	EmptyPathTest.cpp \
-	ErrorTest.cpp \
-	FillPathTest.cpp \
-	FitsInTest.cpp \
-	FlatDataTest.cpp \
-	FlateTest.cpp \
-	FloatingPointTextureTest.cpp \
-	FontHostStreamTest.cpp \
-	FontHostTest.cpp \
-	FontMgrTest.cpp \
-	FontNamesTest.cpp \
-	FontObjTest.cpp \
-	FrontBufferedStreamTest.cpp \
-	GLInterfaceValidationTest.cpp \
-	GLProgramsTest.cpp \
-	GeometryTest.cpp \
-	GifTest.cpp \
-	GpuColorFilterTest.cpp \
-	GpuDrawPathTest.cpp \
-	GpuLayerCacheTest.cpp \
-	GpuRectanizerTest.cpp \
-	GrBinHashKeyTest.cpp \
-	GrContextFactoryTest.cpp \
-	GrDrawTargetTest.cpp \
-	GrMemoryPoolTest.cpp \
-	GrOrderedSetTest.cpp \
-	GrRedBlackTreeTest.cpp \
-	GrSurfaceTest.cpp \
-	GrTBSearchTest.cpp \
-	GradientTest.cpp \
-	ImageCacheTest.cpp \
-	ImageDecodingTest.cpp \
-	ImageFilterTest.cpp \
-	ImageGeneratorTest.cpp \
-	ImageNewShaderTest.cpp \
-	InfRectTest.cpp \
-	InterpolatorTest.cpp \
-	JpegTest.cpp \
-	KtxTest.cpp \
-	LListTest.cpp \
-	LayerDrawLooperTest.cpp \
-	LayerRasterizerTest.cpp \
-	MD5Test.cpp \
-	MallocPixelRefTest.cpp \
-	MathTest.cpp \
-	Matrix44Test.cpp \
-	MatrixClipCollapseTest.cpp \
-	MatrixTest.cpp \
-	MemoryTest.cpp \
-	MemsetTest.cpp \
-	MessageBusTest.cpp \
-	MetaDataTest.cpp \
-	MipMapTest.cpp \
-	NameAllocatorTest.cpp \
-	OSPathTest.cpp \
-	ObjectPoolTest.cpp \
-	OnceTest.cpp \
-	PDFPrimitivesTest.cpp \
-	PackBitsTest.cpp \
-	PaintTest.cpp \
-	ParsePathTest.cpp \
-	PathCoverageTest.cpp \
-	PathMeasureTest.cpp \
-	PathTest.cpp \
-	PathUtilsTest.cpp \
-	PictureShaderTest.cpp \
-	PictureStateTreeTest.cpp \
-	PictureTest.cpp \
-	PixelRefTest.cpp \
-	PointTest.cpp \
-	PremulAlphaRoundTripTest.cpp \
-	QuickRejectTest.cpp \
-	RTConfRegistryTest.cpp \
-	RTreeTest.cpp \
-	RandomTest.cpp \
-	ReadPixelsTest.cpp \
-	ReadWriteAlphaTest.cpp \
-	Reader32Test.cpp \
-	RecordDrawTest.cpp \
-	RecordOptsTest.cpp \
-	RecordPatternTest.cpp \
-	RecordTest.cpp \
-	RecorderTest.cpp \
-	RecordingTest.cpp \
-	RefCntTest.cpp \
-	RefDictTest.cpp \
-	RegionTest.cpp \
-	ResourceCacheTest.cpp \
-	RoundRectTest.cpp \
-	RuntimeConfigTest.cpp \
-	SHA1Test.cpp \
-	SListTest.cpp \
-	ScalarTest.cpp \
-	ScaledImageCache.cpp \
-	SerializationTest.cpp \
-	ShaderImageFilterTest.cpp \
-	ShaderOpacityTest.cpp \
-	SizeTest.cpp \
-	SkBase64Test.cpp \
-	SmallAllocatorTest.cpp \
-	SortTest.cpp \
-	SrcOverTest.cpp \
-	StreamTest.cpp \
-	StringTest.cpp \
-	StrokeTest.cpp \
-	SurfaceTest.cpp \
-	TArrayTest.cpp \
-	TLSTest.cpp \
-	TSetTest.cpp \
-	TextureCompressionTest.cpp \
-	TileGridTest.cpp \
-	ToUnicodeTest.cpp \
-	TracingTest.cpp \
-	TypefaceTest.cpp \
-	UnicodeTest.cpp \
-	UtilsTest.cpp \
-	WArrayTest.cpp \
-	WritePixelsTest.cpp \
-	Writer32Test.cpp \
-	XfermodeTest.cpp \
-	../src/utils/debugger/SkDrawCommand.cpp \
-	../src/utils/debugger/SkDebugCanvas.cpp \
-	../src/utils/debugger/SkObjectParser.cpp \
-	PipeTest.cpp \
-	../src/pipe/utils/SamplePipeControllers.cpp \
-	TDStackNesterTest.cpp \
-	../tools/CrashHandler.cpp \
-	../tools/flags/SkCommandLineFlags.cpp \
-	../tools/Resources.cpp \
-	../experimental/SkSetPoly3To3.cpp \
-	../experimental/SkSetPoly3To3_A.cpp \
-	../experimental/SkSetPoly3To3_D.cpp \
-	../tools/flags/SkCommonFlags.cpp \
-	../tools/picture_utils.cpp \
-	../tools/sk_tool_utils.cpp \
-	../tools/sk_tool_utils_font.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	libskia \
-	libdl \
-	libGLESv2 \
-	libEGL \
-	liblog
-
-LOCAL_C_INCLUDES := \
-	$(LOCAL_PATH)/../include/config \
-	$(LOCAL_PATH)/../include/core \
-	$(LOCAL_PATH)/../include/pathops \
-	$(LOCAL_PATH)/../include/pipe \
-	$(LOCAL_PATH)/../include/effects \
-	$(LOCAL_PATH)/../include/images \
-	$(LOCAL_PATH)/../include/ports \
-	$(LOCAL_PATH)/../src/sfnt \
-	$(LOCAL_PATH)/../include/utils \
-	$(LOCAL_PATH)/../src/utils \
-	$(LOCAL_PATH)/../include/gpu \
-	$(LOCAL_PATH)/../tools/flags \
-	$(LOCAL_PATH)/../src/fonts \
-	$(LOCAL_PATH)/../tools \
-	$(LOCAL_PATH)/../src/core \
-	$(LOCAL_PATH)/../src/effects \
-	$(LOCAL_PATH)/../src/lazy \
-	$(LOCAL_PATH)/../src/pathops \
-	$(LOCAL_PATH)/../src/pipe/utils \
-	$(LOCAL_PATH)/../src/image \
-	$(LOCAL_PATH)/../src/images \
-	$(LOCAL_PATH)/../src/pdf \
-	$(LOCAL_PATH)/../src/utils/debugger \
-	$(LOCAL_PATH)/../experimental/PdfViewer \
-	$(LOCAL_PATH)/../experimental/PdfViewer/src \
-	$(LOCAL_PATH)/../src/gpu \
-	$(LOCAL_PATH)/../experimental \
-	$(LOCAL_PATH)/../include/pdf
-
-LOCAL_MODULE_TAGS := \
-	tests
-
-LOCAL_MODULE := \
-	skia_test
-
-include $(BUILD_NATIVE_TEST)
diff --git a/tests/BadIcoTest.cpp b/tests/BadIcoTest.cpp
index 566f3d6..9543d4b 100644
--- a/tests/BadIcoTest.cpp
+++ b/tests/BadIcoTest.cpp
@@ -16,6 +16,9 @@
         "sigabort_favicon.ico",
         "sigsegv_favicon.ico",
         "sigsegv_favicon_2.ico",
+        "ico_leak01.ico",
+        "ico_fuzz0.ico",
+        "ico_fuzz1.ico"
     };
 
     const char* badIcoFolder = "invalid_images";
diff --git a/tests/BitmapHeapTest.cpp b/tests/BitmapHeapTest.cpp
index 21ce2f3..eb86283 100644
--- a/tests/BitmapHeapTest.cpp
+++ b/tests/BitmapHeapTest.cpp
@@ -18,11 +18,11 @@
 struct SimpleFlatController : public SkFlatController {
     SimpleFlatController() : SkFlatController() {}
     ~SimpleFlatController() { fAllocations.freeAll(); }
-    void* allocThrow(size_t bytes) SK_OVERRIDE {
+    void* allocThrow(size_t bytes) override {
         fAllocations.push(sk_malloc_throw(bytes));
         return fAllocations.top();
     }
-    void unalloc(void*) SK_OVERRIDE { }
+    void unalloc(void*) override { }
     void setBitmapStorage(SkBitmapHeap* h) { this->setBitmapHeap(h); }
 private:
     SkTDArray<void*> fAllocations;
diff --git a/tests/CachedDecodingPixelRefTest.cpp b/tests/CachedDecodingPixelRefTest.cpp
index 3882026..670d849 100644
--- a/tests/CachedDecodingPixelRefTest.cpp
+++ b/tests/CachedDecodingPixelRefTest.cpp
@@ -163,7 +163,6 @@
 class TestImageGenerator : public SkImageGenerator {
 public:
     enum TestType {
-        kFailGetInfo_TestType,
         kFailGetPixels_TestType,
         kSucceedGetPixels_TestType,
         kLast_TestType = kSucceedGetPixels_TestType
@@ -172,25 +171,20 @@
     static int Height() { return 10; }
     static uint32_t Color() { return 0xff123456; }
     TestImageGenerator(TestType type, skiatest::Reporter* reporter)
-        : fType(type), fReporter(reporter) {
+    : INHERITED(GetMyInfo()), fType(type), fReporter(reporter) {
         SkASSERT((fType <= kLast_TestType) && (fType >= 0));
     }
     virtual ~TestImageGenerator() { }
 
 protected:
-    bool onGetInfo(SkImageInfo* info) SK_OVERRIDE {
-        REPORTER_ASSERT(fReporter, info);
-        if ((NULL == info) || (kFailGetInfo_TestType == fType)) {
-            return false;
-        }
-        *info = SkImageInfo::MakeN32(TestImageGenerator::Width(),
-                                     TestImageGenerator::Height(),
-                                     kOpaque_SkAlphaType);
-        return true;
+    static SkImageInfo GetMyInfo() {
+        return SkImageInfo::MakeN32(TestImageGenerator::Width(), TestImageGenerator::Height(),
+                                    kOpaque_SkAlphaType);
     }
 
     virtual Result onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-                               SkPMColor ctable[], int* ctableCount) SK_OVERRIDE {
+                               const Options&,
+                               SkPMColor ctable[], int* ctableCount) override {
         REPORTER_ASSERT(fReporter, pixels != NULL);
         REPORTER_ASSERT(fReporter, rowBytes >= info.minRowBytes());
         if (fType != kSucceedGetPixels_TestType) {
@@ -211,6 +205,8 @@
 private:
     const TestType fType;
     skiatest::Reporter* const fReporter;
+
+    typedef SkImageGenerator INHERITED;
 };
 
 static void check_test_image_generator_bitmap(skiatest::Reporter* reporter,
@@ -255,8 +251,7 @@
     } else {
         success = SkInstallDiscardablePixelRef(gen.detach(), &lazy, factory);
     }
-    REPORTER_ASSERT(reporter, success
-                    == (TestImageGenerator::kFailGetInfo_TestType != type));
+    REPORTER_ASSERT(reporter, success);
     if (TestImageGenerator::kSucceedGetPixels_TestType == type) {
         check_test_image_generator_bitmap(reporter, lazy);
     } else if (TestImageGenerator::kFailGetPixels_TestType == type) {
@@ -282,15 +277,11 @@
 DEF_TEST(DiscardableAndCachingPixelRef, reporter) {
     test_newlockdelete(reporter);
 
-    check_pixelref(TestImageGenerator::kFailGetInfo_TestType,
-                   reporter, kSkCaching_PixelRefType, NULL);
     check_pixelref(TestImageGenerator::kFailGetPixels_TestType,
                    reporter, kSkCaching_PixelRefType, NULL);
     check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType,
                    reporter, kSkCaching_PixelRefType, NULL);
 
-    check_pixelref(TestImageGenerator::kFailGetInfo_TestType,
-                   reporter, kSkDiscardable_PixelRefType, NULL);
     check_pixelref(TestImageGenerator::kFailGetPixels_TestType,
                    reporter, kSkDiscardable_PixelRefType, NULL);
     check_pixelref(TestImageGenerator::kSucceedGetPixels_TestType,
@@ -320,7 +311,6 @@
 
 DEF_TEST(Image_NewFromGenerator, r) {
     TestImageGenerator::TestType testTypes[] = {
-        TestImageGenerator::kFailGetInfo_TestType,
         TestImageGenerator::kFailGetPixels_TestType,
         TestImageGenerator::kSucceedGetPixels_TestType,
     };
@@ -328,10 +318,6 @@
         TestImageGenerator::TestType test = testTypes[i];
         SkImageGenerator* gen = SkNEW_ARGS(TestImageGenerator, (test, r));
         SkAutoTUnref<SkImage> image(SkImage::NewFromGenerator(gen));
-        if (TestImageGenerator::kFailGetInfo_TestType == test) {
-            REPORTER_ASSERT(r, NULL == image.get());
-            continue;
-        }
         if (NULL == image.get()) {
             ERRORF(r, "SkImage::NewFromGenerator unexpecedly failed ["
                    SK_SIZE_T_SPECIFIER "]", i);
diff --git a/tests/CanvasStateTest.cpp b/tests/CanvasStateTest.cpp
index 8f77fb4..f4db1a5 100644
--- a/tests/CanvasStateTest.cpp
+++ b/tests/CanvasStateTest.cpp
@@ -254,7 +254,7 @@
 
 class TestDrawFilter : public SkDrawFilter {
 public:
-    bool filter(SkPaint*, Type) SK_OVERRIDE { return true; }
+    bool filter(SkPaint*, Type) override { return true; }
 };
 
 DEF_TEST(CanvasState_test_draw_filters, reporter) {
diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp
index 98cfbc1..7a4a8ff 100644
--- a/tests/CanvasTest.cpp
+++ b/tests/CanvasTest.cpp
@@ -45,6 +45,7 @@
  */
 #include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "SkClipStack.h"
 #include "SkDeferredCanvas.h"
 #include "SkDevice.h"
 #include "SkDocument.h"
@@ -118,7 +119,7 @@
     { }
 
     SkRect fRect;
-    SkMatrix fMatrix;;
+    SkMatrix fMatrix;
     SkPath fPath;
     SkPath fNearlyZeroLengthPath;
     SkIRect fIRect;
@@ -190,13 +191,13 @@
 public:
     Canvas2CanvasClipVisitor(SkCanvas* target) : fTarget(target) {}
 
-    void clipRect(const SkRect& r, SkRegion::Op op, bool aa) SK_OVERRIDE {
+    void clipRect(const SkRect& r, SkRegion::Op op, bool aa) override {
         fTarget->clipRect(r, op, aa);
     }
-    void clipRRect(const SkRRect& r, SkRegion::Op op, bool aa) SK_OVERRIDE {
+    void clipRRect(const SkRRect& r, SkRegion::Op op, bool aa) override {
         fTarget->clipRRect(r, op, aa);
     }
-    void clipPath(const SkPath& p, SkRegion::Op op, bool aa) SK_OVERRIDE {
+    void clipPath(const SkPath& p, SkRegion::Op op, bool aa) override {
         fTarget->clipPath(p, op, aa);
     }
 
@@ -217,6 +218,18 @@
     REPORTER_ASSERT(reporter, equal_clips(c, *canvas));
 }
 
+static void test_clipstack(skiatest::Reporter* reporter) {
+    // The clipstack is refcounted, and needs to be able to out-live the canvas if a client has
+    // ref'd it.
+    const SkClipStack* cs = NULL;
+    {
+        SkCanvas canvas(10, 10);
+        cs = SkRef(canvas.getClipStack());
+    }
+    REPORTER_ASSERT(reporter, cs->unique());
+    cs->unref();
+}
+
 // Format strings that describe the test context.  The %s token is where
 // the name of the test step is inserted.  The context is required for
 // disambiguating the error in the case of failures that are reported in
@@ -671,6 +684,7 @@
     if (false) { // avoid bit rot, suppress warning
         test_clipVisitor(reporter, &referenceCanvas);
     }
+    test_clipstack(reporter);
 }
 
 static void test_newraster(skiatest::Reporter* reporter) {
@@ -747,3 +761,19 @@
     canvas.restore();
     REPORTER_ASSERT(reporter, 1 == canvas.getSaveCount());
 }
+
+DEF_TEST(Canvas_ClipEmptyPath, reporter) {
+    SkCanvas canvas(10, 10);
+    canvas.save();
+    SkPath path;
+    canvas.clipPath(path);
+    canvas.restore();
+    canvas.save();
+    path.moveTo(5, 5);
+    canvas.clipPath(path);
+    canvas.restore();
+    canvas.save();
+    path.moveTo(7, 7);
+    canvas.clipPath(path);  // should not assert here
+    canvas.restore();
+}
diff --git a/tests/ChecksumTest.cpp b/tests/ChecksumTest.cpp
index 6248678..3658bd7 100644
--- a/tests/ChecksumTest.cpp
+++ b/tests/ChecksumTest.cpp
@@ -50,3 +50,16 @@
         }
     }
 }
+
+DEF_TEST(GoodHash, r) {
+    ASSERT(SkGoodHash(( int32_t)4) ==  614249093);  // 4 bytes.  Hits SkChecksum::Mix fast path.
+    ASSERT(SkGoodHash((uint32_t)4) ==  614249093);  // (Ditto)
+
+    // None of these are 4 byte sized, so they use SkChecksum::Murmur3, not SkChecksum::Mix.
+    ASSERT(SkGoodHash((uint64_t)4) == 3491892518);
+    ASSERT(SkGoodHash((uint16_t)4) ==  899251846);
+    ASSERT(SkGoodHash( (uint8_t)4) ==  962700458);
+
+    // Tests SkString is correctly specialized.
+    ASSERT(SkGoodHash(SkString("Hi")) == 55667557);
+}
diff --git a/tests/ClipCacheTest.cpp b/tests/ClipCacheTest.cpp
index 3d3fa95..71d0380 100644
--- a/tests/ClipCacheTest.cpp
+++ b/tests/ClipCacheTest.cpp
@@ -31,7 +31,7 @@
     desc.fHeight    = Y_SIZE;
 
     // We are initializing the texture with zeros here
-    GrTexture* texture = context->createTexture(desc, false, textureData, 0);
+    GrTexture* texture = context->textureProvider()->createTexture(desc, false, textureData, 0);
     if (!texture) {
         return NULL;
     }
@@ -52,7 +52,7 @@
     desc.fWidth     = kXSize;
     desc.fHeight    = kYSize;
 
-    GrTexture* texture = context->createTexture(desc, false, NULL, 0);
+    GrTexture* texture = context->textureProvider()->createTexture(desc, false, NULL, 0);
     if (!texture) {
         return;
     }
diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp
new file mode 100644
index 0000000..080e6e5
--- /dev/null
+++ b/tests/CodexTest.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Resources.h"
+#include "SkBitmap.h"
+#include "SkCodec.h"
+#include "SkMD5.h"
+#include "Test.h"
+
+static SkStreamAsset* resource(const char path[]) {
+    SkString fullPath = GetResourcePath(path);
+    return SkStream::NewFromFile(fullPath.c_str());
+}
+
+static void md5(const SkBitmap& bm, SkMD5::Digest* digest) {
+    SkAutoLockPixels autoLockPixels(bm);
+    SkASSERT(bm.getPixels());
+    SkMD5 md5;
+    size_t rowLen = bm.info().bytesPerPixel() * bm.width();
+    for (int y = 0; y < bm.height(); ++y) {
+        md5.update(static_cast<uint8_t*>(bm.getAddr(0, y)), rowLen);
+    }
+    md5.finish(*digest);
+}
+
+static void check(skiatest::Reporter* r,
+                  const char path[],
+                  SkISize size,
+                  bool supportsScanlineDecoding) {
+    SkAutoTDelete<SkStream> stream(resource(path));
+    if (!stream) {
+        SkDebugf("Missing resource '%s'\n", path);
+        return;
+    }
+    SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach()));
+    if (!codec) {
+        ERRORF(r, "Unable to decode '%s'", path);
+        return;
+    }
+
+    // This test is used primarily to verify rewinding works properly.  Using kN32 allows
+    // us to test this without the added overhead of creating different bitmaps depending
+    // on the color type (ex: building a color table for kIndex8).  DM is where we test
+    // decodes to all possible destination color types.
+    SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
+    REPORTER_ASSERT(r, info.dimensions() == size);
+    SkBitmap bm;
+    bm.allocPixels(info);
+    SkAutoLockPixels autoLockPixels(bm);
+    SkImageGenerator::Result result =
+        codec->getPixels(info, bm.getPixels(), bm.rowBytes(), NULL, NULL, NULL);
+    REPORTER_ASSERT(r, result == SkImageGenerator::kSuccess);
+
+    SkMD5::Digest digest1, digest2;
+    md5(bm, &digest1);
+
+    bm.eraseColor(SK_ColorYELLOW);
+
+    result =
+        codec->getPixels(info, bm.getPixels(), bm.rowBytes(), NULL, NULL, NULL);
+
+    REPORTER_ASSERT(r, result == SkImageGenerator::kSuccess);
+    // verify that re-decoding gives the same result.
+    md5(bm, &digest2);
+    REPORTER_ASSERT(r, digest1 == digest2);
+
+    SkScanlineDecoder* scanlineDecoder = codec->getScanlineDecoder(info);
+    if (supportsScanlineDecoding) {
+        bm.eraseColor(SK_ColorYELLOW);
+        REPORTER_ASSERT(r, scanlineDecoder);
+        for (int y = 0; y < info.height(); y++) {
+            result = scanlineDecoder->getScanlines(bm.getAddr(0, y), 1, 0);
+            REPORTER_ASSERT(r, result == SkImageGenerator::kSuccess);
+        }
+        // verify that scanline decoding gives the same result.
+        SkMD5::Digest digest3;
+        md5(bm, &digest3);
+        REPORTER_ASSERT(r, digest3 == digest1);
+    } else {
+        REPORTER_ASSERT(r, !scanlineDecoder);
+    }
+}
+
+DEF_TEST(Codec, r) {
+    // WBMP
+    check(r, "mandrill.wbmp", SkISize::Make(512, 512), false);
+
+    // BMP
+    check(r, "randPixels.bmp", SkISize::Make(8, 8), false);
+
+    // ICO
+    // These two tests examine interestingly different behavior:
+    // Decodes an embedded BMP image
+    check(r, "color_wheel.ico", SkISize::Make(128, 128), false);
+    // Decodes an embedded PNG image
+    check(r, "google_chrome.ico", SkISize::Make(256, 256), false);
+
+    // GIF
+    check(r, "box.gif", SkISize::Make(200, 55), false);
+    check(r, "color_wheel.gif", SkISize::Make(128, 128), false);
+    check(r, "randPixels.gif", SkISize::Make(8, 8), false);
+
+    // JPG
+    check(r, "CMYK.jpg", SkISize::Make(642, 516), true);
+    check(r, "color_wheel.jpg", SkISize::Make(128, 128), true);
+    check(r, "grayscale.jpg", SkISize::Make(128, 128), true);
+    check(r, "mandrill_512_q075.jpg", SkISize::Make(512, 512), true);
+    check(r, "randPixels.jpg", SkISize::Make(8, 8), true);
+
+    // PNG
+    check(r, "arrow.png", SkISize::Make(187, 312), true);
+    check(r, "baby_tux.png", SkISize::Make(240, 246), true);
+    check(r, "color_wheel.png", SkISize::Make(128, 128), true);
+    check(r, "half-transparent-white-pixel.png", SkISize::Make(1, 1), true);
+    check(r, "mandrill_128.png", SkISize::Make(128, 128), true);
+    check(r, "mandrill_16.png", SkISize::Make(16, 16), true);
+    check(r, "mandrill_256.png", SkISize::Make(256, 256), true);
+    check(r, "mandrill_32.png", SkISize::Make(32, 32), true);
+    check(r, "mandrill_512.png", SkISize::Make(512, 512), true);
+    check(r, "mandrill_64.png", SkISize::Make(64, 64), true);
+    check(r, "plane.png", SkISize::Make(250, 126), true);
+    check(r, "randPixels.png", SkISize::Make(8, 8), true);
+    check(r, "yellow_rose.png", SkISize::Make(400, 301), true);
+}
+
+static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_t len) {
+    SkCodec* codec = SkCodec::NewFromStream(new SkMemoryStream(stream, len, false));
+    // We should not have gotten a codec. Bots should catch us if we leaked anything.
+    REPORTER_ASSERT(r, !codec);
+}
+
+// Ensure that SkCodec::NewFromStream handles freeing the passed in SkStream,
+// even on failure. Test some bad streams.
+DEF_TEST(Codec_leaks, r) {
+    // No codec should claim this as their format, so this tests SkCodec::NewFromStream.
+    const char nonSupportedStream[] = "hello world";
+    // The other strings should look like the beginning of a file type, so we'll call some
+    // internal version of NewFromStream, which must also delete the stream on failure.
+    const unsigned char emptyPng[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
+    const unsigned char emptyJpeg[] = { 0xFF, 0xD8, 0xFF };
+    const char emptyWebp[] = "RIFF1234WEBPVP";
+    const char emptyBmp[] = { 'B', 'M' };
+    const char emptyIco[] = { '\x00', '\x00', '\x01', '\x00' };
+    const char emptyGif[] = "GIFVER";
+
+    test_invalid_stream(r, nonSupportedStream, sizeof(nonSupportedStream));
+    test_invalid_stream(r, emptyPng, sizeof(emptyPng));
+    test_invalid_stream(r, emptyJpeg, sizeof(emptyJpeg));
+    test_invalid_stream(r, emptyWebp, sizeof(emptyWebp));
+    test_invalid_stream(r, emptyBmp, sizeof(emptyBmp));
+    test_invalid_stream(r, emptyIco, sizeof(emptyIco));
+    test_invalid_stream(r, emptyGif, sizeof(emptyGif));
+}
+
+static void test_dimensions(skiatest::Reporter* r, const char path[]) {
+    // Create the codec from the resource file
+    SkAutoTDelete<SkStream> stream(resource(path));
+    if (!stream) {
+        SkDebugf("Missing resource '%s'\n", path);
+        return;
+    }
+    SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach()));
+    if (!codec) {
+        ERRORF(r, "Unable to create codec '%s'", path);
+        return;
+    }
+
+    // Check that the decode is successful for a variety of scales
+    for (float scale = -0.05f; scale < 2.0f; scale += 0.05f) {
+        // Scale the output dimensions
+        SkISize scaledDims = codec->getScaledDimensions(scale);
+        SkImageInfo scaledInfo = codec->getInfo().makeWH(scaledDims.width(), scaledDims.height());
+
+        // Set up for the decode
+        size_t rowBytes = scaledDims.width() * sizeof(SkPMColor);
+        size_t totalBytes = scaledInfo.getSafeSize(rowBytes);
+        SkAutoTMalloc<SkPMColor> pixels(totalBytes);
+
+        SkImageGenerator::Result result =
+                codec->getPixels(scaledInfo, pixels.get(), rowBytes, NULL, NULL, NULL);
+        REPORTER_ASSERT(r, SkImageGenerator::kSuccess == result);
+    }
+}
+
+// Ensure that onGetScaledDimensions returns valid image dimensions to use for decodes
+DEF_TEST(Codec_Dimensions, r) {
+    // JPG
+    test_dimensions(r, "CMYK.jpg");
+    test_dimensions(r, "color_wheel.jpg");
+    test_dimensions(r, "grayscale.jpg");
+    test_dimensions(r, "mandrill_512_q075.jpg");
+    test_dimensions(r, "randPixels.jpg");
+}
+
+static void test_empty(skiatest::Reporter* r, const char path[]) {
+    SkAutoTDelete<SkStream> stream(resource(path));
+    if (!stream) {
+        SkDebugf("Missing resource '%s'\n", path);
+        return;
+    }
+    SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach()));
+    REPORTER_ASSERT(r, NULL == codec);
+}
+
+DEF_TEST(Codec_Empty, r) {
+    // Test images that should not be able to create a codec
+    test_empty(r, "empty_images/zero-dims.gif");
+    test_empty(r, "empty_images/zero-embedded.ico");
+    test_empty(r, "empty_images/zero-width.bmp");
+    test_empty(r, "empty_images/zero-height.bmp");
+    test_empty(r, "empty_images/zero-width.jpg");
+    test_empty(r, "empty_images/zero-height.jpg");
+    test_empty(r, "empty_images/zero-width.png");
+    test_empty(r, "empty_images/zero-height.png");
+    test_empty(r, "empty_images/zero-width.wbmp");
+    test_empty(r, "empty_images/zero-height.wbmp");
+}
diff --git a/tests/ColorFilterTest.cpp b/tests/ColorFilterTest.cpp
index b2e3718..d0b0d77 100644
--- a/tests/ColorFilterTest.cpp
+++ b/tests/ColorFilterTest.cpp
@@ -147,3 +147,89 @@
         REPORTER_ASSERT(reporter, SkGetPackedB32(out) == 0);
     }
 }
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorMatrixFilter.h"
+
+static void get_brightness_matrix(float amount, float matrix[20]) {
+    // Spec implementation
+    // (http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#brightnessEquivalent)
+    // <feFunc[R|G|B] type="linear" slope="[amount]">
+    memset(matrix, 0, 20 * sizeof(SkScalar));
+    matrix[0] = matrix[6] = matrix[12] = amount;
+    matrix[18] = 1.f;
+}
+
+static void get_grayscale_matrix(float amount, float matrix[20]) {
+    // Note, these values are computed to ensure MatrixNeedsClamping is false
+    // for amount in [0..1]
+    matrix[0] = 0.2126f + 0.7874f * amount;
+    matrix[1] = 0.7152f - 0.7152f * amount;
+    matrix[2] = 1.f - (matrix[0] + matrix[1]);
+    matrix[3] = matrix[4] = 0.f;
+    
+    matrix[5] = 0.2126f - 0.2126f * amount;
+    matrix[6] = 0.7152f + 0.2848f * amount;
+    matrix[7] = 1.f - (matrix[5] + matrix[6]);
+    matrix[8] = matrix[9] = 0.f;
+    
+    matrix[10] = 0.2126f - 0.2126f * amount;
+    matrix[11] = 0.7152f - 0.7152f * amount;
+    matrix[12] = 1.f - (matrix[10] + matrix[11]);
+    matrix[13] = matrix[14] = 0.f;
+    
+    matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0.f;
+    matrix[18] = 1.f;
+}
+
+static SkColorFilter* make_cf0() {
+    SkScalar matrix[20];
+    get_brightness_matrix(0.5f, matrix);
+    return SkColorMatrixFilter::Create(matrix);
+}
+static SkColorFilter* make_cf1() {
+    SkScalar matrix[20];
+    get_grayscale_matrix(1, matrix);
+    return SkColorMatrixFilter::Create(matrix);
+}
+static SkColorFilter* make_cf2() {
+    SkColorMatrix m0, m1;
+    get_brightness_matrix(0.5f, m0.fMat);
+    get_grayscale_matrix(1, m1.fMat);
+    m0.preConcat(m1);
+    return SkColorMatrixFilter::Create(m0);
+}
+static SkColorFilter* make_cf3() {
+    SkColorMatrix m0, m1;
+    get_brightness_matrix(0.5f, m0.fMat);
+    get_grayscale_matrix(1, m1.fMat);
+    m0.postConcat(m1);
+    return SkColorMatrixFilter::Create(m0);
+}
+typedef SkColorFilter* (*CFProc)();
+
+// Test that a colormatrix that "should" preserve opaquness actually does.
+DEF_TEST(ColorMatrixFilter, reporter) {
+    const CFProc procs[] = {
+        make_cf0, make_cf1, make_cf2, make_cf3,
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
+        SkAutoTUnref<SkColorFilter> cf(procs[i]());
+
+        // generate all possible r,g,b triples
+        for (int r = 0; r < 256; ++r) {
+            for (int g = 0; g < 256; ++g) {
+                SkPMColor storage[256];
+                for (int b = 0; b < 256; ++b) {
+                    storage[b] = SkPackARGB32(0xFF, r, g, b);
+                }
+                cf->filterSpan(storage, 256, storage);
+                for (int b = 0; b < 256; ++b) {
+                    REPORTER_ASSERT(reporter, 0xFF == SkGetPackedA32(storage[b]));
+                }
+            }
+        }
+    }
+}
diff --git a/tests/DataRefTest.cpp b/tests/DataRefTest.cpp
index 099e909..981ac54 100644
--- a/tests/DataRefTest.cpp
+++ b/tests/DataRefTest.cpp
@@ -232,3 +232,84 @@
     test_cstring(reporter);
     test_files(reporter);
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkRWBuffer.h"
+
+const char gABC[] = "abcdefghijklmnopqrstuvwxyz";
+
+static void check_abcs(skiatest::Reporter* reporter, const char buffer[], size_t size) {
+    REPORTER_ASSERT(reporter, size % 26 == 0);
+    for (size_t offset = 0; offset < size; offset += 26) {
+        REPORTER_ASSERT(reporter, !memcmp(&buffer[offset], gABC, 26));
+    }
+}
+
+// stream should contains an integral number of copies of gABC.
+static void check_alphabet_stream(skiatest::Reporter* reporter, SkStream* stream) {
+    REPORTER_ASSERT(reporter, stream->hasLength());
+    size_t size = stream->getLength();
+    REPORTER_ASSERT(reporter, size % 26 == 0);
+
+    SkAutoTMalloc<char> storage(size);
+    char* array = storage.get();
+    size_t bytesRead = stream->read(array, size);
+    REPORTER_ASSERT(reporter, bytesRead == size);
+    check_abcs(reporter, array, size);
+
+    // try checking backwards
+    for (size_t offset = size; offset > 0; offset -= 26) {
+        REPORTER_ASSERT(reporter, stream->seek(offset - 26));
+        REPORTER_ASSERT(reporter, stream->getPosition() == offset - 26);
+        REPORTER_ASSERT(reporter, stream->read(array, 26) == 26);
+        check_abcs(reporter, array, 26);
+        REPORTER_ASSERT(reporter, stream->getPosition() == offset);
+    }
+}
+
+// reader should contains an integral number of copies of gABC.
+static void check_alphabet_buffer(skiatest::Reporter* reporter, const SkROBuffer* reader) {
+    size_t size = reader->size();
+    REPORTER_ASSERT(reporter, size % 26 == 0);
+    
+    SkAutoTMalloc<char> storage(size);
+    SkROBuffer::Iter iter(reader);
+    size_t offset = 0;
+    do {
+        SkASSERT(offset + iter.size() <= size);
+        memcpy(storage.get() + offset, iter.data(), iter.size());
+        offset += iter.size();
+    } while (iter.next());
+    REPORTER_ASSERT(reporter, offset == size);
+    check_abcs(reporter, storage.get(), size);
+}
+
+DEF_TEST(RWBuffer, reporter) {
+    // Knowing that the default capacity is 4096, choose N large enough so we force it to use
+    // multiple buffers internally.
+    const int N = 1000;
+    SkROBuffer* readers[N];
+    SkStream* streams[N];
+
+    {
+        SkRWBuffer buffer;
+        for (int i = 0; i < N; ++i) {
+            if (0 == (i & 1)) {
+                buffer.append(gABC, 26);
+            } else {
+                memcpy(buffer.append(26), gABC, 26);
+            }
+            readers[i] = buffer.newRBufferSnapshot();
+            streams[i] = buffer.newStreamSnapshot();
+        }
+        REPORTER_ASSERT(reporter, N*26 == buffer.size());
+    }
+
+    for (int i = 0; i < N; ++i) {
+        REPORTER_ASSERT(reporter, (i + 1) * 26U == readers[i]->size());
+        check_alphabet_buffer(reporter, readers[i]);
+        check_alphabet_stream(reporter, streams[i]);
+        readers[i]->unref();
+        SkDELETE(streams[i]);
+    }
+}
diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp
index 6756f86..56b1bea 100644
--- a/tests/DeferredCanvasTest.cpp
+++ b/tests/DeferredCanvasTest.cpp
@@ -56,32 +56,39 @@
         fBitmap.allocN32Pixels(width, height);
     }
 
-    SkCanvas* onNewCanvas() SK_OVERRIDE {
+    SkCanvas* onNewCanvas() override {
         return SkNEW_ARGS(SkCanvas, (fBitmap));
     }
 
-    SkSurface* onNewSurface(const SkImageInfo&) SK_OVERRIDE {
+    SkSurface* onNewSurface(const SkImageInfo&) override {
         return NULL;
     }
 
-    SkImage* onNewImageSnapshot(Budgeted) SK_OVERRIDE {
+    SkImage* onNewImageSnapshot(Budgeted) override {
         return SkNewImageFromBitmap(fBitmap, true, &this->props());
     }
 
-    void onCopyOnWrite(ContentChangeMode mode) SK_OVERRIDE {
+    void onCopyOnWrite(ContentChangeMode mode) override {
         if (mode == SkSurface::kDiscard_ContentChangeMode) {
-            fDiscardCount++;
+            fCOWDiscardCount++;
         } else {
-            fRetainCount++;
+            fCOWRetainCount++;
         }
     }
 
-    void clearCounts() {
-        fDiscardCount = 0;
-        fRetainCount = 0;
+    void onDiscard() override {
+        fDiscardCount++;
     }
 
-    int fDiscardCount, fRetainCount;
+    void clearCounts() {
+        fCOWDiscardCount = 0;
+        fCOWRetainCount = 0;
+        fDiscardCount = 0;
+    }
+
+    int fCOWDiscardCount;
+    int fCOWRetainCount;
+    int fDiscardCount;
     SkBitmap fBitmap;
 };
 
@@ -95,146 +102,173 @@
     // Tests below depend on this bitmap being recognized as opaque
 
     // Preliminary sanity check: no copy on write if no active snapshot
+    // Discard notification happens on SkSurface::onDiscard, since no
+    // active snapshot.
     surface->clearCounts();
     canvas->clear(SK_ColorWHITE);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->flush();
-    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
+    REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
 
     // Case 1: Discard notification happens upon flushing
     // with an Image attached.
     surface->clearCounts();
     SkAutoTUnref<SkImage> image1(canvas->newImageSnapshot());
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->clear(SK_ColorWHITE);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->flush();
-    REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
+    REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
 
     // Case 2: Opaque writePixels
     surface->clearCounts();
     SkAutoTUnref<SkImage> image2(canvas->newImageSnapshot());
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     // Case 3: writePixels that partially covers the canvas
     surface->clearCounts();
     SkAutoTUnref<SkImage> image3(canvas->newImageSnapshot());
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     // Case 4: unpremultiplied opaque writePixels that entirely
     // covers the canvas
     surface->clearCounts();
     SkAutoTUnref<SkImage> image4(canvas->newImageSnapshot());
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->writePixels(srcBitmap, 0, 0);
-    REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
+    REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
 
     surface->clearCounts();
     canvas->flush();
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     // Case 5: unpremultiplied opaque writePixels that partially
     // covers the canvas
     surface->clearCounts();
     SkAutoTUnref<SkImage> image5(canvas->newImageSnapshot());
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->writePixels(srcBitmap, 5, 0);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 1 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 1 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->flush();
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     // Case 6: unpremultiplied opaque writePixels that entirely
     // covers the canvas, preceded by clear
     surface->clearCounts();
     SkAutoTUnref<SkImage> image6(canvas->newImageSnapshot());
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->clear(SK_ColorWHITE);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->writePixels(srcBitmap, 0, 0);
-    REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
+    REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
 
     surface->clearCounts();
     canvas->flush();
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     // Case 7: unpremultiplied opaque writePixels that partially
     // covers the canvas, preceeded by a clear
     surface->clearCounts();
     SkAutoTUnref<SkImage> image7(canvas->newImageSnapshot());
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->clear(SK_ColorWHITE);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->writePixels(srcBitmap, 5, 0);
-    REPORTER_ASSERT(reporter, 1 == surface->fDiscardCount); // because of the clear
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
+    REPORTER_ASSERT(reporter, 1 == surface->fCOWDiscardCount); // because of the clear
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
 
     surface->clearCounts();
     canvas->flush();
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     // Case 8: unpremultiplied opaque writePixels that partially
     // covers the canvas, preceeded by a drawREct that partially
     // covers the canvas
     surface->clearCounts();
     SkAutoTUnref<SkImage> image8(canvas->newImageSnapshot());
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
     SkPaint paint;
     canvas->drawRect(SkRect::MakeLTRB(0, 0, 5, 5), paint);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->writePixels(srcBitmap, 5, 0);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 1 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 1 == surface->fRetainCount);
 
     surface->clearCounts();
     canvas->flush();
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWDiscardCount);
+    REPORTER_ASSERT(reporter, 0 == surface->fCOWRetainCount);
     REPORTER_ASSERT(reporter, 0 == surface->fDiscardCount);
-    REPORTER_ASSERT(reporter, 0 == surface->fRetainCount);
 }
 
 static void TestDeferredCanvasFlush(skiatest::Reporter* reporter) {
@@ -412,7 +446,7 @@
         fDrawBitmapCallCount = 0;
     }
     virtual void drawBitmap(const SkDraw&, const SkBitmap&,
-                            const SkMatrix&, const SkPaint&) SK_OVERRIDE {
+                            const SkMatrix&, const SkPaint&) override {
         fDrawBitmapCallCount++;
     }
 
@@ -426,16 +460,16 @@
             fFlushedDrawCommandsCount = fSkippedPendingDrawCommandsCount = 0;
     }
 
-    void prepareForDraw() SK_OVERRIDE {
+    void prepareForDraw() override {
         fPrepareForDrawCount++;
     }
-    void storageAllocatedForRecordingChanged(size_t) SK_OVERRIDE {
+    void storageAllocatedForRecordingChanged(size_t) override {
         fStorageAllocatedChangedCount++;
     }
-    void flushedDrawCommands() SK_OVERRIDE {
+    void flushedDrawCommands() override {
         fFlushedDrawCommandsCount++;
     }
-    void skippedPendingDrawCommands() SK_OVERRIDE {
+    void skippedPendingDrawCommands() override {
         fSkippedPendingDrawCommandsCount++;
     }
 
diff --git a/tests/DeflateWStream.cpp b/tests/DeflateWStream.cpp
index 4a602f4..b225746 100644
--- a/tests/DeflateWStream.cpp
+++ b/tests/DeflateWStream.cpp
@@ -9,8 +9,6 @@
 #include "SkRandom.h"
 #include "Test.h"
 
-#ifndef SK_NO_FLATE
-
 DEF_TEST(SkDeflateWStream, r) {
     SkRandom random(123456);
     for (int i = 0; i < 50; ++i) {
@@ -72,4 +70,3 @@
         }
     }
 }
-#endif  // SK_NO_FLATE
diff --git a/tests/DrawBitmapRectTest.cpp b/tests/DrawBitmapRectTest.cpp
index 1ecc590..80167b7 100644
--- a/tests/DrawBitmapRectTest.cpp
+++ b/tests/DrawBitmapRectTest.cpp
@@ -20,15 +20,9 @@
 // A BitmapFactory that always fails when asked to return pixels.
 class FailureImageGenerator : public SkImageGenerator {
 public:
-    FailureImageGenerator() { }
-    virtual ~FailureImageGenerator() { }
-
+    FailureImageGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(100, 100)) {}
 protected:
-    bool onGetInfo(SkImageInfo* info) SK_OVERRIDE {
-        *info = SkImageInfo::MakeN32Premul(100, 100);
-        return true;
-    }
-    // default onGetPixels() returns false, which is what we want.
+    // default onGetPixels() returns kUnimplemented, which is what we want.
 };
 
 // crbug.com/295895
@@ -46,17 +40,17 @@
     SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(200, 200));
     SkCanvas* canvas = surface->getCanvas();
 
-    const SkPaint::FilterLevel levels[] = {
-        SkPaint::kNone_FilterLevel,
-        SkPaint::kLow_FilterLevel,
-        SkPaint::kMedium_FilterLevel,
-        SkPaint::kHigh_FilterLevel,
+    const SkFilterQuality levels[] = {
+        kNone_SkFilterQuality,
+        kLow_SkFilterQuality,
+        kMedium_SkFilterQuality,
+        kHigh_SkFilterQuality,
     };
 
     SkPaint paint;
     canvas->scale(2, 2);    // need a scale, otherwise we may ignore filtering
     for (size_t i = 0; i < SK_ARRAY_COUNT(levels); ++i) {
-        paint.setFilterLevel(levels[i]);
+        paint.setFilterQuality(levels[i]);
         canvas->drawBitmap(bm, 0, 0, &paint);
     }
 }
@@ -130,19 +124,19 @@
     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());
+    const SkScalar bigScale = (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());
+    const SkScalar smallScale = (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());
+    const SkScalar tinyScale = (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));
diff --git a/tests/DrawFilterTest.cpp b/tests/DrawFilterTest.cpp
new file mode 100644
index 0000000..13ae5c6
--- /dev/null
+++ b/tests/DrawFilterTest.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCanvas.h"
+#include "SkDrawFilter.h"
+#include "SkSurface.h"
+#include "Test.h"
+
+namespace {
+class TestFilter : public SkDrawFilter {
+public:
+    bool filter(SkPaint* p, Type) override {
+        return true;
+    }
+};
+}
+
+/**
+ *  canvas.setDrawFilter is defined to be local to the save/restore block, such that if you
+ *  do the following: save / modify-drawfilter / restore, the current drawfilter should be what
+ *  it was before the save.
+ */
+static void test_saverestore(skiatest::Reporter* reporter) {
+    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(10, 10));
+    SkCanvas* canvas = surface->getCanvas();
+
+
+    SkAutoTUnref<TestFilter> df(SkNEW(TestFilter));
+
+    REPORTER_ASSERT(reporter, NULL == canvas->getDrawFilter());
+
+    canvas->save();
+    canvas->setDrawFilter(df);
+    REPORTER_ASSERT(reporter, NULL != canvas->getDrawFilter());
+    canvas->restore();
+
+    REPORTER_ASSERT(reporter, NULL == canvas->getDrawFilter());
+}
+
+DEF_TEST(DrawFilter, reporter) {
+    test_saverestore(reporter);
+}
diff --git a/tests/FlateTest.cpp b/tests/FlateTest.cpp
index 17e8b2d..40efe1a 100644
--- a/tests/FlateTest.cpp
+++ b/tests/FlateTest.cpp
@@ -10,8 +10,6 @@
 #include "SkStream.h"
 #include "Test.h"
 
-#ifndef SK_NO_FLATE
-
 // A memory stream that reports zero size with the standard call, like
 // an unseekable file stream would.
 class SkZeroSizeMemStream : public SkMemoryStream {
@@ -114,4 +112,3 @@
     TestFlate(reporter, &fileStream, 512);
     TestFlate(reporter, &fileStream, 10240);
 }
-#endif  // SK_NO_FLATE
diff --git a/tests/FloatingPointTextureTest.cpp b/tests/FloatingPointTextureTest.cpp
index 7ac27cb..69495b4 100644
--- a/tests/FloatingPointTextureTest.cpp
+++ b/tests/FloatingPointTextureTest.cpp
@@ -11,9 +11,10 @@
  * 32 bit floating point textures, and indeed floating point test values
  * have been selected to require 32 bits of precision and full IEEE conformance
  */
-#if SK_SUPPORT_GPU
+
 #include <float.h>
 #include "Test.h"
+#if SK_SUPPORT_GPU
 #include "GrContext.h"
 #include "GrTexture.h"
 #include "GrContextFactory.h"
@@ -59,8 +60,8 @@
                 continue;
             }
 
-            SkAutoTUnref<GrTexture> fpTexture(context->createTexture(desc, false,
-                                                                     controlPixelData.begin(), 0));
+            SkAutoTUnref<GrTexture> fpTexture(context->textureProvider()->createTexture(
+                desc, false, controlPixelData.begin(), 0));
             // Floating point textures are NOT supported everywhere
             if (NULL == fpTexture) {
                 continue;
@@ -106,8 +107,8 @@
                 continue;
             }
 
-            SkAutoTUnref<GrTexture> fpTexture(context->createTexture(desc, false,
-                                                                     controlPixelData.begin(), 0));
+            SkAutoTUnref<GrTexture> fpTexture(context->textureProvider()->createTexture(
+                desc, false, controlPixelData.begin(), 0));
             // 16-bit floating point textures are NOT supported everywhere
             if (NULL == fpTexture) {
                 continue;
diff --git a/tests/FontHostTest.cpp b/tests/FontHostTest.cpp
index 249fe7b..ff275e2 100644
--- a/tests/FontHostTest.cpp
+++ b/tests/FontHostTest.cpp
@@ -111,8 +111,7 @@
     }
 }
 
-static void test_fontstream(skiatest::Reporter* reporter,
-                            SkStream* stream, int ttcIndex) {
+static void test_fontstream(skiatest::Reporter* reporter, SkStream* stream, int ttcIndex) {
     int n = SkFontStream::GetTableTags(stream, ttcIndex, NULL);
     SkAutoTArray<SkFontTableTag> array(n);
 
@@ -138,7 +137,13 @@
     }
 }
 
-static void test_fontstream(skiatest::Reporter* reporter, SkStream* stream) {
+static void test_fontstream(skiatest::Reporter* reporter) {
+    SkAutoTDelete<SkStreamAsset> stream(GetResourceAsStream("/fonts/test.ttc"));
+    if (!stream) {
+        SkDebugf("Skipping FontHostTest::test_fontstream\n");
+        return;
+    }
+
     int count = SkFontStream::CountTTCEntries(stream);
 #ifdef DUMP_TTC_TABLES
     SkDebugf("CountTTCEntries %d\n", count);
@@ -148,23 +153,6 @@
     }
 }
 
-static void test_fontstream(skiatest::Reporter* reporter) {
-    // This test cannot run if there is no resource path.
-    SkString resourcePath = GetResourcePath();
-    if (resourcePath.isEmpty()) {
-        SkDebugf("Could not run fontstream test because resourcePath not specified.");
-        return;
-    }
-    SkString filename = SkOSPath::Join(resourcePath.c_str(), "test.ttc");
-
-    SkFILEStream stream(filename.c_str());
-    if (stream.isValid()) {
-        test_fontstream(reporter, &stream);
-    } else {
-        SkDebugf("Could not run fontstream test because test.ttc not found.");
-    }
-}
-
 static void test_tables(skiatest::Reporter* reporter, SkTypeface* face) {
     if (false) { // avoid bit rot, suppress warning
         SkFontID fontID = face->uniqueID();
diff --git a/tests/FrontBufferedStreamTest.cpp b/tests/FrontBufferedStreamTest.cpp
index 69dade0..f3ef47d 100644
--- a/tests/FrontBufferedStreamTest.cpp
+++ b/tests/FrontBufferedStreamTest.cpp
@@ -131,7 +131,7 @@
         : INHERITED(data, size, ownMemory)
         , fIsAtEnd(false) {}
 
-    size_t read(void* dst, size_t requested) SK_OVERRIDE {
+    size_t read(void* dst, size_t requested) override {
         size_t bytesRead = this->INHERITED::read(dst, requested);
         if (bytesRead < requested) {
             fIsAtEnd = true;
@@ -139,7 +139,7 @@
         return bytesRead;
     }
 
-    bool isAtEnd() const SK_OVERRIDE {
+    bool isAtEnd() const override {
         return fIsAtEnd;
     }
 
@@ -175,19 +175,19 @@
         , fHasPosition(hasPosition)
     {}
 
-    bool hasLength() const SK_OVERRIDE {
+    bool hasLength() const override {
         return fHasLength;
     }
 
-    bool hasPosition() const SK_OVERRIDE {
+    bool hasPosition() const override {
         return fHasPosition;
     }
 
-    size_t read(void*, size_t) SK_OVERRIDE {
+    size_t read(void*, size_t) override {
         return 0;
     }
 
-    bool isAtEnd() const SK_OVERRIDE {
+    bool isAtEnd() const override {
         return true;
     }
 
@@ -264,7 +264,7 @@
     : fAtEnd(false)
     , fReadAfterEnd(false)
     {}
-    size_t read(void* buffer, size_t size) SK_OVERRIDE {
+    size_t read(void* buffer, size_t size) override {
         if (fAtEnd) {
             fReadAfterEnd = true;
         } else {
@@ -273,7 +273,7 @@
         return 0;
     }
 
-    bool isAtEnd() const SK_OVERRIDE {
+    bool isAtEnd() const override {
         return fAtEnd;
     }
 
diff --git a/tests/FunctionTest.cpp b/tests/FunctionTest.cpp
new file mode 100644
index 0000000..cf6b789
--- /dev/null
+++ b/tests/FunctionTest.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkFunction.h"
+#include "Test.h"
+
+static void test_add_five(skiatest::Reporter* r, SkFunction<int(int)>& f) {
+    REPORTER_ASSERT(r, f(3) == 8);
+    REPORTER_ASSERT(r, f(4) == 9);
+}
+
+static void test_add_five(skiatest::Reporter* r, SkFunction<int(int)>&& f) { test_add_five(r, f); }
+
+static int add_five(int x) { return x + 5; }
+
+struct AddFive {
+    int operator()(int x) const { return x + 5; };
+};
+
+class MoveOnlyThree : SkNoncopyable {
+public:
+    MoveOnlyThree() {}
+    MoveOnlyThree(MoveOnlyThree&&) {}
+    MoveOnlyThree& operator=(MoveOnlyThree&&) { return *this; }
+
+    int val() { return 3; }
+};
+
+DEF_TEST(Function, r) {
+    // We should be able to turn a function pointer, an explicit functor, or a
+    // lambda into an SkFunction all equally well.
+    test_add_five(r, &add_five);
+    test_add_five(r, AddFive());
+    test_add_five(r, [](int x) { return x + 5; });
+
+    // AddFive and the lambda above are both small enough to test small-object optimization.
+    // Now test a lambda that's much too large for the small-object optimization.
+    int a = 1, b = 1, c = 1, d = 1, e = 1;
+    test_add_five(r, [&](int x) { return x + a + b + c + d + e; });
+
+    // Makes sure we forward arguments when calling SkFunction.
+    SkFunction<int(int, MoveOnlyThree&&, int)> f([](int x, MoveOnlyThree&& three, int y) {
+            return x * three.val() + y;
+    });
+    REPORTER_ASSERT(r, f(2, MoveOnlyThree(), 4) == 10);
+
+    // SkFunctions can go in containers.
+    SkTArray<SkFunction<int(int)>> add_fivers;
+    add_fivers.push_back(&add_five);
+    add_fivers.push_back(AddFive());
+    add_fivers.push_back([](int x) { return x + 5; });
+    add_fivers.push_back([&](int x) { return x + a + b + c + d + e; });
+    for (auto& f : add_fivers) {
+        test_add_five(r, f);
+    }
+
+    // SkFunctions are assignable.
+    SkFunction<int(int)> empty;
+    empty = [](int x) { return x + 5; };
+    test_add_five(r, empty);
+
+    // This all is silly acrobatics, but it should at least work correctly.
+    SkFunction<int(int)> emptyA, emptyB(emptyA);
+    emptyA = emptyB;
+    emptyA = emptyA;
+}
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
index 897e261..7f1f4ec 100644
--- a/tests/GLProgramsTest.cpp
+++ b/tests/GLProgramsTest.cpp
@@ -12,6 +12,9 @@
 
 #if SK_SUPPORT_GPU && SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
 
+#include "GrAutoLocaleSetter.h"
+#include "GrBatch.h"
+#include "GrBatchTest.h"
 #include "GrContextFactory.h"
 #include "GrInvariantOutput.h"
 #include "GrPipeline.h"
@@ -41,9 +44,13 @@
                           const char* outputColor,
                           const char* inputColor,
                           const TransformedCoordsArray&,
-                          const TextureSamplerArray&) {}
+                          const TextureSamplerArray&) {
+        // pass through
+        GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
+        fsBuilder->codeAppendf("%s = %s;\n", outputColor, inputColor);
+    }
 
-    static void GenKey(const GrProcessor& processor, const GrGLCaps&, GrProcessorKeyBuilder* b) {
+    static void GenKey(const GrProcessor& processor, const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
         for (uint32_t i = 0; i < kMaxKeySize; i++) {
             b->add32(i);
         }
@@ -60,14 +67,14 @@
         return SkRef(gBigKeyProcessor);
     }
 
-    const char* name() const SK_OVERRIDE { return "Big Ole Key"; }
+    const char* name() const override { return "Big Ole Key"; }
 
-    virtual void getGLProcessorKey(const GrGLCaps& caps,
-                                   GrProcessorKeyBuilder* b) const SK_OVERRIDE {
+    virtual void getGLProcessorKey(const GrGLSLCaps& caps,
+                                   GrProcessorKeyBuilder* b) const override {
         GLBigKeyProcessor::GenKey(*this, caps, b);
     }
 
-    GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
+    GrGLFragmentProcessor* createGLInstance() const override {
         return SkNEW_ARGS(GLBigKeyProcessor, (*this));
     }
 
@@ -75,8 +82,8 @@
     BigKeyProcessor() {
         this->initClassID<BigKeyProcessor>();
     }
-    bool onIsEqual(const GrFragmentProcessor&) const SK_OVERRIDE { return true; }
-    void onComputeInvariantOutput(GrInvariantOutput* inout) const SK_OVERRIDE { }
+    bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
+    void onComputeInvariantOutput(GrInvariantOutput* inout) const override { }
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
 
@@ -98,7 +105,8 @@
 static const int kRenderTargetHeight = 1;
 static const int kRenderTargetWidth = 1;
 
-static GrRenderTarget* random_render_target(GrContext* context, SkRandom* random) {
+static GrRenderTarget* random_render_target(GrContext* context, SkRandom* random,
+                                            const GrDrawTargetCaps* caps) {
     // setup render target
     GrTextureParams params;
     GrSurfaceDesc texDesc;
@@ -108,17 +116,20 @@
     texDesc.fConfig = kRGBA_8888_GrPixelConfig;
     texDesc.fOrigin = random->nextBool() == true ? kTopLeft_GrSurfaceOrigin :
                                                    kBottomLeft_GrSurfaceOrigin;
+    texDesc.fSampleCnt = random->nextBool() == true ? SkTMin(4, caps->maxSampleCount()) : 0;
+
     GrUniqueKey key;
     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
-    GrUniqueKey::Builder builder(&key, kDomain, 1);
+    GrUniqueKey::Builder builder(&key, kDomain, 2);
     builder[0] = texDesc.fOrigin;
+    builder[1] = texDesc.fSampleCnt;
     builder.finish();
 
-    GrTexture* texture = context->findAndRefCachedTexture(key);
+    GrTexture* texture = context->textureProvider()->findAndRefTextureByUniqueKey(key);
     if (!texture) {
-        texture = context->createTexture(texDesc, true);
+        texture = context->textureProvider()->createTexture(texDesc, true);
         if (texture) {
-            context->addResourceToCache(key, texture);
+            context->textureProvider()->assignUniqueKeyToTexture(key, texture);
         }
     }
     return texture ? texture->asRenderTarget() : NULL;
@@ -133,26 +144,14 @@
     pipelineBuilder->setXPFactory(xpf.get());
 }
 
-static const GrGeometryProcessor* get_random_gp(GrContext* context,
-                                                const GrDrawTargetCaps& caps,
-                                                SkRandom* random,
-                                                GrTexture* dummyTextures[]) {
-    return GrProcessorTestFactory<GrGeometryProcessor>::CreateStage(random,
-                                                                    context,
-                                                                    caps,
-                                                                    dummyTextures);
-}
-
 static void set_random_color_coverage_stages(GrGLGpu* gpu,
                                              GrPipelineBuilder* pipelineBuilder,
                                              int maxStages,
-                                             bool usePathRendering,
                                              SkRandom* random,
                                              GrTexture* dummyTextures[]) {
     int numProcs = random->nextULessThan(maxStages + 1);
     int numColorProcs = random->nextULessThan(numProcs + 1);
 
-    int currTextureCoordSet = 0;
     for (int s = 0; s < numProcs;) {
         SkAutoTUnref<const GrFragmentProcessor> fp(
                 GrProcessorTestFactory<GrFragmentProcessor>::CreateStage(random,
@@ -161,18 +160,6 @@
                                                                          dummyTextures));
         SkASSERT(fp);
 
-        // If adding this effect would exceed the max texture coord set count then generate a
-        // new random effect.
-        if (usePathRendering && gpu->glPathRendering()->texturingMode() ==
-                                GrGLPathRendering::FixedFunction_TexturingMode) {;
-            int numTransforms = fp->numTransforms();
-            if (currTextureCoordSet + numTransforms >
-                gpu->glCaps().maxFixedFunctionTextureCoords()) {
-                continue;
-            }
-            currTextureCoordSet += numTransforms;
-        }
-
         // finally add the stage to the correct pipeline in the drawstate
         if (s < numColorProcs) {
             pipelineBuilder->addColorProcessor(fp);
@@ -185,9 +172,15 @@
 
 static void set_random_state(GrPipelineBuilder* pipelineBuilder, SkRandom* random) {
     int state = 0;
-    for (int i = 1; i <= GrPipelineBuilder::kLast_StateBit; i <<= 1) {
+    for (int i = 1; i <= GrPipelineBuilder::kLast_Flag; i <<= 1) {
         state |= random->nextBool() * i;
     }
+
+    // If we don't have an MSAA rendertarget then we have to disable useHWAA
+    if ((state | GrPipelineBuilder::kHWAntialias_Flag) &&
+        !pipelineBuilder->getRenderTarget()->isMultisampled()) {
+        state &= ~GrPipelineBuilder::kHWAntialias_Flag;
+    }
     pipelineBuilder->enableState(state);
 }
 
@@ -240,22 +233,14 @@
     // dummy scissor state
     GrScissorState scissor;
 
-    // setup clip
-    SkRect screen = SkRect::MakeWH(SkIntToScalar(kRenderTargetWidth),
-                                   SkIntToScalar(kRenderTargetHeight));
-
-    SkClipStack stack;
-    stack.clipDevRect(screen, SkRegion::kReplace_Op, false);
-
-    // wrap the SkClipStack in a GrClip
+    // wide open clip
     GrClip clip;
-    clip.setClipStack(&stack);
 
     SkRandom random;
-    static const int NUM_TESTS = 512;
-    for (int t = 0; t < NUM_TESTS;) {
+    static const int NUM_TESTS = 2048;
+    for (int t = 0; t < NUM_TESTS; t++) {
         // setup random render target(can fail)
-        SkAutoTUnref<GrRenderTarget> rt(random_render_target(fContext, &random));
+        SkAutoTUnref<GrRenderTarget> rt(random_render_target(fContext, &random, this->caps()));
         if (!rt.get()) {
             SkDebugf("Could not allocate render target");
             return false;
@@ -265,22 +250,12 @@
         pipelineBuilder.setRenderTarget(rt.get());
         pipelineBuilder.setClip(clip);
 
-        // if path rendering we have to setup a couple of things like the draw type
-        bool usePathRendering = gpu->glCaps().pathRenderingSupport() && random.nextBool();
+        SkAutoTUnref<GrBatch> batch(GrRandomBatch(&random, fContext));
+        SkASSERT(batch);
 
-        // twiddle drawstate knobs randomly
-        bool hasGeometryProcessor = !usePathRendering;
-        SkAutoTUnref<const GrGeometryProcessor> gp;
-        SkAutoTUnref<const GrPathProcessor> pathProc;
-        if (hasGeometryProcessor) {
-            gp.reset(get_random_gp(fContext, gpu->glCaps(), &random, dummyTextures));
-        } else {
-            pathProc.reset(GrPathProcessor::Create(GrColor_WHITE));
-        }
         set_random_color_coverage_stages(gpu,
                                          &pipelineBuilder,
-                                         maxStages - hasGeometryProcessor,
-                                         usePathRendering,
+                                         maxStages,
                                          &random,
                                          dummyTextures);
 
@@ -290,55 +265,29 @@
         set_random_state(&pipelineBuilder, &random);
         set_random_stencil(&pipelineBuilder, &random);
 
-        GrDeviceCoordTexture dstCopy;
-
-        const GrPrimitiveProcessor* primProc;
-        if (hasGeometryProcessor) {
-            primProc = gp.get();
-        } else {
-            primProc = pathProc.get();
-        }
-
-        const GrProcOptInfo& colorPOI = pipelineBuilder.colorProcInfo(primProc);
-        const GrProcOptInfo& coveragePOI = pipelineBuilder.coverageProcInfo(primProc);
-
-        if (!this->setupDstReadIfNecessary(pipelineBuilder, colorPOI, coveragePOI, &dstCopy,
-                                           NULL)) {
-            SkDebugf("Couldn't setup dst read texture");
-            return false;
-        }
-
-        // create optimized draw state, setup readDst texture if required, and build a descriptor
-        // and program.  ODS creation can fail, so we have to check
-        GrPipeline pipeline(pipelineBuilder, colorPOI, coveragePOI,
-                            *gpu->caps(), scissor, &dstCopy);
-        if (pipeline.mustSkip()) {
-            continue;
-        }
-        GrBatchTracker bt;
-        primProc->initBatchTracker(&bt, pipeline.getInitBatchTracker());
-
-        GrProgramDesc desc;
-        gpu->buildProgramDesc(&desc, *primProc, pipeline, bt);
-
-        GrGpu::DrawArgs args(primProc, &pipeline, &desc, &bt);
-        SkAutoTUnref<GrGLProgram> program(GrGLProgramBuilder::CreateProgram(args, gpu));
-
-        if (NULL == program.get()) {
-            SkDebugf("Failed to create program!");
-            return false;
-        }
-
-        // because occasionally optimized drawstate creation will fail for valid reasons, we only
-        // want to increment on success
-        ++t;
+        this->drawBatch(&pipelineBuilder, batch);
     }
+
+    // Flush everything, test passes if flush is successful(ie, no asserts are hit, no crashes)
+    this->flush();
     return true;
 }
 
 DEF_GPUTEST(GLPrograms, reporter, factory) {
+    // Set a locale that would cause shader compilation to fail because of , as decimal separator.
+    // skbug 3330
+#ifdef SK_BUILD_FOR_WIN
+    GrAutoLocaleSetter als("sv-SE");
+#else
+    GrAutoLocaleSetter als("sv_SE.UTF-8");
+#endif
+
+    // We suppress prints to avoid spew
+    GrContext::Options opts;
+    opts.fSuppressPrints = true;
+    GrContextFactory debugFactory(opts);
     for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
-        GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(type));
+        GrContext* context = debugFactory.get(static_cast<GrContextFactory::GLContextType>(type));
         if (context) {
             GrGLGpu* gpu = static_cast<GrGLGpu*>(context->getGpu());
 
diff --git a/tests/GeometryTest.cpp b/tests/GeometryTest.cpp
index 5151b70..00fc779 100644
--- a/tests/GeometryTest.cpp
+++ b/tests/GeometryTest.cpp
@@ -7,6 +7,7 @@
 
 #include "SkGeometry.h"
 #include "Test.h"
+#include "SkRandom.h"
 
 static bool nearly_equal(const SkPoint& a, const SkPoint& b) {
     return SkScalarNearlyEqual(a.fX, b.fX) && SkScalarNearlyEqual(a.fY, b.fY);
@@ -33,6 +34,77 @@
     }
 }
 
+static void check_pairs(skiatest::Reporter* reporter, int index, SkScalar t, const char name[],
+                        SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1) {
+    bool eq = SkScalarNearlyEqual(x0, x1) && SkScalarNearlyEqual(y0, y1);
+    if (!eq) {
+        SkDebugf("%s [%d %g] p0 [%10.8f %10.8f] p1 [%10.8f %10.8f]\n",
+                 name, index, t, x0, y0, x1, y1);
+        REPORTER_ASSERT(reporter, eq);
+    }
+}
+
+static void test_evalquadat(skiatest::Reporter* reporter) {
+    SkRandom rand;
+    for (int i = 0; i < 1000; ++i) {
+        SkPoint pts[3];
+        for (int j = 0; j < 3; ++j) {
+            pts[j].set(rand.nextSScalar1() * 100, rand.nextSScalar1() * 100);
+        }
+        const SkScalar dt = SK_Scalar1 / 128;
+        SkScalar t = dt;
+        for (int j = 1; j < 128; ++j) {
+            SkPoint r0;
+            SkEvalQuadAt(pts, t, &r0);
+            SkPoint r1 = SkEvalQuadAt(pts, t);
+            check_pairs(reporter, i, t, "quad-pos", r0.fX, r0.fY, r1.fX, r1.fY);
+            
+            SkVector v0;
+            SkEvalQuadAt(pts, t, NULL, &v0);
+            SkVector v1 = SkEvalQuadTangentAt(pts, t);
+            check_pairs(reporter, i, t, "quad-tan", v0.fX, v0.fY, v1.fX, v1.fY);
+
+            t += dt;
+        }
+    }
+}
+
+static void test_conic_eval_pos(skiatest::Reporter* reporter, const SkConic& conic, SkScalar t) {
+    SkPoint p0, p1;
+    conic.evalAt(t, &p0, NULL);
+    p1 = conic.evalAt(t);
+    check_pairs(reporter, 0, t, "conic-pos", p0.fX, p0.fY, p1.fX, p1.fY);
+}
+
+static void test_conic_eval_tan(skiatest::Reporter* reporter, const SkConic& conic, SkScalar t) {
+    SkVector v0, v1;
+    conic.evalAt(t, NULL, &v0);
+    v1 = conic.evalTangentAt(t);
+    check_pairs(reporter, 0, t, "conic-tan", v0.fX, v0.fY, v1.fX, v1.fY);
+}
+
+static void test_conic(skiatest::Reporter* reporter) {
+    SkRandom rand;
+    for (int i = 0; i < 1000; ++i) {
+        SkPoint pts[3];
+        for (int j = 0; j < 3; ++j) {
+            pts[j].set(rand.nextSScalar1() * 100, rand.nextSScalar1() * 100);
+        }
+        for (int k = 0; k < 10; ++k) {
+            SkScalar w = rand.nextUScalar1() * 2;
+            SkConic conic(pts, w);
+
+            const SkScalar dt = SK_Scalar1 / 128;
+            SkScalar t = dt;
+            for (int j = 1; j < 128; ++j) {
+                test_conic_eval_pos(reporter, conic, t);
+                test_conic_eval_tan(reporter, conic, t);
+                t += dt;
+            }
+        }
+    }
+}
+
 DEF_TEST(Geometry, reporter) {
     SkPoint pts[3], dst[5];
 
@@ -44,18 +116,17 @@
     REPORTER_ASSERT(reporter, count == 1 || count == 2);
 
     pts[0].set(0, 0);
-    pts[1].set(SkIntToScalar(3), 0);
-    pts[2].set(SkIntToScalar(3), SkIntToScalar(3));
+    pts[1].set(3, 0);
+    pts[2].set(3, 3);
     SkConvertQuadToCubic(pts, dst);
     const SkPoint cubic[] = {
-        { 0, 0, },
-        { SkIntToScalar(2), 0, },
-        { SkIntToScalar(3), SkIntToScalar(1), },
-        { SkIntToScalar(3), SkIntToScalar(3) },
+        { 0, 0, }, { 2, 0, }, { 3, 1, }, { 3, 3 },
     };
     for (int i = 0; i < 4; ++i) {
         REPORTER_ASSERT(reporter, nearly_equal(cubic[i], dst[i]));
     }
 
     testChopCubic(reporter);
+    test_evalquadat(reporter);
+    test_conic(reporter);
 }
diff --git a/tests/GifTest.cpp b/tests/GifTest.cpp
index e2e0384..978e374 100644
--- a/tests/GifTest.cpp
+++ b/tests/GifTest.cpp
@@ -5,12 +5,15 @@
  * found in the LICENSE file.
  */
 
+#include "SkTypes.h"
+
 // This tests out GIF decoder (SkImageDecoder_libgif.cpp)
 // It is not used on these platforms:
 #if (!defined(SK_BUILD_FOR_WIN32)) &&           \
     (!defined(SK_BUILD_FOR_IOS)) &&             \
     (!defined(SK_BUILD_FOR_MAC))
 
+#include "Resources.h"
 #include "SkBitmap.h"
 #include "SkData.h"
 #include "SkForceLinking.h"
@@ -197,4 +200,25 @@
     // "libgif warning [interlace DGifGetLine]"
 }
 
+// Regression test for decoding a gif image with sampleSize of 4, which was
+// previously crashing.
+DEF_TEST(Gif_Sampled, r) {
+    SkFILEStream fileStream(GetResourcePath("test640x479.gif").c_str());
+    REPORTER_ASSERT(r, fileStream.isValid());
+    if (!fileStream.isValid()) {
+        return;
+    }
+
+    SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&fileStream));
+    REPORTER_ASSERT(r, decoder);
+    if (!decoder) {
+        return;
+    }
+    decoder->setSampleSize(4);
+    SkBitmap bm;
+    const SkImageDecoder::Result result = decoder->decode(&fileStream, &bm,
+            SkImageDecoder::kDecodePixels_Mode);
+    REPORTER_ASSERT(r, result == SkImageDecoder::kSuccess);
+}
+
 #endif  // !(SK_BUILD_FOR_WIN32||SK_BUILD_FOR_IOS||SK_BUILD_FOR_MAC)
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index 094ca78..685bf41 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -27,7 +27,7 @@
         desc.fWidth = 256;
         desc.fHeight = 256;
         desc.fSampleCnt = 0;
-        GrSurface* texRT1 = context->createTexture(desc, false, NULL, 0);
+        GrSurface* texRT1 = context->textureProvider()->createTexture(desc, false, NULL, 0);
 
         REPORTER_ASSERT(reporter, texRT1 == texRT1->asRenderTarget());
         REPORTER_ASSERT(reporter, texRT1 == texRT1->asTexture());
@@ -39,7 +39,7 @@
                                   static_cast<GrSurface*>(texRT1->asTexture()));
 
         desc.fFlags = kNone_GrSurfaceFlags;
-        GrSurface* tex1 = context->createTexture(desc, false, NULL, 0);
+        GrSurface* tex1 = context->textureProvider()->createTexture(desc, false, NULL, 0);
         REPORTER_ASSERT(reporter, NULL == tex1->asRenderTarget());
         REPORTER_ASSERT(reporter, tex1 == tex1->asTexture());
         REPORTER_ASSERT(reporter, static_cast<GrSurface*>(tex1) == tex1->asTexture());
@@ -51,7 +51,7 @@
         backendDesc.fHeight = 256;
         backendDesc.fSampleCnt = 0;
         backendDesc.fTextureHandle = 5;
-        GrSurface* texRT2 = context->wrapBackendTexture(backendDesc);
+        GrSurface* texRT2 = context->textureProvider()->wrapBackendTexture(backendDesc);
         REPORTER_ASSERT(reporter, texRT2 == texRT2->asRenderTarget());
         REPORTER_ASSERT(reporter, texRT2 == texRT2->asTexture());
         REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT2->asRenderTarget()) ==
diff --git a/tests/GrTRecorderTest.cpp b/tests/GrTRecorderTest.cpp
index fde70ac..cef870f 100644
--- a/tests/GrTRecorderTest.cpp
+++ b/tests/GrTRecorderTest.cpp
@@ -5,14 +5,15 @@
  * found in the LICENSE file.
  */
 
-#if SK_SUPPORT_GPU
-
-#include "GrTRecorder.h"
 #include "SkMatrix.h"
 #include "SkRandom.h"
 #include "SkString.h"
 #include "Test.h"
 
+#if SK_SUPPORT_GPU
+
+#include "GrTRecorder.h"
+
 ////////////////////////////////////////////////////////////////////////////////
 
 static int activeRecorderItems = 0;
@@ -101,6 +102,15 @@
     }
     REPORTER_ASSERT(reporter, !iter.next());
 
+    ExtraData::Recorder::ReverseIter reverseIter(recorder);
+    for (int i = 99; i >= 0; --i) {
+        REPORTER_ASSERT(reporter, i == reverseIter->fData);
+        for (int j = 0; j < i; j++) {
+            REPORTER_ASSERT(reporter, i == reverseIter->extraData()[j]);
+        }
+        REPORTER_ASSERT(reporter, reverseIter.previous() == !!i);
+    }
+
     recorder.reset();
     REPORTER_ASSERT(reporter, 0 == activeRecorderItems);
 }
@@ -197,19 +207,20 @@
     virtual ClassType getType() { return kSubclassEmpty_ClassType; }
 };
 
+class Order {
+public:
+    Order() { this->reset(); }
+    void reset() { fCurrent = 0; }
+    ClassType next() {
+        fCurrent = 1664525 * fCurrent + 1013904223;
+        return static_cast<ClassType>(fCurrent % kNumClassTypes);
+    }
+private:
+    uint32_t fCurrent;
+};
+static void test_subclasses_iters(skiatest::Reporter*, Order&, Base::Recorder::Iter&,
+                                  Base::Recorder::ReverseIter&, int = 0);
 static void test_subclasses(skiatest::Reporter* reporter) {
-    class Order {
-    public:
-        Order() { this->reset(); }
-        void reset() { fCurrent = 0; }
-        ClassType next() {
-            fCurrent = 1664525 * fCurrent + 1013904223;
-            return static_cast<ClassType>(fCurrent % kNumClassTypes);
-        }
-    private:
-        uint32_t fCurrent;
-    };
-
     Base::Recorder recorder(1024);
 
     Order order;
@@ -244,15 +255,33 @@
 
     order.reset();
     Base::Recorder::Iter iter(recorder);
-    for (int i = 0; i < 1000; ++i) {
-        REPORTER_ASSERT(reporter, iter.next());
-        REPORTER_ASSERT(reporter, order.next() == iter->getType());
-        iter->validate(reporter);
-    }
+    Base::Recorder::ReverseIter reverseIter(recorder);
+
+    test_subclasses_iters(reporter, order, iter, reverseIter);
+
     REPORTER_ASSERT(reporter, !iter.next());
 
     // Don't reset the recorder. It should automatically destruct all its items.
 }
+static void test_subclasses_iters(skiatest::Reporter* reporter, Order& order,
+                                  Base::Recorder::Iter& iter,
+                                  Base::Recorder::ReverseIter& reverseIter, int i) {
+    if (i >= 1000) {
+        return;
+    }
+
+    ClassType classType = order.next();
+
+    REPORTER_ASSERT(reporter, iter.next());
+    REPORTER_ASSERT(reporter, classType == iter->getType());
+    iter->validate(reporter);
+
+    test_subclasses_iters(reporter, order, iter, reverseIter, i + 1);
+
+    REPORTER_ASSERT(reporter, classType == reverseIter->getType());
+    reverseIter->validate(reporter);
+    REPORTER_ASSERT(reporter, reverseIter.previous() == !!i);
+}
 
 DEF_GPUTEST(GrTRecorder, reporter, factory) {
     test_empty_back_and_pop(reporter);
diff --git a/tests/GradientTest.cpp b/tests/GradientTest.cpp
index f530b3e..a2814a3 100644
--- a/tests/GradientTest.cpp
+++ b/tests/GradientTest.cpp
@@ -107,22 +107,6 @@
     REPORTER_ASSERT(reporter, info.fRadius[0] == rec.fRadius[0]);
 }
 
-static void radial2_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
-    SkAutoTUnref<SkShader> s(SkGradientShader::CreateTwoPointRadial(rec.fPoint[0],
-                                                            rec.fRadius[0],
-                                                            rec.fPoint[1],
-                                                            rec.fRadius[1],
-                                                            rec.fColors,
-                                                            rec.fPos,
-                                                            rec.fColorCount,
-                                                            rec.fTileMode));
-
-    SkShader::GradientInfo info;
-    rec.gradCheck(reporter, s, &info, SkShader::kRadial2_GradientType);
-    REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint)));
-    REPORTER_ASSERT(reporter, !memcmp(info.fRadius, rec.fRadius, 2 * sizeof(SkScalar)));
-}
-
 static void sweep_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
     SkAutoTUnref<SkShader> s(SkGradientShader::CreateSweep(rec.fPoint[0].fX,
                                                            rec.fPoint[0].fY,
@@ -203,7 +187,6 @@
         color_gradproc,
         linear_gradproc,
         radial_gradproc,
-        radial2_gradproc,
         sweep_gradproc,
         conical_gradproc,
     };
diff --git a/tests/HashTest.cpp b/tests/HashTest.cpp
index 623e597..8489861 100644
--- a/tests/HashTest.cpp
+++ b/tests/HashTest.cpp
@@ -1,14 +1,24 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "SkChecksum.h"
 #include "SkString.h"
 #include "SkTHash.h"
 #include "Test.h"
 
-namespace { uint32_t hash_int(const int& k) { return SkChecksum::Mix(k); } }
-
-static void set_negative_key(int key, double* d) { *d = -key; }
+// Tests use of const foreach().  map.count() is of course the better way to do this.
+static int count(const SkTHashMap<int, double>& map) {
+    int n = 0;
+    map.foreach([&n](int, double) { n++; });
+    return n;
+}
 
 DEF_TEST(HashMap, r) {
-    SkTHashMap<int, double, hash_int> map;
+    SkTHashMap<int, double> map;
 
     map.set(3, 4.0);
     REPORTER_ASSERT(r, map.count() == 1);
@@ -17,7 +27,9 @@
     REPORTER_ASSERT(r, found);
     REPORTER_ASSERT(r, *found == 4.0);
 
-    map.foreach(set_negative_key);
+    map.foreach([](int key, double* d){ *d = -key; });
+    REPORTER_ASSERT(r, count(map) == 1);
+
     found = map.find(3);
     REPORTER_ASSERT(r, found);
     REPORTER_ASSERT(r, *found == -3.0);
@@ -30,7 +42,7 @@
         map.set(i, 2.0*i);
     }
     for (int i = 0; i < N; i++) {
-        double* found = map.find(i);;
+        double* found = map.find(i);
         REPORTER_ASSERT(r, found);
         REPORTER_ASSERT(r, *found == i*2.0);
     }
@@ -40,14 +52,21 @@
 
     REPORTER_ASSERT(r, map.count() == N);
 
+    for (int i = 0; i < N/2; i++) {
+        map.remove(i);
+    }
+    for (int i = 0; i < N; i++) {
+        double* found = map.find(i);
+        REPORTER_ASSERT(r, (found == nullptr) ==  (i < N/2));
+    }
+    REPORTER_ASSERT(r, map.count() == N/2);
+
     map.reset();
     REPORTER_ASSERT(r, map.count() == 0);
 }
 
-namespace { uint32_t hash_string(const SkString& s) { return SkToInt(s.size()); } }
-
 DEF_TEST(HashSet, r) {
-    SkTHashSet<SkString, hash_string> set;
+    SkTHashSet<SkString> set;
 
     set.add(SkString("Hello"));
     set.add(SkString("World"));
@@ -58,6 +77,13 @@
     REPORTER_ASSERT(r, set.contains(SkString("World")));
     REPORTER_ASSERT(r, !set.contains(SkString("Goodbye")));
 
+    REPORTER_ASSERT(r, set.find(SkString("Hello")));
+    REPORTER_ASSERT(r, *set.find(SkString("Hello")) == SkString("Hello"));
+
+    set.remove(SkString("Hello"));
+    REPORTER_ASSERT(r, !set.contains(SkString("Hello")));
+    REPORTER_ASSERT(r, set.count() == 1);
+
     set.reset();
     REPORTER_ASSERT(r, set.count() == 0);
 }
diff --git a/tests/ImageCacheTest.cpp b/tests/ImageCacheTest.cpp
index 45dee92..2244bb0 100644
--- a/tests/ImageCacheTest.cpp
+++ b/tests/ImageCacheTest.cpp
@@ -24,8 +24,8 @@
     TestingKey  fKey;
     intptr_t    fValue;
 
-    const Key& getKey() const SK_OVERRIDE { return fKey; }
-    size_t bytesUsed() const SK_OVERRIDE { return sizeof(fKey) + sizeof(fValue); }
+    const Key& getKey() const override { return fKey; }
+    size_t bytesUsed() const override { return sizeof(fKey) + sizeof(fValue); }
 
     static bool Visitor(const SkResourceCache::Rec& baseRec, void* context) {
         const TestingRec& rec = static_cast<const TestingRec&>(baseRec);
diff --git a/tests/ImageDecodingTest.cpp b/tests/ImageDecodingTest.cpp
index b94e29c..e3861d2 100644
--- a/tests/ImageDecodingTest.cpp
+++ b/tests/ImageDecodingTest.cpp
@@ -218,7 +218,9 @@
     // decoding the bounds.
     if (requireUnpremul) {
         REPORTER_ASSERT(reporter, kUnpremul_SkAlphaType == boundsAlphaType
-                                  || kOpaque_SkAlphaType == boundsAlphaType);
+                                  || kOpaque_SkAlphaType == boundsAlphaType
+                                  || filename.endsWith(".ico"));
+        // TODO(halcanary): Find out why color_wheel.ico fails this test.
     } else {
         REPORTER_ASSERT(reporter, kPremul_SkAlphaType == boundsAlphaType
                                   || kOpaque_SkAlphaType == boundsAlphaType);
@@ -696,7 +698,7 @@
     SingleAllocator(void* p, size_t s) : fPixels(p), fSize(s) { }
     ~SingleAllocator() {}
     // If the pixels in fPixels are big enough, use them.
-    bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
+    bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) override {
         SkASSERT(bm);
         if (bm->info().getSafeSize(bm->rowBytes()) <= fSize) {
             bm->setPixels(fPixels, ct);
diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp
index 5e92ee3..6c59615 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -20,7 +20,6 @@
 #include "SkGradientShader.h"
 #include "SkLightingImageFilter.h"
 #include "SkMatrixConvolutionImageFilter.h"
-#include "SkMatrixImageFilter.h"
 #include "SkMergeImageFilter.h"
 #include "SkMorphologyImageFilter.h"
 #include "SkOffsetImageFilter.h"
@@ -51,7 +50,7 @@
     }
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context& ctx,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE {
+                               SkBitmap* result, SkIPoint* offset) const override {
         REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix);
         return true;
     }
@@ -60,7 +59,7 @@
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(MatrixTestImageFilter)
 
 protected:
-    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
+    void flatten(SkWriteBuffer& buffer) const override {
         this->INHERITED::flatten(buffer);
         buffer.writeFunctionPtr(fReporter);
         buffer.writeMatrix(fExpectedMatrix);
@@ -298,7 +297,7 @@
         SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
         SkDropShadowImageFilter::Create(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1,
             SK_ColorGREEN, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
-            input.get(), &cropRect, 0),
+            input.get(), &cropRect),
         SkLightingImageFilter::CreatePointLitDiffuse(location, SK_ColorGREEN, 0, 0, input.get(), &cropRect),
         SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0, input.get(), &cropRect),
         SkMatrixConvolutionImageFilter::Create(kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1), SkMatrixConvolutionImageFilter::kRepeat_TileMode, false, input.get(), &cropRect),
@@ -470,7 +469,7 @@
         { "erode", SkErodeImageFilter::Create(2, 3) },
         { "tile", SkTileImageFilter::Create(SkRect::MakeXYWH(0, 0, 50, 50),
                                             SkRect::MakeXYWH(0, 0, 100, 100), NULL) },
-        { "matrix", SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel) },
+        { "matrix", SkImageFilter::CreateMatrixFilter(matrix, kLow_SkFilterQuality) },
         { "blur and offset", SkOffsetImageFilter::Create(five, five, blur.get()) },
         { "picture and blur", SkBlurImageFilter::Create(five, five, pictureFilter.get()) },
         { "rect shader and blur", SkBlurImageFilter::Create(five, five, rectShaderFilter.get()) },
@@ -533,7 +532,7 @@
 
     SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorWHITE, SkXfermode::kSrc_Mode));
     SkAutoTUnref<SkImageFilter> cfif(SkColorFilterImageFilter::Create(cf.get()));
-    SkAutoTUnref<SkImageFilter> imageFilter(SkMatrixImageFilter::Create(matrix, SkPaint::kNone_FilterLevel, cfif.get()));
+    SkAutoTUnref<SkImageFilter> imageFilter(SkImageFilter::CreateMatrixFilter(matrix, kNone_SkFilterQuality, cfif.get()));
 
     SkPaint paint;
     paint.setImageFilter(imageFilter.get());
@@ -585,7 +584,7 @@
         SkIntToScalar(100), SkIntToScalar(100),
         SkIntToScalar(10), SkIntToScalar(10),
         SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
-        input, NULL, 0);
+        input, NULL);
 }
 
 DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) {
@@ -1076,7 +1075,7 @@
     matrix.setScale(SkIntToScalar(2), SkIntToScalar(2));
     matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20));
     SkAutoTUnref<SkImageFilter> matrixFilter(
-        SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel));
+        SkImageFilter::CreateMatrixFilter(matrix, kLow_SkFilterQuality));
 
     // Test that saveLayer() with a filter nested inside another saveLayer() applies the
     // correct offset to the filter matrix.
@@ -1142,6 +1141,9 @@
 
 DEF_GPUTEST(ImageFilterCropRectGPU, reporter, factory) {
     GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
+    if (NULL == context) {
+        return;
+    }
     SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
                                                          SkSurface::kNo_Budgeted,
                                                          SkImageInfo::MakeN32Premul(100, 100),
@@ -1152,6 +1154,9 @@
 
 DEF_GPUTEST(HugeBlurImageFilterGPU, reporter, factory) {
     GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
+    if (NULL == context) {
+        return;
+    }
     SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
                                                          SkSurface::kNo_Budgeted,
                                                          SkImageInfo::MakeN32Premul(100, 100),
@@ -1162,6 +1167,9 @@
 
 DEF_GPUTEST(XfermodeImageFilterCroppedInputGPU, reporter, factory) {
     GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
+    if (NULL == context) {
+        return;
+    }
     SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
                                                          SkSurface::kNo_Budgeted,
                                                          SkImageInfo::MakeN32Premul(1, 1),
@@ -1172,6 +1180,9 @@
 
 DEF_GPUTEST(TestNegativeBlurSigmaGPU, reporter, factory) {
     GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
+    if (NULL == context) {
+        return;
+    }
     SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
                                                          SkSurface::kNo_Budgeted,
                                                          SkImageInfo::MakeN32Premul(1, 1),
diff --git a/tests/ImageGeneratorTest.cpp b/tests/ImageGeneratorTest.cpp
index 1f960ea..94867bc 100644
--- a/tests/ImageGeneratorTest.cpp
+++ b/tests/ImageGeneratorTest.cpp
@@ -8,8 +8,13 @@
 #include "SkImageGenerator.h"
 #include "Test.h"
 
+class MyImageGenerator : public SkImageGenerator {
+public:
+    MyImageGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(0, 0)) {}
+};
+
 DEF_TEST(ImageGenerator, reporter) {
-    SkImageGenerator ig;
+    MyImageGenerator ig;
     SkISize sizes[3];
     sizes[0] = SkISize::Make(200, 200);
     sizes[1] = SkISize::Make(100, 100);
diff --git a/tests/IndexedPngOverflowTest.cpp b/tests/IndexedPngOverflowTest.cpp
new file mode 100644
index 0000000..914053a
--- /dev/null
+++ b/tests/IndexedPngOverflowTest.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkForceLinking.h"
+#include "SkImageDecoder.h"
+#include "SkImageInfo.h"
+#include "SkSurface.h"
+#include "Test.h"
+
+// A 20x1 image with 8 bits per pixel and a palette size of 2. Pixel values are 255, 254... Run
+// this test with ASAN to make sure we don't try to access before/after any palette-sized buffers.
+unsigned char gPng[] = {
+    0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
+    0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01,
+    0x08, 0x03, 0x00, 0x00, 0x00, 0xe9, 0x4c, 0x7e, 0x17, 0x00, 0x00, 0x00,
+    0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+    0x1c, 0x00, 0x0f, 0x01, 0xb9, 0x8f, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c,
+    0x54, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x67, 0xb9, 0xcf,
+    0x00, 0x00, 0x00, 0x20, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xfd,
+    0x07, 0x01, 0x00, 0x20, 0x08, 0x00, 0x41, 0xbc, 0x5b, 0xe8, 0xdf, 0x97,
+    0x99, 0xe3, 0x92, 0xa0, 0xf2, 0xdf, 0x3d, 0x7b, 0x0d, 0xda, 0x04, 0x1c,
+    0x03, 0xad, 0x00, 0x38, 0x5c, 0x2e, 0xad, 0x12, 0x00, 0x00, 0x00, 0x00,
+    0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
+DEF_TEST(IndexedPngOverflow, reporter) {
+    SkBitmap image;
+    SkForceLinking(false);
+    bool success = SkImageDecoder::DecodeMemory(
+        gPng, sizeof(gPng), &image, SkColorType::kUnknown_SkColorType,
+        SkImageDecoder::kDecodePixels_Mode);
+    REPORTER_ASSERT(reporter, success);
+
+    SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(SkImageInfo::MakeN32Premul(20, 1)));
+    SkCanvas* canvas = surface->getCanvas();
+    SkRect destRect = SkRect::MakeXYWH(0, 0, 20, 1);
+    canvas->drawBitmapRect(image, destRect, NULL);
+}
diff --git a/tests/LayerDrawLooperTest.cpp b/tests/LayerDrawLooperTest.cpp
index bc76a02..2c9b525 100644
--- a/tests/LayerDrawLooperTest.cpp
+++ b/tests/LayerDrawLooperTest.cpp
@@ -30,7 +30,7 @@
     FakeDevice() : SkBitmapDevice(make_bm(100, 100)) { }
 
     virtual void drawRect(const SkDraw& draw, const SkRect& r,
-                          const SkPaint& paint) SK_OVERRIDE {
+                          const SkPaint& paint) override {
         fLastMatrix = *draw.fMatrix;
         this->INHERITED::drawRect(draw, r, paint);
     }
diff --git a/tests/LazyPtrTest.cpp b/tests/LazyPtrTest.cpp
index 8bded1e..1b845bc 100644
--- a/tests/LazyPtrTest.cpp
+++ b/tests/LazyPtrTest.cpp
@@ -49,7 +49,7 @@
 struct Racer : public SkRunnable {
     Racer() : fLazy(NULL), fSeen(NULL) {}
 
-    void run() SK_OVERRIDE { fSeen = fLazy->get(); }
+    void run() override { fSeen = fLazy->get(); }
 
     SkLazyPtr<int>* fLazy;
     int* fSeen;
diff --git a/tests/MathTest.cpp b/tests/MathTest.cpp
index 4347096..772bade 100644
--- a/tests/MathTest.cpp
+++ b/tests/MathTest.cpp
@@ -537,6 +537,9 @@
         } else if (check < -SK_MaxS32) {
             check = SK_MinS32;
         }
+        if (result != (int32_t)check) {
+            ERRORF(reporter, "\nFixed Divide: %8x / %8x -> %8x %8x\n", numer, denom, result, check);
+        }
         REPORTER_ASSERT(reporter, result == (int32_t)check);
     }
 
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
index 7875d9e..72e0dcc 100644
--- a/tests/MatrixTest.cpp
+++ b/tests/MatrixTest.cpp
@@ -270,8 +270,8 @@
         mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
         for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
             SkScalar d = vectors[i].length();
-            REPORTER_ASSERT(reporter, SkScalarDiv(d, maxScale) < gVectorScaleTol);
-            REPORTER_ASSERT(reporter, SkScalarDiv(minScale, d) < gVectorScaleTol);
+            REPORTER_ASSERT(reporter, d / maxScale < gVectorScaleTol);
+            REPORTER_ASSERT(reporter, minScale / d < gVectorScaleTol);
             if (max < d) {
                 max = d;
             }
@@ -279,8 +279,8 @@
                 min = d;
             }
         }
-        REPORTER_ASSERT(reporter, SkScalarDiv(max, maxScale) >= gCloseScaleTol);
-        REPORTER_ASSERT(reporter, SkScalarDiv(minScale, min) >= gCloseScaleTol);
+        REPORTER_ASSERT(reporter, max / maxScale >= gCloseScaleTol);
+        REPORTER_ASSERT(reporter, minScale / min >= gCloseScaleTol);
     }
 }
 
@@ -771,6 +771,34 @@
 
 }
 
+static bool check_decompScale(const SkMatrix& matrix) {
+    SkSize scale;
+    SkMatrix remaining;
+
+    if (!matrix.decomposeScale(&scale, &remaining)) {
+        return false;
+    }
+    if (scale.width() <= 0 || scale.height() <= 0) {
+        return false;
+    }
+    remaining.preScale(scale.width(), scale.height());
+    return nearly_equal(matrix, remaining);
+}
+
+static void test_decompScale(skiatest::Reporter* reporter) {
+    SkMatrix m;
+
+    m.reset();
+    REPORTER_ASSERT(reporter, check_decompScale(m));
+    m.setScale(2, 3);
+    REPORTER_ASSERT(reporter, check_decompScale(m));
+    m.setRotate(35, 0, 0);
+    REPORTER_ASSERT(reporter, check_decompScale(m));
+
+    m.setScale(1, 0);
+    REPORTER_ASSERT(reporter, !check_decompScale(m));
+}
+
 DEF_TEST(Matrix, reporter) {
     SkMatrix    mat, inverse, iden1, iden2;
 
@@ -889,6 +917,8 @@
     test_matrix_decomposition(reporter);
     test_matrix_homogeneous(reporter);
     test_set9(reporter);
+
+    test_decompScale(reporter);
 }
 
 DEF_TEST(Matrix_Concat, r) {
diff --git a/tests/OnceTest.cpp b/tests/OnceTest.cpp
index 1344cee..034c5d9 100644
--- a/tests/OnceTest.cpp
+++ b/tests/OnceTest.cpp
@@ -39,7 +39,7 @@
     SkOnceFlag* once;
     int* ptr;
 
-    void run() SK_OVERRIDE {
+    void run() override {
         SkOnce(once, add_six, ptr);
     }
 };
diff --git a/tests/PDFInvalidBitmapTest.cpp b/tests/PDFInvalidBitmapTest.cpp
index bc7143a..8a9b1fa 100644
--- a/tests/PDFInvalidBitmapTest.cpp
+++ b/tests/PDFInvalidBitmapTest.cpp
@@ -23,8 +23,8 @@
 public:
     InvalidPixelRef(const SkImageInfo& info) : SkPixelRef(info) {}
 private:
-    bool onNewLockPixels(LockRec*) SK_OVERRIDE { return false; }
-    void onUnlockPixels() SK_OVERRIDE {
+    bool onNewLockPixels(LockRec*) override { return false; }
+    void onUnlockPixels() override {
         SkDEBUGFAIL("InvalidPixelRef can't be locked");
     }
 };
diff --git a/tests/PDFJpegEmbedTest.cpp b/tests/PDFJpegEmbedTest.cpp
index beb949d..cfe6776 100644
--- a/tests/PDFJpegEmbedTest.cpp
+++ b/tests/PDFJpegEmbedTest.cpp
@@ -81,19 +81,47 @@
     SkASSERT(pdfData);
     pdf.reset();
 
-    // Test disabled, waiting on resolution to http://skbug.com/3180
-    // REPORTER_ASSERT(r, is_subset_of(mandrillData, pdfData));
+    REPORTER_ASSERT(r, is_subset_of(mandrillData, pdfData));
 
     // This JPEG uses a nonstandard colorspace - it can not be
     // embedded into the PDF directly.
     REPORTER_ASSERT(r, !is_subset_of(cmykData, pdfData));
+}
 
-    // The following is for debugging purposes only.
-    const char* outputPath = getenv("SKIA_TESTS_PDF_JPEG_EMBED_OUTPUT_PATH");
-    if (outputPath) {
-        SkFILEWStream output(outputPath);
-        if (output.isValid()) {
-            output.write(pdfData->data(), pdfData->size());
+#include "SkJpegInfo.h"
+
+DEF_TEST(JpegIdentification, r) {
+    static struct {
+        const char* path;
+        bool isJfif;
+        SkJFIFInfo::Type type;
+    } kTests[] = {{"CMYK.jpg", false, SkJFIFInfo::kGrayscale},
+                  {"color_wheel.jpg", true, SkJFIFInfo::kYCbCr},
+                  {"grayscale.jpg", true, SkJFIFInfo::kGrayscale},
+                  {"mandrill_512_q075.jpg", true, SkJFIFInfo::kYCbCr},
+                  {"randPixels.jpg", true, SkJFIFInfo::kYCbCr}};
+    for (size_t i = 0; i < SK_ARRAY_COUNT(kTests); ++i) {
+        SkAutoTUnref<SkData> data(
+                load_resource(r, "JpegIdentification", kTests[i].path));
+        if (!data) {
+            continue;
+        }
+        SkJFIFInfo info;
+        bool isJfif = SkIsJFIF(data, &info);
+        if (isJfif != kTests[i].isJfif) {
+            ERRORF(r, "%s failed isJfif test", kTests[i].path);
+            continue;
+        }
+        if (!isJfif) {
+            continue;  // not applicable
+        }
+        if (kTests[i].type != info.fType) {
+            ERRORF(r, "%s failed jfif type test", kTests[i].path);
+            continue;
+        }
+        if (r->verbose()) {
+            SkDebugf("\nJpegIdentification: %s [%d x %d]\n", kTests[i].path,
+                     info.fWidth, info.fHeight);
         }
     }
 }
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index 7861ef0..013d586 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -13,7 +13,6 @@
 #include "SkImageEncoder.h"
 #include "SkMatrix.h"
 #include "SkPDFCanon.h"
-#include "SkPDFCatalog.h"
 #include "SkPDFDevice.h"
 #include "SkPDFStream.h"
 #include "SkPDFTypes.h"
@@ -23,116 +22,73 @@
 #include "SkTypes.h"
 #include "Test.h"
 
-class SkPDFTestDict : public SkPDFDict {
-public:
-  virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
-                            SkTSet<SkPDFObject*>* newResourceObjects) {
-        for (int i = 0; i < fResources.count(); i++) {
-            newResourceObjects->add(fResources[i]);
-            fResources[i]->ref();
-        }
-    }
-
-    void addResource(SkPDFObject* object) {
-        fResources.append(1, &object);
-    }
-
-private:
-    SkTDArray<SkPDFObject*> fResources;
-};
-
 #define DUMMY_TEXT "DCT compessed stream."
 
-static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
-                          const void* buffer, size_t len) {
-    SkAutoDataUnref data(stream.copyToData());
-    if (offset + len > data->size()) {
-        return false;
-    }
-    return memcmp(data->bytes() + offset, buffer, len) == 0;
-}
+namespace {
+struct Catalog {
+    SkPDFSubstituteMap substitutes;
+    SkPDFObjNumMap numbers;
+};
+}  // namespace
 
-static void emit_object(SkPDFObject* object,
-                        SkWStream* stream,
-                        SkPDFCatalog* catalog,
-                        bool indirect) {
-    SkPDFObject* realObject = catalog->getSubstituteObject(object);
-    if (indirect) {
-        stream->writeDecAsText(catalog->getObjectNumber(object));
-        stream->writeText(" 0 obj\n");  // Generation number is always 0.
-        realObject->emitObject(stream, catalog);
-        stream->writeText("\nendobj\n");
-    } else {
-        realObject->emitObject(stream, catalog);
-    }
-}
-
-static size_t get_output_size(SkPDFObject* object,
-                              SkPDFCatalog* catalog,
-                              bool indirect) {
+template <typename T>
+static SkString emit_to_string(T& obj, Catalog* catPtr = NULL) {
+    Catalog catalog;
     SkDynamicMemoryWStream buffer;
-    emit_object(object, &buffer, catalog, indirect);
-    return buffer.getOffset();
-}
-
-static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
-                              const char* expectedData, size_t expectedSize,
-                              bool indirect) {
-    SkPDFCatalog catalog;
-    size_t directSize = get_output_size(obj, &catalog, false);
-    REPORTER_ASSERT(reporter, directSize == expectedSize);
-
-    SkDynamicMemoryWStream buffer;
-    emit_object(obj, &buffer, &catalog, false);
-    REPORTER_ASSERT(reporter, directSize == buffer.getOffset());
-    REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData,
-                                            directSize));
-
-    if (indirect) {
-        // Indirect output.
-        static char header[] = "1 0 obj\n";
-        static size_t headerLen = strlen(header);
-        static char footer[] = "\nendobj\n";
-        static size_t footerLen = strlen(footer);
-
-        catalog.addObject(obj, false);
-
-        size_t indirectSize = get_output_size(obj, &catalog, true);
-        REPORTER_ASSERT(reporter,
-                        indirectSize == directSize + headerLen + footerLen);
-
-        buffer.reset();
-        emit_object(obj, &buffer, &catalog, true);
-        REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset());
-        REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen));
-        REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData,
-                                                directSize));
-        REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize,
-                                                footer, footerLen));
+    if (!catPtr) {
+        catPtr = &catalog;
     }
+    obj.emitObject(&buffer, catPtr->numbers, catPtr->substitutes);
+    SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
+    SkString tmp(asset->getLength());
+    asset->read(tmp.writable_str(), asset->getLength());
+    return tmp;
 }
 
-static void SimpleCheckObjectOutput(skiatest::Reporter* reporter,
-                                    SkPDFObject* obj,
-                                    const char* expectedResult) {
-    CheckObjectOutput(reporter, obj, expectedResult,
-                      strlen(expectedResult), true);
+static bool eq(const SkString& str, const char* strPtr, size_t len) {
+    return len == str.size() && 0 == memcmp(str.c_str(), strPtr, len);
 }
 
+#define ASSERT_EQL(REPORTER, SKSTRING, STRING, LEN)                     \
+    do {                                                                \
+        const char* strptr = STRING;                                    \
+        const SkString& sks = SKSTRING;                                 \
+        if (!eq(sks, strptr, LEN)) {                                    \
+            REPORT_FAILURE(                                             \
+                    REPORTER,                                           \
+                    "",                                                 \
+                    SkStringPrintf("'%s' != '%s'", strptr, sks.c_str()));  \
+        }                                                               \
+    } while (false)
+
+#define ASSERT_EQ(REPORTER, SKSTRING, STRING)             \
+    do {                                                  \
+        const char* str = STRING;                         \
+        ASSERT_EQL(REPORTER, SKSTRING, str, strlen(str)); \
+    } while (false)
+
+#define ASSERT_EMIT_EQ(REPORTER, OBJECT, STRING)          \
+    do {                                                  \
+        SkString result = emit_to_string(OBJECT);         \
+        ASSERT_EQ(REPORTER, result, STRING);              \
+    } while (false)
+
+
+
 static void TestPDFStream(skiatest::Reporter* reporter) {
     char streamBytes[] = "Test\nFoo\tBar";
     SkAutoTDelete<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");
-    stream->insert("Attribute", new SkPDFInt(42))->unref();
-    SimpleCheckObjectOutput(reporter, stream.get(),
-                            "<</Length 12\n/Attribute 42\n>> stream\n"
-                                "Test\nFoo\tBar\nendstream");
+    ASSERT_EMIT_EQ(reporter,
+                   *stream,
+                   "<</Length 12>> stream\nTest\nFoo\tBar\nendstream");
+    stream->insertInt("Attribute", 42);
+    ASSERT_EMIT_EQ(reporter,
+                   *stream,
+                   "<</Length 12\n/Attribute 42>> stream\n"
+                   "Test\nFoo\tBar\nendstream");
 
-#ifndef SK_NO_FLATE
     {
         char streamBytes2[] = "This is a longer string, so that compression "
                               "can do something with it. With shorter strings, "
@@ -147,68 +103,67 @@
         SkAutoDataUnref compressedData(compressedByteStream.copyToData());
 
         SkDynamicMemoryWStream expected;
-        expected.writeText("<</Filter /FlateDecode\n/Length 116\n"
-                                 ">> stream\n");
+        expected.writeText("<</Filter /FlateDecode\n/Length 116>> stream\n");
         expected.write(compressedData->data(), compressedData->size());
         expected.writeText("\nendstream");
         SkAutoDataUnref expectedResultData2(expected.copyToData());
-        CheckObjectOutput(reporter, stream.get(),
-                          (const char*) expectedResultData2->data(),
-                          expectedResultData2->size(), true);
+        SkString result = emit_to_string(*stream);
+        ASSERT_EQL(reporter,
+                   result,
+                   (const char*)expectedResultData2->data(),
+                   expectedResultData2->size());
     }
-#endif  // SK_NO_FLATE
 }
 
-static void TestCatalog(skiatest::Reporter* reporter) {
-    SkPDFCatalog catalog;
-    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());
+static void TestObjectNumberMap(skiatest::Reporter* reporter) {
+    SkPDFObjNumMap objNumMap;
+    SkAutoTUnref<SkPDFArray> a1(new SkPDFArray);
+    SkAutoTUnref<SkPDFArray> a2(new SkPDFArray);
+    SkAutoTUnref<SkPDFArray> a3(new SkPDFArray);
 
-    catalog.addObject(int1.get(), false);
-    catalog.addObject(int2.get(), false);
-    catalog.addObject(int3.get(), false);
+    objNumMap.addObject(a1.get());
+    objNumMap.addObject(a2.get());
+    objNumMap.addObject(a3.get());
 
-    REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1.get()) == 1);
-    REPORTER_ASSERT(reporter, catalog.getObjectNumber(int2.get()) == 2);
-    REPORTER_ASSERT(reporter, catalog.getObjectNumber(int3.get()) == 3);
-    REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1Again.get()) == 1);
+    // The objects should be numbered in the order they are added,
+    // starting with 1.
+    REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a1.get()) == 1);
+    REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a2.get()) == 2);
+    REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a3.get()) == 3);
+    // Assert that repeated calls to get the object number return
+    // consistent result.
+    REPORTER_ASSERT(reporter, objNumMap.getObjectNumber(a1.get()) == 1);
 }
 
 static void TestObjectRef(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
-    SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
-    SkAutoTUnref<SkPDFObjRef> int2ref(new SkPDFObjRef(int2.get()));
+    SkAutoTUnref<SkPDFArray> a1(new SkPDFArray);
+    SkAutoTUnref<SkPDFArray> a2(new SkPDFArray);
+    a2->appendObjRef(SkRef(a1.get()));
 
-    SkPDFCatalog catalog;
-    catalog.addObject(int1.get(), false);
-    catalog.addObject(int2.get(), false);
-    REPORTER_ASSERT(reporter, catalog.getObjectNumber(int1.get()) == 1);
-    REPORTER_ASSERT(reporter, catalog.getObjectNumber(int2.get()) == 2);
+    Catalog catalog;
+    catalog.numbers.addObject(a1.get());
+    REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(a1.get()) == 1);
 
-    char expectedResult[] = "2 0 R";
-    SkDynamicMemoryWStream buffer;
-    int2ref->emitObject(&buffer, &catalog);
-    REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
-    REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
-                                            buffer.getOffset()));
+    SkString result = emit_to_string(*a2, &catalog);
+    // If appendObjRef misbehaves, then the result would
+    // be [[]], not [1 0 R].
+    ASSERT_EQ(reporter, result, "[1 0 R]");
 }
 
 static void TestSubstitute(skiatest::Reporter* reporter) {
-    SkAutoTUnref<SkPDFTestDict> proxy(new SkPDFTestDict());
-    SkAutoTUnref<SkPDFTestDict> stub(new SkPDFTestDict());
+    SkAutoTUnref<SkPDFDict> proxy(new SkPDFDict());
+    SkAutoTUnref<SkPDFDict> stub(new SkPDFDict());
 
-    proxy->insert("Value", new SkPDFInt(33))->unref();
-    stub->insert("Value", new SkPDFInt(44))->unref();
+    proxy->insertInt("Value", 33);
+    stub->insertInt("Value", 44);
 
-    SkPDFCatalog catalog;
-    catalog.addObject(proxy.get(), false);
-    catalog.setSubstitute(proxy.get(), stub.get());
+    SkPDFSubstituteMap substituteMap;
+    substituteMap.setSubstitute(proxy.get(), stub.get());
+    SkPDFObjNumMap catalog;
+    catalog.addObject(proxy.get());
 
-    REPORTER_ASSERT(reporter, stub.get() == catalog.getSubstituteObject(proxy));
-    REPORTER_ASSERT(reporter, proxy.get() != catalog.getSubstituteObject(stub));
+    REPORTER_ASSERT(reporter, stub.get() == substituteMap.getSubstitute(proxy));
+    REPORTER_ASSERT(reporter, proxy.get() != substituteMap.getSubstitute(stub));
 }
 
 // This test used to assert without the fix submitted for
@@ -228,86 +183,178 @@
     doc->close();
 }
 
-DEF_TEST(PDFPrimitives, reporter) {
-    SkAutoTUnref<SkPDFInt> int42(new SkPDFInt(42));
-    SimpleCheckObjectOutput(reporter, int42.get(), "42");
+static void TestPDFUnion(skiatest::Reporter* reporter) {
+    SkPDFUnion boolTrue = SkPDFUnion::Bool(true);
+    ASSERT_EMIT_EQ(reporter, boolTrue, "true");
 
-    SkAutoTUnref<SkPDFScalar> realHalf(new SkPDFScalar(SK_ScalarHalf));
-    SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
+    SkPDFUnion boolFalse = SkPDFUnion::Bool(false);
+    ASSERT_EMIT_EQ(reporter, boolFalse, "false");
 
-    SkAutoTUnref<SkPDFScalar> bigScalar(new SkPDFScalar(110999.75f));
+    SkPDFUnion int42 = SkPDFUnion::Int(42);
+    ASSERT_EMIT_EQ(reporter, int42, "42");
+
+    SkPDFUnion realHalf = SkPDFUnion::Scalar(SK_ScalarHalf);
+    ASSERT_EMIT_EQ(reporter, realHalf, "0.5");
+
+    SkPDFUnion bigScalar = SkPDFUnion::Scalar(110999.75f);
 #if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
-    SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000");
+    ASSERT_EMIT_EQ(reporter, bigScalar, "111000");
 #else
-    SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75");
+    ASSERT_EMIT_EQ(reporter, bigScalar, "110999.75");
 
-    SkAutoTUnref<SkPDFScalar> biggerScalar(new SkPDFScalar(50000000.1));
-    SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
+    SkPDFUnion biggerScalar = SkPDFUnion::Scalar(50000000.1);
+    ASSERT_EMIT_EQ(reporter, biggerScalar, "50000000");
 
-    SkAutoTUnref<SkPDFScalar> smallestScalar(new SkPDFScalar(1.0/65536));
-    SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526");
+    SkPDFUnion smallestScalar = SkPDFUnion::Scalar(1.0 / 65536);
+    ASSERT_EMIT_EQ(reporter, smallestScalar, "0.00001526");
 #endif
 
-    SkAutoTUnref<SkPDFString> stringSimple(
-        new SkPDFString("test ) string ( foo"));
-    SimpleCheckObjectOutput(reporter, stringSimple.get(),
-                            "(test \\) string \\( foo)");
-    SkAutoTUnref<SkPDFString> stringComplex(
-        new SkPDFString("\ttest ) string ( foo"));
-    SimpleCheckObjectOutput(reporter, stringComplex.get(),
-                            "<0974657374202920737472696E67202820666F6F>");
+    SkPDFUnion stringSimple = SkPDFUnion::String("test ) string ( foo");
+    ASSERT_EMIT_EQ(reporter, stringSimple, "(test \\) string \\( foo)");
 
-    SkAutoTUnref<SkPDFName> name(new SkPDFName("Test name\twith#tab"));
-    const char expectedResult[] = "/Test#20name#09with#23tab";
-    CheckObjectOutput(reporter, name.get(), expectedResult,
-                      strlen(expectedResult), false);
+    SkString stringComplexInput("\ttest ) string ( foo");
+    SkPDFUnion stringComplex = SkPDFUnion::String(stringComplexInput);
+    ASSERT_EMIT_EQ(reporter,
+                   stringComplex,
+                   "<0974657374202920737472696E67202820666F6F>");
 
-    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);
+    SkString nameInput("Test name\twith#tab");
+    SkPDFUnion name = SkPDFUnion::Name(nameInput);
+    ASSERT_EMIT_EQ(reporter, name, "/Test#20name#09with#23tab");
+
+    SkString nameInput2("A#/%()<>[]{}B");
+    SkPDFUnion name2 = SkPDFUnion::Name(nameInput2);
+    ASSERT_EMIT_EQ(reporter, name2, "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB");
+
+    SkPDFUnion name3 = SkPDFUnion::Name("SimpleNameWithOnlyPrintableASCII");
+    ASSERT_EMIT_EQ(reporter, name3, "/SimpleNameWithOnlyPrintableASCII");
 
     // Test that we correctly handle characters with the high-bit set.
-    const unsigned char highBitCString[] = {0xDE, 0xAD, 'b', 'e', 0xEF, 0};
-    SkAutoTUnref<SkPDFName> highBitName(
-        new SkPDFName((const char*)highBitCString));
-    const char highBitExpectedResult[] = "/#DE#ADbe#EF";
-    CheckObjectOutput(reporter, highBitName.get(), highBitExpectedResult,
-                      strlen(highBitExpectedResult), false);
+    SkString highBitString("\xDE\xAD" "be\xEF");
+    SkPDFUnion highBitName = SkPDFUnion::Name(highBitString);
+    ASSERT_EMIT_EQ(reporter, highBitName, "/#DE#ADbe#EF");
+}
 
+static void TestPDFArray(skiatest::Reporter* reporter) {
     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]");
-    SkAutoTUnref<SkPDFInt> int0(new SkPDFInt(0));
-    array->append(int0.get());
-    SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]");
-    SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
-    array->setAt(0, int1.get());
-    SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]");
+    ASSERT_EMIT_EQ(reporter, *array, "[]");
 
+    array->appendInt(42);
+    ASSERT_EMIT_EQ(reporter, *array, "[42]");
+
+    array->appendScalar(SK_ScalarHalf);
+    ASSERT_EMIT_EQ(reporter, *array, "[42 0.5]");
+
+    array->appendInt(0);
+    ASSERT_EMIT_EQ(reporter, *array, "[42 0.5 0]");
+
+    array->appendBool(true);
+    ASSERT_EMIT_EQ(reporter, *array, "[42 0.5 0 true]");
+
+    array->appendName("ThisName");
+    ASSERT_EMIT_EQ(reporter, *array, "[42 0.5 0 true /ThisName]");
+
+    array->appendName(SkString("AnotherName"));
+    ASSERT_EMIT_EQ(reporter, *array, "[42 0.5 0 true /ThisName /AnotherName]");
+
+    array->appendString("This String");
+    ASSERT_EMIT_EQ(reporter, *array,
+                   "[42 0.5 0 true /ThisName /AnotherName (This String)]");
+
+    array->appendString(SkString("Another String"));
+    ASSERT_EMIT_EQ(reporter, *array,
+                   "[42 0.5 0 true /ThisName /AnotherName (This String) "
+                   "(Another String)]");
+
+    SkAutoTUnref<SkPDFArray> innerArray(new SkPDFArray);
+    innerArray->appendInt(-1);
+    array->appendObject(innerArray.detach());
+    ASSERT_EMIT_EQ(reporter, *array,
+                   "[42 0.5 0 true /ThisName /AnotherName (This String) "
+                   "(Another String) [-1]]");
+
+    SkAutoTUnref<SkPDFArray> referencedArray(new SkPDFArray);
+    Catalog catalog;
+    catalog.numbers.addObject(referencedArray.get());
+    REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(
+                            referencedArray.get()) == 1);
+    array->appendObjRef(referencedArray.detach());
+
+    SkString result = emit_to_string(*array, &catalog);
+    ASSERT_EQ(reporter, result,
+              "[42 0.5 0 true /ThisName /AnotherName (This String) "
+              "(Another String) [-1] 1 0 R]");
+}
+
+static void TestPDFDict(skiatest::Reporter* reporter) {
     SkAutoTUnref<SkPDFDict> dict(new SkPDFDict);
-    SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
-    SkAutoTUnref<SkPDFName> n1(new SkPDFName("n1"));
-    dict->insert(n1.get(), int42.get());
-    SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>");
-    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(),
-                            "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>");
+    ASSERT_EMIT_EQ(reporter, *dict, "<<>>");
 
+    dict->insertInt("n1", SkToSizeT(42));
+    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42>>");
+
+    dict.reset(new SkPDFDict);
+    ASSERT_EMIT_EQ(reporter, *dict, "<<>>");
+
+    dict->insertInt("n1", 42);
+    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42>>");
+
+    dict->insertScalar("n2", SK_ScalarHalf);
+
+    SkString n3("n3");
+    SkAutoTUnref<SkPDFArray> innerArray(new SkPDFArray);
+    innerArray->appendInt(-100);
+    dict->insertObject(n3, innerArray.detach());
+    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42\n/n2 0.5\n/n3 [-100]>>");
+
+    dict.reset(new SkPDFDict);
+    ASSERT_EMIT_EQ(reporter, *dict, "<<>>");
+
+    dict->insertInt("n1", 24);
+    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24>>");
+
+    dict->insertInt("n2", SkToSizeT(99));
+    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99>>");
+
+    dict->insertScalar("n3", SK_ScalarHalf);
+    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 0.5>>");
+
+    dict->insertName("n4", "AName");
+    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 0.5\n/n4 /AName>>");
+
+    dict->insertName("n5", SkString("AnotherName"));
+    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 0.5\n/n4 /AName\n"
+                   "/n5 /AnotherName>>");
+
+    dict->insertString("n6", "A String");
+    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 0.5\n/n4 /AName\n"
+                   "/n5 /AnotherName\n/n6 (A String)>>");
+
+    dict->insertString("n7", SkString("Another String"));
+    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 0.5\n/n4 /AName\n"
+                   "/n5 /AnotherName\n/n6 (A String)\n/n7 (Another String)>>");
+
+    dict.reset(new SkPDFDict("DType"));
+    ASSERT_EMIT_EQ(reporter, *dict, "<</Type /DType>>");
+    
+    SkAutoTUnref<SkPDFArray> referencedArray(new SkPDFArray);
+    Catalog catalog;
+    catalog.numbers.addObject(referencedArray.get());
+    REPORTER_ASSERT(reporter, catalog.numbers.getObjectNumber(
+                            referencedArray.get()) == 1);
+    dict->insertObjRef("n1", referencedArray.detach());
+    SkString result = emit_to_string(*dict, &catalog);
+    ASSERT_EQ(reporter, result, "<</Type /DType\n/n1 1 0 R>>");
+}
+
+DEF_TEST(PDFPrimitives, reporter) {
+    TestPDFUnion(reporter);
+    TestPDFArray(reporter);
+    TestPDFDict(reporter);
     TestPDFStream(reporter);
-
-    TestCatalog(reporter);
-
+    TestObjectNumberMap(reporter);
     TestObjectRef(reporter);
-
     TestSubstitute(reporter);
-
     test_issue1083();
 }
 
@@ -316,9 +363,9 @@
 class DummyImageFilter : public SkImageFilter {
 public:
     DummyImageFilter(bool visited = false) : SkImageFilter(0, NULL), fVisited(visited) {}
-    ~DummyImageFilter() SK_OVERRIDE {}
+    ~DummyImageFilter() override {}
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
-                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE {
+                               SkBitmap* result, SkIPoint* offset) const override {
         fVisited = true;
         offset->fX = offset->fY = 0;
         *result = src;
diff --git a/tests/PMFloatTest.cpp b/tests/PMFloatTest.cpp
index f370b39..1c53081 100644
--- a/tests/PMFloatTest.cpp
+++ b/tests/PMFloatTest.cpp
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "SkPMFloat.h"
 #include "Test.h"
 
@@ -9,14 +16,17 @@
     REPORTER_ASSERT(r, SkScalarNearlyEqual(204.0f, pmf.r()));
     REPORTER_ASSERT(r, SkScalarNearlyEqual(153.0f, pmf.g()));
     REPORTER_ASSERT(r, SkScalarNearlyEqual( 51.0f, pmf.b()));
-    REPORTER_ASSERT(r, c == pmf.get());
+    REPORTER_ASSERT(r, c == pmf.round());
 
-    // Test rounding.  (Don't bother testing .5... we don't care which way it goes.)
-    pmf = SkPMFloat(254.6f, 204.3f, 153.1f, 50.8f);
-    REPORTER_ASSERT(r, c == pmf.get());
+    // Test rounding.
+    pmf = SkPMFloat(254.5f, 203.5f, 153.1f, 50.8f);
+    REPORTER_ASSERT(r, c == pmf.round());
+
+    pmf = SkPMFloat(255.9f, 204.01f, 153.0f, -0.9f);
+    REPORTER_ASSERT(r, SkPreMultiplyColor(0xFFCC9900) == pmf.trunc());
 
     // Test clamping.
-    SkPMFloat clamped(SkPMFloat(510.0f, 153.0f, 1.0f, -0.2f).clamped());
+    SkPMFloat clamped(SkPMFloat(510.0f, 153.0f, 1.0f, -0.2f).roundClamp());
     REPORTER_ASSERT(r, SkScalarNearlyEqual(255.0f, clamped.a()));
     REPORTER_ASSERT(r, SkScalarNearlyEqual(153.0f, clamped.r()));
     REPORTER_ASSERT(r, SkScalarNearlyEqual(  1.0f, clamped.g()));
@@ -24,7 +34,7 @@
 
     // Test SkPMFloat <-> Sk4f conversion.
     Sk4f fs = clamped;
-    SkPMFloat scaled = fs.multiply(Sk4f(0.25f));
+    SkPMFloat scaled = fs * Sk4f(0.25f);
     REPORTER_ASSERT(r, SkScalarNearlyEqual(63.75f, scaled.a()));
     REPORTER_ASSERT(r, SkScalarNearlyEqual(38.25f, scaled.r()));
     REPORTER_ASSERT(r, SkScalarNearlyEqual( 0.25f, scaled.g()));
@@ -33,15 +43,15 @@
     // Test 4-at-a-time conversions.
     SkPMColor colors[4] = { 0xFF000000, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF };
     SkPMFloat floats[4];
-    SkPMFloat::From4PMColors(floats, colors);
+    SkPMFloat::From4PMColors(colors, floats+0, floats+1, floats+2, floats+3);
 
     SkPMColor back[4];
-    SkPMFloat::To4PMColors(back, floats);
+    SkPMFloat::RoundTo4PMColors(floats[0], floats[1], floats[2], floats[3], back);
     for (int i = 0; i < 4; i++) {
         REPORTER_ASSERT(r, back[i] == colors[i]);
     }
 
-    SkPMFloat::ClampTo4PMColors(back, floats);
+    SkPMFloat::RoundClampTo4PMColors(floats[0], floats[1], floats[2], floats[3], back);
     for (int i = 0; i < 4; i++) {
         REPORTER_ASSERT(r, back[i] == colors[i]);
     }
diff --git a/tests/PaintTest.cpp b/tests/PaintTest.cpp
index bf22de1..1fca8dc 100644
--- a/tests/PaintTest.cpp
+++ b/tests/PaintTest.cpp
@@ -116,27 +116,25 @@
 }
 
 // temparary api for bicubic, just be sure we can set/clear it
-DEF_TEST(Paint_filterlevel, reporter) {
+DEF_TEST(Paint_filterQuality, reporter) {
     SkPaint p0, p1;
 
-    REPORTER_ASSERT(reporter,
-                    SkPaint::kNone_FilterLevel == p0.getFilterLevel());
+    REPORTER_ASSERT(reporter, kNone_SkFilterQuality == p0.getFilterQuality());
 
-    static const SkPaint::FilterLevel gLevels[] = {
-        SkPaint::kNone_FilterLevel,
-        SkPaint::kLow_FilterLevel,
-        SkPaint::kMedium_FilterLevel,
-        SkPaint::kHigh_FilterLevel
+    static const SkFilterQuality gQualitys[] = {
+        kNone_SkFilterQuality,
+        kLow_SkFilterQuality,
+        kMedium_SkFilterQuality,
+        kHigh_SkFilterQuality
     };
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gLevels); ++i) {
-        p0.setFilterLevel(gLevels[i]);
-        REPORTER_ASSERT(reporter, gLevels[i] == p0.getFilterLevel());
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) {
+        p0.setFilterQuality(gQualitys[i]);
+        REPORTER_ASSERT(reporter, gQualitys[i] == p0.getFilterQuality());
         p1 = p0;
-        REPORTER_ASSERT(reporter, gLevels[i] == p1.getFilterLevel());
+        REPORTER_ASSERT(reporter, gQualitys[i] == p1.getFilterQuality());
 
         p0.reset();
-        REPORTER_ASSERT(reporter,
-                        SkPaint::kNone_FilterLevel == p0.getFilterLevel());
+        REPORTER_ASSERT(reporter, kNone_SkFilterQuality == p0.getFilterQuality());
     }
 }
 
@@ -205,11 +203,11 @@
 }
 
 DEF_TEST(Paint_flattening, reporter) {
-    const SkPaint::FilterLevel levels[] = {
-        SkPaint::kNone_FilterLevel,
-        SkPaint::kLow_FilterLevel,
-        SkPaint::kMedium_FilterLevel,
-        SkPaint::kHigh_FilterLevel,
+    const SkFilterQuality levels[] = {
+        kNone_SkFilterQuality,
+        kLow_SkFilterQuality,
+        kMedium_SkFilterQuality,
+        kHigh_SkFilterQuality,
     };
     const SkPaint::Hinting hinting[] = {
         SkPaint::kNo_Hinting,
@@ -251,7 +249,7 @@
     SkPaint paint;
     paint.setFlags(0x1234);
 
-    FOR_SETUP(i, levels, setFilterLevel)
+    FOR_SETUP(i, levels, setFilterQuality)
     FOR_SETUP(j, hinting, setHinting)
     FOR_SETUP(k, align, setTextAlign)
     FOR_SETUP(l, caps, setStrokeCap)
diff --git a/tests/PathCoverageTest.cpp b/tests/PathCoverageTest.cpp
index 0e4a153..303ddcc 100644
--- a/tests/PathCoverageTest.cpp
+++ b/tests/PathCoverageTest.cpp
@@ -60,7 +60,7 @@
     if (d < tol) {
        return 1;
     } else {
-       int temp = SkScalarCeilToInt(SkScalarSqrt(SkScalarDiv(d, tol)));
+       int temp = SkScalarCeilToInt(SkScalarSqrt(d / tol));
        uint32_t count = SkMin32(SkNextPow2(temp), MAX_POINTS_PER_CURVE);
        return count;
     }
diff --git a/tests/PathOpsAngleIdeas.cpp b/tests/PathOpsAngleIdeas.cpp
index 901cab2..79e09b9 100755
--- a/tests/PathOpsAngleIdeas.cpp
+++ b/tests/PathOpsAngleIdeas.cpp
@@ -6,8 +6,8 @@
  */
 #include "PathOpsTestCommon.h"
 #include "SkIntersections.h"
+#include "SkOpContour.h"
 #include "SkOpSegment.h"
-#include "SkPathOpsTriangle.h"
 #include "SkRandom.h"
 #include "SkTArray.h"
 #include "SkTSort.h"
@@ -18,12 +18,12 @@
 
 class PathOpsAngleTester {
 public:
-    static int ConvexHullOverlaps(const SkOpAngle& lh, const SkOpAngle& rh) {
-        return lh.convexHullOverlaps(rh);
+    static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
+        return lh.convexHullOverlaps(&rh);
     }
 
-    static int EndsIntersect(const SkOpAngle& lh, const SkOpAngle& rh) {
-        return lh.endsIntersect(rh);
+    static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) {
+        return lh.endsIntersect(&rh);
     }
 };
 
@@ -406,28 +406,29 @@
     return ccw == upperRange.ccw;
 }
 
-class PathOpsSegmentTester {
-public:
-    static void ConstructQuad(SkOpSegment* segment, SkPoint shortQuad[3]) {
-        segment->debugConstructQuad(shortQuad);
-    }
-};
-
-static void makeSegment(const SkDQuad& quad, SkPoint shortQuad[3], SkOpSegment* result) {
+static void makeSegment(SkOpContour* contour, const SkDQuad& quad, SkPoint shortQuad[3],
+        SkChunkAlloc* allocator) {
     shortQuad[0] = quad[0].asSkPoint();
     shortQuad[1] = quad[1].asSkPoint();
     shortQuad[2] = quad[2].asSkPoint();
-    PathOpsSegmentTester::ConstructQuad(result, shortQuad);
+    contour->addQuad(shortQuad, allocator);
 }
 
 static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2,
-        int testNo) {
+        int testNo, SkChunkAlloc* allocator) {
     SkPoint shortQuads[2][3];
-    SkOpSegment seg[2];
-    makeSegment(quad1, shortQuads[0], &seg[0]);
-    makeSegment(quad2, shortQuads[1], &seg[1]);
-    int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg[0].debugLastAngle(),
-            *seg[1].debugLastAngle());
+
+    SkOpContourHead contour;
+    SkOpGlobalState state(NULL, &contour);
+    contour.init(&state, false, false);
+    makeSegment(&contour, quad1, shortQuads[0], allocator);
+    makeSegment(&contour, quad1, shortQuads[1], allocator);
+    SkOpSegment* seg1 = contour.first();
+    seg1->debugAddAngle(0, 1, allocator);
+    SkOpSegment* seg2 = seg1->next();
+    seg2->debugAddAngle(0, 1, allocator);
+    int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg1->debugLastAngle(),
+            *seg2->debugLastAngle());
     const SkDPoint& origin = quad1[0];
     REPORTER_ASSERT(reporter, origin == quad2[0]);
     double a1s = atan2(origin.fY - quad1[1].fY, quad1[1].fX - origin.fX);
@@ -545,25 +546,27 @@
     }
     if (overlap < 0) {
         SkDEBUGCODE(int realEnds =)
-                PathOpsAngleTester::EndsIntersect(*seg[0].debugLastAngle(),
-                *seg[1].debugLastAngle());
+                PathOpsAngleTester::EndsIntersect(*seg1->debugLastAngle(),
+                *seg2->debugLastAngle());
         SkASSERT(realEnds == (firstInside ? 1 : 0));
     }
     bruteForce(reporter, quad1, quad2, firstInside);
 }
 
 DEF_TEST(PathOpsAngleOverlapHullsOne, reporter) {
+    SkChunkAlloc allocator(4096);
 //    gPathOpsAngleIdeasVerbose = true;
     const SkDQuad quads[] = {
 {{{939.4808349609375, 914.355224609375}, {-357.7921142578125, 590.842529296875}, {736.8936767578125, -350.717529296875}}},
 {{{939.4808349609375, 914.355224609375}, {-182.85418701171875, 634.4552001953125}, {-509.62615966796875, 576.1182861328125}}}
     };
     for (int index = 0; index < (int) SK_ARRAY_COUNT(quads); index += 2) {
-        testQuadAngles(reporter, quads[index], quads[index + 1], 0);
+        testQuadAngles(reporter, quads[index], quads[index + 1], 0, &allocator);
     }
 }
 
 DEF_TEST(PathOpsAngleOverlapHulls, reporter) {
+    SkChunkAlloc allocator(4096);
     if (!gPathOpsAngleIdeasVerbose) {  // takes a while to run -- so exclude it by default
         return;
     }
@@ -587,7 +590,7 @@
         if (i.used() > 1) {
             continue;
         }
-        testQuadAngles(reporter, quad1, quad2, index);
+        testQuadAngles(reporter, quad1, quad2, index, &allocator);
     }
 }
 
diff --git a/tests/PathOpsAngleTest.cpp b/tests/PathOpsAngleTest.cpp
index faf6158..9c70774 100644
--- a/tests/PathOpsAngleTest.cpp
+++ b/tests/PathOpsAngleTest.cpp
@@ -6,10 +6,9 @@
  */
 #include "PathOpsTestCommon.h"
 #include "SkIntersections.h"
+#include "SkOpContour.h"
 #include "SkOpSegment.h"
-#include "SkPathOpsTriangle.h"
 #include "SkRandom.h"
-#include "SkTArray.h"
 #include "SkTSort.h"
 #include "Test.h"
 
@@ -191,20 +190,20 @@
 
 class PathOpsAngleTester {
 public:
-    static int After(const SkOpAngle& lh, const SkOpAngle& rh) {
+    static int After(SkOpAngle& lh, SkOpAngle& rh) {
         return lh.after(&rh);
     }
 
-    static int ConvexHullOverlaps(const SkOpAngle& lh, const SkOpAngle& rh) {
-        return lh.convexHullOverlaps(rh);
+    static int ConvexHullOverlaps(SkOpAngle& lh, SkOpAngle& rh) {
+        return lh.convexHullOverlaps(&rh);
     }
 
-    static int Orderable(const SkOpAngle& lh, const SkOpAngle& rh) {
-        return lh.orderable(rh);
+    static int Orderable(SkOpAngle& lh, SkOpAngle& rh) {
+        return lh.orderable(&rh);
     }
 
-    static int EndsIntersect(const SkOpAngle& lh, const SkOpAngle& rh) {
-        return lh.endsIntersect(rh);
+    static int EndsIntersect(SkOpAngle& lh, SkOpAngle& rh) {
+        return lh.endsIntersect(&rh);
     }
 
     static void SetNext(SkOpAngle& lh, SkOpAngle& rh) {
@@ -214,18 +213,6 @@
 
 class PathOpsSegmentTester {
 public:
-    static void ConstructCubic(SkOpSegment* segment, SkPoint shortCubic[4]) {
-        segment->debugConstructCubic(shortCubic);
-    }
-
-    static void ConstructLine(SkOpSegment* segment, SkPoint shortLine[2]) {
-        segment->debugConstructLine(shortLine);
-    }
-
-    static void ConstructQuad(SkOpSegment* segment, SkPoint shortQuad[3]) {
-        segment->debugConstructQuad(shortQuad);
-    }
-
     static void DebugReset(SkOpSegment* segment) {
         segment->debugReset();
     }
@@ -246,7 +233,10 @@
 static const int circleDataSetSize = (int) SK_ARRAY_COUNT(circleDataSet);
 
 DEF_TEST(PathOpsAngleCircle, reporter) {
-    SkOpSegment segment[2];
+    SkChunkAlloc allocator(4096);
+    SkOpContourHead contour;
+    SkOpGlobalState state(NULL, &contour);
+    contour.init(&state, false, false);
     for (int index = 0; index < circleDataSetSize; ++index) {
         CircleData& data = circleDataSet[index];
         for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
@@ -254,17 +244,21 @@
         }
         switch (data.fPtCount) {
             case 2:
-                PathOpsSegmentTester::ConstructLine(&segment[index], data.fShortPts);
+                contour.addLine(data.fShortPts, &allocator);
                 break;
             case 3:
-                PathOpsSegmentTester::ConstructQuad(&segment[index], data.fShortPts);
+                contour.addQuad(data.fShortPts, &allocator);
                 break;
             case 4:
-                PathOpsSegmentTester::ConstructCubic(&segment[index], data.fShortPts);
+                contour.addCubic(data.fShortPts, &allocator);
                 break;
         }
     }
-    PathOpsAngleTester::Orderable(*segment[0].debugLastAngle(), *segment[1].debugLastAngle());
+    SkOpSegment* first = contour.first();
+    first->debugAddAngle(0, 1, &allocator);
+    SkOpSegment* next = first->next();
+    next->debugAddAngle(0, 1, &allocator);
+    PathOpsAngleTester::Orderable(*first->debugLastAngle(), *next->debugLastAngle());
 }
 
 struct IntersectData {
@@ -379,11 +373,39 @@
     { {{{5.000,4.000}, {2.000,3.000}}}, 2, 0.5, 0, {} }, // pathops_visualizer.htm:7377
 }; //
 
+// from skpi_gino_com_16
+static IntersectData intersectDataSet17[] = {
+    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
+        , 3, 0.74590454, 0.547660352, {} },
+    { /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}}
+        , 4, 0.12052623, 0, {} },
+    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
+        , 3, 0.74590454, 1, {} },
+};
+
+static IntersectData intersectDataSet18[] = {
+    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
+        , 3, 0.74590454, 1, {} },
+    { /*seg=8*/ {{{185, 734}, {252.93103f, 734}, {308, 789.06897f}, {308, 857}}}
+        , 4, 0.12052623, 0.217351928, {} },
+    { /*seg=7*/ {{{270.974121f, 770.025879f}, {234.948273f, 734}, {184, 734}}}
+        , 3, 0.74590454, 0.547660352, {} },
+};
+
+static IntersectData intersectDataSet19[] = {
+    { /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}}
+        , 4, 0.135148995, 0.134791946, {} },
+    { /*seg=3*/ {{{1, 2}, {1, 2.15061641f}, {1, 2.21049166f}, {1.01366711f, 2.21379328f}}}
+        , 4, 0.956740456, 0.894913214, {} },
+    { /*seg=1*/ {{{0, 1}, {3, 5}, {2, 1}, {3, 1}}}
+        , 4, 0.135148995, 0.551812363, {} },
+};
+
 #define I(x) intersectDataSet##x
 
 static IntersectData* intersectDataSets[] = {
     I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
-    I(11), I(12), I(13), I(14), I(15), I(16),
+    I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19),
 };
 
 #undef I
@@ -391,56 +413,55 @@
 
 static const int intersectDataSetSizes[] = {
     I(1), I(2), I(3), I(4), I(5), I(6), I(7), I(8), I(9), I(10),
-    I(11), I(12), I(13), I(14), I(15), I(16),
+    I(11), I(12), I(13), I(14), I(15), I(16), I(17), I(18), I(19),
 };
 
 #undef I
 
 static const int intersectDataSetsSize = (int) SK_ARRAY_COUNT(intersectDataSetSizes);
 
+struct FourPoints {
+    SkPoint pts[4];
+};
+
 DEF_TEST(PathOpsAngleAfter, reporter) {
+    SkChunkAlloc allocator(4096);
+    SkOpContourHead contour;
+    SkOpGlobalState state(NULL, &contour);
+    contour.init(&state, false, false);
     for (int index = intersectDataSetsSize - 1; index >= 0; --index) {
         IntersectData* dataArray = intersectDataSets[index];
         const int dataSize = intersectDataSetSizes[index];
-        SkOpSegment segment[3];
         for (int index2 = 0; index2 < dataSize - 2; ++index2) {
-            for (int temp = 0; temp < (int) SK_ARRAY_COUNT(segment); ++temp) {
-                PathOpsSegmentTester::DebugReset(&segment[temp]);
-            }
-            for (int index3 = 0; index3 < (int) SK_ARRAY_COUNT(segment); ++index3) {
+            allocator.reset();
+            contour.reset();
+            for (int index3 = 0; index3 < 3; ++index3) {
                 IntersectData& data = dataArray[index2 + index3];
-                SkPoint temp[4];
+                SkPoint* temp = (SkPoint*) SkOpTAllocator<FourPoints>::Allocate(&allocator);
                 for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) {
                     temp[idx2] = data.fPts.fPts[idx2].asSkPoint();
                 }
                 switch (data.fPtCount) {
                     case 2: {
-                        SkDLine seg = SkDLine::SubDivide(temp, data.fTStart,
-                                data.fTStart < data.fTEnd ? 1 : 0);
-                        data.fShortPts[0] = seg[0].asSkPoint();
-                        data.fShortPts[1] = seg[1].asSkPoint();
-                        PathOpsSegmentTester::ConstructLine(&segment[index3], data.fShortPts);
+                        contour.addLine(temp, &allocator);
                         } break;
                     case 3: {
-                        SkDQuad seg = SkDQuad::SubDivide(temp, data.fTStart, data.fTEnd);
-                        data.fShortPts[0] = seg[0].asSkPoint();
-                        data.fShortPts[1] = seg[1].asSkPoint();
-                        data.fShortPts[2] = seg[2].asSkPoint();
-                        PathOpsSegmentTester::ConstructQuad(&segment[index3], data.fShortPts);
+                        contour.addQuad(temp, &allocator);
                         } break;
                     case 4: {
-                        SkDCubic seg = SkDCubic::SubDivide(temp, data.fTStart, data.fTEnd);
-                        data.fShortPts[0] = seg[0].asSkPoint();
-                        data.fShortPts[1] = seg[1].asSkPoint();
-                        data.fShortPts[2] = seg[2].asSkPoint();
-                        data.fShortPts[3] = seg[3].asSkPoint();
-                        PathOpsSegmentTester::ConstructCubic(&segment[index3], data.fShortPts);
+                        contour.addCubic(temp, &allocator);
                         } break;
                 }
             }
-            SkOpAngle& angle1 = *const_cast<SkOpAngle*>(segment[0].debugLastAngle());
-            SkOpAngle& angle2 = *const_cast<SkOpAngle*>(segment[1].debugLastAngle());
-            SkOpAngle& angle3 = *const_cast<SkOpAngle*>(segment[2].debugLastAngle());
+            SkOpSegment* seg1 = contour.first();
+            seg1->debugAddAngle(dataArray[index2 + 0].fTStart, dataArray[index2 + 0].fTEnd, &allocator);
+            SkOpSegment* seg2 = seg1->next();
+            seg2->debugAddAngle(dataArray[index2 + 1].fTStart, dataArray[index2 + 1].fTEnd, &allocator);
+            SkOpSegment* seg3 = seg2->next();
+            seg3->debugAddAngle(dataArray[index2 + 2].fTStart, dataArray[index2 + 2].fTEnd, &allocator);
+            SkOpAngle& angle1 = *seg1->debugLastAngle();
+            SkOpAngle& angle2 = *seg2->debugLastAngle();
+            SkOpAngle& angle3 = *seg3->debugLastAngle();
             PathOpsAngleTester::SetNext(angle1, angle3);
        // These data sets are seeded when the set itself fails, so likely the dataset does not
        // match the expected result. The tests above return 1 when first added, but
@@ -451,35 +472,26 @@
     }
 }
 
-void SkOpSegment::debugConstruct() {
-    addStartSpan(1);
-    addEndSpan(1);
-    debugAddAngle(0, 1);
-}
-
-void SkOpSegment::debugAddAngle(int start, int end) {
-    SkASSERT(start != end);
-    SkOpAngle& angle = fAngles.push_back();
-    angle.set(this, start, end);
-}
-
-void SkOpSegment::debugConstructCubic(SkPoint shortQuad[4]) {
-    addCubic(shortQuad, false, false);
-    addT(NULL, shortQuad[0], 0);
-    addT(NULL, shortQuad[3], 1);
-    debugConstruct();
-}
-
-void SkOpSegment::debugConstructLine(SkPoint shortQuad[2]) {
-    addLine(shortQuad, false, false);
-    addT(NULL, shortQuad[0], 0);
-    addT(NULL, shortQuad[1], 1);
-    debugConstruct();
-}
-
-void SkOpSegment::debugConstructQuad(SkPoint shortQuad[3]) {
-    addQuad(shortQuad, false, false);
-    addT(NULL, shortQuad[0], 0);
-    addT(NULL, shortQuad[2], 1);
-    debugConstruct();
+void SkOpSegment::debugAddAngle(double startT, double endT, SkChunkAlloc* allocator) {
+    SkOpPtT* startPtT = startT == 0 ? fHead.ptT() : startT == 1 ? fTail.ptT()
+            : this->addT(startT, kNoAlias, allocator);
+    SkOpPtT* endPtT = endT == 0 ? fHead.ptT() : endT == 1 ? fTail.ptT()
+            : this->addT(endT, kNoAlias, allocator);
+    SkOpAngle* angle = SkOpTAllocator<SkOpAngle>::Allocate(allocator);
+    SkOpSpanBase* startSpan = &fHead;
+    while (startSpan->ptT() != startPtT) {
+        startSpan = startSpan->upCast()->next();
+    }
+    SkOpSpanBase* endSpan = &fHead;
+    while (endSpan->ptT() != endPtT) {
+        endSpan = endSpan->upCast()->next();
+    }
+    angle->set(startSpan, endSpan);
+    if (startT < endT) {
+        startSpan->upCast()->setToAngle(angle);
+        endSpan->setFromAngle(angle);
+    } else {
+        endSpan->upCast()->setToAngle(angle);
+        startSpan->setFromAngle(angle);
+    }
 }
diff --git a/tests/PathOpsBattles.cpp b/tests/PathOpsBattles.cpp
index 455f2e9..8f59f9f 100644
--- a/tests/PathOpsBattles.cpp
+++ b/tests/PathOpsBattles.cpp
@@ -36,7 +36,7 @@
     path2.cubicTo(-15.5552f, 63.4296f, 12.6591f, 64.0704f, 33.9313f, 49.484f);
     path2.lineTo(46.9383f, 68.4529f);
     path2.close();
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void issue414409b(skiatest::Reporter* reporter, const char* filename) {
@@ -59,8 +59,7 @@
 path2.cubicTo(SkBits2Float(0x42383446), SkBits2Float(0x421ac98f), SkBits2Float(0x4242b98a), SkBits2Float(0x420d5308), SkBits2Float(0x424bbb17), SkBits2Float(0x41fdb8ee));
 path2.lineTo(SkBits2Float(0x428ce9ef), SkBits2Float(0x422f7dc6));
 path2.close();
-// SkOpSegment.cpp:3488: failed assertion "other->fTs[min].fWindSum == oppWinding"
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void issue414409c(skiatest::Reporter* reporter, const char* filename) {
@@ -85,7 +84,7 @@
 path2.lineTo(SkBits2Float(0x3eccef1a), SkBits2Float(0xc2a5ff81));
 path2.close();
 
-testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 // fails to draw correctly
@@ -1296,7 +1295,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -1512,7 +1511,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -1754,7 +1753,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -1884,11 +1883,7 @@
 path.close();
 
     SkPath path2(path);
-    if (FLAGS_runFail) {
-        testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
-    } else {
-        testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
-    }
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -3982,7 +3977,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -4092,7 +4087,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -4240,7 +4235,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -4414,7 +4409,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -4956,7 +4951,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -5367,7 +5362,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -5443,7 +5438,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -6297,7 +6292,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -7054,7 +7049,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -7443,7 +7438,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -7560,7 +7555,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -7863,7 +7858,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 // op end success 1
 
@@ -10686,7 +10681,7 @@
     testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp68;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = battleOp121;
 static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static struct TestDesc tests[] = {
@@ -11128,5 +11123,5 @@
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, NULL, stopTest, runReverse);
 }
diff --git a/tests/PathOpsBoundsTest.cpp b/tests/PathOpsBoundsTest.cpp
index 8683051..573a0ce 100644
--- a/tests/PathOpsBoundsTest.cpp
+++ b/tests/PathOpsBoundsTest.cpp
@@ -6,6 +6,7 @@
  */
 #include "PathOpsTestCommon.h"
 #include "SkPathOpsBounds.h"
+#include "SkPathOpsCurve.h"
 #include "Test.h"
 
 static const SkRect sectTests[][2] = {
@@ -28,24 +29,6 @@
 
 static const size_t noSectTestsCount = SK_ARRAY_COUNT(noSectTests);
 
-static const SkRect reallyEmpty[] = {
-    {0, 0, 0, 0},
-    {1, 1, 1, 0},
-    {1, 1, 0, 1},
-    {1, 1, 0, 0},
-    {1, 2, 3, SK_ScalarNaN},
-};
-
-static const size_t emptyTestsCount = SK_ARRAY_COUNT(reallyEmpty);
-
-static const SkRect notReallyEmpty[] = {
-    {0, 0, 1, 0},
-    {0, 0, 0, 1},
-    {0, 0, 1, 1},
-};
-
-static const size_t notEmptyTestsCount = SK_ARRAY_COUNT(notReallyEmpty);
-
 DEF_TEST(PathOpsBounds, reporter) {
     for (size_t index = 0; index < sectTestsCount; ++index) {
         const SkPathOpsBounds& bounds1 = static_cast<const SkPathOpsBounds&>(sectTests[index][0]);
@@ -74,37 +57,18 @@
     ordinal.set(1, 2, 3, 4);
     bounds.add(ordinal);
     REPORTER_ASSERT(reporter, bounds == expected);
-    SkPoint topLeft = {0, 0};
-    bounds.setPointBounds(topLeft);
-    SkPoint botRight = {3, 4};
+    bounds.setEmpty();
+    SkDPoint botRight = {3, 4};
     bounds.add(botRight);
     REPORTER_ASSERT(reporter, bounds == expected);
-    for (size_t index = 0; index < emptyTestsCount; ++index) {
-        const SkPathOpsBounds& bounds = static_cast<const SkPathOpsBounds&>(reallyEmpty[index]);
-        // SkASSERT(ValidBounds(bounds));  // don't check because test may contain nan
-        bool empty = bounds.isReallyEmpty();
-        REPORTER_ASSERT(reporter, empty);
-    }
-    for (size_t index = 0; index < notEmptyTestsCount; ++index) {
-        const SkPathOpsBounds& bounds = static_cast<const SkPathOpsBounds&>(notReallyEmpty[index]);
-        SkASSERT(ValidBounds(bounds));
-        bool empty = bounds.isReallyEmpty();
-        REPORTER_ASSERT(reporter, !empty);
-    }
     const SkPoint curvePts[] = {{0, 0}, {1, 2}, {3, 4}, {5, 6}};
-    bounds.setLineBounds(curvePts);
-    expected.set(0, 0, 1, 2);
-    REPORTER_ASSERT(reporter, bounds == expected);
-    (bounds.*SetCurveBounds[1])(curvePts);
-    REPORTER_ASSERT(reporter, bounds == expected);
-    bounds.setQuadBounds(curvePts);
+    SkDCurve curve;
+    curve.fQuad.set(curvePts);
+    curve.setQuadBounds(curvePts, 1, 0, 1, &bounds);
     expected.set(0, 0, 3, 4);
     REPORTER_ASSERT(reporter, bounds == expected);
-    (bounds.*SetCurveBounds[2])(curvePts);
-    REPORTER_ASSERT(reporter, bounds == expected);
-    bounds.setCubicBounds(curvePts);
+    curve.fCubic.set(curvePts);
+    curve.setCubicBounds(curvePts, 1, 0, 1, &bounds);
     expected.set(0, 0, 5, 6);
     REPORTER_ASSERT(reporter, bounds == expected);
-    (bounds.*SetCurveBounds[3])(curvePts);
-    REPORTER_ASSERT(reporter, bounds == expected);
 }
diff --git a/tests/PathOpsBuilderTest.cpp b/tests/PathOpsBuilderTest.cpp
index 1eadebc..8ab92c3 100644
--- a/tests/PathOpsBuilderTest.cpp
+++ b/tests/PathOpsBuilderTest.cpp
@@ -4,7 +4,10 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
+#include "PathOpsExtendedTest.h"
 #include "PathOpsTestCommon.h"
+#include "SkBitmap.h"
 #include "Test.h"
 
 DEF_TEST(PathOpsBuilder, reporter) {
@@ -13,17 +16,18 @@
     REPORTER_ASSERT(reporter, builder.resolve(&result));
     REPORTER_ASSERT(reporter, result.isEmpty());
 
-    builder.add(result, kDifference_PathOp);
+    builder.add(result, kDifference_SkPathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
     REPORTER_ASSERT(reporter, result.isEmpty());
 
-    builder.add(result, kUnion_PathOp);
+    builder.add(result, kUnion_SkPathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
     REPORTER_ASSERT(reporter, result.isEmpty());
 
     SkPath rectPath;
+    rectPath.setFillType(SkPath::kEvenOdd_FillType);
     rectPath.addRect(0, 1, 2, 3, SkPath::kCW_Direction);
-    builder.add(rectPath, kUnion_PathOp);
+    builder.add(rectPath, kUnion_SkPathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
     bool closed;
     SkPath::Direction dir;
@@ -33,24 +37,25 @@
     REPORTER_ASSERT(reporter, rectPath == result);
 
     rectPath.reset();
+    rectPath.setFillType(SkPath::kEvenOdd_FillType);
     rectPath.addRect(0, 1, 2, 3, SkPath::kCCW_Direction);
-    builder.add(rectPath, kUnion_PathOp);
+    builder.add(rectPath, kUnion_SkPathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
     REPORTER_ASSERT(reporter, result.isRect(NULL, &closed, &dir));
     REPORTER_ASSERT(reporter, closed);
     REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
-    REPORTER_ASSERT(reporter, rectPath == result);
+     REPORTER_ASSERT(reporter, rectPath == result);
 
-    builder.add(rectPath, kDifference_PathOp);
+    builder.add(rectPath, kDifference_SkPathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
     REPORTER_ASSERT(reporter, result.isEmpty());
 
     SkPath rect2, rect3;
     rect2.addRect(2, 1, 4, 3, SkPath::kCW_Direction);
     rect3.addRect(4, 1, 5, 3, SkPath::kCCW_Direction);
-    builder.add(rectPath, kUnion_PathOp);
-    builder.add(rect2, kUnion_PathOp);
-    builder.add(rect3, kUnion_PathOp);
+    builder.add(rectPath, kUnion_SkPathOp);
+    builder.add(rect2, kUnion_SkPathOp);
+    builder.add(rect3, kUnion_SkPathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
     REPORTER_ASSERT(reporter, result.isRect(NULL, &closed, &dir));
     REPORTER_ASSERT(reporter, closed);
@@ -63,11 +68,39 @@
     circle2.addCircle(7, 4, 8, SkPath::kCCW_Direction);
     circle3.addCircle(6, 5, 6, SkPath::kCW_Direction);
     SkPath opCompare;
-    Op(circle1, circle2, kUnion_PathOp, &opCompare);
-    Op(opCompare, circle3, kDifference_PathOp, &opCompare);
-    builder.add(circle1, kUnion_PathOp);
-    builder.add(circle2, kUnion_PathOp);
-    builder.add(circle3, kDifference_PathOp);
+    Op(circle1, circle2, kUnion_SkPathOp, &opCompare);
+    Op(opCompare, circle3, kDifference_SkPathOp, &opCompare);
+    builder.add(circle1, kUnion_SkPathOp);
+    builder.add(circle2, kUnion_SkPathOp);
+    builder.add(circle3, kDifference_SkPathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
-    REPORTER_ASSERT(reporter, opCompare == result);
+    SkBitmap bitmap;
+    int pixelDiff = comparePaths(reporter, __FUNCTION__, opCompare, result, bitmap);
+    REPORTER_ASSERT(reporter, pixelDiff == 0);
+}
+
+DEF_TEST(Issue3838, reporter) {
+    SkPath path;
+    path.moveTo(200, 170);
+    path.lineTo(220, 170);
+    path.lineTo(220, 230);
+    path.lineTo(240, 230);
+    path.lineTo(240, 210);
+    path.lineTo(180, 210);
+    path.lineTo(180, 190);
+    path.lineTo(260, 190);
+    path.lineTo(260, 250);
+    path.lineTo(200, 250);
+    path.lineTo(200, 170);
+    path.close();
+    testSimplify(reporter, path, __FUNCTION__);
+    SkPath path3;
+    Simplify(path, &path3);
+    SkPath path2;
+    SkOpBuilder builder;
+    builder.add(path, kUnion_SkPathOp);
+    builder.resolve(&path2);
+    SkBitmap bitmap;
+    int pixelDiff = comparePaths(reporter, __FUNCTION__, path, path2, bitmap);
+    REPORTER_ASSERT(reporter, pixelDiff == 0);
 }
diff --git a/tests/PathOpsConicIntersectionTest.cpp b/tests/PathOpsConicIntersectionTest.cpp
new file mode 100644
index 0000000..1c15255
--- /dev/null
+++ b/tests/PathOpsConicIntersectionTest.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsTestCommon.h"
+#include "SkGeometry.h"
+#include "SkIntersections.h"
+#include "Test.h"
+
+/*
+manually compute the intersection of a pair of circles and see if the conic intersection matches
+  given two circles
+    construct a line connecting their centers
+    
+ */
+
+static const SkDConic testSet[] = {
+    {{{{-4,1}, {-4,5}, {0,5}}}, 0.707106769f},
+    {{{{-3,4}, {-3,1}, {0,1}}}, 0.707106769f},
+
+    {{{{0, 0}, {0, 1}, {1, 1}}}, 0.5f},
+    {{{{1, 0}, {0, 0}, {0, 1}}}, 0.5f},
+};
+
+const int testSetCount = (int) SK_ARRAY_COUNT(testSet);
+
+static void oneOff(skiatest::Reporter* reporter, const SkDConic& c1, const SkDConic& c2,
+        bool coin) {
+    SkASSERT(ValidConic(c1));
+    SkASSERT(ValidConic(c2));
+    SkIntersections intersections;
+    intersections.intersect(c1, c2);
+    if (coin && intersections.used() != 2) {
+        SkDebugf("");
+    }
+    REPORTER_ASSERT(reporter, !coin || intersections.used() == 2);
+    double tt1, tt2;
+    SkDPoint xy1, xy2;
+    for (int pt3 = 0; pt3 < intersections.used(); ++pt3) {
+        tt1 = intersections[0][pt3];
+        xy1 = c1.ptAtT(tt1);
+        tt2 = intersections[1][pt3];
+        xy2 = c2.ptAtT(tt2);
+        const SkDPoint& iPt = intersections.pt(pt3);
+        REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt));
+        REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt));
+        REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
+    }
+    reporter->bumpTestCount();
+}
+
+static void oneOff(skiatest::Reporter* reporter, int outer, int inner) {
+    const SkDConic& c1 = testSet[outer];
+    const SkDConic& c2 = testSet[inner];
+    oneOff(reporter, c1, c2, false);
+}
+
+static void oneOffTests(skiatest::Reporter* reporter) {
+    for (int outer = 0; outer < testSetCount - 1; ++outer) {
+        for (int inner = outer + 1; inner < testSetCount; ++inner) {
+            oneOff(reporter, outer, inner);
+        }
+    }
+}
+
+DEF_TEST(PathOpsConicIntersectionOneOff, reporter) {
+    oneOff(reporter, 0, 1);
+}
+
+DEF_TEST(PathOpsConicIntersection, reporter) {
+    oneOffTests(reporter);
+}
diff --git a/tests/PathOpsConicLineIntersectionTest.cpp b/tests/PathOpsConicLineIntersectionTest.cpp
new file mode 100644
index 0000000..2a958ed
--- /dev/null
+++ b/tests/PathOpsConicLineIntersectionTest.cpp
@@ -0,0 +1,143 @@
+/*
+ * 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 "PathOpsExtendedTest.h"
+#include "PathOpsTestCommon.h"
+#include "SkIntersections.h"
+#include "SkPathOpsConic.h"
+#include "SkPathOpsLine.h"
+#include "SkReduceOrder.h"
+#include "Test.h"
+
+static struct lineConic {
+    SkDConic conic;
+    SkDLine line;
+    int result;
+    SkDPoint expected[2];
+} lineConicTests[] = {
+    {
+     {{{{30.6499996,25.6499996}, {30.6499996,20.6499996}, {25.6499996,20.6499996}}}, 0.707107008f},
+      {{{25.6499996,20.6499996}, {45.6500015,20.6499996}}},
+          1, 
+       {{25.6499996,20.6499996}, {0,0}}
+    },
+};
+
+static size_t lineConicTests_count = SK_ARRAY_COUNT(lineConicTests);
+
+static int doIntersect(SkIntersections& intersections, const SkDConic& conic, const SkDLine& line,
+                       bool& flipped) {
+    int result;
+    flipped = false;
+    if (line[0].fX == line[1].fX) {
+        double top = line[0].fY;
+        double bottom = line[1].fY;
+        flipped = top > bottom;
+        if (flipped) {
+            SkTSwap<double>(top, bottom);
+        }
+        result = intersections.vertical(conic, top, bottom, line[0].fX, flipped);
+    } else if (line[0].fY == line[1].fY) {
+        double left = line[0].fX;
+        double right = line[1].fX;
+        flipped = left > right;
+        if (flipped) {
+            SkTSwap<double>(left, right);
+        }
+        result = intersections.horizontal(conic, left, right, line[0].fY, flipped);
+    } else {
+        intersections.intersect(conic, line);
+        result = intersections.used();
+    }
+    return result;
+}
+
+static struct oneLineConic {
+    SkDConic conic;
+    SkDLine line;
+} oneOffs[] = {
+    {{{{{30.6499996,25.6499996}, {30.6499996,20.6499996}, {25.6499996,20.6499996}}}, 0.707107008f},
+      {{{25.6499996,20.6499996}, {45.6500015,20.6499996}}}}
+};
+
+static size_t oneOffs_count = SK_ARRAY_COUNT(oneOffs);
+
+static void testOneOffs(skiatest::Reporter* reporter) {
+    bool flipped = false;
+    for (size_t index = 0; index < oneOffs_count; ++index) {
+        const SkDConic& conic = oneOffs[index].conic;
+        SkASSERT(ValidConic(conic));
+        const SkDLine& line = oneOffs[index].line;
+        SkASSERT(ValidLine(line));
+        SkIntersections intersections;
+        int result = doIntersect(intersections, conic, line, flipped);
+        for (int inner = 0; inner < result; ++inner) {
+            double conicT = intersections[0][inner];
+            SkDPoint conicXY = conic.ptAtT(conicT);
+            double lineT = intersections[1][inner];
+            SkDPoint lineXY = line.ptAtT(lineT);
+            if (!conicXY.approximatelyEqual(lineXY)) {
+                conicXY.approximatelyEqual(lineXY);
+                SkDebugf("");
+            }
+            REPORTER_ASSERT(reporter, conicXY.approximatelyEqual(lineXY));
+        }
+    }
+}
+
+DEF_TEST(PathOpsConicLineIntersectionOneOff, reporter) {
+    testOneOffs(reporter);
+}
+
+DEF_TEST(PathOpsConicLineIntersection, reporter) {
+    for (size_t index = 0; index < lineConicTests_count; ++index) {
+        int iIndex = static_cast<int>(index);
+        const SkDConic& conic = lineConicTests[index].conic;
+        SkASSERT(ValidConic(conic));
+        const SkDLine& line = lineConicTests[index].line;
+        SkASSERT(ValidLine(line));
+        SkReduceOrder reducer;
+        SkPoint pts[3] = { conic.fPts.fPts[0].asSkPoint(), conic.fPts.fPts[1].asSkPoint(), 
+            conic.fPts.fPts[2].asSkPoint() };
+        SkPoint reduced[3];
+        SkPath::Verb order1 = SkReduceOrder::Conic(pts, conic.fWeight, reduced);
+        if (order1 != SkPath::kConic_Verb) {
+            SkDebugf("%s [%d] conic verb=%d\n", __FUNCTION__, iIndex, order1);
+            REPORTER_ASSERT(reporter, 0);
+        }
+        int order2 = reducer.reduce(line);
+        if (order2 < 2) {
+            SkDebugf("%s [%d] line order=%d\n", __FUNCTION__, iIndex, order2);
+            REPORTER_ASSERT(reporter, 0);
+        }
+        SkIntersections intersections;
+        bool flipped = false;
+        int result = doIntersect(intersections, conic, line, flipped);
+        REPORTER_ASSERT(reporter, result == lineConicTests[index].result);
+        if (intersections.used() <= 0) {
+            continue;
+        }
+        for (int pt = 0; pt < result; ++pt) {
+            double tt1 = intersections[0][pt];
+            REPORTER_ASSERT(reporter, tt1 >= 0 && tt1 <= 1);
+            SkDPoint t1 = conic.ptAtT(tt1);
+            double tt2 = intersections[1][pt];
+            REPORTER_ASSERT(reporter, tt2 >= 0 && tt2 <= 1);
+            SkDPoint t2 = line.ptAtT(tt2);
+            if (!t1.approximatelyEqual(t2)) {
+                SkDebugf("%s [%d,%d] x!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n",
+                    __FUNCTION__, iIndex, pt, tt1, t1.fX, t1.fY, tt2, t2.fX, t2.fY);
+                REPORTER_ASSERT(reporter, 0);
+            }
+            if (!t1.approximatelyEqual(lineConicTests[index].expected[0])
+                    && (lineConicTests[index].result == 1
+                    || !t1.approximatelyEqual(lineConicTests[index].expected[1]))) {
+                SkDebugf("%s t1=(%1.9g,%1.9g)\n", __FUNCTION__, t1.fX, t1.fY);
+                REPORTER_ASSERT(reporter, 0);
+            }
+        }
+    }
+}
diff --git a/tests/PathOpsCubicIntersectionTest.cpp b/tests/PathOpsCubicIntersectionTest.cpp
index b6a9e59..138e6f4 100644
--- a/tests/PathOpsCubicIntersectionTest.cpp
+++ b/tests/PathOpsCubicIntersectionTest.cpp
@@ -6,6 +6,7 @@
  */
 #include "PathOpsCubicIntersectionTestData.h"
 #include "PathOpsTestCommon.h"
+#include "SkGeometry.h"
 #include "SkIntersections.h"
 #include "SkPathOpsRect.h"
 #include "SkReduceOrder.h"
@@ -162,6 +163,75 @@
 const int testSetCount = (int) SK_ARRAY_COUNT(testSet);
 
 static const SkDCubic newTestSet[] = {
+{{{1,3}, {-1.0564518,1.79032254}, {1.45265341,0.229448318}, {1.45381773,0.22913377}}},
+{{{1.45381773,0.22913377}, {1.45425761,0.229014933}, {1.0967741,0.451612949}, {0,1}}},
+
+{{{1.64551306f, 3.57876182f}, {0.298127174f, 3.70454836f}, {-0.809808373f, 6.39524937f}, {-3.66666651f, 13.333334f}}},
+{{{1, 2}, {1, 2}, {-3.66666651f, 13.333334f}, {5, 6}}},
+
+{{{0.0660428554,1.65340209}, {-0.251940489,1.43560803}, {-0.782382965,-0.196299091}, {3.33333325,-0.666666627}}},
+{{{1,3}, {-1.22353387,1.09411383}, {0.319867611,0.12996155}, {0.886705518,0.107543148}}},
+
+{{{-0.13654758,2.10514426}, {-0.585797966,1.89349782}, {-0.807703257,-0.192306399}, {6,-1}}},
+{{{1,4}, {-2.25000453,1.42241001}, {1.1314013,0.0505309105}, {1.87140274,0.0363764353}}},
+
+{{{1.3127951622009277, 2.0637707710266113}, {1.8210518360137939, 1.9148571491241455}, {1.6106204986572266, -0.68700540065765381}, {8.5, -2.5}}},
+{{{3, 4}, {0.33333325386047363, 1.3333332538604736}, {3.6666667461395264, -0.66666674613952637}, {3.6666665077209473, -0.66666656732559204}}},
+
+{{{980.026001,1481.276}, {980.026001,1481.276}, {980.02594,1481.27576}, {980.025879,1481.27527}}},
+{{{980.025879,1481.27527}, {980.025452,1481.27222}, {980.023743,1481.26038}, {980.02179,1481.24072}}},
+
+{{{1.80943513,3.07782435}, {1.66686702,2.16806936}, {1.68301272,0}, {3,0}}},
+{{{0,1}, {0,3}, {3,2}, {5,2}}},
+
+{{{3.4386673,2.66977954}, {4.06668949,2.17046738}, {4.78887367,1.59629118}, {6,2}}},
+{{{1.71985495,3.49467373}, {2.11620402,2.7201426}, {2.91897964,1.15138781}, {6,3}}},
+
+{{{0,1}, {0.392703831,1.78540766}, {0.219947904,2.05676103}, {0.218561709,2.05630541}}},
+{{{0.218561709,2.05630541}, {0.216418028,2.05560064}, {0.624105453,1.40486407}, {4.16666651,1.00000012}}},
+
+{{{0, 1}, {3, 5}, {2, 1}, {3, 1}}},
+{{{1.01366711f, 2.21379328f}, {1.09074128f, 2.23241305f}, {1.60246587f, 0.451849401f}, {5, 3}}},
+
+{{{0, 1}, {0.541499972f, 3.16599989f}, {1.08299994f, 2.69299984f}, {2.10083938f, 1.80391729f}}},
+{{{0.806384504f, 2.85426903f}, {1.52740121f, 1.99355423f}, {2.81689167f, 0.454222918f}, {5, 1}}},
+
+{{{0, 1}, {1.90192389f, 2.90192389f}, {2.59807634f, 2.79422879f}, {3.1076951f, 2.71539044f}}},
+{{{2, 3}, {2.36602545f, 3.36602545f}, {2.330127f, 3.06217766f}, {2.28460979f, 2.67691422f}}},
+
+{{{0, 1}, {1.90192389f, 2.90192389f}, {2.59807634f, 2.79422879f}, {3.1076951f, 2.71539044f}}},
+{{{2.28460979f, 2.67691422f}, {2.20577145f, 2.00961876f}, {2.09807634f, 1.09807622f}, {4, 3}}},
+
+{{{0, 1}, {0.8211091160774231, 2.0948121547698975}, {0.91805583238601685, 2.515404224395752}, {0.91621249914169312, 2.5146586894989014}}},
+{{{0.91621249914169312, 2.5146586894989014}, {0.91132104396820068, 2.5126807689666748}, {0.21079301834106445, -0.45617169141769409}, {10.5, -1.6666665077209473}}},
+
+{{{42.6237564,68.9841232}, {32.449646,81.963089}, {14.7713947,103.565269}, {12.6310005,105.247002}}},
+{{{37.2640038,95.3540039}, {37.2640038,95.3540039}, {11.3710003,83.7339935}, {-25.0779991,124.912003}}},
+
+{{{0,1}, {4,5}, {6,0}, {1,0}}},
+{{{0,6}, {0,1}, {1,0}, {5,4}}},
+
+{{{0,1}, {4,6}, {5,1}, {6,2}}},
+{{{1,5}, {2,6}, {1,0}, {6,4}}},
+
+{{{322, 896.04803466796875}, {314.09201049804687, 833.4376220703125}, {260.24713134765625, 785}, {195, 785}}},
+{{{195, 785}, {265.14016723632812, 785}, {322, 842.30755615234375}, {322, 913}}},
+
+{{{1, 4}, {4, 5}, {3, 2}, {6, 3}}},
+{{{2, 3}, {3, 6}, {4, 1}, {5, 4}}},
+
+{{{67, 913}, {67, 917.388916015625}, {67.224380493164063, 921.72576904296875}, {67.662384033203125, 926}}},
+{{{194, 1041}, {123.85984039306641, 1041}, {67, 983.69244384765625}, {67, 913}}},
+
+{{{1,4}, {1,5}, {6,0}, {5,1}}},
+{{{0,6}, {1,5}, {4,1}, {5,1}}},
+
+{{{0,1}, {4,5}, {6,0}, {1,0}}},
+{{{0,6}, {0,1}, {1,0}, {5,4}}},
+
+{{{0,1}, {4,6}, {2,0}, {2,0}}},
+{{{0,2}, {0,2}, {1,0}, {6,4}}},
+
 {{{980.9000244140625, 1474.3280029296875}, {980.9000244140625, 1474.3280029296875}, {978.89300537109375, 1471.95703125}, {981.791015625, 1469.487060546875}}},
 {{{981.791015625, 1469.487060546875}, {981.791015625, 1469.4859619140625}, {983.3580322265625, 1472.72900390625}, {980.9000244140625, 1474.3280029296875}}},
 
@@ -306,7 +376,6 @@
 };
 
 const int newTestSetCount = (int) SK_ARRAY_COUNT(newTestSet);
-
 static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const SkDCubic& cubic2,
         bool coin) {
     SkASSERT(ValidCubic(cubic1));
@@ -320,28 +389,19 @@
         cubic2[0].fX, cubic2[0].fY, cubic2[1].fX, cubic2[1].fY,
         cubic2[2].fX, cubic2[2].fY, cubic2[3].fX, cubic2[3].fY);
 #endif
-    SkTArray<SkDQuad, true> quads1;
-    CubicToQuads(cubic1, cubic1.calcPrecision(), quads1);
-#if ONE_OFF_DEBUG
-    SkDebugf("computed quadratics set 1\n");
-    for (int index = 0; index < quads1.count(); ++index) {
-        const SkDQuad& q = quads1[index];
-        SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY,
-                 q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
-    }
-#endif
-    SkTArray<SkDQuad, true> quads2;
-    CubicToQuads(cubic2, cubic2.calcPrecision(), quads2);
-#if ONE_OFF_DEBUG
-    SkDebugf("computed quadratics set 2\n");
-    for (int index = 0; index < quads2.count(); ++index) {
-        const SkDQuad& q = quads2[index];
-        SkDebugf("  {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY,
-                 q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
-    }
-#endif
     SkIntersections intersections;
     intersections.intersect(cubic1, cubic2);
+#if DEBUG_T_SECT_DUMP == 3
+    SkDebugf("</div>\n\n");
+    SkDebugf("<script type=\"text/javascript\">\n\n");
+    SkDebugf("var testDivs = [\n");
+    for (int index = 1; index <= gDumpTSectNum; ++index) {
+        SkDebugf("sect%d,\n", index);
+    }
+#endif
+    if (coin && intersections.used() != 2) {
+        SkDebugf("");
+    }
     REPORTER_ASSERT(reporter, !coin || intersections.used() == 2);
     double tt1, tt2;
     SkDPoint xy1, xy2;
@@ -558,37 +618,31 @@
 
 static void selfOneOff(skiatest::Reporter* reporter, int index) {
     const SkDCubic& cubic = selfSet[index];
-#if ONE_OFF_DEBUG
-    int idx2;
-    double max[3];
-    int ts = cubic.findMaxCurvature(max);
-    for (idx2 = 0; idx2 < ts; ++idx2) {
-        SkDebugf("%s max[%d]=%1.9g (%1.9g, %1.9g)\n", __FUNCTION__, idx2,
-                max[idx2], cubic.ptAtT(max[idx2]).fX, cubic.ptAtT(max[idx2]).fY);
+    SkPoint c[4];
+    for (int i = 0; i < 4; ++i) {
+        c[i] = cubic[i].asSkPoint();
     }
-    SkTArray<double, true> ts1;
-    SkTArray<SkDQuad, true> quads1;
-    cubic.toQuadraticTs(cubic.calcPrecision(), &ts1);
-    for (idx2 = 0; idx2 < ts1.count(); ++idx2) {
-        SkDebugf("%s t[%d]=%1.9g\n", __FUNCTION__, idx2, ts1[idx2]);
+    SkScalar loopT;
+    SkScalar d[3];
+    SkCubicType cubicType = SkClassifyCubic(c, d);
+    SkDCubic::CubicType dType;
+    if (SkDCubic::ComplexBreak(c, &loopT, &dType) && cubicType == SkCubicType::kLoop_SkCubicType) {
+        SkIntersections i;
+        SkPoint twoCubics[7];
+        SkChopCubicAt(c, twoCubics, loopT);
+        SkDCubic chopped[2];
+        chopped[0].set(&twoCubics[0]);
+        chopped[1].set(&twoCubics[3]);
+        int result = i.intersect(chopped[0], chopped[1]);
+        REPORTER_ASSERT(reporter, result == 2);
+        REPORTER_ASSERT(reporter, i.used() == 2);
+        for (int index = 0; index < result; ++index) {
+            SkDPoint pt1 = chopped[0].ptAtT(i[0][index]);
+            SkDPoint pt2 = chopped[1].ptAtT(i[1][index]);
+            REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
+            reporter->bumpTestCount();
+        }
     }
-    CubicToQuads(cubic, cubic.calcPrecision(), quads1);
-    for (idx2 = 0; idx2 < quads1.count(); ++idx2) {
-        const SkDQuad& q = quads1[idx2];
-        SkDebugf("  {{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}},\n",
-                q[0].fX, q[0].fY,  q[1].fX, q[1].fY,  q[2].fX, q[2].fY);
-    }
-    SkDebugf("\n");
-#endif
-    SkIntersections i;
-    int result = i.intersect(cubic);
-    REPORTER_ASSERT(reporter, result == 1);
-    REPORTER_ASSERT(reporter, i.used() == 1);
-    REPORTER_ASSERT(reporter, !approximately_equal(i[0][0], i[1][0]));
-    SkDPoint pt1 = cubic.ptAtT(i[0][0]);
-    SkDPoint pt2 = cubic.ptAtT(i[1][0]);
-    REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
-    reporter->bumpTestCount();
 }
 
 static void cubicIntersectionSelfTest(skiatest::Reporter* reporter) {
@@ -599,12 +653,12 @@
 }
 
 static const SkDCubic coinSet[] = {
+    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
+    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
+
     {{{317, 711}, {322.52285766601562, 711}, {327, 715.4771728515625}, {327, 721}}},
     {{{324.07107543945312, 713.928955078125}, {324.4051513671875, 714.26300048828125},
             {324.71566772460937, 714.62060546875}, {325, 714.9990234375}}},
-
-    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
-    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
 };
 
 static int coinSetCount = (int) SK_ARRAY_COUNT(coinSet);
diff --git a/tests/PathOpsCubicIntersectionTestData.cpp b/tests/PathOpsCubicIntersectionTestData.cpp
index 31056d2..7f725ef 100644
--- a/tests/PathOpsCubicIntersectionTestData.cpp
+++ b/tests/PathOpsCubicIntersectionTestData.cpp
@@ -46,8 +46,8 @@
 const size_t pointDegenerates_count = SK_ARRAY_COUNT(pointDegenerates);
 
 const SkDCubic notPointDegenerates[] = {
-    {{{1 + FLT_EPSILON * 2, 1}, {1, FLT_EPSILON * 2}, {1, 1}, {1, 1}}},
-    {{{1 + FLT_EPSILON * 2, 1}, {1 - FLT_EPSILON * 2, 1}, {1, 1}, {1, 1}}}
+    {{{1 + FLT_EPSILON * 8, 1}, {1, FLT_EPSILON * 8}, {1, 1}, {1, 1}}},
+    {{{1 + FLT_EPSILON * 8, 1}, {1 - FLT_EPSILON * 8, 1}, {1, 1}, {1, 1}}}
 };
 
 const size_t notPointDegenerates_count =
@@ -156,8 +156,8 @@
 
 const size_t notLines_count = SK_ARRAY_COUNT(notLines);
 
-static const double E = FLT_EPSILON * 2;
-static const double F = FLT_EPSILON * 3;
+static const double E = FLT_EPSILON * 8;
+static const double F = FLT_EPSILON * 8;
 
 const SkDCubic modEpsilonLines[] = {
     {{{0, E}, {0, 0}, {0, 0}, {1, 0}}},  // horizontal
@@ -187,8 +187,8 @@
     {{{1, 1}, {2, 2}, {2, 2+E}, {1, 1}}},
     {{{1, 1}, {1, 1+E}, {3, 3}, {3, 3}}},  // first-middle middle-last coincident
     {{{1, 1}, {2+E, 2}, {3, 3}, {4, 4}}},  // no coincident
-    {{{1, 1}, {3, 3}, {2, 2}, {4, 4+F}}},  // INVESTIGATE: why the epsilon is bigger
-    {{{1, 1+F}, {2, 2}, {4, 4}, {3, 3}}},  // INVESTIGATE: why the epsilon is bigger
+    {{{1, 1}, {3, 3}, {2, 2}, {4, 4+F+F}}},  // INVESTIGATE: why the epsilon is bigger
+    {{{1, 1+F+F}, {2, 2}, {4, 4}, {3, 3}}},  // INVESTIGATE: why the epsilon is bigger
     {{{1, 1}, {3, 3}, {4, 4+E}, {2, 2}}},
     {{{1, 1}, {4, 4}, {2, 2}, {3, 3+E}}},
     {{{1, 1}, {4, 4}, {3, 3}, {2+E, 2}}},
diff --git a/tests/PathOpsCubicLineIntersectionTest.cpp b/tests/PathOpsCubicLineIntersectionTest.cpp
index 234a538..a1f1d9f 100644
--- a/tests/PathOpsCubicLineIntersectionTest.cpp
+++ b/tests/PathOpsCubicLineIntersectionTest.cpp
@@ -49,6 +49,12 @@
 }
 
 static lineCubic lineCubicTests[] = {
+    {{{{0, 6}, {1.0851458311080933, 4.3722810745239258}, {1.5815209150314331, 3.038947582244873}, {1.9683018922805786, 1.9999997615814209}}},
+     {{{3,2}, {1,2}}}},
+
+    {{{{0.468027353,4}, {1.06734705,1.33333337}, {1.36700678,0}, {3,0}}},
+    {{{2,1}, {0,1}}}},
+
     {{{{-634.60540771484375, -481.262939453125}, {266.2696533203125, -752.70867919921875},
             {-751.8370361328125, -317.37921142578125}, {-969.7427978515625, 824.7255859375}}},
             {{{-287.9506133720805678, -557.1376476615772617},
diff --git a/tests/PathOpsCubicQuadIntersectionTest.cpp b/tests/PathOpsCubicQuadIntersectionTest.cpp
index 967dfc7..98665af 100644
--- a/tests/PathOpsCubicQuadIntersectionTest.cpp
+++ b/tests/PathOpsCubicQuadIntersectionTest.cpp
@@ -15,44 +15,38 @@
 static struct quadCubic {
     SkDCubic cubic;
     SkDQuad quad;
-    int answerCount;
-    SkDPoint answers[2];
 } quadCubicTests[] = {
-#if 0  // FIXME : this should not fail (root problem behind skpcarrot_is24 )
-    {{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}},
-     {{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}}, 1,
-      {{1019.421, 662.449}}},
-#endif
+    {{{{945.08099365234375, 747.1619873046875}, {982.5679931640625, 747.1619873046875}, {1013.6290283203125, 719.656005859375}, {1019.1910400390625, 683.72601318359375}}},
+     {{{945, 747}, {976.0660400390625, 747}, {998.03302001953125, 725.03302001953125}}}},
 
     {{{{778, 14089}, {778, 14091.208984375}, {776.20916748046875, 14093}, {774, 14093}}},
-     {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}, 2,
-     {{778, 14089}, {776.82855609581270,14091.828250841330}}},
+     {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}},
+
+    {{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}},
+     {{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}}},
+
+    {{{{778, 14089}, {778, 14091.208984375}, {776.20916748046875, 14093}, {774, 14093}}},
+     {{{778, 14089}, {777.99957275390625, 14090.65625}, {776.82843017578125, 14091.828125}}}},
 
     {{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}},
-     {{{1110.70715f, 817.292908f}, {1110.41406f, 817.000122f}, {1110, 817}}}, 2,
-      {{1110, 817}, {1110.70715f, 817.292908f}}},
+     {{{1110.70715f, 817.292908f}, {1110.41406f, 817.000122f}, {1110, 817}}}},
 
     {{{{1110, 817}, {1110.55225f, 817}, {1111, 817.447693f}, {1111, 818}}},
-     {{{1111, 818}, {1110.99988f, 817.585876f}, {1110.70715f, 817.292908f}}}, 2,
-      {{1110.70715f, 817.292908f}, {1111, 818}}},
+     {{{1111, 818}, {1110.99988f, 817.585876f}, {1110.70715f, 817.292908f}}}},
 
     {{{{55, 207}, {52.238574981689453, 207}, {50, 204.76142883300781}, {50, 202}}},
      {{{55, 207}, {52.929431915283203, 206.99949645996094},
-       {51.464466094970703, 205.53553771972656}}}, 2,
-      {{55, 207}, {51.464466094970703, 205.53553771972656}}},
+       {51.464466094970703, 205.53553771972656}}}},
 
     {{{{49, 47}, {49, 74.614250183105469}, {26.614250183105469, 97}, {-1, 97}}},
      {{{-8.659739592076221e-015, 96.991401672363281}, {20.065492630004883, 96.645187377929688},
-       {34.355339050292969, 82.355339050292969}}}, 2,
-      {{34.355339050292969,82.355339050292969}, {34.28654835573549, 82.424006509351585}}},
+       {34.355339050292969, 82.355339050292969}}}},
 
     {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}},
-     {{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}, 1,
-      {{18,226}, {0,0}}},
+     {{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}},
 
     {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}},
-     {{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}, 1,
-      {{10,234}, {0,0}}},
+     {{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}},
 };
 
 static const int quadCubicTests_count = (int) SK_ARRAY_COUNT(quadCubicTests);
@@ -77,7 +71,6 @@
     }
     SkIntersections i;
     int roots = i.intersect(cubic, quad);
-    SkASSERT(roots == quadCubicTests[index].answerCount);
     for (int pt = 0; pt < roots; ++pt) {
         double tt1 = i[0][pt];
         SkDPoint xy1 = cubic.ptAtT(tt1);
@@ -88,15 +81,6 @@
                 __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY);
         }
         REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
-        bool found = false;
-        for (int idx2 = 0; idx2 < quadCubicTests[index].answerCount; ++idx2) {
-            found |= quadCubicTests[index].answers[idx2].approximatelyEqual(xy1);
-        }
-        if (!found) {
-            SkDebugf("%s [%d,%d] xy1=(%g,%g) != \n",
-                __FUNCTION__, iIndex, pt, xy1.fX, xy1.fY);
-        }
-        REPORTER_ASSERT(reporter, found);
     }
     reporter->bumpTestCount();
 }
@@ -111,195 +95,3 @@
 DEF_TEST(PathOpsCubicQuadIntersectionOneOff, reporter) {
     cubicQuadIntersection(reporter, 0);
 }
-
-static bool gPathOpCubicQuadSlopVerbose = false;
-static const int kCubicToQuadSubdivisionDepth = 8; // slots reserved for cubic to quads subdivision
-
-// determine that slop required after quad/quad finds a candidate intersection
-// use the cross of the tangents plus the distance from 1 or 0 as knobs
-DEF_TEST(PathOpsCubicQuadSlop, reporter) {
-    // create a random non-selfintersecting cubic
-    // break it into quadratics
-    // offset the quadratic, measuring the slop required to find the intersection
-    if (!gPathOpCubicQuadSlopVerbose) {  // takes a while to run -- so exclude it by default
-        return;
-    }
-    int results[101];
-    sk_bzero(results, sizeof(results));
-    double minCross[101];
-    sk_bzero(minCross, sizeof(minCross));
-    double maxCross[101];
-    sk_bzero(maxCross, sizeof(maxCross));
-    double sumCross[101];
-    sk_bzero(sumCross, sizeof(sumCross));
-    int foundOne = 0;
-    int slopCount = 1;
-    SkRandom ran;
-    for (int index = 0; index < 10000000; ++index) {
-        if (index % 1000 == 999) SkDebugf(".");
-        SkDCubic cubic = {{
-                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
-                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
-                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)},
-                {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}
-        }};
-        SkIntersections i;
-        if (i.intersect(cubic)) {
-            continue;
-        }
-        SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts;
-        cubic.toQuadraticTs(cubic.calcPrecision(), &ts);
-        double tStart = 0;
-        int tsCount = ts.count();
-        for (int i1 = 0; i1 <= tsCount; ++i1) {
-            const double tEnd = i1 < tsCount ? ts[i1] : 1;
-            SkDCubic part = cubic.subDivide(tStart, tEnd);
-            SkDQuad quad = part.toQuad();
-            SkReduceOrder reducer;
-            int order = reducer.reduce(quad);
-            if (order != 3) {
-                continue;
-            }
-            for (int i2 = 0; i2 < 100; ++i2) {
-                SkDPoint endDisplacement = {ran.nextRangeF(-100, 100), ran.nextRangeF(-100, 100)};
-                SkDQuad nearby = {{
-                        {quad[0].fX + endDisplacement.fX, quad[0].fY + endDisplacement.fY},
-                        {quad[1].fX + ran.nextRangeF(-100, 100), quad[1].fY + ran.nextRangeF(-100, 100)},
-                        {quad[2].fX - endDisplacement.fX, quad[2].fY - endDisplacement.fY}
-                }};
-                order = reducer.reduce(nearby);
-                if (order != 3) {
-                    continue;
-                }
-                SkIntersections locals;
-                locals.allowNear(false);
-                locals.intersect(quad, nearby);
-                if (locals.used() != 1) {
-                    continue;
-                }
-                // brute force find actual intersection
-                SkDLine cubicLine = {{ {0, 0}, {cubic[0].fX, cubic[0].fY } }};
-                SkIntersections liner;
-                int i3;
-                int found = -1;
-                int foundErr = true;
-                for (i3 = 1; i3 <= 1000; ++i3) {
-                    cubicLine[0] = cubicLine[1];
-                    cubicLine[1] = cubic.ptAtT(i3 / 1000.);
-                    liner.reset();
-                    liner.allowNear(false);
-                    liner.intersect(nearby, cubicLine);
-                    if (liner.used() == 0) {
-                        continue;
-                    }
-                    if (liner.used() > 1) {
-                        foundErr = true;
-                        break;
-                    }
-                    if (found > 0) {
-                        foundErr = true;
-                        break;
-                    }
-                    foundErr = false;
-                    found = i3;
-                }
-                if (foundErr) {
-                    continue;
-                }
-                SkDVector dist = liner.pt(0) - locals.pt(0);
-                SkDVector qV = nearby.dxdyAtT(locals[0][0]);
-                double cubicT = (found - 1 + liner[1][0]) / 1000.;
-                SkDVector cV = cubic.dxdyAtT(cubicT);
-                double qxc = qV.crossCheck(cV);
-                double qvLen = qV.length();
-                double cvLen = cV.length();
-                double maxLen = SkTMax(qvLen, cvLen);
-                qxc /= maxLen;
-                double quadT = tStart + (tEnd - tStart) * locals[0][0];
-                double diffT = fabs(cubicT - quadT);
-                int diffIdx = (int) (diffT * 100);
-                results[diffIdx]++;
-                double absQxc = fabs(qxc);
-                if (sumCross[diffIdx] == 0) {
-                    minCross[diffIdx] = maxCross[diffIdx] = sumCross[diffIdx] = absQxc;
-                } else {
-                    minCross[diffIdx] = SkTMin(minCross[diffIdx], absQxc);
-                    maxCross[diffIdx] = SkTMax(maxCross[diffIdx], absQxc);
-                    sumCross[diffIdx] +=  absQxc;
-                }
-                if (diffIdx >= 20) {
-#if 01
-                    SkDebugf("cubic={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-                        " quad={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-                        " {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}"
-                        " qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
-                        cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
-                        cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
-                        nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
-                        nearby[2].fX, nearby[2].fY,
-                        liner.pt(0).fX, liner.pt(0).fY,
-                        locals.pt(0).fX, locals.pt(0).fY, quadT, cubicT, dist.length(), qxc);
-#else
-                    SkDebugf("qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n",
-                        quadT, cubicT, dist.length(), qxc);
-                    SkDebugf("<div id=\"slop%d\">\n", ++slopCount);
-                    SkDebugf("{{{%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}}}\n"
-                        "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n",
-                        cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY,
-                        cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY,
-                        nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY,
-                        nearby[2].fX, nearby[2].fY,
-                        liner.pt(0).fX, liner.pt(0).fY,
-                        locals.pt(0).fX, locals.pt(0).fY);
-                    SkDebugf("</div>\n\n");
-#endif
-                }
-                ++foundOne;
-            }
-            tStart = tEnd;
-        }
-        if (++foundOne >= 100000) {
-            break;
-        }
-    }
-#if 01
-    SkDebugf("slopCount=%d\n", slopCount);
-    int max = 100;
-    while (results[max] == 0) {
-        --max;
-    }
-    for (int i = 0; i <= max; ++i) {
-        if (i > 0 && i % 10 == 0) {
-            SkDebugf("\n");
-        }
-        SkDebugf("%d ", results[i]);
-    }
-    SkDebugf("min\n");
-    for (int i = 0; i <= max; ++i) {
-        if (i > 0 && i % 10 == 0) {
-            SkDebugf("\n");
-        }
-        SkDebugf("%1.9g ", minCross[i]);
-    }
-    SkDebugf("max\n");
-    for (int i = 0; i <= max; ++i) {
-        if (i > 0 && i % 10 == 0) {
-            SkDebugf("\n");
-        }
-        SkDebugf("%1.9g ", maxCross[i]);
-    }
-    SkDebugf("avg\n");
-    for (int i = 0; i <= max; ++i) {
-        if (i > 0 && i % 10 == 0) {
-            SkDebugf("\n");
-        }
-        SkDebugf("%1.9g ", sumCross[i] / results[i]);
-    }
-#else
-    for (int i = 1; i < slopCount; ++i) {
-        SkDebugf("        slop%d,\n", i);
-    }
-#endif
-    SkDebugf("\n");
-}
diff --git a/tests/PathOpsCubicReduceOrderTest.cpp b/tests/PathOpsCubicReduceOrderTest.cpp
index dc779b8..6b5cd9b 100644
--- a/tests/PathOpsCubicReduceOrderTest.cpp
+++ b/tests/PathOpsCubicReduceOrderTest.cpp
@@ -172,7 +172,7 @@
     for (index = firstQuadraticPointTest; index < quadraticPoints_count; ++index) {
         const SkDQuad& quad = quadraticPoints[index];
         SkASSERT(ValidQuad(quad));
-        SkDCubic cubic = quad.toCubic();
+        SkDCubic cubic = quad.debugToCubic();
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order != 1) {
             SkDebugf("[%d] point quad order=%d\n", static_cast<int>(index), order);
@@ -182,7 +182,7 @@
     for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) {
         const SkDQuad& quad = quadraticLines[index];
         SkASSERT(ValidQuad(quad));
-        SkDCubic cubic = quad.toCubic();
+        SkDCubic cubic = quad.debugToCubic();
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order != 2) {
             SkDebugf("[%d] line quad order=%d\n", static_cast<int>(index), order);
@@ -192,7 +192,7 @@
     for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) {
         const SkDQuad& quad = quadraticModEpsilonLines[index];
         SkASSERT(ValidQuad(quad));
-        SkDCubic cubic = quad.toCubic();
+        SkDCubic cubic = quad.debugToCubic();
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics);
         if (order != 3) {
             SkDebugf("[%d] line mod quad order=%d\n", static_cast<int>(index), order);
diff --git a/tests/PathOpsCubicToQuadsTest.cpp b/tests/PathOpsCubicToQuadsTest.cpp
deleted file mode 100644
index ab22a83..0000000
--- a/tests/PathOpsCubicToQuadsTest.cpp
+++ /dev/null
@@ -1,198 +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.
- */
-#include "PathOpsCubicIntersectionTestData.h"
-#include "PathOpsQuadIntersectionTestData.h"
-#include "PathOpsTestCommon.h"
-#include "SkGeometry.h"
-#include "SkIntersections.h"
-#include "SkPathOpsRect.h"
-#include "SkReduceOrder.h"
-#include "Test.h"
-
-static void test(skiatest::Reporter* reporter, const SkDCubic* cubics, const char* name,
-                 int firstTest, size_t testCount) {
-    for (size_t index = firstTest; index < testCount; ++index) {
-        const SkDCubic& cubic = cubics[index];
-        SkASSERT(ValidCubic(cubic));
-        double precision = cubic.calcPrecision();
-        SkTArray<SkDQuad, true> quads;
-        CubicToQuads(cubic, precision, quads);
-        if (quads.count() != 1 && quads.count() != 2) {
-            SkDebugf("%s [%d] cubic to quadratics failed count=%d\n", name, static_cast<int>(index),
-                    quads.count());
-        }
-        REPORTER_ASSERT(reporter, quads.count() == 1);
-    }
-}
-
-static void test(skiatest::Reporter* reporter, const SkDQuad* quadTests, const char* name,
-                 int firstTest, size_t testCount) {
-    for (size_t index = firstTest; index < testCount; ++index) {
-        const SkDQuad& quad = quadTests[index];
-        SkASSERT(ValidQuad(quad));
-        SkDCubic cubic = quad.toCubic();
-        double precision = cubic.calcPrecision();
-        SkTArray<SkDQuad, true> quads;
-        CubicToQuads(cubic, precision, quads);
-        if (quads.count() != 1 && quads.count() != 2) {
-            SkDebugf("%s [%d] cubic to quadratics failed count=%d\n", name, static_cast<int>(index),
-                    quads.count());
-        }
-        REPORTER_ASSERT(reporter, quads.count() <= 2);
-    }
-}
-
-static void testC(skiatest::Reporter* reporter, const SkDCubic* cubics, const char* name,
-                  int firstTest, size_t testCount) {
-    // test if computed line end points are valid
-    for (size_t index = firstTest; index < testCount; ++index) {
-        const SkDCubic& cubic = cubics[index];
-        SkASSERT(ValidCubic(cubic));
-        double precision = cubic.calcPrecision();
-        SkTArray<SkDQuad, true> quads;
-        CubicToQuads(cubic, precision, quads);
-        if (!AlmostEqualUlps(cubic[0].fX, quads[0][0].fX)
-                || !AlmostEqualUlps(cubic[0].fY, quads[0][0].fY)) {
-            SkDebugf("[%d] unmatched start\n", static_cast<int>(index));
-            REPORTER_ASSERT(reporter, 0);
-        }
-        int last = quads.count() - 1;
-        if (!AlmostEqualUlps(cubic[3].fX, quads[last][2].fX)
-                || !AlmostEqualUlps(cubic[3].fY, quads[last][2].fY)) {
-            SkDebugf("[%d] unmatched end\n", static_cast<int>(index));
-            REPORTER_ASSERT(reporter, 0);
-        }
-    }
-}
-
-static void testC(skiatest::Reporter* reporter, const SkDCubic(* cubics)[2], const char* name,
-                  int firstTest, size_t testCount) {
-    for (size_t index = firstTest; index < testCount; ++index) {
-        for (int idx2 = 0; idx2 < 2; ++idx2) {
-            const SkDCubic& cubic = cubics[index][idx2];
-            SkASSERT(ValidCubic(cubic));
-            double precision = cubic.calcPrecision();
-            SkTArray<SkDQuad, true> quads;
-            CubicToQuads(cubic, precision, quads);
-            if (!AlmostEqualUlps(cubic[0].fX, quads[0][0].fX)
-                    || !AlmostEqualUlps(cubic[0].fY, quads[0][0].fY)) {
-                SkDebugf("[%d][%d] unmatched start\n", static_cast<int>(index), idx2);
-                REPORTER_ASSERT(reporter, 0);
-            }
-            int last = quads.count() - 1;
-            if (!AlmostEqualUlps(cubic[3].fX, quads[last][2].fX)
-                    || !AlmostEqualUlps(cubic[3].fY, quads[last][2].fY)) {
-                SkDebugf("[%d][%d] unmatched end\n", static_cast<int>(index), idx2);
-                REPORTER_ASSERT(reporter, 0);
-            }
-        }
-    }
-}
-
-DEF_TEST(CubicToQuads, reporter) {
-    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(reporter, pointDegenerates, "pointDegenerates", firstPointDegeneratesTest,
-            pointDegenerates_count);
-    testC(reporter, notPointDegenerates, "notPointDegenerates", firstNotPointDegeneratesTest,
-            notPointDegenerates_count);
-    test(reporter, lines, "lines", firstLinesTest, lines_count);
-    testC(reporter, notLines, "notLines", firstNotLinesTest, notLines_count);
-    testC(reporter, modEpsilonLines, "modEpsilonLines", firstModEpsilonTest, modEpsilonLines_count);
-    test(reporter, lessEpsilonLines, "lessEpsilonLines", firstLessEpsilonTest,
-            lessEpsilonLines_count);
-    test(reporter, negEpsilonLines, "negEpsilonLines", firstNegEpsilonTest, negEpsilonLines_count);
-    test(reporter, quadraticLines, "quadraticLines", firstQuadraticLineTest, quadraticLines_count);
-    test(reporter, quadraticModEpsilonLines, "quadraticModEpsilonLines", firstQuadraticModLineTest,
-            quadraticModEpsilonLines_count);
-    testC(reporter, lines, "computed lines", firstComputedLinesTest, lines_count);
-    testC(reporter, tests, "computed tests", firstComputedCubicsTest, tests_count);
-}
-
-static SkDCubic locals[] = {
-    {{{0, 1}, {1.9274705288631189e-19, 1.0000000000000002},
-            {0.0017190297609673323, 0.99828097023903239},
-            {0.0053709083094631276, 0.99505672974365911}}},
-    {{{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 = SK_ARRAY_COUNT(locals);
-
-#define DEBUG_CRASH 0
-#define TEST_AVERAGE_END_POINTS 0  // must take const off to test
-extern const bool AVERAGE_END_POINTS;
-
-static void oneOff(skiatest::Reporter* reporter, size_t x) {
-    const SkDCubic& cubic = locals[x];
-    SkASSERT(ValidCubic(cubic));
-    const SkPoint skcubic[4] = {
-            {static_cast<float>(cubic[0].fX), static_cast<float>(cubic[0].fY)},
-            {static_cast<float>(cubic[1].fX), static_cast<float>(cubic[1].fY)},
-            {static_cast<float>(cubic[2].fX), static_cast<float>(cubic[2].fY)},
-            {static_cast<float>(cubic[3].fX), static_cast<float>(cubic[3].fY)}};
-    SkScalar skinflect[2];
-    int skin = SkFindCubicInflections(skcubic, skinflect);
-    if (false) SkDebugf("%s %d %1.9g\n", __FUNCTION__, skin, skinflect[0]);
-    SkTArray<SkDQuad, true> quads;
-    double precision = cubic.calcPrecision();
-    CubicToQuads(cubic, precision, quads);
-    if (false) SkDebugf("%s quads=%d\n", __FUNCTION__, quads.count());
-}
-
-DEF_TEST(CubicsToQuadratics_OneOff_Loop, reporter) {
-    for (size_t x = 0; x < localsCount; ++x) {
-        oneOff(reporter, x);
-    }
-}
-
-DEF_TEST(CubicsToQuadratics_OneOff_Single, reporter) {
-    oneOff(reporter, 0);
-}
diff --git a/tests/PathOpsDCubicTest.cpp b/tests/PathOpsDCubicTest.cpp
index 422236d..4b5e7b4 100644
--- a/tests/PathOpsDCubicTest.cpp
+++ b/tests/PathOpsDCubicTest.cpp
@@ -8,22 +8,16 @@
 #include "SkPathOpsCubic.h"
 #include "Test.h"
 
-static const SkDCubic tests[] = {
-    {{{2, 0}, {3, 1}, {2, 2}, {1, 1}}},
-    {{{3, 1}, {2, 2}, {1, 1}, {2, 0}}},
-    {{{3, 0}, {2, 1}, {3, 2}, {1, 1}}},
+static const SkDCubic hullTests[] = {
+{{{2.6250000819563866, 2.3750000223517418}, {2.833333432674408, 2.3333333432674408}, {3.1111112236976624, 2.3333333134651184}, {3.4074075222015381, 2.3333332538604736}}},
 };
 
-static const size_t tests_count = SK_ARRAY_COUNT(tests);
+static const size_t hullTests_count = SK_ARRAY_COUNT(hullTests);
 
-DEF_TEST(PathOpsDCubic, reporter) {
-    for (size_t index = 0; index < tests_count; ++index) {
-        const SkDCubic& cubic = tests[index];
-        SkASSERT(ValidCubic(cubic));
-        bool result = cubic.clockwise();
-        if (!result) {
-            SkDebugf("%s [%d] expected clockwise\n", __FUNCTION__, index);
-            REPORTER_ASSERT(reporter, 0);
-        }
+DEF_TEST(PathOpsCubicHull, reporter) {
+    for (size_t index = 0; index < hullTests_count; ++index) {
+        const SkDCubic& cubic = hullTests[index];
+        char order[4];
+        cubic.convexHull(order);
     }
 }
diff --git a/tests/PathOpsDLineTest.cpp b/tests/PathOpsDLineTest.cpp
index dd86dd3..bfad134 100644
--- a/tests/PathOpsDLineTest.cpp
+++ b/tests/PathOpsDLineTest.cpp
@@ -43,10 +43,6 @@
             SkDebugf("%s [%d] expected left\n", __FUNCTION__, index);
             REPORTER_ASSERT(reporter, 0);
         }
-        line2 = line.subDivide(1, 0);
-        REPORTER_ASSERT(reporter, line[0] == line2[1] && line[1] == line2[0]);
-        line2 = SkDLine::SubDivide(pts, 1, 0);
-        REPORTER_ASSERT(reporter, line[0] == line2[1] && line[1] == line2[0]);
         SkDPoint mid = line.ptAtT(.5);
         REPORTER_ASSERT(reporter, approximately_equal((line[0].fX + line[1].fX) / 2, mid.fX));
         REPORTER_ASSERT(reporter, approximately_equal((line[0].fY + line[1].fY) / 2, mid.fY));
diff --git a/tests/PathOpsDPointTest.cpp b/tests/PathOpsDPointTest.cpp
index 186d691..e197d5d 100644
--- a/tests/PathOpsDPointTest.cpp
+++ b/tests/PathOpsDPointTest.cpp
@@ -38,7 +38,6 @@
         REPORTER_ASSERT(reporter, p == pt);
         REPORTER_ASSERT(reporter, p.approximatelyEqual(sPt));
         REPORTER_ASSERT(reporter, p.roughlyEqual(pt));
-        REPORTER_ASSERT(reporter, p.moreRoughlyEqual(pt));
         p.fX = p.fY = 0;
         REPORTER_ASSERT(reporter, p.fX == 0 && p.fY == 0);
         REPORTER_ASSERT(reporter, p.approximatelyZero());
diff --git a/tests/PathOpsDQuadTest.cpp b/tests/PathOpsDQuadTest.cpp
deleted file mode 100644
index bd29ff1..0000000
--- a/tests/PathOpsDQuadTest.cpp
+++ /dev/null
@@ -1,63 +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.
- */
-#include "PathOpsTestCommon.h"
-#include "SkPath.h"
-#include "SkPathOpsQuad.h"
-#include "SkRRect.h"
-#include "Test.h"
-
-static const SkDQuad tests[] = {
-    {{{1, 1}, {2, 1}, {0, 2}}},
-    {{{0, 0}, {1, 1}, {3, 1}}},
-    {{{2, 0}, {1, 1}, {2, 2}}},
-    {{{4, 0}, {0, 1}, {4, 2}}},
-    {{{0, 0}, {0, 1}, {1, 1}}},
-};
-
-static const SkDPoint inPoint[]= {
-    {1,   1.2},
-    {1,   0.8},
-    {1.8, 1},
-    {1.5, 1},
-    {0.4999, 0.5},  // was 0.5, 0.5; points on the hull are considered outside
-};
-
-static const SkDPoint outPoint[]= {
-    {1,   1.6},
-    {1,   1.5},
-    {2.2, 1},
-    {1.5, 1.5},
-    {1.1, 0.5},
-};
-
-static const size_t tests_count = SK_ARRAY_COUNT(tests);
-
-DEF_TEST(PathOpsDQuad, reporter) {
-    for (size_t index = 0; index < tests_count; ++index) {
-        const SkDQuad& quad = tests[index];
-        SkASSERT(ValidQuad(quad));
-        bool result = quad.pointInHull(inPoint[index]);
-        if (!result) {
-            SkDebugf("%s [%d] expected in hull\n", __FUNCTION__, index);
-            REPORTER_ASSERT(reporter, 0);
-        }
-        result = quad.pointInHull(outPoint[index]);
-        if (result) {
-            SkDebugf("%s [%d] expected outside hull\n", __FUNCTION__, index);
-            REPORTER_ASSERT(reporter, 0);
-        }
-    }
-}
-
-DEF_TEST(PathOpsRRect, reporter) {
-    SkPath path;
-    SkRRect rRect;
-    SkRect rect = {135, 143, 250, 177};
-    SkVector radii[4] = {{8, 8}, {8, 8}, {0, 0}, {0, 0}};
-    rRect.setRectRadii(rect, radii);
-    path.addRRect(rRect);
-}
diff --git a/tests/PathOpsDRectTest.cpp b/tests/PathOpsDRectTest.cpp
index 874e82b..70d39d1 100644
--- a/tests/PathOpsDRectTest.cpp
+++ b/tests/PathOpsDRectTest.cpp
@@ -11,15 +11,6 @@
 #include "SkPathOpsRect.h"
 #include "Test.h"
 
-static const SkDLine lineTests[] = {
-    {{{2, 1}, {2, 1}}},
-    {{{2, 1}, {1, 1}}},
-    {{{2, 1}, {2, 2}}},
-    {{{1, 1}, {2, 2}}},
-    {{{3, 0}, {2, 1}}},
-    {{{3, 2}, {1, 1}}},
-};
-
 static const SkDQuad quadTests[] = {
     {{{1, 1}, {2, 1}, {0, 2}}},
     {{{0, 0}, {1, 1}, {3, 1}}},
@@ -34,44 +25,31 @@
     {{{3, 0}, {2, 1}, {3, 2}, {1, 1}}},
 };
 
-static const size_t lineTests_count = SK_ARRAY_COUNT(lineTests);
 static const size_t quadTests_count = SK_ARRAY_COUNT(quadTests);
 static const size_t cubicTests_count = SK_ARRAY_COUNT(cubicTests);
 
+static void setRawBounds(const SkDQuad& quad, SkDRect* rect) {
+    rect->set(quad[0]);
+    rect->add(quad[1]);
+    rect->add(quad[2]);
+}
+
+static void setRawBounds(const SkDCubic& cubic, SkDRect* rect) {
+    rect->set(cubic[0]);
+    rect->add(cubic[1]);
+    rect->add(cubic[2]);
+    rect->add(cubic[3]);
+}
+
 DEF_TEST(PathOpsDRect, reporter) {
     size_t index;
     SkDRect rect, rect2;
-    for (index = 0; index < lineTests_count; ++index) {
-        const SkDLine& line = lineTests[index];
-        SkASSERT(ValidLine(line));
-        rect.setBounds(line);
-        REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(line[0].fX, line[1].fX));
-        REPORTER_ASSERT(reporter, rect.fTop == SkTMin(line[0].fY, line[1].fY));
-        REPORTER_ASSERT(reporter, rect.fRight == SkTMax(line[0].fX, line[1].fX));
-        REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(line[0].fY, line[1].fY));
-        rect2.set(line[0]);
-        rect2.add(line[1]);
-        REPORTER_ASSERT(reporter, rect2.fLeft == SkTMin(line[0].fX, line[1].fX));
-        REPORTER_ASSERT(reporter, rect2.fTop == SkTMin(line[0].fY, line[1].fY));
-        REPORTER_ASSERT(reporter, rect2.fRight == SkTMax(line[0].fX, line[1].fX));
-        REPORTER_ASSERT(reporter, rect2.fBottom == SkTMax(line[0].fY, line[1].fY));
-        REPORTER_ASSERT(reporter, rect.contains(line[0]));
-        REPORTER_ASSERT(reporter, rect.intersects(&rect2));
-    }
     for (index = 0; index < quadTests_count; ++index) {
         const SkDQuad& quad = quadTests[index];
         SkASSERT(ValidQuad(quad));
-        rect.setRawBounds(quad);
-        REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(quad[0].fX,
-                SkTMin(quad[1].fX, quad[2].fX)));
-        REPORTER_ASSERT(reporter, rect.fTop == SkTMin(quad[0].fY,
-                SkTMin(quad[1].fY, quad[2].fY)));
-        REPORTER_ASSERT(reporter, rect.fRight == SkTMax(quad[0].fX,
-                SkTMax(quad[1].fX, quad[2].fX)));
-        REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(quad[0].fY,
-                SkTMax(quad[1].fY, quad[2].fY)));
+        setRawBounds(quad, &rect);
         rect2.setBounds(quad);
-        REPORTER_ASSERT(reporter, rect.intersects(&rect2));
+        REPORTER_ASSERT(reporter, rect.intersects(rect2));
         // FIXME: add a recursive box subdivision method to verify that tight bounds is correct
         SkDPoint leftTop = {rect2.fLeft, rect2.fTop};
         REPORTER_ASSERT(reporter, rect.contains(leftTop));
@@ -81,17 +59,9 @@
     for (index = 0; index < cubicTests_count; ++index) {
         const SkDCubic& cubic = cubicTests[index];
         SkASSERT(ValidCubic(cubic));
-        rect.setRawBounds(cubic);
-        REPORTER_ASSERT(reporter, rect.fLeft == SkTMin(cubic[0].fX,
-                SkTMin(cubic[1].fX, SkTMin(cubic[2].fX, cubic[3].fX))));
-        REPORTER_ASSERT(reporter, rect.fTop == SkTMin(cubic[0].fY,
-                SkTMin(cubic[1].fY, SkTMin(cubic[2].fY, cubic[3].fY))));
-        REPORTER_ASSERT(reporter, rect.fRight == SkTMax(cubic[0].fX,
-                SkTMax(cubic[1].fX, SkTMax(cubic[2].fX, cubic[3].fX))));
-        REPORTER_ASSERT(reporter, rect.fBottom == SkTMax(cubic[0].fY,
-                SkTMax(cubic[1].fY, SkTMax(cubic[2].fY, cubic[3].fY))));
+        setRawBounds(cubic, &rect);
         rect2.setBounds(cubic);
-        REPORTER_ASSERT(reporter, rect.intersects(&rect2));
+        REPORTER_ASSERT(reporter, rect.intersects(rect2));
         // FIXME: add a recursive box subdivision method to verify that tight bounds is correct
         SkDPoint leftTop = {rect2.fLeft, rect2.fTop};
         REPORTER_ASSERT(reporter, rect.contains(leftTop));
diff --git a/tests/PathOpsDTriangleTest.cpp b/tests/PathOpsDTriangleTest.cpp
deleted file mode 100644
index b5e2d41..0000000
--- a/tests/PathOpsDTriangleTest.cpp
+++ /dev/null
@@ -1,70 +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.
- */
-#include "PathOpsTestCommon.h"
-#include "SkPathOpsTriangle.h"
-#include "Test.h"
-
-static const SkDTriangle tests[] = {
-    {{{2, 0}, {3, 1}, {2, 2}}},
-    {{{3, 1}, {2, 2}, {1, 1}}},
-    {{{3, 0}, {2, 1}, {3, 2}}},
-};
-
-static const SkDPoint inPoint[] = {
-    {2.5, 1},
-    {2, 1.5},
-    {2.5, 1},
-};
-
-static const SkDPoint outPoint[] = {
-    {3, 0},
-    {2.5, 2},
-    {2.5, 2},
-};
-
-static const size_t tests_count = SK_ARRAY_COUNT(tests);
-
-DEF_TEST(PathOpsTriangleUtilities, reporter) {
-    for (size_t index = 0; index < tests_count; ++index) {
-        const SkDTriangle& triangle = tests[index];
-        SkASSERT(ValidTriangle(triangle));
-        bool result = triangle.contains(inPoint[index]);
-        if (!result) {
-            SkDebugf("%s [%d] expected point in triangle\n", __FUNCTION__, index);
-            REPORTER_ASSERT(reporter, 0);
-        }
-        result = triangle.contains(outPoint[index]);
-        if (result) {
-            SkDebugf("%s [%d] expected point outside triangle\n", __FUNCTION__, index);
-            REPORTER_ASSERT(reporter, 0);
-        }
-    }
-}
-
-static const SkDTriangle oneOff[] = {
-    {{{271.03291625750461, 5.0402503630087025e-05}, {275.21652430019037, 3.6997300650817753},
-      {279.25839233398438, 7.7416000366210938}}},
-
-    {{{271.03291625750461, 5.0402503617874572e-05}, {275.21652430019037, 3.6997300650817877},
-      {279.25839233398438, 7.7416000366210938}}}
-};
-
-static const size_t oneOff_count = SK_ARRAY_COUNT(oneOff);
-
-DEF_TEST(PathOpsTriangleOneOff, reporter) {
-    for (size_t index = 0; index < oneOff_count; ++index) {
-        const SkDTriangle& triangle = oneOff[index];
-        SkASSERT(ValidTriangle(triangle));
-        for (int inner = 0; inner < 3; ++inner) {
-            bool result = triangle.contains(triangle.fPts[inner]);
-            if (result) {
-                SkDebugf("%s [%d][%d] point on triangle is not in\n", __FUNCTION__, index, inner);
-                REPORTER_ASSERT(reporter, 0);
-            }
-        }
-    }
-}
diff --git a/tests/PathOpsDVectorTest.cpp b/tests/PathOpsDVectorTest.cpp
index ab291b2..583a868 100644
--- a/tests/PathOpsDVectorTest.cpp
+++ b/tests/PathOpsDVectorTest.cpp
@@ -28,8 +28,6 @@
         SkASSERT(ValidVector(v2));
         v1 += v2;
         REPORTER_ASSERT(reporter, v1.fX == 0 && v1.fY == 0);
-        SkDPoint p = tests[index + 1] + v2;
-        REPORTER_ASSERT(reporter, p == tests[index]);
         v2 -= v2;
         REPORTER_ASSERT(reporter, v2.fX == 0 && v2.fY == 0);
         v1 = tests[index + 1] - tests[index];
diff --git a/tests/PathOpsDebug.cpp b/tests/PathOpsDebug.cpp
index c4fbbfa..95f06e5 100755
--- a/tests/PathOpsDebug.cpp
+++ b/tests/PathOpsDebug.cpp
@@ -1,5 +1,15 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "PathOpsTSectDebug.h"
+#include "SkOpCoincidence.h"
 #include "SkOpContour.h"
 #include "SkIntersectionHelper.h"
+#include "SkMutex.h"
 #include "SkOpSegment.h"
 #include "SkString.h"
 
@@ -23,136 +33,6 @@
     SkDebugf("SkBits2Float(0x%08x)", SkFloat2Bits(x));
 }
 
-#if DEBUG_SHOW_TEST_NAME
-
-static void output_scalar(SkScalar num) {
-    if (num == (int) num) {
-        SkDebugf("%d", (int) num);
-    } else {
-        SkString str;
-        str.printf("%1.9g", num);
-        int width = (int) str.size();
-        const char* cStr = str.c_str();
-        while (cStr[width - 1] == '0') {
-            --width;
-        }
-        str.resize(width);
-        SkDebugf("%sf", str.c_str());
-    }
-}
-
-static void output_points(const SkPoint* pts, int count) {
-    for (int index = 0; index < count; ++index) {
-        output_scalar(pts[index].fX);
-        SkDebugf(", ");
-        output_scalar(pts[index].fY);
-        if (index + 1 < count) {
-            SkDebugf(", ");
-        }
-    }
-    SkDebugf(");\n");
-}
-
-static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
-    uint8_t verb;
-    SkPoint pts[4];
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-        switch (verb) {
-            case SkPath::kMove_Verb:
-                SkDebugf("    %s.moveTo(", pathName);
-                output_points(&pts[0], 1);
-                continue;
-            case SkPath::kLine_Verb:
-                SkDebugf("    %s.lineTo(", pathName);
-                output_points(&pts[1], 1);
-                break;
-            case SkPath::kQuad_Verb:
-                SkDebugf("    %s.quadTo(", pathName);
-                output_points(&pts[1], 2);
-                break;
-            case SkPath::kCubic_Verb:
-                SkDebugf("    %s.cubicTo(", pathName);
-                output_points(&pts[1], 3);
-                break;
-            case SkPath::kClose_Verb:
-                SkDebugf("    %s.close();\n", pathName);
-                break;
-            default:
-                SkDEBUGFAIL("bad verb");
-                return;
-        }
-    }
-}
-
-static const char* gFillTypeStr[] = {
-    "kWinding_FillType",
-    "kEvenOdd_FillType",
-    "kInverseWinding_FillType",
-    "kInverseEvenOdd_FillType"
-};
-
-void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
-    SkPath::RawIter iter(path);
-#define SUPPORT_RECT_CONTOUR_DETECTION 0
-#if SUPPORT_RECT_CONTOUR_DETECTION
-    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;
-    }
-#endif
-    SkPath::FillType fillType = path.getFillType();
-    SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
-    if (includeDeclaration) {
-        SkDebugf("    SkPath %s;\n", name);
-    }
-    SkDebugf("    %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
-    iter.setPath(path);
-    showPathContours(iter, name);
-}
-
-static void show_function_header(const char* functionName) {
-    SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
-    if (strcmp("skphealth_com76", functionName) == 0) {
-        SkDebugf("found it\n");
-    }
-}
-
-static const char* gOpStrs[] = {
-    "kDifference_PathOp",
-    "kIntersect_PathOp",
-    "kUnion_PathOp",
-    "kXor_PathOp",
-    "kReverseDifference_PathOp",
-};
-
-static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
-    SkDebugf("    testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
-    SkDebugf("}\n");
-}
-
-SK_DECLARE_STATIC_MUTEX(gTestMutex);
-
-void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
-        const char* testName) {
-    SkAutoMutexAcquire ac(gTestMutex);
-    show_function_header(testName);
-    ShowOnePath(a, "path", true);
-    ShowOnePath(b, "pathB", true);
-    show_op(shapeOp, "path", "pathB");
-}
-#endif
-
 // if not defined by PathOpsDebug.cpp ...
 #if !defined SK_DEBUG && FORCE_RELEASE
 bool SkPathOpsDebug::ValidWind(int wind) {
@@ -168,236 +48,96 @@
 }
 #endif
 
-void SkOpAngle::dump() const {
-    dumpOne(true);
-    SkDebugf("\n");
+void SkDConic::dump() const {
+    dumpInner();
+    SkDebugf("},\n");
 }
 
-void SkOpAngle::dumpOne(bool functionHeader) const {
-//    fSegment->debugValidate();
-    const SkOpSpan& mSpan = fSegment->span(SkMin32(fStart, fEnd));
-    if (functionHeader) {
-        SkDebugf("%s ", __FUNCTION__);
-    }
-    SkDebugf("[%d", fSegment->debugID());
-    SkDebugf("/%d", debugID());
-    SkDebugf("] next=");
-    if (fNext) {
-        SkDebugf("%d", fNext->fSegment->debugID());
-        SkDebugf("/%d", fNext->debugID());
-    } else {
-        SkDebugf("?");
-    }
-    SkDebugf(" sect=%d/%d ", fSectorStart, fSectorEnd);
-    SkDebugf(" s=%1.9g [%d] e=%1.9g [%d]", fSegment->span(fStart).fT, fStart,
-            fSegment->span(fEnd).fT, fEnd);
-    SkDebugf(" sgn=%d windVal=%d", sign(), mSpan.fWindValue);
-
-    SkDebugf(" windSum=");
-    SkPathOpsDebug::WindingPrintf(mSpan.fWindSum);
-    if (mSpan.fOppValue != 0 || mSpan.fOppSum != SK_MinS32) {
-        SkDebugf(" oppVal=%d", mSpan.fOppValue);
-        SkDebugf(" oppSum=");
-        SkPathOpsDebug::WindingPrintf(mSpan.fOppSum);
-    }
-    if (mSpan.fDone) {
-        SkDebugf(" done");
-    }
-    if (unorderable()) {
-        SkDebugf(" unorderable");
-    }
-    if (small()) {
-        SkDebugf(" small");
-    }
-    if (mSpan.fTiny) {
-        SkDebugf(" tiny");
-    }
-    if (fSegment->operand()) {
-        SkDebugf(" operand");
-    }
-    if (fStop) {
-        SkDebugf(" stop");
-    }
+void SkDConic::dumpID(int id) const {
+    dumpInner();
+    SkDebugf("} id=%d\n", id);
 }
 
-void SkOpAngle::dumpTo(const SkOpSegment* segment, const SkOpAngle* to) const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    const char* indent = "";
-    do {
-        SkDebugf("%s", indent);
-        next->dumpOne(false);
-        if (segment == next->fSegment) {
-            if (this == fNext) {
-                SkDebugf(" << from");
-            }
-            if (to == fNext) {
-                SkDebugf(" << to");
-            }
-        }
-        SkDebugf("\n");
-        indent = "           ";
-        next = next->fNext;
-    } while (next && next != first);
-}
-
-void SkOpAngle::dumpLoop() const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    do {
-        next->dumpOne(false);
-        SkDebugf("\n");
-        next = next->fNext;
-    } while (next && next != first);
-}
-
-void SkOpAngle::dumpPartials() const {
-    const SkOpAngle* first = this;
-    const SkOpAngle* next = this;
-    do {
-        next->fCurvePart.dumpNumber();
-        next = next->fNext;
-    } while (next && next != first);
-}
-
-void SkOpAngleSet::dump() const {
-    // FIXME: unimplemented
-/* This requires access to the internal SkChunkAlloc data
-   Defer implementing this until it is needed for debugging
-*/
-    SkASSERT(0);
-}
-
-void SkOpContour::dump() const {
-    int segmentCount = fSegments.count();
-    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
-    for (int test = 0; test < segmentCount; ++test) {
-        SkDebugf("  [%d] ((SkOpSegment*) 0x%p) [%d]\n", test, &fSegments[test],
-                fSegments[test].debugID());
-    }
-}
-
-void SkOpContour::dumpAngles() const {
-    int segmentCount = fSegments.count();
-    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
-    for (int test = 0; test < segmentCount; ++test) {
-        SkDebugf("  [%d] ", test);
-        fSegments[test].dumpAngles();
-    }
-}
-
-void SkOpContour::dumpCoincidence(const SkCoincidence& coin) const {
-    int thisIndex = coin.fSegments[0];
-    const SkOpSegment& s1 = fSegments[thisIndex];
-    int otherIndex = coin.fSegments[1];
-    const SkOpSegment& s2 = coin.fOther->fSegments[otherIndex];
-    SkDebugf("((SkOpSegment*) 0x%p) [%d]  ((SkOpSegment*) 0x%p) [%d]\n", &s1, s1.debugID(),
-            &s2, s2.debugID());
-    for (int index = 0; index < 2; ++index) {
-        SkDebugf("    {%1.9gf, %1.9gf}", coin.fPts[0][index].fX, coin.fPts[0][index].fY);
-        if (coin.fNearly[index]) {
-            SkDebugf("    {%1.9gf, %1.9gf}", coin.fPts[1][index].fX, coin.fPts[1][index].fY);
-        }
-        SkDebugf("  seg1t=%1.9g seg2t=%1.9g\n", coin.fTs[0][index], coin.fTs[1][index]);
-    }
-}
-
-void SkOpContour::dumpCoincidences() const {
-    int count = fCoincidences.count();
-    if (count > 0) {
-        SkDebugf("fCoincidences count=%d\n", count);
-        for (int test = 0; test < count; ++test) {
-            dumpCoincidence(fCoincidences[test]);
-        }
-    }
-    count = fPartialCoincidences.count();
-    if (count == 0) {
-        return;
-    }
-    SkDebugf("fPartialCoincidences count=%d\n", count);
-    for (int test = 0; test < count; ++test) {
-        dumpCoincidence(fPartialCoincidences[test]);
-    }
-}
-
-void SkOpContour::dumpPt(int index) const {
-    int segmentCount = fSegments.count();
-    for (int test = 0; test < segmentCount; ++test) {
-        const SkOpSegment& segment = fSegments[test];
-        if (segment.debugID() == index) {
-            fSegments[test].dumpPts();
-        }
-    }
-}
-
-void SkOpContour::dumpPts() const {
-    int segmentCount = fSegments.count();
-    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
-    for (int test = 0; test < segmentCount; ++test) {
-        SkDebugf("  [%d] ", test);
-        fSegments[test].dumpPts();
-    }
-}
-
-void SkOpContour::dumpSpan(int index) const {
-    int segmentCount = fSegments.count();
-    for (int test = 0; test < segmentCount; ++test) {
-        const SkOpSegment& segment = fSegments[test];
-        if (segment.debugID() == index) {
-            fSegments[test].dumpSpans();
-        }
-    }
-}
-
-void SkOpContour::dumpSpans() const {
-    int segmentCount = fSegments.count();
-    SkDebugf("((SkOpContour*) 0x%p) [%d]\n", this, debugID());
-    for (int test = 0; test < segmentCount; ++test) {
-        SkDebugf("  [%d] ", test);
-        fSegments[test].dumpSpans();
-    }
-}
-
-void SkDCubic::dump() const {
+void SkDConic::dumpInner() const {
     SkDebugf("{{");
     int index = 0;
     do {
         fPts[index].dump();
         SkDebugf(", ");
-    } while (++index < 3);
+    } while (++index < 2);
     fPts[index].dump();
-    SkDebugf("}}\n");
+    SkDebugf("}, %1.9g", fWeight);
 }
 
-void SkDCubic::dumpNumber() const {
+void SkDCubic::dump() const {
+    this->dumpInner();
+    SkDebugf("}},\n");
+}
+
+void SkDCubic::dumpID(int id) const {
+    this->dumpInner();
+    SkDebugf("}} id=%d\n", id);
+}
+
+static inline bool double_is_NaN(double x) { return x != x; }
+
+void SkDCubic::dumpInner() const {
     SkDebugf("{{");
     int index = 0;
-    bool dumpedOne = false;
     do {
-        if (!(fPts[index].fX == fPts[index].fX && fPts[index].fY == fPts[index].fY)) {
-            continue;
-        }
-        if (dumpedOne) {
+        if (index != 0) {
+            if (double_is_NaN(fPts[index].fX) && double_is_NaN(fPts[index].fY)) {
+                return;
+            }
             SkDebugf(", ");
         }
         fPts[index].dump();
-        dumpedOne = true;
     } while (++index < 3);
-    if (fPts[index].fX == fPts[index].fX && fPts[index].fY == fPts[index].fY) {
-        if (dumpedOne) {
-            SkDebugf(", ");
-        }
-        fPts[index].dump();
+    if (double_is_NaN(fPts[index].fX) && double_is_NaN(fPts[index].fY)) {
+        return;
     }
-    SkDebugf("}}\n");
+    SkDebugf(", ");
+    fPts[index].dump();
+}
+
+void SkDCurve::dumpID(int id) const {
+#ifndef SK_RELEASE
+    switch(fVerb) {
+        case SkPath::kLine_Verb:
+            fLine.dumpID(id);
+            break;
+        case SkPath::kQuad_Verb:
+            fQuad.dumpID(id);
+            break;
+        case SkPath::kConic_Verb:
+            fConic.dumpID(id);
+            break;
+        case SkPath::kCubic_Verb:
+            fCubic.dumpID(id);
+            break;
+        default:
+            SkASSERT(0);
+    }
+#else
+    fCubic.dumpID(id);
+#endif
 }
 
 void SkDLine::dump() const {
+    this->dumpInner();
+    SkDebugf("}},\n");
+}
+
+void SkDLine::dumpID(int id) const {
+    this->dumpInner();
+    SkDebugf("}} id=%d\n", id);
+}
+
+void SkDLine::dumpInner() const {
     SkDebugf("{{");
     fPts[0].dump();
     SkDebugf(", ");
     fPts[1].dump();
-    SkDebugf("}}\n");
 }
 
 void SkDPoint::dump() const {
@@ -425,10 +165,16 @@
 }
 
 void SkDQuad::dump() const {
-    dumpComma("");
+    dumpInner();
+    SkDebugf("}},\n");
 }
 
-void SkDQuad::dumpComma(const char* comma) const {
+void SkDQuad::dumpID(int id) const {
+    dumpInner();
+    SkDebugf("}} id=%d\n", id);
+}
+
+void SkDQuad::dumpInner() const {
     SkDebugf("{{");
     int index = 0;
     do {
@@ -436,436 +182,506 @@
         SkDebugf(", ");
     } while (++index < 2);
     fPts[index].dump();
-    SkDebugf("}}%s\n", comma ? comma : "");
 }
 
-void SkIntersectionHelper::dump() const {
-    SkDPoint::Dump(pts()[0]);
-    SkDPoint::Dump(pts()[1]);
-    if (verb() >= SkPath::kQuad_Verb) {
-        SkDPoint::Dump(pts()[2]);
-    }
-    if (verb() >= SkPath::kCubic_Verb) {
-        SkDPoint::Dump(pts()[3]);
-    }
-}
-
-const SkTDArray<SkOpSpan>& SkOpSegment::debugSpans() const {
-    return fTs;
-}
-
-void SkOpSegment::dumpAngles() const {
-    SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", this, debugID());
-    const SkOpAngle* fromAngle = NULL;
-    const SkOpAngle* toAngle = NULL;
-    for (int index = 0; index < count(); ++index) {
-        const SkOpAngle* fAngle = fTs[index].fFromAngle;
-        const SkOpAngle* tAngle = fTs[index].fToAngle;
-        if (fromAngle == fAngle && toAngle == tAngle) {
-            continue;
+void SkIntersections::dump() const {
+    SkDebugf("used=%d of %d", fUsed, fMax);
+    for (int index = 0; index < fUsed; ++index) {
+        SkDebugf(" t=(%s%1.9g,%s%1.9g) pt=(%1.9g,%1.9g)",
+                fIsCoincident[0] & (1 << index) ? "*" : "", fT[0][index],
+                fIsCoincident[1] & (1 << index) ? "*" : "", fT[1][index],
+                fPt[index].fX, fPt[index].fY);
+        if (index < 2 && fNearlySame[index]) {
+            SkDebugf(" pt2=(%1.9g,%1.9g)",fPt2[index].fX, fPt2[index].fY);
         }
-        if (fAngle) {
-            SkDebugf("  [%d] from=%d ", index, fAngle->debugID());
-            fAngle->dumpTo(this, tAngle);
-        }
-        if (tAngle) {
-            SkDebugf("  [%d] to=%d   ", index, tAngle->debugID());
-            tAngle->dumpTo(this, fAngle);
-        }
-        fromAngle = fAngle;
-        toAngle = tAngle;
-    }
-}
-
-void SkOpSegment::dumpContour(int firstID, int lastID) const {
-    if (debugID() < 0) {
-        return;
-    }
-    const SkOpSegment* test = this - (debugID() - 1);
-    test += (firstID - 1);
-    const SkOpSegment* last = test + (lastID - firstID);
-    while (test <= last) {
-        test->dumpSpans();
-        ++test;
-    }
-}
-
-void SkOpSegment::dumpPts() const {
-    int last = SkPathOpsVerbToPoints(fVerb);
-    SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID());
-    int index = 0;
-    do {
-        SkDPoint::Dump(fPts[index]);
-        SkDebugf(", ");
-    } while (++index < last);
-    SkDPoint::Dump(fPts[index]);
-    SkDebugf("}}\n");
-}
-
-void SkOpSegment::dumpHexPts() const {
-    int last = SkPathOpsVerbToPoints(fVerb);
-    SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID());
-    int index = 0;
-    do {
-        SkDPoint::DumpHex(fPts[index]);
-        SkDebugf(", ");
-    } while (++index < last);
-    SkDPoint::DumpHex(fPts[index]);
-    SkDebugf("}}\n");
-}
-
-void SkOpSegment::dumpDPts() const {
-    int count = SkPathOpsVerbToPoints(fVerb);
-    SkDebugf("((SkOpSegment*) 0x%p) [%d] {{", this, debugID());
-    int index = 0;
-    do {
-        SkDPoint dPt = {fPts[index].fX, fPts[index].fY};
-        dPt.dump();
-        if (index != count) {
-            SkDebugf(", ");
-        }
-    } while (++index <= count);
-    SkDebugf("}}\n");
-}
-
-void SkOpSegment::dumpSpans() const {
-    int count = this->count();
-    SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", this, debugID());
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan& span = this->span(index);
-        SkDebugf("  [%d] ", index);
-        span.dumpOne();
-    }
-}
-
-void SkPathOpsDebug::DumpCoincidence(const SkTArray<SkOpContour, true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpCoincidences();
-    }
-}
-
-void SkPathOpsDebug::DumpCoincidence(const SkTArray<SkOpContour* , true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpCoincidences();
-    }
-}
-
-void SkPathOpsDebug::DumpContours(const SkTArray<SkOpContour, true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dump();
-    }
-}
-
-void SkPathOpsDebug::DumpContours(const SkTArray<SkOpContour* , true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dump();
-    }
-}
-
-void SkPathOpsDebug::DumpContourAngles(const SkTArray<SkOpContour, true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpAngles();
-    }
-}
-
-void SkPathOpsDebug::DumpContourAngles(const SkTArray<SkOpContour* , true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpAngles();
-    }
-}
-
-void SkPathOpsDebug::DumpContourPts(const SkTArray<SkOpContour, true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpPts();
-    }
-}
-
-void SkPathOpsDebug::DumpContourPts(const SkTArray<SkOpContour* , true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpPts();
-    }
-}
-
-void SkPathOpsDebug::DumpContourPt(const SkTArray<SkOpContour, true>& contours, int segmentID) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpPt(segmentID);
-    }
-}
-
-void SkPathOpsDebug::DumpContourPt(const SkTArray<SkOpContour* , true>& contours, int segmentID) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpPt(segmentID);
-    }
-}
-
-void SkPathOpsDebug::DumpContourSpans(const SkTArray<SkOpContour, true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpSpans();
-    }
-}
-
-void SkPathOpsDebug::DumpContourSpans(const SkTArray<SkOpContour* , true>& contours) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpSpans();
-    }
-}
-
-void SkPathOpsDebug::DumpContourSpan(const SkTArray<SkOpContour, true>& contours, int segmentID) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index].dumpSpan(segmentID);
-    }
-}
-
-void SkPathOpsDebug::DumpContourSpan(const SkTArray<SkOpContour* , true>& contours, int segmentID) {
-    int count = contours.count();
-    for (int index = 0; index < count; ++index) {
-        contours[index]->dumpSpan(segmentID);
-    }
-}
-
-void SkPathOpsDebug::DumpSpans(const SkTDArray<SkOpSpan *>& spans) {
-    int count = spans.count();
-    for (int index = 0; index < count; ++index) {
-        const SkOpSpan* span = spans[index];
-        const SkOpSpan& oSpan = span->fOther->span(span->fOtherIndex);
-        const SkOpSegment* segment = oSpan.fOther;
-        SkDebugf("((SkOpSegment*) 0x%p) [%d] ", segment, segment->debugID());
-        SkDebugf("spanIndex:%d ", oSpan.fOtherIndex);
-        span->dumpOne();
-    }
-}
-
-// this does not require that other T index is initialized or correct
-const SkOpSegment* SkOpSpan::debugToSegment(ptrdiff_t* spanIndex) const {
-    if (!fOther) {
-        return NULL;
-    }
-    int oppCount = fOther->count();
-    for (int index = 0; index < oppCount; ++index) {
-        const SkOpSpan& otherSpan = fOther->span(index);
-        double otherTestT = otherSpan.fT;
-        if (otherTestT < fOtherT) {
-            continue;
-        }
-        SkASSERT(otherTestT == fOtherT);
-        const SkOpSegment* candidate = otherSpan.fOther;
-        const SkOpSpan* first = candidate->debugSpans().begin();
-        const SkOpSpan* last = candidate->debugSpans().end() - 1;
-        if (first <= this && this <= last) {
-            if (spanIndex) {
-                *spanIndex = this - first;
-            }
-            return candidate;
-        }
-    }
-    SkASSERT(0);
-    return NULL;
-}
-
-void SkOpSpan::dumpOne() const {
-    SkDebugf("t=");
-    DebugDumpDouble(fT);
-    SkDebugf(" pt=");
-    SkDPoint::Dump(fPt);
-    if (fOther) {
-        SkDebugf(" other.fID=%d", fOther->debugID());
-        SkDebugf(" [%d] otherT=", fOtherIndex);
-        DebugDumpDouble(fOtherT);
-    } else {
-        SkDebugf(" other.fID=? [?] otherT=?");
-    }
-    if (fWindSum != SK_MinS32) {
-        SkDebugf(" windSum=%d", fWindSum);
-    }
-    if (fOppSum != SK_MinS32 && (SkPathOpsDebug::ValidWind(fOppSum) || fOppValue != 0)) {
-        SkDebugf(" oppSum=%d", fOppSum);
-    }
-    SkDebugf(" windValue=%d", fWindValue);
-    if (SkPathOpsDebug::ValidWind(fOppSum) || fOppValue != 0) {
-        SkDebugf(" oppValue=%d", fOppValue);
-    }
-    if (fFromAngle && fFromAngle->debugID()) {
-        SkDebugf(" from=%d", fFromAngle->debugID());
-    }
-    if (fToAngle && fToAngle->debugID()) {
-        SkDebugf(" to=%d", fToAngle->debugID());
-    }
-    if (fChased) {
-        SkDebugf(" chased");
-    }
-    if (fCoincident) {
-        SkDebugf(" coincident");
-    }
-    if (fDone) {
-        SkDebugf(" done");
-    }
-    if (fLoop) {
-        SkDebugf(" loop");
-    }
-    if (fMultiple) {
-        SkDebugf(" multiple");
-    }
-    if (fNear) {
-        SkDebugf(" near");
-    }
-    if (fSmall) {
-        SkDebugf(" small");
-    }
-    if (fTiny) {
-        SkDebugf(" tiny");
     }
     SkDebugf("\n");
 }
 
-void SkOpSpan::dump() const {
-    ptrdiff_t spanIndex;
-    const SkOpSegment* segment = debugToSegment(&spanIndex);
-    if (segment) {
-        SkDebugf("((SkOpSegment*) 0x%p) [%d]\n", segment, segment->debugID());
-        SkDebugf("  [%d] ", spanIndex);
-    } else {
-        SkDebugf("((SkOpSegment*) ?) [?]\n");
-        SkDebugf("  [?] ");
-    }
-    dumpOne();
+const SkOpAngle* SkPathOpsDebug::DebugAngleAngle(const SkOpAngle* angle, int id) {
+    return angle->debugAngle(id);
 }
 
-void Dump(const SkTArray<class SkOpContour, true>& contours) {
-    SkPathOpsDebug::DumpContours(contours);
+SkOpContour* SkPathOpsDebug::DebugAngleContour(SkOpAngle* angle, int id) {
+    return angle->debugContour(id);
 }
 
-void Dump(const SkTArray<class SkOpContour* , true>& contours) {
-    SkPathOpsDebug::DumpContours(contours);
+const SkOpPtT* SkPathOpsDebug::DebugAnglePtT(const SkOpAngle* angle, int id) {
+    return angle->debugPtT(id);
 }
 
-void Dump(const SkTArray<class SkOpContour, true>* contours) {
-    SkPathOpsDebug::DumpContours(*contours);
+const SkOpSegment* SkPathOpsDebug::DebugAngleSegment(const SkOpAngle* angle, int id) {
+    return angle->debugSegment(id);
 }
 
-void Dump(const SkTArray<class SkOpContour* , true>* contours) {
-    SkPathOpsDebug::DumpContours(*contours);
+const SkOpSpanBase* SkPathOpsDebug::DebugAngleSpan(const SkOpAngle* angle, int id) {
+    return angle->debugSpan(id);
 }
 
-void Dump(const SkTDArray<SkOpSpan *>& chase) {
-    SkPathOpsDebug::DumpSpans(chase);
+const SkOpAngle* SkPathOpsDebug::DebugContourAngle(SkOpContour* contour, int id) {
+    return contour->debugAngle(id);
 }
 
-void Dump(const SkTDArray<SkOpSpan *>* chase) {
-    SkPathOpsDebug::DumpSpans(*chase);
+SkOpContour* SkPathOpsDebug::DebugContourContour(SkOpContour* contour, int id) {
+    return contour->debugContour(id);
 }
 
-void DumpAngles(const SkTArray<class SkOpContour, true>& contours) {
-    SkPathOpsDebug::DumpContourAngles(contours);
+const SkOpPtT* SkPathOpsDebug::DebugContourPtT(SkOpContour* contour, int id) {
+    return contour->debugPtT(id);
 }
 
-void DumpAngles(const SkTArray<class SkOpContour* , true>& contours) {
-    SkPathOpsDebug::DumpContourAngles(contours);
+const SkOpSegment* SkPathOpsDebug::DebugContourSegment(SkOpContour* contour, int id) {
+    return contour->debugSegment(id);
 }
 
-void DumpAngles(const SkTArray<class SkOpContour, true>* contours) {
-    SkPathOpsDebug::DumpContourAngles(*contours);
+const SkOpSpanBase* SkPathOpsDebug::DebugContourSpan(SkOpContour* contour, int id) {
+    return contour->debugSpan(id);
 }
 
-void DumpAngles(const SkTArray<class SkOpContour* , true>* contours) {
-    SkPathOpsDebug::DumpContourAngles(*contours);
+const SkOpAngle* SkPathOpsDebug::DebugPtTAngle(const SkOpPtT* ptT, int id) {
+    return ptT->debugAngle(id);
 }
 
-void DumpCoin(const SkTArray<class SkOpContour, true>& contours) {
-    SkPathOpsDebug::DumpCoincidence(contours);
+SkOpContour* SkPathOpsDebug::DebugPtTContour(SkOpPtT* ptT, int id) {
+    return ptT->debugContour(id);
 }
 
-void DumpCoin(const SkTArray<class SkOpContour* , true>& contours) {
-    SkPathOpsDebug::DumpCoincidence(contours);
+const SkOpPtT* SkPathOpsDebug::DebugPtTPtT(const SkOpPtT* ptT, int id) {
+    return ptT->debugPtT(id);
 }
 
-void DumpCoin(const SkTArray<class SkOpContour, true>* contours) {
-    SkPathOpsDebug::DumpCoincidence(*contours);
+const SkOpSegment* SkPathOpsDebug::DebugPtTSegment(const SkOpPtT* ptT, int id) {
+    return ptT->debugSegment(id);
 }
 
-void DumpCoin(const SkTArray<class SkOpContour* , true>* contours) {
-    SkPathOpsDebug::DumpCoincidence(*contours);
+const SkOpSpanBase* SkPathOpsDebug::DebugPtTSpan(const SkOpPtT* ptT, int id) {
+    return ptT->debugSpan(id);
 }
 
-void DumpSpans(const SkTArray<class SkOpContour, true>& contours) {
-    SkPathOpsDebug::DumpContourSpans(contours);
+const SkOpAngle* SkPathOpsDebug::DebugSegmentAngle(const SkOpSegment* span, int id) {
+    return span->debugAngle(id);
 }
 
-void DumpSpans(const SkTArray<class SkOpContour* , true>& contours) {
-    SkPathOpsDebug::DumpContourSpans(contours);
+SkOpContour* SkPathOpsDebug::DebugSegmentContour(SkOpSegment* span, int id) {
+    return span->debugContour(id);
 }
 
-void DumpSpans(const SkTArray<class SkOpContour, true>* contours) {
-    SkPathOpsDebug::DumpContourSpans(*contours);
+const SkOpPtT* SkPathOpsDebug::DebugSegmentPtT(const SkOpSegment* span, int id) {
+    return span->debugPtT(id);
 }
 
-void DumpSpans(const SkTArray<class SkOpContour* , true>* contours) {
-    SkPathOpsDebug::DumpContourSpans(*contours);
+const SkOpSegment* SkPathOpsDebug::DebugSegmentSegment(const SkOpSegment* span, int id) {
+    return span->debugSegment(id);
 }
 
-void DumpSpan(const SkTArray<class SkOpContour, true>& contours, int segmentID) {
-    SkPathOpsDebug::DumpContourSpan(contours, segmentID);
+const SkOpSpanBase* SkPathOpsDebug::DebugSegmentSpan(const SkOpSegment* span, int id) {
+    return span->debugSpan(id);
 }
 
-void DumpSpan(const SkTArray<class SkOpContour* , true>& contours, int segmentID) {
-    SkPathOpsDebug::DumpContourSpan(contours, segmentID);
+const SkOpAngle* SkPathOpsDebug::DebugSpanAngle(const SkOpSpanBase* span, int id) {
+    return span->debugAngle(id);
 }
 
-void DumpSpan(const SkTArray<class SkOpContour, true>* contours, int segmentID) {
-    SkPathOpsDebug::DumpContourSpan(*contours, segmentID);
+SkOpContour* SkPathOpsDebug::DebugSpanContour(SkOpSpanBase* span, int id) {
+    return span->debugContour(id);
 }
 
-void DumpSpan(const SkTArray<class SkOpContour* , true>* contours, int segmentID) {
-    SkPathOpsDebug::DumpContourSpan(*contours, segmentID);
+const SkOpPtT* SkPathOpsDebug::DebugSpanPtT(const SkOpSpanBase* span, int id) {
+    return span->debugPtT(id);
 }
 
-void DumpPts(const SkTArray<class SkOpContour, true>& contours) {
-    SkPathOpsDebug::DumpContourPts(contours);
+const SkOpSegment* SkPathOpsDebug::DebugSpanSegment(const SkOpSpanBase* span, int id) {
+    return span->debugSegment(id);
 }
 
-void DumpPts(const SkTArray<class SkOpContour* , true>& contours) {
-    SkPathOpsDebug::DumpContourPts(contours);
+const SkOpSpanBase* SkPathOpsDebug::DebugSpanSpan(const SkOpSpanBase* span, int id) {
+    return span->debugSpan(id);
 }
 
-void DumpPts(const SkTArray<class SkOpContour, true>* contours) {
-    SkPathOpsDebug::DumpContourPts(*contours);
+void SkOpContour::dumpContours() const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dump();
+    } while ((contour = contour->next()));
 }
 
-void DumpPts(const SkTArray<class SkOpContour* , true>* contours) {
-    SkPathOpsDebug::DumpContourPts(*contours);
+void SkOpContour::dumpContoursAll() const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpAll();
+    } while ((contour = contour->next()));
 }
 
-void DumpPt(const SkTArray<class SkOpContour, true>& contours, int segmentID) {
-    SkPathOpsDebug::DumpContourPt(contours, segmentID);
+void SkOpContour::dumpContoursAngles() const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpAngles();
+    } while ((contour = contour->next()));
 }
 
-void DumpPt(const SkTArray<class SkOpContour* , true>& contours, int segmentID) {
-    SkPathOpsDebug::DumpContourPt(contours, segmentID);
+void SkOpContour::dumpContoursPts() const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpPts();
+    } while ((contour = contour->next()));
 }
 
-void DumpPt(const SkTArray<class SkOpContour, true>* contours, int segmentID) {
-    SkPathOpsDebug::DumpContourPt(*contours, segmentID);
+void SkOpContour::dumpContoursPt(int segmentID) const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpPt(segmentID);
+    } while ((contour = contour->next()));
 }
 
-void DumpPt(const SkTArray<class SkOpContour* , true>* contours, int segmentID) {
-    SkPathOpsDebug::DumpContourPt(*contours, segmentID);
+void SkOpContour::dumpContoursSegment(int segmentID) const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpSegment(segmentID);
+    } while ((contour = contour->next()));
+}
+
+void SkOpContour::dumpContoursSpan(int spanID) const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpSpan(spanID);
+    } while ((contour = contour->next()));
+}
+
+void SkOpContour::dumpContoursSpans() const {
+    SkOpContour* contour = this->globalState()->contourHead();
+    do {
+        contour->dumpSpans();
+    } while ((contour = contour->next()));
+}
+
+template <typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* DebugSpan(const SkTSect<TCurve, OppCurve>* sect, int id) {
+    return sect->debugSpan(id);
+}
+
+void DontCallDebugSpan(int id);
+void DontCallDebugSpan(int id) {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DebugSpan(&q1q2, id);
+    DebugSpan(&q1k2, id);
+    DebugSpan(&q1c2, id);
+    DebugSpan(&k1q2, id);
+    DebugSpan(&k1k2, id);
+    DebugSpan(&k1c2, id);
+    DebugSpan(&c1q2, id);
+    DebugSpan(&c1k2, id);
+    DebugSpan(&c1c2, id);
+}
+
+template <typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* DebugT(const SkTSect<TCurve, OppCurve>* sect, double t) {
+    return sect->debugT(t);
+}
+
+void DontCallDebugT(double t);
+void DontCallDebugT(double t) {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DebugT(&q1q2, t);
+    DebugT(&q1k2, t);
+    DebugT(&q1c2, t);
+    DebugT(&k1q2, t);
+    DebugT(&k1k2, t);
+    DebugT(&k1c2, t);
+    DebugT(&c1q2, t);
+    DebugT(&c1k2, t);
+    DebugT(&c1c2, t);
+}
+
+template <typename TCurve, typename OppCurve>
+void Dump(const SkTSect<TCurve, OppCurve>* sect) {
+    sect->dump();
+}
+
+void DontCallDumpTSect();
+void DontCallDumpTSect() {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    Dump(&q1q2);
+    Dump(&q1k2);
+    Dump(&q1c2);
+    Dump(&k1q2);
+    Dump(&k1k2);
+    Dump(&k1c2);
+    Dump(&c1q2);
+    Dump(&c1k2);
+    Dump(&c1c2);
+}
+
+template <typename TCurve, typename OppCurve>
+void DumpBoth(SkTSect<TCurve, OppCurve>* sect1, SkTSect<OppCurve, TCurve>* sect2) {
+    sect1->dumpBoth(sect2);
+}
+
+void DontCallDumpBoth();
+void DontCallDumpBoth() {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpBoth(&q1q2, &q1q2);
+    DumpBoth(&q1k2, &k1q2);
+    DumpBoth(&q1c2, &c1q2);
+    DumpBoth(&k1q2, &q1k2);
+    DumpBoth(&k1k2, &k1k2);
+    DumpBoth(&k1c2, &c1k2);
+    DumpBoth(&c1q2, &q1c2);
+    DumpBoth(&c1k2, &k1c2);
+    DumpBoth(&c1c2, &c1c2);
+}
+
+template <typename TCurve, typename OppCurve>
+void DumpBounded(SkTSect<TCurve, OppCurve>* sect1, int id) {
+    sect1->dumpBounded(id);
+}
+
+void DontCallDumpBounded();
+void DontCallDumpBounded() {
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpBounded(&q1q2, 0);
+    DumpBounded(&q1k2, 0);
+    DumpBounded(&q1c2, 0);
+    DumpBounded(&k1q2, 0);
+    DumpBounded(&k1k2, 0);
+    DumpBounded(&k1c2, 0);
+    DumpBounded(&c1q2, 0);
+    DumpBounded(&c1k2, 0);
+    DumpBounded(&c1c2, 0);
+}
+
+template <typename TCurve, typename OppCurve>
+void DumpBounds(SkTSect<TCurve, OppCurve>* sect1) {
+    sect1->dumpBounds();
+}
+
+void DontCallDumpBounds();
+void DontCallDumpBounds() {
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpBounds(&q1q2);
+    DumpBounds(&q1k2);
+    DumpBounds(&q1c2);
+    DumpBounds(&k1q2);
+    DumpBounds(&k1k2);
+    DumpBounds(&k1c2);
+    DumpBounds(&c1q2);
+    DumpBounds(&c1k2);
+    DumpBounds(&c1c2);
+}
+
+template <typename TCurve, typename OppCurve>
+void DumpCoin(SkTSect<TCurve, OppCurve>* sect1) {
+    sect1->dumpCoin();
+}
+
+void DontCallDumpCoin();
+void DontCallDumpCoin() {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpCoin(&q1q2);
+    DumpCoin(&q1k2);
+    DumpCoin(&q1c2);
+    DumpCoin(&k1q2);
+    DumpCoin(&k1k2);
+    DumpCoin(&k1c2);
+    DumpCoin(&c1q2);
+    DumpCoin(&c1k2);
+    DumpCoin(&c1c2);
+}
+
+template <typename TCurve, typename OppCurve>
+void DumpCoinCurves(SkTSect<TCurve, OppCurve>* sect1) {
+    sect1->dumpCoinCurves();
+}
+
+void DontCallDumpCoinCurves();
+void DontCallDumpCoinCurves() {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpCoinCurves(&q1q2);
+    DumpCoinCurves(&q1k2);
+    DumpCoinCurves(&q1c2);
+    DumpCoinCurves(&k1q2);
+    DumpCoinCurves(&k1k2);
+    DumpCoinCurves(&k1c2);
+    DumpCoinCurves(&c1q2);
+    DumpCoinCurves(&c1k2);
+    DumpCoinCurves(&c1c2);
+}
+
+template <typename TCurve, typename OppCurve>
+void DumpCurves(const SkTSect<TCurve, OppCurve>* sect) {
+    sect->dumpCurves();
+}
+
+void DontCallDumpCurves();
+void DontCallDumpCurves() {  // exists to instantiate the templates
+    SkDQuad quad;
+    SkDConic conic;
+    SkDCubic cubic;
+    SkTSect<SkDQuad, SkDQuad> q1q2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDConic> q1k2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDQuad, SkDCubic> q1c2(quad PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDQuad> k1q2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDConic> k1k2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDConic, SkDCubic> k1c2(conic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDQuad> c1q2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDConic> c1k2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    SkTSect<SkDCubic, SkDCubic> c1c2(cubic PATH_OPS_DEBUG_T_SECT_PARAMS(1));
+    DumpCurves(&q1q2);
+    DumpCurves(&q1k2);
+    DumpCurves(&q1c2);
+    DumpCurves(&k1q2);
+    DumpCurves(&k1k2);
+    DumpCurves(&k1c2);
+    DumpCurves(&c1q2);
+    DumpCurves(&c1k2);
+    DumpCurves(&c1c2);
+}
+
+template <typename TCurve, typename OppCurve>
+void Dump(const SkTSpan<TCurve, OppCurve>* span) {
+    span->dump();
+}
+
+void DontCallDumpTSpan();
+void DontCallDumpTSpan() {  // exists to instantiate the templates
+    SkTSpan<SkDQuad, SkDQuad> q1q2; q1q2.debugInit();
+    SkTSpan<SkDQuad, SkDConic> q1k2; q1k2.debugInit();
+    SkTSpan<SkDQuad, SkDCubic> q1c2; q1c2.debugInit();
+    SkTSpan<SkDConic, SkDQuad> k1q2; k1q2.debugInit();
+    SkTSpan<SkDConic, SkDConic> k1k2; k1k2.debugInit();
+    SkTSpan<SkDConic, SkDCubic> k1c2; k1c2.debugInit();
+    SkTSpan<SkDCubic, SkDQuad> c1q2; c1q2.debugInit();
+    SkTSpan<SkDCubic, SkDConic> c1k2; c1k2.debugInit();
+    SkTSpan<SkDCubic, SkDCubic> c1c2; c1c2.debugInit();
+    Dump(&q1q2);
+    Dump(&q1k2);
+    Dump(&q1c2);
+    Dump(&k1q2);
+    Dump(&k1k2);
+    Dump(&k1c2);
+    Dump(&c1q2);
+    Dump(&c1k2);
+    Dump(&c1c2);
+}
+
+template <typename TCurve, typename OppCurve>
+void DumpCoin(const SkTSpan<TCurve, OppCurve>* span) {
+    span->dumpCoin();
+}
+
+void DontCallDumpSpanCoin();
+void DontCallDumpSpanCoin() {  // exists to instantiate the templates
+    SkTSpan<SkDQuad, SkDQuad> q1q2; q1q2.debugInit();
+    SkTSpan<SkDQuad, SkDConic> q1k2; q1k2.debugInit();
+    SkTSpan<SkDQuad, SkDCubic> q1c2; q1c2.debugInit();
+    SkTSpan<SkDConic, SkDQuad> k1q2; k1q2.debugInit();
+    SkTSpan<SkDConic, SkDConic> k1k2; k1k2.debugInit();
+    SkTSpan<SkDConic, SkDCubic> k1c2; k1c2.debugInit();
+    SkTSpan<SkDCubic, SkDQuad> c1q2; c1q2.debugInit();
+    SkTSpan<SkDCubic, SkDConic> c1k2; c1k2.debugInit();
+    SkTSpan<SkDCubic, SkDCubic> c1c2; c1c2.debugInit();
+    DumpCoin(&q1q2);
+    DumpCoin(&q1k2);
+    DumpCoin(&q1c2);
+    DumpCoin(&k1q2);
+    DumpCoin(&k1k2);
+    DumpCoin(&k1c2);
+    DumpCoin(&c1q2);
+    DumpCoin(&c1k2);
+    DumpCoin(&c1c2);
 }
 
 static void dumpTestCase(const SkDQuad& quad1, const SkDQuad& quad2, int testNo) {
-    SkDebugf("<div id=\"quad%d\">\n", testNo);
-    quad1.dumpComma(",");
+    SkDebugf("\n<div id=\"quad%d\">\n", testNo);
+    quad1.dumpInner();
+    SkDebugf("}}, ");
     quad2.dump();
     SkDebugf("</div>\n\n");
 }
@@ -895,3 +711,621 @@
     SkDLine line = {{quad.ptAtT(t), quad[0]}};
     line.dump();
 }
+
+const SkOpAngle* SkOpAngle::debugAngle(int id) const {
+    return this->segment()->debugAngle(id);
+}
+
+SkOpContour* SkOpAngle::debugContour(int id) {
+    return this->segment()->debugContour(id);
+}
+
+const SkOpPtT* SkOpAngle::debugPtT(int id) const {
+    return this->segment()->debugPtT(id);
+}
+
+const SkOpSegment* SkOpAngle::debugSegment(int id) const {
+    return this->segment()->debugSegment(id);
+}
+
+int SkOpAngle::debugSign() const {
+    SkASSERT(fStart->t() != fEnd->t());
+    return fStart->t() < fEnd->t() ? -1 : 1;
+}
+
+const SkOpSpanBase* SkOpAngle::debugSpan(int id) const {
+    return this->segment()->debugSpan(id);
+}
+
+void SkOpAngle::dump() const {
+    dumpOne(true);
+    SkDebugf("\n");
+}
+
+void SkOpAngle::dumpOne(bool functionHeader) const {
+//    fSegment->debugValidate();
+    const SkOpSegment* segment = this->segment();
+    const SkOpSpan& mSpan = *fStart->starter(fEnd);
+    if (functionHeader) {
+        SkDebugf("%s ", __FUNCTION__);
+    }
+    SkDebugf("[%d", segment->debugID());
+    SkDebugf("/%d", debugID());
+    SkDebugf("] next=");
+    if (fNext) {
+        SkDebugf("%d", fNext->fStart->segment()->debugID());
+        SkDebugf("/%d", fNext->debugID());
+    } else {
+        SkDebugf("?");
+    }
+    SkDebugf(" sect=%d/%d ", fSectorStart, fSectorEnd);
+    SkDebugf(" s=%1.9g [%d] e=%1.9g [%d]", fStart->t(), fStart->debugID(),
+                fEnd->t(), fEnd->debugID());
+    SkDebugf(" sgn=%d windVal=%d", this->debugSign(), mSpan.windValue());
+
+    SkDebugf(" windSum=");
+    SkPathOpsDebug::WindingPrintf(mSpan.windSum());
+    if (mSpan.oppValue() != 0 || mSpan.oppSum() != SK_MinS32) {
+        SkDebugf(" oppVal=%d", mSpan.oppValue());
+        SkDebugf(" oppSum=");
+        SkPathOpsDebug::WindingPrintf(mSpan.oppSum());
+    }
+    if (mSpan.done()) {
+        SkDebugf(" done");
+    }
+    if (unorderable()) {
+        SkDebugf(" unorderable");
+    }
+    if (segment->operand()) {
+        SkDebugf(" operand");
+    }
+}
+
+void SkOpAngle::dumpTo(const SkOpSegment* segment, const SkOpAngle* to) const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    const char* indent = "";
+    do {
+        SkDebugf("%s", indent);
+        next->dumpOne(false);
+        if (segment == next->fStart->segment()) {
+            if (this == fNext) {
+                SkDebugf(" << from");
+            }
+            if (to == fNext) {
+                SkDebugf(" << to");
+            }
+        }
+        SkDebugf("\n");
+        indent = "           ";
+        next = next->fNext;
+    } while (next && next != first);
+}
+
+void SkOpAngle::dumpCurves() const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    do {
+        next->fCurvePart.dumpID(next->segment()->debugID());
+        next = next->fNext;
+    } while (next && next != first);
+}
+
+void SkOpAngle::dumpLoop() const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    do {
+        next->dumpOne(false);
+        SkDebugf("\n");
+        next = next->fNext;
+    } while (next && next != first);
+}
+
+void SkOpAngle::dumpTest() const {
+    const SkOpAngle* first = this;
+    const SkOpAngle* next = this;
+    do {
+        SkDebugf("{ ");
+        SkOpSegment* segment = next->segment();
+        segment->dumpPts();
+        SkDebugf(", %d, %1.9g, %1.9g, {} },\n", SkPathOpsVerbToPoints(segment->verb()) + 1,
+                next->start()->t(), next->end()->t());
+        next = next->fNext;
+    } while (next && next != first);
+}
+
+bool SkOpPtT::debugMatchID(int id) const {
+    int limit = this->debugLoopLimit(false);
+    int loop = 0;
+    const SkOpPtT* ptT = this;
+    do {
+        if (ptT->debugID() == id) {
+            return true;
+        }
+    } while ((!limit || ++loop <= limit) && (ptT = ptT->next()) && ptT != this);
+    return false;
+}
+
+const SkOpAngle* SkOpPtT::debugAngle(int id) const {
+    return this->span()->debugAngle(id);
+}
+
+SkOpContour* SkOpPtT::debugContour(int id) {
+    return this->span()->debugContour(id);
+}
+
+const SkOpPtT* SkOpPtT::debugPtT(int id) const {
+    return this->span()->debugPtT(id);
+}
+
+const SkOpSegment* SkOpPtT::debugSegment(int id) const {
+    return this->span()->debugSegment(id);
+}
+
+const SkOpSpanBase* SkOpPtT::debugSpan(int id) const {
+    return this->span()->debugSpan(id);
+}
+
+void SkOpPtT::dump() const {
+    SkDebugf("seg=%d span=%d ptT=%d",
+            this->segment()->debugID(), this->span()->debugID(), this->debugID());
+    this->dumpBase();
+    SkDebugf("\n");
+}
+
+void SkOpPtT::dumpAll() const {
+    contour()->indentDump();
+    const SkOpPtT* next = this;
+    int limit = debugLoopLimit(true);
+    int loop = 0;
+    do {
+        SkDebugf("%.*s", contour()->debugIndent(), "        ");
+        SkDebugf("seg=%d span=%d ptT=%d",
+                next->segment()->debugID(), next->span()->debugID(), next->debugID());
+        next->dumpBase();
+        SkDebugf("\n");
+        if (limit && ++loop >= limit) {
+            SkDebugf("*** abort loop ***\n");
+            break;
+        }
+    } while ((next = next->fNext) && next != this);
+    contour()->outdentDump();
+}
+
+void SkOpPtT::dumpBase() const {
+    SkDebugf(" t=%1.9g pt=(%1.9g,%1.9g)%s%s", this->fT, this->fPt.fX, this->fPt.fY,
+            this->fDuplicatePt ? " dup" : "", this->fDeleted ? " deleted" : "");
+}
+
+const SkOpAngle* SkOpSpanBase::debugAngle(int id) const {
+    return this->segment()->debugAngle(id);
+}
+
+SkOpContour* SkOpSpanBase::debugContour(int id) {
+    return this->segment()->debugContour(id);
+}
+
+const SkOpPtT* SkOpSpanBase::debugPtT(int id) const {
+    return this->segment()->debugPtT(id);
+}
+
+const SkOpSegment* SkOpSpanBase::debugSegment(int id) const {
+    return this->segment()->debugSegment(id);
+}
+
+const SkOpSpanBase* SkOpSpanBase::debugSpan(int id) const {
+    return this->segment()->debugSpan(id);
+}
+
+void SkOpSpanBase::dump() const {
+    this->dumpAll();
+    SkDebugf("\n");
+}
+
+void SkOpSpanBase::dumpAll() const {
+    SkDebugf("%.*s", contour()->debugIndent(), "        ");
+    SkDebugf("seg=%d span=%d", this->segment()->debugID(), this->debugID());
+    this->dumpBase();
+    SkDebugf("\n");
+    this->fPtT.dumpAll();
+}
+
+void SkOpSpanBase::dumpBase() const {
+    if (this->fAligned) {
+        SkDebugf(" aligned");
+    }
+    if (this->fChased) {
+        SkDebugf(" chased");
+    }
+    if (!this->final()) {
+        this->upCast()->dumpSpan();
+    }
+    const SkOpSpanBase* coin = this->coinEnd();
+    if (this != coin) {
+        SkDebugf(" coinEnd seg/span=%d/%d", coin->segment()->debugID(), coin->debugID());
+    } else if (this->final() || !this->upCast()->isCoincident()) {
+        const SkOpPtT* oPt = this->ptT()->next();
+        SkDebugf(" seg/span=%d/%d", oPt->segment()->debugID(), oPt->span()->debugID());
+    }
+    SkDebugf(" adds=%d", fSpanAdds);
+}
+
+void SkOpSpanBase::dumpCoin() const {
+    const SkOpSpan* span = this->upCastable();
+    if (!span) {
+        return;
+    }
+    if (!span->isCoincident()) {
+        return;
+    }
+    span->dumpCoin();
+}
+
+void SkOpSpan::dumpCoin() const {
+    const SkOpSpan* coincident = fCoincident;
+    bool ok = debugCoinLoopCheck();
+    this->dump();
+    int loop = 0;
+    do {
+        coincident->dump();
+        if (!ok && ++loop > 10) {
+            SkDebugf("*** abort loop ***\n");
+            break;
+        }
+    } while ((coincident = coincident->fCoincident) != this);
+}
+
+bool SkOpSpan::dumpSpan() const {
+    SkOpSpan* coin = fCoincident;
+    if (this != coin) {
+        SkDebugf(" coinStart seg/span=%d/%d", coin->segment()->debugID(), coin->debugID());
+    }
+    SkDebugf(" windVal=%d", this->windValue());
+    SkDebugf(" windSum=");
+    SkPathOpsDebug::WindingPrintf(this->windSum());
+    if (this->oppValue() != 0 || this->oppSum() != SK_MinS32) {
+        SkDebugf(" oppVal=%d", this->oppValue());
+        SkDebugf(" oppSum=");
+        SkPathOpsDebug::WindingPrintf(this->oppSum());
+    }
+    if (this->done()) {
+        SkDebugf(" done");
+    }
+    return this != coin;
+}
+
+const SkOpAngle* SkOpSegment::debugAngle(int id) const {
+    return this->contour()->debugAngle(id);
+}
+
+SkOpContour* SkOpSegment::debugContour(int id) {
+    return this->contour()->debugContour(id);
+}
+
+const SkOpPtT* SkOpSegment::debugPtT(int id) const {
+    return this->contour()->debugPtT(id);
+}
+
+const SkOpSegment* SkOpSegment::debugSegment(int id) const {
+    return this->contour()->debugSegment(id);
+}
+
+const SkOpSpanBase* SkOpSegment::debugSpan(int id) const {
+    return this->contour()->debugSpan(id);
+}
+
+void SkOpSegment::dump() const {
+    SkDebugf("%.*s", contour()->debugIndent(), "        ");
+    this->dumpPts();
+    const SkOpSpanBase* span = &fHead;
+    contour()->indentDump();
+    do {
+        SkDebugf("%.*s span=%d ", contour()->debugIndent(), "        ", span->debugID());
+        span->ptT()->dumpBase();
+        span->dumpBase();
+        SkDebugf("\n");
+    } while (!span->final() && (span = span->upCast()->next()));
+    contour()->outdentDump();
+}
+
+void SkOpSegment::dumpAll() const {
+    SkDebugf("%.*s", contour()->debugIndent(), "        ");
+    this->dumpPts();
+    const SkOpSpanBase* span = &fHead;
+    contour()->indentDump();
+    do {
+        span->dumpAll();
+    } while (!span->final() && (span = span->upCast()->next()));
+    contour()->outdentDump();
+}
+
+void SkOpSegment::dumpAngles() const {
+    SkDebugf("seg=%d\n", debugID());
+    const SkOpSpanBase* span = &fHead;
+    do {
+        const SkOpAngle* fAngle = span->fromAngle();
+        const SkOpAngle* tAngle = span->final() ? NULL : span->upCast()->toAngle();
+        if (fAngle) {
+            SkDebugf("  span=%d from=%d ", span->debugID(), fAngle->debugID());
+            fAngle->dumpTo(this, tAngle);
+        }
+        if (tAngle) {
+            SkDebugf("  span=%d to=%d   ", span->debugID(), tAngle->debugID());
+            tAngle->dumpTo(this, fAngle);
+        }
+    } while (!span->final() && (span = span->upCast()->next()));
+}
+
+void SkOpSegment::dumpCoin() const {
+    const SkOpSpan* span = &fHead;
+    do {
+        span->dumpCoin();
+    } while ((span = span->next()->upCastable()));
+}
+
+void SkOpSegment::dumpPtsInner() const {
+    int last = SkPathOpsVerbToPoints(fVerb);
+    SkDebugf("seg=%d {{", this->debugID());
+    if (fVerb == SkPath::kConic_Verb) {
+        SkDebugf("{");
+    }
+    int index = 0;
+    do {
+        SkDPoint::Dump(fPts[index]);
+        SkDebugf(", ");
+    } while (++index < last);
+    SkDPoint::Dump(fPts[index]);
+    SkDebugf("}}");
+    if (fVerb == SkPath::kConic_Verb) {
+        SkDebugf(", %1.9gf}", fWeight);
+    }
+}
+
+void SkOpSegment::dumpPts() const {
+    dumpPtsInner();
+    SkDebugf("\n");
+}
+
+void SkCoincidentSpans::dump() const {
+    SkDebugf("- seg=%d span=%d ptT=%d ", fCoinPtTStart->segment()->debugID(),
+        fCoinPtTStart->span()->debugID(), fCoinPtTStart->debugID());
+    fCoinPtTStart->dumpBase();
+    SkDebugf(" span=%d ptT=%d ", fCoinPtTEnd->span()->debugID(), fCoinPtTEnd->debugID());
+    fCoinPtTEnd->dumpBase();
+    if (fCoinPtTStart->segment()->operand()) {
+        SkDebugf(" operand");
+    }
+    if (fCoinPtTStart->segment()->isXor()) {
+        SkDebugf(" xor");
+    }
+    SkDebugf("\n");
+    SkDebugf("+ seg=%d span=%d ptT=%d ", fOppPtTStart->segment()->debugID(),
+        fOppPtTStart->span()->debugID(), fOppPtTStart->debugID());
+    fOppPtTStart->dumpBase();
+    SkDebugf(" span=%d ptT=%d ", fOppPtTEnd->span()->debugID(), fOppPtTEnd->debugID());
+    fOppPtTEnd->dumpBase();
+    if (fOppPtTStart->segment()->operand()) {
+        SkDebugf(" operand");
+    }
+    if (fOppPtTStart->segment()->isXor()) {
+        SkDebugf(" xor");
+    }
+    SkDebugf("\n");
+}
+
+void SkOpCoincidence::dump() const {
+    SkCoincidentSpans* span = fHead;
+    while (span) {
+        span->dump();
+        span = span->fNext;
+    }
+}
+
+void SkOpContour::dump() const {
+    SkDebugf("contour=%d count=%d op=%d xor=%d\n", this->debugID(), fCount, fOperand, fXor);
+    if (!fCount) {
+        return;
+    }
+    const SkOpSegment* segment = &fHead;
+    SkDEBUGCODE(fDebugIndent = 0);
+    this->indentDump();
+    do {
+        segment->dump();
+    } while ((segment = segment->next()));
+    this->outdentDump();
+}
+
+void SkOpContour::dumpAll() const {
+    SkDebugf("contour=%d count=%d op=%d xor=%d\n", this->debugID(), fCount, fOperand, fXor);
+    if (!fCount) {
+        return;
+    }
+    const SkOpSegment* segment = &fHead;
+    SkDEBUGCODE(fDebugIndent = 0);
+    this->indentDump();
+    do {
+        segment->dumpAll();
+    } while ((segment = segment->next()));
+    this->outdentDump();
+}
+
+
+void SkOpContour::dumpAngles() const {
+    SkDebugf("contour=%d\n", this->debugID());
+    const SkOpSegment* segment = &fHead;
+    do {
+        SkDebugf("  seg=%d ", segment->debugID());
+        segment->dumpAngles();
+    } while ((segment = segment->next()));
+}
+
+void SkOpContour::dumpPt(int index) const {
+    const SkOpSegment* segment = &fHead;
+    do {
+        if (segment->debugID() == index) {
+            segment->dumpPts();
+        }
+    } while ((segment = segment->next()));
+}
+
+void SkOpContour::dumpPts() const {
+    SkDebugf("contour=%d\n", this->debugID());
+    const SkOpSegment* segment = &fHead;
+    do {
+        SkDebugf("  seg=%d ", segment->debugID());
+        segment->dumpPts();
+    } while ((segment = segment->next()));
+}
+
+void SkOpContour::dumpPtsX() const {
+    if (!this->fCount) {
+        SkDebugf("<empty>\n");
+        return;
+    }
+    const SkOpSegment* segment = &fHead;
+    do {
+        segment->dumpPts();
+    } while ((segment = segment->next()));
+}
+
+void SkOpContour::dumpSegment(int index) const {
+    debugSegment(index)->dump();
+}
+
+void SkOpContour::dumpSegments(SkPathOp op) const {
+    bool firstOp = false;
+    const SkOpContour* c = this;
+    do {
+        if (!firstOp && c->operand()) {
+#if DEBUG_ACTIVE_OP
+            SkDebugf("op %s\n", SkPathOpsDebug::kPathOpStr[op]);
+#endif
+            firstOp = true;
+        }
+        c->dumpPtsX();
+    } while ((c = c->next()));
+}
+
+void SkOpContour::dumpSpan(int index) const {
+    debugSpan(index)->dump();
+}
+
+void SkOpContour::dumpSpans() const {
+    SkDebugf("contour=%d\n", this->debugID());
+    const SkOpSegment* segment = &fHead;
+    do {
+        SkDebugf("  seg=%d ", segment->debugID());
+        segment->dump();
+    } while ((segment = segment->next()));
+}
+
+void SkOpCurve::dump() const {
+    int count = SkPathOpsVerbToPoints(SkDEBUGRELEASE(fVerb, SkPath::kCubic_Verb));
+    SkDebugf("{{");
+    int index;
+    for (index = 0; index <= count - 1; ++index) {
+        SkDebugf("{%1.9gf,%1.9gf}, ", fPts[index].fX, fPts[index].fY);
+    }
+    SkDebugf("{%1.9gf,%1.9gf}}}\n", fPts[index].fX, fPts[index].fY);
+}
+
+#ifdef SK_DEBUG
+const SkOpAngle* SkOpGlobalState::debugAngle(int id) const {
+    const SkOpContour* contour = fContourHead;
+    do {
+        const SkOpSegment* segment = contour->first();
+        while (segment) {
+            const SkOpSpan* span = segment->head();
+            do {
+                SkOpAngle* angle = span->fromAngle();
+                if (angle && angle->debugID() == id) {
+                    return angle;
+                }
+                angle = span->toAngle();
+                if (angle && angle->debugID() == id) {
+                    return angle;
+                }
+            } while ((span = span->next()->upCastable()));
+            const SkOpSpanBase* tail = segment->tail();
+            SkOpAngle* angle = tail->fromAngle();
+            if (angle && angle->debugID() == id) {
+                return angle;
+            }
+            segment = segment->next();
+        }
+    } while ((contour = contour->next()));
+    return NULL;
+}
+
+SkOpContour* SkOpGlobalState::debugContour(int id) {
+    SkOpContour* contour = fContourHead;
+    do {
+        if (contour->debugID() == id) {
+            return contour;
+        }
+    } while ((contour = contour->next()));
+    return NULL;
+}
+
+const SkOpPtT* SkOpGlobalState::debugPtT(int id) const {
+    const SkOpContour* contour = fContourHead;
+    do {
+        const SkOpSegment* segment = contour->first();
+        while (segment) {
+            const SkOpSpan* span = segment->head();
+            do {
+                const SkOpPtT* ptT = span->ptT();
+                if (ptT->debugMatchID(id)) {
+                    return ptT;
+                }
+            } while ((span = span->next()->upCastable()));
+            const SkOpSpanBase* tail = segment->tail();
+            const SkOpPtT* ptT = tail->ptT();
+            if (ptT->debugMatchID(id)) {
+                return ptT;
+            }
+            segment = segment->next();
+        }
+    } while ((contour = contour->next()));
+    return NULL;
+}
+
+const SkOpSegment* SkOpGlobalState::debugSegment(int id) const {
+    const SkOpContour* contour = fContourHead;
+    do {
+        const SkOpSegment* segment = contour->first();
+        while (segment) {
+            if (segment->debugID() == id) {
+                return segment;
+            }
+            segment = segment->next();
+        }
+    } while ((contour = contour->next()));
+    return NULL;
+}
+
+const SkOpSpanBase* SkOpGlobalState::debugSpan(int id) const {
+    const SkOpContour* contour = fContourHead;
+    do {
+        const SkOpSegment* segment = contour->first();
+        while (segment) {
+            const SkOpSpan* span = segment->head();
+            do {
+                if (span->debugID() == id) {
+                    return span;
+                }
+            } while ((span = span->next()->upCastable()));
+            const SkOpSpanBase* tail = segment->tail();
+            if (tail->debugID() == id) {
+                return tail;
+            }
+            segment = segment->next();
+        }
+    } while ((contour = contour->next()));
+    return NULL;
+}
+#endif
+
+#if DEBUG_T_SECT_DUMP > 1
+int gDumpTSectNum;
+#endif
diff --git a/tests/PathOpsExtendedTest.cpp b/tests/PathOpsExtendedTest.cpp
index e0d30ba..a8079cf 100644
--- a/tests/PathOpsExtendedTest.cpp
+++ b/tests/PathOpsExtendedTest.cpp
@@ -24,6 +24,7 @@
 __SK_FORCE_IMAGE_DECODER_LINKING;
 
 DEFINE_bool2(runFail, f, false, "run tests known to fail.");
+DEFINE_bool2(runBinary, f, false, "run tests known to fail binary sect.");
 
 static const char marker[] =
     "</div>\n"
@@ -33,11 +34,11 @@
     "var testDivs = [\n";
 
 static const char* opStrs[] = {
-    "kDifference_PathOp",
-    "kIntersect_PathOp",
-    "kUnion_PathOp",
+    "kDifference_SkPathOp",
+    "kIntersect_SkPathOp",
+    "kUnion_SkPathOp",
     "kXor_PathOp",
-    "kReverseDifference_PathOp",
+    "kReverseDifference_SkPathOp",
 };
 
 static const char* opSuffixes[] = {
@@ -47,10 +48,6 @@
     "o",
 };
 
-static bool gShowPath = false;
-static bool gComparePathsAssert = true;
-static bool gPathStrAssert = true;
-
 #if DEBUG_SHOW_TEST_NAME
 static void showPathData(const SkPath& path) {
     SkPath::RawIter iter(path);
@@ -82,6 +79,13 @@
                 lastPt = pts[2];
                 lastPtSet = true;
                 break;
+            case SkPath::kConic_Verb:
+                SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},  //weight=%1.9g\n",
+                        pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
+                        iter.conicWeight());
+                lastPt = pts[2];
+                lastPtSet = true;
+                break;
             case SkPath::kCubic_Verb:
                 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
                         pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
@@ -110,19 +114,19 @@
 
 void showOp(const SkPathOp op) {
     switch (op) {
-        case kDifference_PathOp:
+        case kDifference_SkPathOp:
             SkDebugf("op difference\n");
             break;
-        case kIntersect_PathOp:
+        case kIntersect_SkPathOp:
             SkDebugf("op intersect\n");
             break;
-        case kUnion_PathOp:
+        case kUnion_SkPathOp:
             SkDebugf("op union\n");
             break;
-        case kXOR_PathOp:
+        case kXOR_SkPathOp:
             SkDebugf("op xor\n");
             break;
-        case kReverseDifference_PathOp:
+        case kReverseDifference_SkPathOp:
             SkDebugf("op reverse difference\n");
             break;
         default:
@@ -273,7 +277,7 @@
     return true;
 }
 
-static int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
+int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
         const SkPath& two, SkBitmap& bitmap) {
     int errors2x2;
     SkPath scaledOne, scaledTwo;
@@ -282,11 +286,10 @@
         return 0;
     }
     const int MAX_ERRORS = 9;
-    REPORTER_ASSERT(reporter, errors2x2 <= MAX_ERRORS || !gComparePathsAssert);
     return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
 }
 
-const int gTestFirst = 4;
+const int gTestFirst = 41;
 static int gTestNo = gTestFirst;
 static SkTDArray<SkPathOp> gTestOp;
 
@@ -294,32 +297,32 @@
         const SkPath& a, const SkPath& b, const SkPath& scaledOne, const SkPath& scaledTwo,
         const SkPathOp shapeOp, const SkMatrix& scale) {
     SkASSERT((unsigned) shapeOp < SK_ARRAY_COUNT(opStrs));
-    SkString defaultTestName;
     if (!testName) {
-        defaultTestName.printf("xOp%d%s", gTestNo, opSuffixes[shapeOp]);
-        testName = defaultTestName.c_str();
+        testName = "xOp";
     }
-    SkDebugf("static void %s(skiatest::Reporter* reporter, const char* filename) {\n", testName);
+    SkDebugf("static void %s%d%s(skiatest::Reporter* reporter, const char* filename) {\n",
+        testName, gTestNo, opSuffixes[shapeOp]);
     *gTestOp.append() = shapeOp;
     ++gTestNo;
     SkDebugf("    SkPath path, pathB;\n");
-#if DEBUG_SHOW_TEST_NAME
     SkPathOpsDebug::ShowOnePath(a, "path", false);
     SkPathOpsDebug::ShowOnePath(b, "pathB", false);
-#endif
     SkDebugf("    testPathOp(reporter, path, pathB, %s, filename);\n", opStrs[shapeOp]);
     SkDebugf("}\n");
-    drawAsciiPaths(scaledOne, scaledTwo, true);
+    drawAsciiPaths(scaledOne, scaledTwo, false);
 }
 
-void ShowTestArray() {
+void ShowTestArray(const char* testName) {
+    if (!testName) {
+        testName = "xOp";
+    }
     for (int x = gTestFirst; x < gTestNo; ++x) {
-        SkDebugf("    TEST(xOp%d%s),\n", x, opSuffixes[gTestOp[x - gTestFirst]]);
+        SkDebugf("    TEST(%s%d%s),\n", testName, x, opSuffixes[gTestOp[x - gTestFirst]]);
     }
 }
 
 SK_DECLARE_STATIC_MUTEX(compareDebugOut3);
-SK_DECLARE_STATIC_MUTEX(compareDebugOut4);
+
 static int comparePaths(skiatest::Reporter* reporter, const char* testName, const SkPath& one,
         const SkPath& scaledOne, const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap,
         const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const SkMatrix& scale,
@@ -328,27 +331,22 @@
     const int MAX_ERRORS = 8;
     (void) pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
     if (!expectSuccess) {
-        if (errors2x2 <= MAX_ERRORS) {
+        if (errors2x2 < MAX_ERRORS) {
             REPORTER_ASSERT(reporter, 0);
         }
         return 0;
     }
     if (errors2x2 == 0) {
-        if (gShowPath) {
-            showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
-        }
         return 0;
     }
-    if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
+    if (errors2x2 >= MAX_ERRORS) {
         SkAutoMutexAcquire autoM(compareDebugOut3);
-        SkDebugf("\n*** this test fails ***\n");
         showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
+        SkDebugf("\n/*");
         REPORTER_ASSERT(reporter, 0);
-    } else if (gShowPath || errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
-        SkAutoMutexAcquire autoM(compareDebugOut4);
-        showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
+        SkDebugf(" */\n");
     }
-    return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
+    return errors2x2 >= MAX_ERRORS ? errors2x2 : 0;
 }
 
 // Default values for when reporter->verbose() is false.
@@ -367,7 +365,7 @@
 static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
         const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) {
 #if 0
-    outFile.writeText("<div id=\"");
+    outFile.writeText("\n<div id=\"");
     writeTestName(nameSuffix, outFile);
     outFile.writeText("\">\n");
     if (pathPrefix) {
@@ -412,15 +410,12 @@
 }
 
 SK_DECLARE_STATIC_MUTEX(simplifyDebugOut);
+
 bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
                   const char* pathStr) {
     SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
     path.setFillType(fillType);
-#if DEBUG_SHOW_TEST_NAME
-    if (gShowPath) {
-        SkPathOpsDebug::ShowOnePath(path, "path", false);
-    }
-#endif
+    state.fReporter->bumpTestCount();
     if (!Simplify(path, &out)) {
         SkDebugf("%s did not expect failure\n", __FUNCTION__);
         REPORTER_ASSERT(state.fReporter, 0);
@@ -430,7 +425,7 @@
         return true;
     }
     int result = comparePaths(state.fReporter, NULL, path, out, *state.fBitmap);
-    if (result && gPathStrAssert) {
+    if (result) {
         SkAutoMutexAcquire autoM(simplifyDebugOut);
         char temp[8192];
         sk_bzero(temp, sizeof(temp));
@@ -450,23 +445,39 @@
     return result == 0;
 }
 
-bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
-#if DEBUG_SHOW_TEST_NAME
+static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
+        bool checkFail) {
+#if 0 && DEBUG_SHOW_TEST_NAME
     showPathData(path);
 #endif
     SkPath out;
     if (!Simplify(path, &out)) {
-        SkDebugf("%s did not expect failure\n", __FUNCTION__);
+        SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename);
         REPORTER_ASSERT(reporter, 0);
         return false;
     }
     SkBitmap bitmap;
-    int result = comparePaths(reporter, filename, path, out, bitmap);
-    if (result && gPathStrAssert) {
+    int errors = comparePaths(reporter, filename, path, out, bitmap);
+    if (!checkFail) {
+        if (!errors) {
+            SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, filename);
+            REPORTER_ASSERT(reporter, 0);
+            return false;
+        }
+    } else if (errors) {
         REPORTER_ASSERT(reporter, 0);
     }
     reporter->bumpTestCount();
-    return result == 0;
+    return errors == 0;
+}
+
+bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
+    return inner_simplify(reporter, path, filename, true);
+}
+
+bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
+        bool checkFail) {
+    return inner_simplify(reporter, path, filename, checkFail);
 }
 
 #if DEBUG_SHOW_TEST_NAME
@@ -478,18 +489,20 @@
 }
 #endif
 
+bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result, bool expectSuccess);
+
 static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
-        const SkPathOp shapeOp, const char* testName, bool threaded, bool expectSuccess) {
-#if DEBUG_SHOW_TEST_NAME
+        const SkPathOp shapeOp, const char* testName, bool expectSuccess) {
+#if 0 && DEBUG_SHOW_TEST_NAME
     showName(a, b, shapeOp);
 #endif
     SkPath out;
-    if (!Op(a, b, shapeOp, &out) ) {
+    if (!OpDebug(a, b, shapeOp, &out, expectSuccess)) {
         SkDebugf("%s did not expect failure\n", __FUNCTION__);
         REPORTER_ASSERT(reporter, 0);
         return false;
     }
-    if (threaded && !reporter->verbose()) {
+    if (!reporter->verbose()) {
         return true;
     }
     SkPath pathOut, scaledPathOut;
@@ -518,21 +531,23 @@
     scaledOut.setFillType(out.getFillType());
     int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap,
             a, b, shapeOp, scale, expectSuccess);
-    if (result && gPathStrAssert) {
-        REPORTER_ASSERT(reporter, 0);
-    }
     reporter->bumpTestCount();
     return result == 0;
 }
 
 bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
         const SkPathOp shapeOp, const char* testName) {
-    return innerPathOp(reporter, a, b, shapeOp, testName, false, true);
+    return innerPathOp(reporter, a, b, shapeOp, testName, true);
 }
 
 bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
         const SkPathOp shapeOp, const char* testName, bool checkFail) {
-    return innerPathOp(reporter, a, b, shapeOp, testName, false, checkFail);
+    return innerPathOp(reporter, a, b, shapeOp, testName, checkFail);
+}
+
+bool testPathOpFailCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+        const SkPathOp shapeOp, const char* testName) {
+    return innerPathOp(reporter, a, b, shapeOp, testName, false);
 }
 
 bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
@@ -540,20 +555,18 @@
 #if DEBUG_SHOW_TEST_NAME
     showName(a, b, shapeOp);
 #endif
-    SkPath out;
+    SkPath orig;
+    orig.lineTo(54, 43);
+    SkPath out = orig;
     if (Op(a, b, shapeOp, &out) ) {
         SkDebugf("%s test is expected to fail\n", __FUNCTION__);
         REPORTER_ASSERT(reporter, 0);
         return false;
     }
+    SkASSERT(out == orig);
     return true;
 }
 
-bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
-                 const SkPathOp shapeOp, const char* testName) {
-    return innerPathOp(reporter, a, b, shapeOp, testName, true, true);
-}
-
 SK_DECLARE_STATIC_MUTEX(gMutex);
 
 void initializeTests(skiatest::Reporter* reporter, const char* test) {
@@ -604,6 +617,7 @@
 
 void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
                 void (*firstTest)(skiatest::Reporter* , const char* filename),
+                void (*skipTest)(skiatest::Reporter* , const char* filename),
                 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse) {
     size_t index;
     if (firstTest) {
@@ -612,8 +626,7 @@
             --index;
         }
 #if DEBUG_SHOW_TEST_NAME
-        SkDebugf("<div id=\"%s\">\n", tests[index].str);
-        SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
+        SkDebugf("\n<div id=\"%s\">\n", tests[index].str);
 #endif
         (*tests[index].fun)(reporter, tests[index].str);
         if (tests[index].fun == stopTest) {
@@ -622,21 +635,45 @@
     }
     index = reverse ? count - 1 : 0;
     size_t last = reverse ? 0 : count - 1;
+    bool foundSkip = !skipTest;
     do {
-        if (tests[index].fun != firstTest) {
+        if (tests[index].fun == skipTest) {
+            foundSkip = true;
+        }
+        if (foundSkip && tests[index].fun != firstTest) {
     #if DEBUG_SHOW_TEST_NAME
-            SkDebugf("<div id=\"%s\">\n", tests[index].str);
-            SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
+            SkDebugf("\n<div id=\"%s\">\n", tests[index].str);
     #endif
             (*tests[index].fun)(reporter, tests[index].str);
         }
-        if (tests[index].fun == stopTest) {
-            SkDebugf("lastTest\n");
-            break;
-        }
-        if (index == last) {
+        if (tests[index].fun == stopTest || index == last) {
             break;
         }
         index += reverse ? -1 : 1;
     } while (true);
+#if DEBUG_SHOW_TEST_NAME
+    SkDebugf(
+            "\n"
+            "</div>\n"
+            "\n"
+            "<script type=\"text/javascript\">\n"
+            "\n"
+            "var testDivs = [\n"
+    );
+    index = reverse ? count - 1 : 0;
+    last = reverse ? 0 : count - 1;
+    foundSkip = !skipTest;
+    do {
+        if (tests[index].fun == skipTest) {
+            foundSkip = true;
+        }
+        if (foundSkip && tests[index].fun != firstTest) {
+            SkDebugf("    %s,\n", tests[index].str);
+        }
+        if (tests[index].fun == stopTest || index == last) {
+            break;
+        }
+        index += reverse ? -1 : 1;
+    } while (true);
+#endif
 }
diff --git a/tests/PathOpsExtendedTest.h b/tests/PathOpsExtendedTest.h
index 5f7e972..a604761 100644
--- a/tests/PathOpsExtendedTest.h
+++ b/tests/PathOpsExtendedTest.h
@@ -17,6 +17,7 @@
 #include "Test.h"
 
 DECLARE_bool(runFail);
+DECLARE_bool(runBinary);
 
 struct PathOpsThreadState;
 
@@ -26,20 +27,23 @@
 };
 
 //extern int comparePaths(const SkPath& one, const SkPath& two);
-extern int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap);
+extern int comparePaths(skiatest::Reporter* reporter, const char* filename,
+                        const SkPath& one, const SkPath& two, SkBitmap& bitmap);
 extern bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths);
 extern void showOp(const SkPathOp op);
 extern bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
                        const SkPathOp , const char* testName);
 extern bool testPathOpCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
                             const SkPathOp , const char* testName, bool checkFail);
+extern bool testPathOpFailCheck(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
+                                const SkPathOp , const char* testName);
 extern bool testPathFailOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
                            const SkPathOp , const char* testName);
-extern bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
-                               const SkPathOp , const char* testName);
 extern bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
                          const char* pathStr);
 extern bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename);
+extern bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path,
+                              const char* filename, bool checkFail);
 
 void initializeTests(skiatest::Reporter* reporter, const char* testName);
 void outputProgress(char* ramStr, const char* pathStr, SkPath::FillType );
@@ -47,8 +51,9 @@
 
 void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
                 void (*firstTest)(skiatest::Reporter* , const char* filename),
+                void (*skipTest)(skiatest::Reporter* , const char* filename),
                 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse);
-void ShowTestArray();
+void ShowTestArray(const char* testName);
 void ShowTestName(PathOpsThreadState* data, int a, int b, int c, int d);
 void ShowFunctionHeader(const char* name);
 void ShowPath(const SkPath& path, const char* pathName);
diff --git a/tests/PathOpsFuzz763Test.cpp b/tests/PathOpsFuzz763Test.cpp
index cd851a7..5552573 100755
--- a/tests/PathOpsFuzz763Test.cpp
+++ b/tests/PathOpsFuzz763Test.cpp
@@ -121,11 +121,6 @@
 }
 
 static void fuzz763_378(skiatest::Reporter* reporter, const char* filename) {
-#ifdef SK_BUILD_FOR_ANDROID
-	if (!FLAGS_runFail) {
-		return;  // fails on nexus 9 in release, possibly related to fused multiply-add
-	}
-#endif
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0x41013776), SkBits2Float(0xc25007a8));
@@ -215,15 +210,10 @@
 path.quadTo(SkBits2Float(0xc238d4f6), SkBits2Float(0x41a554c0), SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4));
 path.close();
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_378b(skiatest::Reporter* reporter, const char* filename) {
-#ifdef SK_BUILD_FOR_ANDROID
-	if (!FLAGS_runFail) {
-		return;  // fails on nexus 9 in release, possibly related to fused multiply-add
-	}
-#endif
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(-47.1494f, 4.35143f);
@@ -243,7 +233,7 @@
 path.quadTo(SkBits2Float(0xc238d4f6), SkBits2Float(0x41a554c0), SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4));
 path.close();
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_378c(skiatest::Reporter* reporter, const char* filename) {
@@ -264,7 +254,7 @@
     path.quadTo(-39.8065f, 18.9507f, -43.0072f, 19.8086f);
     path.close();
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 static void fuzz763_378d(skiatest::Reporter* reporter, const char* filename) {
@@ -464,7 +454,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 
@@ -505,7 +495,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 static void fuzz763_8712(skiatest::Reporter* reporter, const char* filename) {
@@ -595,7 +585,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_8712a(skiatest::Reporter* reporter, const char* filename) {
@@ -630,7 +620,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_4014(skiatest::Reporter* reporter, const char* filename) {
@@ -719,7 +709,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_4014a(skiatest::Reporter* reporter, const char* filename) {
@@ -1141,7 +1131,6 @@
     testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
-//SkOpSegment.cpp:3475: failed assertion "firstAngle"
 static void fuzz763_17370(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
@@ -1447,7 +1436,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_1597464(skiatest::Reporter* reporter, const char* filename) {
@@ -1539,13 +1528,13 @@
 path.close();
 
     SkPath path2(path);
-    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
-// SkOpSegment.cpp:4010: failed assertion "span->fOppSum == -0x7FFFFFFF || span->fOppSum == oppWinding
 static void fuzz763_34974(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
+#if 00
 path.moveTo(SkBits2Float(0x41015326), SkBits2Float(0xc2500694));
 path.quadTo(SkBits2Float(0x412f3e30), SkBits2Float(0xc256a6fa), SkBits2Float(0x41627462), SkBits2Float(0xc253387e));
 path.quadTo(SkBits2Float(0x418ad549), SkBits2Float(0xc24fca02), SkBits2Float(0x41981613), SkBits2Float(0xc2444f40));
@@ -1556,6 +1545,8 @@
 path.quadTo(SkBits2Float(0x405fd0f0), SkBits2Float(0xc22fcb17), SkBits2Float(0x408b5c58), SkBits2Float(0xc23c98a3));
 path.quadTo(SkBits2Float(0x40a6d038), SkBits2Float(0xc249662f), SkBits2Float(0x41015326), SkBits2Float(0xc2500694));
 path.close();
+#endif
+#if 000
 path.moveTo(SkBits2Float(0xc21a9c18), SkBits2Float(0xc21aa524));
 path.quadTo(SkBits2Float(0xc2113c71), SkBits2Float(0xc2240440), SkBits2Float(0xc203fb34), SkBits2Float(0xc22403dc));
 path.quadTo(SkBits2Float(0xc1ed73ee), SkBits2Float(0xc2240379), SkBits2Float(0xc1dab5b7), SkBits2Float(0xc21aa3d1));
@@ -1566,6 +1557,8 @@
 path.quadTo(SkBits2Float(0xc223fc87), SkBits2Float(0xc1ed871e), SkBits2Float(0xc223fc24), SkBits2Float(0xc20404cc));
 path.quadTo(SkBits2Float(0xc223fbc0), SkBits2Float(0xc2114609), SkBits2Float(0xc21a9c18), SkBits2Float(0xc21aa524));
 path.close();
+#endif
+#if 00
 path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
 path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
 path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
@@ -1582,11 +1575,15 @@
 path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000));
 path.quadTo(SkBits2Float(0xc1e00000), SkBits2Float(0xc1399153), SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
 path.close();
+#endif
+#if 01
 path.moveTo(SkBits2Float(0xc2533a24), SkBits2Float(0x41625bba));
 path.lineTo(SkBits2Float(0xc2533ab2), SkBits2Float(0x4162536e));
 path.lineTo(SkBits2Float(0xc2533af7), SkBits2Float(0x41624f68));
 path.quadTo(SkBits2Float(0xc2533a8e), SkBits2Float(0x41625591), SkBits2Float(0xc2533a24), SkBits2Float(0x41625bba));
 path.close();
+#endif
+#if 0
 path.moveTo(SkBits2Float(0x41dac664), SkBits2Float(0x41dab723));
 path.quadTo(SkBits2Float(0x41ed82ea), SkBits2Float(0x41c80000), SkBits2Float(0x42040000), SkBits2Float(0x41c80000));
 path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x41c80000), SkBits2Float(0x421aa09e), SkBits2Float(0x41dabec3));
@@ -1602,6 +1599,8 @@
 path.lineTo(SkBits2Float(0x41dabec3), SkBits2Float(0x41dabec3));
 path.quadTo(SkBits2Float(0x41dac293), SkBits2Float(0x41dabaf3), SkBits2Float(0x41dac664), SkBits2Float(0x41dab723));
 path.close();
+#endif
+#if 00001
 path.moveTo(SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180));
 path.quadTo(SkBits2Float(0xc22fcba2), SkBits2Float(0x405f6340), SkBits2Float(0xc2245122), SkBits2Float(0x40a4b85c));
 path.quadTo(SkBits2Float(0xc218dd36), SkBits2Float(0x40d9a0b8), SkBits2Float(0xc2156c96), SkBits2Float(0x411fdb9a));
@@ -1622,10 +1621,12 @@
 path.quadTo(SkBits2Float(0xc256a842), SkBits2Float(0x412f19c8), SkBits2Float(0xc25007d7), SkBits2Float(0x410132b2));
 path.quadTo(SkBits2Float(0xc24966ff), SkBits2Float(0x40a69160), SkBits2Float(0xc23c9951), SkBits2Float(0x408b2180));
 path.close();
+#endif
 
     SkPath path1(path);
     path.reset();
     path.setFillType((SkPath::FillType) 0);
+#if 01
 path.moveTo(SkBits2Float(0xc2445236), SkBits2Float(0x419806c2));
 path.quadTo(SkBits2Float(0xc24fccb6), SkBits2Float(0x418ac513), SkBits2Float(0xc2533ab2), SkBits2Float(0x4162536e));
 path.quadTo(SkBits2Float(0xc256a8ae), SkBits2Float(0x412f1cb2), SkBits2Float(0xc25007d7), SkBits2Float(0x410132b2));
@@ -1636,9 +1637,9 @@
 path.quadTo(SkBits2Float(0xc21f3c59), SkBits2Float(0x41979082), SkBits2Float(0xc22c0a07), SkBits2Float(0x419e6c7a));
 path.quadTo(SkBits2Float(0xc238d7b6), SkBits2Float(0x41a54872), SkBits2Float(0xc2445236), SkBits2Float(0x419806c2));
 path.close();
-
+#endif
     SkPath path2(path);
-    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
 static void fuzz763_2211264(skiatest::Reporter* reporter, const char* filename) {
@@ -2201,9 +2202,6 @@
 }
 
 static void fuzz763_2674194(skiatest::Reporter* reporter, const char* filename) {
-    if (!FLAGS_runFail) { // FIXME: asserts in alignSpanState
-        return;
-    }
     SkPath path;
     path.setFillType((SkPath::FillType) 1);
 path.moveTo(SkBits2Float(0xbfb16e10), SkBits2Float(0xc252733b));
@@ -2396,7 +2394,8 @@
     testPathOp(reporter, path1, path2, (SkPathOp) 2, filename);
 }
 
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = fuzz763_2674194;
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static struct TestDesc tests[] = {
@@ -2440,5 +2439,5 @@
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
 }
diff --git a/tests/PathOpsInverseTest.cpp b/tests/PathOpsInverseTest.cpp
index 23dbd1f..929851e 100644
--- a/tests/PathOpsInverseTest.cpp
+++ b/tests/PathOpsInverseTest.cpp
@@ -8,7 +8,7 @@
 
 DEF_TEST(PathOpsInverse, reporter) {
     SkPath one, two;
-    for (int op = kDifference_PathOp; op <= kReverseDifference_PathOp; ++op) {
+    for (int op = kDifference_SkPathOp; op <= kReverseDifference_SkPathOp; ++op) {
         for (int oneFill = SkPath::kWinding_FillType; oneFill <= SkPath::kInverseEvenOdd_FillType;
                     ++oneFill) {
             for (int oneDir = SkPath::kCW_Direction; oneDir != SkPath::kCCW_Direction; ++oneDir) {
diff --git a/tests/PathOpsIssue3651.cpp b/tests/PathOpsIssue3651.cpp
new file mode 100644
index 0000000..583c38f
--- /dev/null
+++ b/tests/PathOpsIssue3651.cpp
@@ -0,0 +1,1573 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsExtendedTest.h"
+#include "PathOpsTestCommon.h"
+
+#define TEST(name) { name, #name }
+
+static SkPath path1() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42823333), SkBits2Float(0x431ee666), SkBits2Float(0x4282368d), SkBits2Float(0x431f999a), SkBits2Float(0x42823333));  // 158.2f, 65.1f, 158.9f, 65.1065f, 159.6f, 65.1f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42822fd9), SkBits2Float(0x43210000), SkBits2Float(0x42822861), SkBits2Float(0x4321b333), SkBits2Float(0x42821f17));  // 160.3f, 65.0935f, 161, 65.0789f, 161.7f, 65.0607f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x428215ce), SkBits2Float(0x4323199a), SkBits2Float(0x4282071d), SkBits2Float(0x4323cccd), SkBits2Float(0x4281fb7b));  // 162.4f, 65.0426f, 163.1f, 65.0139f, 163.8f, 64.9912f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x4281efd8), SkBits2Float(0x43253333), SkBits2Float(0x4281e467), SkBits2Float(0x4325e666), SkBits2Float(0x4281d94a));  // 164.5f, 64.9684f, 165.2f, 64.9461f, 165.9f, 64.9244f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x4281ce2c), SkBits2Float(0x43274ccd), SkBits2Float(0x4281c15d), SkBits2Float(0x43280000), SkBits2Float(0x4281b8cb));  // 166.6f, 64.9027f, 167.3f, 64.8777f, 168, 64.8609f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x4281b039), SkBits2Float(0x43296666), SkBits2Float(0x4281a66d), SkBits2Float(0x432a199a), SkBits2Float(0x4281a5dd));  // 168.7f, 64.8442f, 169.4f, 64.8251f, 170.1f, 64.824f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x4281a54c), SkBits2Float(0x432b8000), SkBits2Float(0x4281aecf), SkBits2Float(0x432c3333), SkBits2Float(0x4281b566));  // 170.8f, 64.8228f, 171.5f, 64.8414f, 172.2f, 64.8543f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x4281bbfe), SkBits2Float(0x432d999a), SkBits2Float(0x4281c612), SkBits2Float(0x432e4ccd), SkBits2Float(0x4281cd6b));  // 172.9f, 64.8672f, 173.6f, 64.8869f, 174.3f, 64.9012f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x4281d4c4), SkBits2Float(0x432fb333), SkBits2Float(0x4281dc73), SkBits2Float(0x43306666), SkBits2Float(0x4281e17e));  // 175, 64.9156f, 175.7f, 64.9306f, 176.4f, 64.9404f
+path.cubicTo(SkBits2Float(0x4331199a), SkBits2Float(0x4281e688), SkBits2Float(0x4331cccd), SkBits2Float(0x4281e967), SkBits2Float(0x43328000), SkBits2Float(0x4281ebaa));  // 177.1f, 64.9503f, 177.8f, 64.9559f, 178.5f, 64.9603f
+path.cubicTo(SkBits2Float(0x43333333), SkBits2Float(0x4281eded), SkBits2Float(0x4333e666), SkBits2Float(0x4281eec6), SkBits2Float(0x4334999a), SkBits2Float(0x4281ef0f));  // 179.2f, 64.9647f, 179.9f, 64.9664f, 180.6f, 64.9669f
+path.cubicTo(SkBits2Float(0x43354ccd), SkBits2Float(0x4281ef57), SkBits2Float(0x43360000), SkBits2Float(0x4281eeba), SkBits2Float(0x4336b333), SkBits2Float(0x4281ed5c));  // 181.3f, 64.9675f, 182, 64.9663f, 182.7f, 64.9636f
+path.cubicTo(SkBits2Float(0x43376666), SkBits2Float(0x4281ebfe), SkBits2Float(0x4338199a), SkBits2Float(0x4281e8c9), SkBits2Float(0x4338cccd), SkBits2Float(0x4281e6db));  // 183.4f, 64.9609f, 184.1f, 64.9547f, 184.8f, 64.9509f
+path.cubicTo(SkBits2Float(0x43398000), SkBits2Float(0x4281e4ec), SkBits2Float(0x433a3333), SkBits2Float(0x4281e29d), SkBits2Float(0x433ae666), SkBits2Float(0x4281e1c4));  // 185.5f, 64.9471f, 186.2f, 64.9426f, 186.9f, 64.9409f
+path.cubicTo(SkBits2Float(0x433b999a), SkBits2Float(0x4281e0eb), SkBits2Float(0x433c4ccd), SkBits2Float(0x4281e188), SkBits2Float(0x433d0000), SkBits2Float(0x4281e1c4));  // 187.6f, 64.9393f, 188.3f, 64.9405f, 189, 64.9409f
+path.cubicTo(SkBits2Float(0x433db333), SkBits2Float(0x4281e201), SkBits2Float(0x433e6666), SkBits2Float(0x4281e415), SkBits2Float(0x433f199a), SkBits2Float(0x4281e330));  // 189.7f, 64.9414f, 190.4f, 64.9455f, 191.1f, 64.9437f
+path.cubicTo(SkBits2Float(0x433fcccd), SkBits2Float(0x4281e24b), SkBits2Float(0x43408000), SkBits2Float(0x4281df77), SkBits2Float(0x43413333), SkBits2Float(0x4281dc67));  // 191.8f, 64.942f, 192.5f, 64.9365f, 193.2f, 64.9305f
+path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x4281d957), SkBits2Float(0x4342999a), SkBits2Float(0x4281d35a), SkBits2Float(0x43434ccd), SkBits2Float(0x4281d0cf));  // 193.9f, 64.9245f, 194.6f, 64.9128f, 195.3f, 64.9078f
+path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x4281ce44), SkBits2Float(0x4344b333), SkBits2Float(0x4281cd6c), SkBits2Float(0x43456666), SkBits2Float(0x4281cd24));  // 196, 64.9029f, 196.7f, 64.9012f, 197.4f, 64.9007f
+path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x4281ccdc), SkBits2Float(0x4346cccd), SkBits2Float(0x4281cf1d), SkBits2Float(0x43478000), SkBits2Float(0x4281cf1d));  // 198.1f, 64.9001f, 198.8f, 64.9045f, 199.5f, 64.9045f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x4281cf1d), SkBits2Float(0x4348e666), SkBits2Float(0x4281ce8e), SkBits2Float(0x4349999a), SkBits2Float(0x4281cd24));  // 200.2f, 64.9045f, 200.9f, 64.9034f, 201.6f, 64.9007f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x4281cbba), SkBits2Float(0x434b0000), SkBits2Float(0x4281c854), SkBits2Float(0x434bb333), SkBits2Float(0x4281c6a2));  // 202.3f, 64.8979f, 203, 64.8913f, 203.7f, 64.888f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x4281c4f0), SkBits2Float(0x434d199a), SkBits2Float(0x4281c46d), SkBits2Float(0x434dcccd), SkBits2Float(0x4281c2f7));  // 204.4f, 64.8846f, 205.1f, 64.8836f, 205.8f, 64.8808f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x4281c182), SkBits2Float(0x434f3333), SkBits2Float(0x4281bf4b), SkBits2Float(0x434fe666), SkBits2Float(0x4281bde1));  // 206.5f, 64.8779f, 207.2f, 64.8736f, 207.9f, 64.8709f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x4281bc77), SkBits2Float(0x43514ccd), SkBits2Float(0x4281bb92), SkBits2Float(0x43520000), SkBits2Float(0x4281ba7d));  // 208.6f, 64.8681f, 209.3f, 64.8663f, 210, 64.8642f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x4281b967), SkBits2Float(0x43536666), SkBits2Float(0x4281b95a), SkBits2Float(0x4354199a), SkBits2Float(0x4281b75f));  // 210.7f, 64.8621f, 211.4f, 64.862f, 212.1f, 64.8581f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x4281b565), SkBits2Float(0x43558000), SkBits2Float(0x4281b0a4), SkBits2Float(0x43563333), SkBits2Float(0x4281ae9e));  // 212.8f, 64.8543f, 213.5f, 64.845f, 214.2f, 64.841f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x4281ac98), SkBits2Float(0x4357999a), SkBits2Float(0x4281aca3), SkBits2Float(0x43584ccd), SkBits2Float(0x4281ab3a));  // 214.9f, 64.8371f, 215.6f, 64.8372f, 216.3f, 64.8344f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x4281a9d0), SkBits2Float(0x4359b333), SkBits2Float(0x4281a82a), SkBits2Float(0x435a6666), SkBits2Float(0x4281a623));  // 217, 64.8317f, 217.7f, 64.8284f, 218.4f, 64.8245f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x4281a41d), SkBits2Float(0x435bcccd), SkBits2Float(0x4281a157), SkBits2Float(0x435c8000), SkBits2Float(0x42819f14));  // 219.1f, 64.8205f, 219.8f, 64.8151f, 220.5f, 64.8107f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42819cd1), SkBits2Float(0x435de666), SkBits2Float(0x42819a39), SkBits2Float(0x435e999a), SkBits2Float(0x42819892));  // 221.2f, 64.8063f, 221.9f, 64.8012f, 222.6f, 64.798f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x428196ec), SkBits2Float(0x43600000), SkBits2Float(0x42819455), SkBits2Float(0x4360b333), SkBits2Float(0x4281952e));  // 223.3f, 64.7948f, 224, 64.7897f, 224.7f, 64.7914f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42819607), SkBits2Float(0x4362199a), SkBits2Float(0x428198e7), SkBits2Float(0x4362cccd), SkBits2Float(0x42819da9));  // 225.4f, 64.793f, 226.1f, 64.7986f, 226.8f, 64.8079f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x4281a26b), SkBits2Float(0x43643333), SkBits2Float(0x4281ad8a), SkBits2Float(0x4364e666), SkBits2Float(0x4281b1bc));  // 227.5f, 64.8172f, 228.2f, 64.8389f, 228.9f, 64.8471f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x4281b5ed), SkBits2Float(0x43664ccd), SkBits2Float(0x4281b70f), SkBits2Float(0x43670000), SkBits2Float(0x4281b6d2));  // 229.6f, 64.8553f, 230.3f, 64.8575f, 231, 64.8571f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x4281b695), SkBits2Float(0x43686666), SkBits2Float(0x4281b2db), SkBits2Float(0x4369199a), SkBits2Float(0x4281b050));  // 231.7f, 64.8566f, 232.4f, 64.8493f, 233.1f, 64.8444f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x4281adc5), SkBits2Float(0x436a8000), SkBits2Float(0x4281a9e9), SkBits2Float(0x436b3333), SkBits2Float(0x4281a78f));  // 233.8f, 64.8394f, 234.5f, 64.8319f, 235.2f, 64.8273f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x4281a535), SkBits2Float(0x436c999a), SkBits2Float(0x4281a55a), SkBits2Float(0x436d4ccd), SkBits2Float(0x4281a232));  // 235.9f, 64.8227f, 236.6f, 64.823f, 237.3f, 64.8168f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42819f0a), SkBits2Float(0x436eb333), SkBits2Float(0x42819ad9), SkBits2Float(0x436f6666), SkBits2Float(0x428194a1));  // 238, 64.8106f, 238.7f, 64.8024f, 239.4f, 64.7903f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42818e69), SkBits2Float(0x4370cccd), SkBits2Float(0x4281843c), SkBits2Float(0x43718000), SkBits2Float(0x42817ce3));  // 240.1f, 64.7781f, 240.8f, 64.7583f, 241.5f, 64.7439f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x4281758a), SkBits2Float(0x4372e666), SkBits2Float(0x42816c36), SkBits2Float(0x4373999a), SkBits2Float(0x4281688a));  // 242.2f, 64.7296f, 242.9f, 64.7113f, 243.6f, 64.7042f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x428164dd), SkBits2Float(0x43750000), SkBits2Float(0x428167a5), SkBits2Float(0x4375b333), SkBits2Float(0x428166d8));  // 244.3f, 64.697f, 245, 64.7024f, 245.7f, 64.7009f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x4281660a), SkBits2Float(0x4377199a), SkBits2Float(0x42816651), SkBits2Float(0x4377cccd), SkBits2Float(0x428163ba));  // 246.4f, 64.6993f, 247.1f, 64.6998f, 247.8f, 64.6948f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42816123), SkBits2Float(0x43793333), SkBits2Float(0x42815b5b), SkBits2Float(0x4379e666), SkBits2Float(0x4281574e));  // 248.5f, 64.6897f, 249.2f, 64.6784f, 249.9f, 64.6705f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42815342), SkBits2Float(0x437b4ccd), SkBits2Float(0x42814fad), SkBits2Float(0x437c0000), SkBits2Float(0x42814b6f));  // 250.6f, 64.6626f, 251.3f, 64.6556f, 252, 64.6473f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42814732), SkBits2Float(0x437d6666), SkBits2Float(0x42813eb7), SkBits2Float(0x437e199a), SkBits2Float(0x42813dde));  // 252.7f, 64.6391f, 253.4f, 64.6225f, 254.1f, 64.6208f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42813d05), SkBits2Float(0x437f8000), SkBits2Float(0x428137d7), SkBits2Float(0x4380199a), SkBits2Float(0x42814659));  // 254.8f, 64.6192f, 255.5f, 64.6091f, 256.2f, 64.6374f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x428154da), SkBits2Float(0x4380cccd), SkBits2Float(0x42817565), SkBits2Float(0x43812666), SkBits2Float(0x428194e8));  // 256.9f, 64.6657f, 257.6f, 64.7293f, 258.3f, 64.7908f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x4281b46a), SkBits2Float(0x4381d99a), SkBits2Float(0x4281e906), SkBits2Float(0x43823333), SkBits2Float(0x42820368));  // 259, 64.8524f, 259.7f, 64.9551f, 260.4f, 65.0067f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42821dca), SkBits2Float(0x4382e666), SkBits2Float(0x42822b3c), SkBits2Float(0x43834000), SkBits2Float(0x42823333));  // 261.1f, 65.0582f, 261.8f, 65.0844f, 262.5f, 65.1f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42823b2a), SkBits2Float(0x4383f333), SkBits2Float(0x42823333), SkBits2Float(0x43844ccd), SkBits2Float(0x42823333));  // 263.2f, 65.1156f, 263.9f, 65.1f, 264.6f, 65.1f
+path.lineTo(SkBits2Float(0x43844ccd), SkBits2Float(0x42823333));  // 264.6f, 65.1f
+path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x428230fb), SkBits2Float(0x438e7333), SkBits2Float(0x4282293a), SkBits2Float(0x438ecccd), SkBits2Float(0x428225e0));  // 284.2f, 65.0957f, 284.9f, 65.0805f, 285.6f, 65.074f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42822286), SkBits2Float(0x438f8000), SkBits2Float(0x42821cde), SkBits2Float(0x438fd99a), SkBits2Float(0x42821f17));  // 286.3f, 65.0674f, 287, 65.0564f, 287.7f, 65.0607f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42822150), SkBits2Float(0x43908ccd), SkBits2Float(0x42822fd9), SkBits2Float(0x4390e666), SkBits2Float(0x42823333));  // 288.4f, 65.0651f, 289.1f, 65.0935f, 289.8f, 65.1f
+path.lineTo(SkBits2Float(0x4390e666), SkBits2Float(0x42823333));  // 289.8f, 65.1f
+path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823332), SkBits2Float(0x439a0000), SkBits2Float(0x42823842), SkBits2Float(0x439a599a), SkBits2Float(0x4282332a));  // 307.3f, 65.1f, 308, 65.1099f, 308.7f, 65.0999f
+path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42822e12), SkBits2Float(0x439b0ccd), SkBits2Float(0x42821e94), SkBits2Float(0x439b6666), SkBits2Float(0x428214a4));  // 309.4f, 65.09f, 310.1f, 65.0597f, 310.8f, 65.0403f
+path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42820ab4), SkBits2Float(0x439c199a), SkBits2Float(0x42820185), SkBits2Float(0x439c7333), SkBits2Float(0x4281f789));  // 311.5f, 65.0209f, 312.2f, 65.003f, 312.9f, 64.9835f
+path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x4281ed8d), SkBits2Float(0x439d2666), SkBits2Float(0x4281e391), SkBits2Float(0x439d8000), SkBits2Float(0x4281d8bc));  // 313.6f, 64.964f, 314.3f, 64.9445f, 315, 64.9233f
+path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4281cde7), SkBits2Float(0x439e3333), SkBits2Float(0x4281c0c4), SkBits2Float(0x439e8ccd), SkBits2Float(0x4281b68b));  // 315.7f, 64.9022f, 316.4f, 64.8765f, 317.1f, 64.8565f
+path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4281ac53), SkBits2Float(0x439f4000), SkBits2Float(0x4281a27a), SkBits2Float(0x439f999a), SkBits2Float(0x42819b69));  // 317.8f, 64.8366f, 318.5f, 64.8173f, 319.2f, 64.8035f
+path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x42819459), SkBits2Float(0x43a04ccd), SkBits2Float(0x42818f8b), SkBits2Float(0x43a0a666), SkBits2Float(0x42818c26));  // 319.9f, 64.7897f, 320.6f, 64.7804f, 321.3f, 64.7737f
+path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x428188c2), SkBits2Float(0x43a1599a), SkBits2Float(0x42818795), SkBits2Float(0x43a1b333), SkBits2Float(0x42818710));  // 322, 64.7671f, 322.7f, 64.7648f, 323.4f, 64.7638f
+path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4281868b), SkBits2Float(0x43a26666), SkBits2Float(0x42818824), SkBits2Float(0x43a2c000), SkBits2Float(0x42818909));  // 324.1f, 64.7628f, 324.8f, 64.7659f, 325.5f, 64.7676f
+path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x428189ee), SkBits2Float(0x43a37333), SkBits2Float(0x42818de2), SkBits2Float(0x43a3cccd), SkBits2Float(0x42818c6d));  // 326.2f, 64.7694f, 326.9f, 64.7771f, 327.6f, 64.7743f
+path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x42818af7), SkBits2Float(0x43a48000), SkBits2Float(0x428185be), SkBits2Float(0x43a4d99a), SkBits2Float(0x42818048));  // 328.3f, 64.7714f, 329, 64.7612f, 329.7f, 64.7505f
+path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x42817ad1), SkBits2Float(0x43a58ccd), SkBits2Float(0x42816e33), SkBits2Float(0x43a5e666), SkBits2Float(0x42816ba7));  // 330.4f, 64.7399f, 331.1f, 64.7152f, 331.8f, 64.7103f
+path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4281691c), SkBits2Float(0x43a6999a), SkBits2Float(0x42816b46), SkBits2Float(0x43a6f333), SkBits2Float(0x42817104));  // 332.5f, 64.7053f, 333.2f, 64.7095f, 333.9f, 64.7207f
+path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x428176c3), SkBits2Float(0x43a7a666), SkBits2Float(0x42817fa9), SkBits2Float(0x43a80000), SkBits2Float(0x42818e1f));  // 334.6f, 64.732f, 335.3f, 64.7493f, 336, 64.7776f
+path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x42819c95), SkBits2Float(0x43a8b333), SkBits2Float(0x4281b1ec), SkBits2Float(0x43a90ccd), SkBits2Float(0x4281c7c7));  // 336.7f, 64.8058f, 337.4f, 64.8475f, 338.1f, 64.8902f
+path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x4281dda2), SkBits2Float(0x43a9c000), SkBits2Float(0x428209cf), SkBits2Float(0x43aa199a), SkBits2Float(0x42821140));  // 338.8f, 64.9329f, 339.5f, 65.0192f, 340.2f, 65.0337f
+path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x428218b0), SkBits2Float(0x43aacccd), SkBits2Float(0x42820dff), SkBits2Float(0x43ab2666), SkBits2Float(0x4281f46b));  // 340.9f, 65.0482f, 341.6f, 65.0273f, 342.3f, 64.9774f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x4281dad8), SkBits2Float(0x43abd99a), SkBits2Float(0x42819956), SkBits2Float(0x43ac3333), SkBits2Float(0x428177cd));  // 343, 64.9274f, 343.7f, 64.7995f, 344.4f, 64.734f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42815644), SkBits2Float(0x43ace666), SkBits2Float(0x42813910), SkBits2Float(0x43ad4000), SkBits2Float(0x42812b37));  // 345.1f, 64.6685f, 345.8f, 64.6115f, 346.5f, 64.5844f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42811d5e), SkBits2Float(0x43adf333), SkBits2Float(0x42812394), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428124b5));  // 347.2f, 64.5574f, 347.9f, 64.5695f, 348.6f, 64.5717f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x428125d6), SkBits2Float(0x43af0000), SkBits2Float(0x42812c1c), SkBits2Float(0x43af599a), SkBits2Float(0x428131ff));  // 349.3f, 64.5739f, 350, 64.5862f, 350.7f, 64.5976f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x428137e3), SkBits2Float(0x43b00ccd), SkBits2Float(0x4281417f), SkBits2Float(0x43b06666), SkBits2Float(0x4281480b));  // 351.4f, 64.6092f, 352.1f, 64.6279f, 352.8f, 64.6407f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42814e97), SkBits2Float(0x43b1199a), SkBits2Float(0x4281534c), SkBits2Float(0x43b17333), SkBits2Float(0x42815947));  // 353.5f, 64.6535f, 354.2f, 64.6627f, 354.9f, 64.6744f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42815f42), SkBits2Float(0x43b22666), SkBits2Float(0x428165ff), SkBits2Float(0x43b28000), SkBits2Float(0x42816bee));  // 355.6f, 64.6861f, 356.3f, 64.6992f, 357, 64.7108f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x428171de), SkBits2Float(0x43b33333), SkBits2Float(0x42817af5), SkBits2Float(0x43b38ccd), SkBits2Float(0x42817ce3));  // 357.7f, 64.7224f, 358.4f, 64.7402f, 359.1f, 64.7439f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42817ed2), SkBits2Float(0x43b44000), SkBits2Float(0x42817bcf), SkBits2Float(0x43b4999a), SkBits2Float(0x42817786));  // 359.8f, 64.7477f, 360.5f, 64.7418f, 361.2f, 64.7334f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4281733d), SkBits2Float(0x43b54ccd), SkBits2Float(0x428167a7), SkBits2Float(0x43b5a666), SkBits2Float(0x4281632d));  // 361.9f, 64.7251f, 362.6f, 64.7024f, 363.3f, 64.6937f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42815eb3), SkBits2Float(0x43b6599a), SkBits2Float(0x42815b7e), SkBits2Float(0x43b6b333), SkBits2Float(0x42815cab));  // 364, 64.685f, 364.7f, 64.6787f, 365.4f, 64.681f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42815dd8), SkBits2Float(0x43b76666), SkBits2Float(0x4281644d), SkBits2Float(0x43b7c000), SkBits2Float(0x42816a3c));  // 366.1f, 64.6833f, 366.8f, 64.6959f, 367.5f, 64.7075f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4281702b), SkBits2Float(0x43b87333), SkBits2Float(0x428179d3), SkBits2Float(0x43b8cccd), SkBits2Float(0x42818048));  // 368.2f, 64.7191f, 368.9f, 64.7379f, 369.6f, 64.7505f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x428186bc), SkBits2Float(0x43b98000), SkBits2Float(0x42818d4a), SkBits2Float(0x43b9d99a), SkBits2Float(0x428190f6));  // 370.3f, 64.7632f, 371, 64.776f, 371.7f, 64.7831f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x428194a3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x428193b0), SkBits2Float(0x43bae666), SkBits2Float(0x42819653));  // 372.4f, 64.7903f, 373.1f, 64.7885f, 373.8f, 64.7936f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x428198f6), SkBits2Float(0x43bb999a), SkBits2Float(0x42819840), SkBits2Float(0x43bbf333), SkBits2Float(0x4281a0c6));  // 374.5f, 64.7988f, 375.2f, 64.7974f, 375.9f, 64.814f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4281a94d), SkBits2Float(0x43bca666), SkBits2Float(0x4281bc0d), SkBits2Float(0x43bd0000), SkBits2Float(0x4281c979));  // 376.6f, 64.8307f, 377.3f, 64.8673f, 378, 64.8935f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x4281d6e5), SkBits2Float(0x43bdb333), SkBits2Float(0x4281e6fe), SkBits2Float(0x43be0ccd), SkBits2Float(0x4281f14e));  // 378.7f, 64.9197f, 379.4f, 64.9512f, 380.1f, 64.9713f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x4281fb9e), SkBits2Float(0x43bec000), SkBits2Float(0x4281fd75), SkBits2Float(0x43bf199a), SkBits2Float(0x42820759));  // 380.8f, 64.9914f, 381.5f, 64.995f, 382.2f, 65.0144f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x4282113e), SkBits2Float(0x43bfcccd), SkBits2Float(0x42822559), SkBits2Float(0x43c02666), SkBits2Float(0x42822ca8));  // 382.9f, 65.0337f, 383.6f, 65.0729f, 384.3f, 65.0872f
+path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333));  // 384.3f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42823333), SkBits2Float(0x43c2f333), SkBits2Float(0x428239f1), SkBits2Float(0x43c34ccd), SkBits2Float(0x42823333));  // 389.2f, 65.1f, 389.9f, 65.1132f, 390.6f, 65.1f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42822c75), SkBits2Float(0x43c40000), SkBits2Float(0x42822289), SkBits2Float(0x43c4599a), SkBits2Float(0x42820abe));  // 391.3f, 65.0868f, 392, 65.0675f, 392.7f, 65.021f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x4281f2f3), SkBits2Float(0x43c50ccd), SkBits2Float(0x4281be4d), SkBits2Float(0x43c56666), SkBits2Float(0x4281a471));  // 393.4f, 64.9745f, 394.1f, 64.8717f, 394.8f, 64.8212f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42818a96), SkBits2Float(0x43c6199a), SkBits2Float(0x428177e3), SkBits2Float(0x43c67333), SkBits2Float(0x42816f99));  // 395.5f, 64.7707f, 396.2f, 64.7342f, 396.9f, 64.718f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x4281674f), SkBits2Float(0x43c72666), SkBits2Float(0x42817195), SkBits2Float(0x43c78000), SkBits2Float(0x428172b7));  // 397.6f, 64.7018f, 398.3f, 64.7218f, 399, 64.7241f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x428173d8), SkBits2Float(0x43c83333), SkBits2Float(0x42817528), SkBits2Float(0x43c88ccd), SkBits2Float(0x42817661));  // 399.7f, 64.7263f, 400.4f, 64.7288f, 401.1f, 64.7312f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x4281779a), SkBits2Float(0x43c94000), SkBits2Float(0x4281778d), SkBits2Float(0x43c9999a), SkBits2Float(0x42817a0c));  // 401.8f, 64.7336f, 402.5f, 64.7335f, 403.2f, 64.7384f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42817c8c), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42817f49), SkBits2Float(0x43caa666), SkBits2Float(0x4281855e));  // 403.9f, 64.7433f, 404.6f, 64.7486f, 405.3f, 64.7605f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42818b72), SkBits2Float(0x43cb599a), SkBits2Float(0x4281985b), SkBits2Float(0x43cbb333), SkBits2Float(0x42819e87));  // 406, 64.7724f, 406.7f, 64.7976f, 407.4f, 64.8096f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x4281a4b3), SkBits2Float(0x43cc6666), SkBits2Float(0x4281a6ea), SkBits2Float(0x43ccc000), SkBits2Float(0x4281aa66));  // 408.1f, 64.8217f, 408.8f, 64.826f, 409.5f, 64.8328f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x4281ade2), SkBits2Float(0x43cd7333), SkBits2Float(0x4281aad0), SkBits2Float(0x43cdcccd), SkBits2Float(0x4281b36e));  // 410.2f, 64.8396f, 410.9f, 64.8336f, 411.6f, 64.8504f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x4281bc0c), SkBits2Float(0x43ce8000), SkBits2Float(0x4281d071), SkBits2Float(0x43ced99a), SkBits2Float(0x4281de19));  // 412.3f, 64.8673f, 413, 64.9071f, 413.7f, 64.9338f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x4281ebc2), SkBits2Float(0x43cf8ccd), SkBits2Float(0x4281fb65), SkBits2Float(0x43cfe666), SkBits2Float(0x42820561));  // 414.4f, 64.9605f, 415.1f, 64.991f, 415.8f, 65.0105f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42820f5d), SkBits2Float(0x43d0999a), SkBits2Float(0x428217a6), SkBits2Float(0x43d0f333), SkBits2Float(0x42821a01));  // 416.5f, 65.03f, 417.2f, 65.0462f, 417.9f, 65.0508f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42821c5b), SkBits2Float(0x43d1a666), SkBits2Float(0x42821a47), SkBits2Float(0x43d20000), SkBits2Float(0x4282137f));  // 418.6f, 65.0554f, 419.3f, 65.0513f, 420, 65.0381f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42820cb6), SkBits2Float(0x43d2b333), SkBits2Float(0x4281fcb3), SkBits2Float(0x43d30ccd), SkBits2Float(0x4281f14e));  // 420.7f, 65.0248f, 421.4f, 64.9936f, 422.1f, 64.9713f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x4281e5e8), SkBits2Float(0x43d3c000), SkBits2Float(0x4281d645), SkBits2Float(0x43d4199a), SkBits2Float(0x4281cf1d));  // 422.8f, 64.949f, 423.5f, 64.9185f, 424.2f, 64.9045f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x4281c7f4), SkBits2Float(0x43d4cccd), SkBits2Float(0x4281c5d7), SkBits2Float(0x43d52666), SkBits2Float(0x4281c65c));  // 424.9f, 64.8905f, 425.6f, 64.8864f, 426.3f, 64.8874f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x4281c6e1), SkBits2Float(0x43d5d99a), SkBits2Float(0x4281d040), SkBits2Float(0x43d63333), SkBits2Float(0x4281d23a));  // 427, 64.8884f, 427.7f, 64.9067f, 428.4f, 64.9106f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x4281d435), SkBits2Float(0x43d6e666), SkBits2Float(0x4281d7ed), SkBits2Float(0x43d74000), SkBits2Float(0x4281d23a));  // 429.1f, 64.9145f, 429.8f, 64.9217f, 430.5f, 64.9106f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x4281cc88), SkBits2Float(0x43d7f333), SkBits2Float(0x4281ba4e), SkBits2Float(0x43d84ccd), SkBits2Float(0x4281b009));  // 431.2f, 64.8995f, 431.9f, 64.8639f, 432.6f, 64.8438f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x4281a5c5), SkBits2Float(0x43d90000), SkBits2Float(0x4281997b), SkBits2Float(0x43d9599a), SkBits2Float(0x428194a1));  // 433.3f, 64.8238f, 434, 64.7998f, 434.7f, 64.7903f
+path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x42818fc7), SkBits2Float(0x43da0ccd), SkBits2Float(0x4281929b), SkBits2Float(0x43da6666), SkBits2Float(0x428192ef));  // 435.4f, 64.7808f, 436.1f, 64.7863f, 436.8f, 64.787f
+path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x42819343), SkBits2Float(0x43db199a), SkBits2Float(0x428194dc), SkBits2Float(0x43db7333), SkBits2Float(0x4281969a));  // 437.5f, 64.7876f, 438.2f, 64.7907f, 438.9f, 64.7941f
+path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x42819858), SkBits2Float(0x43dc2666), SkBits2Float(0x42819925), SkBits2Float(0x43dc8000), SkBits2Float(0x42819d62));  // 439.6f, 64.7975f, 440.3f, 64.7991f, 441, 64.8074f
+path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x4281a19f), SkBits2Float(0x43dd3333), SkBits2Float(0x4281a9d2), SkBits2Float(0x43dd8ccd), SkBits2Float(0x4281b009));  // 441.7f, 64.8157f, 442.4f, 64.8317f, 443.1f, 64.8438f
+path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x4281b641), SkBits2Float(0x43de4000), SkBits2Float(0x4281be1f), SkBits2Float(0x43de999a), SkBits2Float(0x4281c2b1));  // 443.8f, 64.856f, 444.5f, 64.8713f, 445.2f, 64.8803f
+path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x4281c742), SkBits2Float(0x43df4ccd), SkBits2Float(0x4281ca45), SkBits2Float(0x43dfa666), SkBits2Float(0x4281cb72));  // 445.9f, 64.8892f, 446.6f, 64.8951f, 447.3f, 64.8974f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x4281cc9f), SkBits2Float(0x43e0599a), SkBits2Float(0x4281cb72), SkBits2Float(0x43e0b333), SkBits2Float(0x4281c9c0));  // 448, 64.8997f, 448.7f, 64.8974f, 449.4f, 64.894f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x4281c80e), SkBits2Float(0x43e16666), SkBits2Float(0x4281c34c), SkBits2Float(0x43e1c000), SkBits2Float(0x4281c145));  // 450.1f, 64.8907f, 450.8f, 64.8814f, 451.5f, 64.8775f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x4281bf3f), SkBits2Float(0x43e27333), SkBits2Float(0x4281c026), SkBits2Float(0x43e2cccd), SkBits2Float(0x4281bd9a));  // 452.2f, 64.8735f, 452.9f, 64.8753f, 453.6f, 64.8703f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x4281bb0f), SkBits2Float(0x43e38000), SkBits2Float(0x4281b877), SkBits2Float(0x43e3d99a), SkBits2Float(0x4281b202));  // 454.3f, 64.8653f, 455, 64.8603f, 455.7f, 64.8477f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x4281ab8e), SkBits2Float(0x43e48ccd), SkBits2Float(0x4281a1fe), SkBits2Float(0x43e4e666), SkBits2Float(0x428196e0));  // 456.4f, 64.8351f, 457.1f, 64.8164f, 457.8f, 64.7947f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42818bc3), SkBits2Float(0x43e5999a), SkBits2Float(0x42817cb2), SkBits2Float(0x43e5f333), SkBits2Float(0x42816f52));  // 458.5f, 64.773f, 459.2f, 64.7435f, 459.9f, 64.7174f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x428161f2), SkBits2Float(0x43e6a666), SkBits2Float(0x428151a5), SkBits2Float(0x43e70000), SkBits2Float(0x4281469f));  // 460.6f, 64.6913f, 461.3f, 64.6595f, 462, 64.6379f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42813b9a), SkBits2Float(0x43e7b333), SkBits2Float(0x428132d7), SkBits2Float(0x43e80ccd), SkBits2Float(0x42812d30));  // 462.7f, 64.6164f, 463.4f, 64.5993f, 464.1f, 64.5883f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42812789), SkBits2Float(0x43e8c000), SkBits2Float(0x428125d6), SkBits2Float(0x43e9199a), SkBits2Float(0x428124b5));  // 464.8f, 64.5772f, 465.5f, 64.5739f, 466.2f, 64.5717f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42812394), SkBits2Float(0x43e9cccd), SkBits2Float(0x4281258e), SkBits2Float(0x43ea2666), SkBits2Float(0x42812667));  // 466.9f, 64.5695f, 467.6f, 64.5733f, 468.3f, 64.575f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42812740), SkBits2Float(0x43ead99a), SkBits2Float(0x42812819), SkBits2Float(0x43eb3333), SkBits2Float(0x428129cb));  // 469, 64.5767f, 469.7f, 64.5783f, 470.4f, 64.5816f
+path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42812b7e), SkBits2Float(0x43ebe666), SkBits2Float(0x42812734), SkBits2Float(0x43ec4000), SkBits2Float(0x42813094));  // 471.1f, 64.5849f, 471.8f, 64.5766f, 472.5f, 64.5949f
+path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x428139f3), SkBits2Float(0x43ecf333), SkBits2Float(0x42814838), SkBits2Float(0x43ed4ccd), SkBits2Float(0x42816208));  // 473.2f, 64.6132f, 473.9f, 64.6411f, 474.6f, 64.6915f
+path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x42817bd8), SkBits2Float(0x43ee0000), SkBits2Float(0x4281a8c8), SkBits2Float(0x43ee599a), SkBits2Float(0x4281cb72));  // 475.3f, 64.7419f, 476, 64.8297f, 476.7f, 64.8974f
+path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x4281ee1c), SkBits2Float(0x43ef0ccd), SkBits2Float(0x428220ba), SkBits2Float(0x43ef6666), SkBits2Float(0x42823205));  // 477.4f, 64.9651f, 478.1f, 65.0639f, 478.8f, 65.0977f
+path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42824350), SkBits2Float(0x43f0199a), SkBits2Float(0x42823301), SkBits2Float(0x43f07333), SkBits2Float(0x42823333));  // 479.5f, 65.1315f, 480.2f, 65.0996f, 480.9f, 65.1f
+path.lineTo(SkBits2Float(0x43f07333), SkBits2Float(0x42823333));  // 480.9f, 65.1f
+path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42823333), SkBits2Float(0x43fcb333), SkBits2Float(0x42823772), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42823333));  // 504.7f, 65.1f, 505.4f, 65.1083f, 506.1f, 65.1f
+path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42822ef4), SkBits2Float(0x43fdc000), SkBits2Float(0x428227e9), SkBits2Float(0x43fe199a), SkBits2Float(0x428219ba));  // 506.8f, 65.0917f, 507.5f, 65.0779f, 508.2f, 65.0502f
+path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42820b8b), SkBits2Float(0x43fecccd), SkBits2Float(0x4281ebf3), SkBits2Float(0x43ff2666), SkBits2Float(0x4281de19));  // 508.9f, 65.0225f, 509.6f, 64.9608f, 510.3f, 64.9338f
+path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x4281d040), SkBits2Float(0x43ffd99a), SkBits2Float(0x4281cbde), SkBits2Float(0x4400199a), SkBits2Float(0x4281c6a2));  // 511, 64.9067f, 511.7f, 64.8982f, 512.4f, 64.888f
+path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x4281c167), SkBits2Float(0x44007333), SkBits2Float(0x4281bf82), SkBits2Float(0x4400a000), SkBits2Float(0x4281beb5));  // 513.1f, 64.8777f, 513.8f, 64.874f, 514.5f, 64.8725f
+path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x4281bde8), SkBits2Float(0x4400f99a), SkBits2Float(0x4281bfe4), SkBits2Float(0x44012666), SkBits2Float(0x4281c1d2));  // 515.2f, 64.8709f, 515.9f, 64.8748f, 516.6f, 64.8786f
+path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x4281c3c1), SkBits2Float(0x44018000), SkBits2Float(0x4281c822), SkBits2Float(0x4401accd), SkBits2Float(0x4281ca4d));  // 517.3f, 64.8823f, 518, 64.8909f, 518.7f, 64.8951f
+path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x4281cc78), SkBits2Float(0x44020666), SkBits2Float(0x4281cf4e), SkBits2Float(0x44023333), SkBits2Float(0x4281ced6));  // 519.4f, 64.8994f, 520.1f, 64.9049f, 520.8f, 64.904f
+path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x4281ce5f), SkBits2Float(0x44028ccd), SkBits2Float(0x4281cb81), SkBits2Float(0x4402b99a), SkBits2Float(0x4281c781));  // 521.5f, 64.9031f, 522.2f, 64.8975f, 522.9f, 64.8897f
+path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x4281c380), SkBits2Float(0x44031333), SkBits2Float(0x4281babb), SkBits2Float(0x44034000), SkBits2Float(0x4281b6d2));  // 523.6f, 64.8818f, 524.3f, 64.8647f, 525, 64.8571f
+path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x4281b2e9), SkBits2Float(0x4403999a), SkBits2Float(0x4281b0a6), SkBits2Float(0x4403c666), SkBits2Float(0x4281b009));  // 525.7f, 64.8494f, 526.4f, 64.845f, 527.1f, 64.8438f
+path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x4281af6d), SkBits2Float(0x44042000), SkBits2Float(0x42819d4b), SkBits2Float(0x44044ccd), SkBits2Float(0x4281b327));  // 527.8f, 64.8426f, 528.5f, 64.8072f, 529.2f, 64.8499f
+path.cubicTo(SkBits2Float(0x4404799a), SkBits2Float(0x4281c903), SkBits2Float(0x4404a666), SkBits2Float(0x42821ddc), SkBits2Float(0x4404d333), SkBits2Float(0x42823333));  // 529.9f, 64.8926f, 530.6f, 65.0583f, 531.3f, 65.1f
+path.cubicTo(SkBits2Float(0x44050000), SkBits2Float(0x4282488b), SkBits2Float(0x44052ccd), SkBits2Float(0x42823333), SkBits2Float(0x4405599a), SkBits2Float(0x42823333));  // 532, 65.1417f, 532.7f, 65.1f, 533.4f, 65.1f
+path.lineTo(SkBits2Float(0x4405599a), SkBits2Float(0x42823333));  // 533.4f, 65.1f
+path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.close();
+    return path;
+}
+
+static SkPath path2() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42823333), SkBits2Float(0x431ee666), SkBits2Float(0x42822fd9), SkBits2Float(0x431f999a), SkBits2Float(0x42823333));  // 158.2f, 65.1f, 158.9f, 65.0935f, 159.6f, 65.1f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x4282368d), SkBits2Float(0x43210000), SkBits2Float(0x42823e05), SkBits2Float(0x4321b333), SkBits2Float(0x4282474f));  // 160.3f, 65.1065f, 161, 65.1211f, 161.7f, 65.1393f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42825098), SkBits2Float(0x4323199a), SkBits2Float(0x42825f49), SkBits2Float(0x4323cccd), SkBits2Float(0x42826aeb));  // 162.4f, 65.1574f, 163.1f, 65.1861f, 163.8f, 65.2088f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x4282768e), SkBits2Float(0x43253333), SkBits2Float(0x428281ff), SkBits2Float(0x4325e666), SkBits2Float(0x42828d1c));  // 164.5f, 65.2316f, 165.2f, 65.2539f, 165.9f, 65.2756f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x4282983a), SkBits2Float(0x43274ccd), SkBits2Float(0x4282a509), SkBits2Float(0x43280000), SkBits2Float(0x4282ad9b));  // 166.6f, 65.2973f, 167.3f, 65.3223f, 168, 65.3391f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x4282b62d), SkBits2Float(0x43296666), SkBits2Float(0x4282bff9), SkBits2Float(0x432a199a), SkBits2Float(0x4282c089));  // 168.7f, 65.3558f, 169.4f, 65.3749f, 170.1f, 65.376f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x4282c11a), SkBits2Float(0x432b8000), SkBits2Float(0x4282b797), SkBits2Float(0x432c3333), SkBits2Float(0x4282b100));  // 170.8f, 65.3772f, 171.5f, 65.3586f, 172.2f, 65.3457f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x4282aa68), SkBits2Float(0x432d999a), SkBits2Float(0x4282a054), SkBits2Float(0x432e4ccd), SkBits2Float(0x428298fb));  // 172.9f, 65.3328f, 173.6f, 65.3131f, 174.3f, 65.2988f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x428291a2), SkBits2Float(0x432fb333), SkBits2Float(0x428289f3), SkBits2Float(0x43306666), SkBits2Float(0x428284e8));  // 175, 65.2844f, 175.7f, 65.2694f, 176.4f, 65.2596f
+path.cubicTo(SkBits2Float(0x4331199a), SkBits2Float(0x42827fde), SkBits2Float(0x4331cccd), SkBits2Float(0x42827cff), SkBits2Float(0x43328000), SkBits2Float(0x42827abc));  // 177.1f, 65.2497f, 177.8f, 65.2441f, 178.5f, 65.2397f
+path.cubicTo(SkBits2Float(0x43333333), SkBits2Float(0x42827879), SkBits2Float(0x4333e666), SkBits2Float(0x428277a0), SkBits2Float(0x4334999a), SkBits2Float(0x42827757));  // 179.2f, 65.2353f, 179.9f, 65.2336f, 180.6f, 65.2331f
+path.cubicTo(SkBits2Float(0x43354ccd), SkBits2Float(0x4282770f), SkBits2Float(0x43360000), SkBits2Float(0x428277ac), SkBits2Float(0x4336b333), SkBits2Float(0x4282790a));  // 181.3f, 65.2325f, 182, 65.2337f, 182.7f, 65.2364f
+path.cubicTo(SkBits2Float(0x43376666), SkBits2Float(0x42827a68), SkBits2Float(0x4338199a), SkBits2Float(0x42827d9d), SkBits2Float(0x4338cccd), SkBits2Float(0x42827f8b));  // 183.4f, 65.2391f, 184.1f, 65.2453f, 184.8f, 65.2491f
+path.cubicTo(SkBits2Float(0x43398000), SkBits2Float(0x4282817a), SkBits2Float(0x433a3333), SkBits2Float(0x428283c9), SkBits2Float(0x433ae666), SkBits2Float(0x428284a2));  // 185.5f, 65.2529f, 186.2f, 65.2574f, 186.9f, 65.259f
+path.cubicTo(SkBits2Float(0x433b999a), SkBits2Float(0x4282857b), SkBits2Float(0x433c4ccd), SkBits2Float(0x428284de), SkBits2Float(0x433d0000), SkBits2Float(0x428284a2));  // 187.6f, 65.2607f, 188.3f, 65.2595f, 189, 65.259f
+path.cubicTo(SkBits2Float(0x433db333), SkBits2Float(0x42828465), SkBits2Float(0x433e6666), SkBits2Float(0x42828251), SkBits2Float(0x433f199a), SkBits2Float(0x42828336));  // 189.7f, 65.2586f, 190.4f, 65.2545f, 191.1f, 65.2563f
+path.cubicTo(SkBits2Float(0x433fcccd), SkBits2Float(0x4282841b), SkBits2Float(0x43408000), SkBits2Float(0x428286ef), SkBits2Float(0x43413333), SkBits2Float(0x428289ff));  // 191.8f, 65.258f, 192.5f, 65.2635f, 193.2f, 65.2695f
+path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x42828d0f), SkBits2Float(0x4342999a), SkBits2Float(0x4282930c), SkBits2Float(0x43434ccd), SkBits2Float(0x42829597));  // 193.9f, 65.2755f, 194.6f, 65.2872f, 195.3f, 65.2922f
+path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x42829822), SkBits2Float(0x4344b333), SkBits2Float(0x428298fa), SkBits2Float(0x43456666), SkBits2Float(0x42829942));  // 196, 65.2971f, 196.7f, 65.2988f, 197.4f, 65.2993f
+path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x4282998a), SkBits2Float(0x4346cccd), SkBits2Float(0x42829749), SkBits2Float(0x43478000), SkBits2Float(0x42829749));  // 198.1f, 65.2999f, 198.8f, 65.2955f, 199.5f, 65.2955f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42829749), SkBits2Float(0x4348e666), SkBits2Float(0x428297d8), SkBits2Float(0x4349999a), SkBits2Float(0x42829942));  // 200.2f, 65.2955f, 200.9f, 65.2966f, 201.6f, 65.2993f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42829aac), SkBits2Float(0x434b0000), SkBits2Float(0x42829e12), SkBits2Float(0x434bb333), SkBits2Float(0x42829fc4));  // 202.3f, 65.3021f, 203, 65.3087f, 203.7f, 65.312f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x4282a176), SkBits2Float(0x434d199a), SkBits2Float(0x4282a1f9), SkBits2Float(0x434dcccd), SkBits2Float(0x4282a36f));  // 204.4f, 65.3154f, 205.1f, 65.3164f, 205.8f, 65.3192f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x4282a4e4), SkBits2Float(0x434f3333), SkBits2Float(0x4282a71b), SkBits2Float(0x434fe666), SkBits2Float(0x4282a885));  // 206.5f, 65.3221f, 207.2f, 65.3264f, 207.9f, 65.3291f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x4282a9ef), SkBits2Float(0x43514ccd), SkBits2Float(0x4282aad4), SkBits2Float(0x43520000), SkBits2Float(0x4282abe9));  // 208.6f, 65.3319f, 209.3f, 65.3336f, 210, 65.3358f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x4282acff), SkBits2Float(0x43536666), SkBits2Float(0x4282ad0c), SkBits2Float(0x4354199a), SkBits2Float(0x4282af07));  // 210.7f, 65.3379f, 211.4f, 65.338f, 212.1f, 65.3419f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x4282b101), SkBits2Float(0x43558000), SkBits2Float(0x4282b5c2), SkBits2Float(0x43563333), SkBits2Float(0x4282b7c8));  // 212.8f, 65.3457f, 213.5f, 65.355f, 214.2f, 65.3589f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x4282b9ce), SkBits2Float(0x4357999a), SkBits2Float(0x4282b9c3), SkBits2Float(0x43584ccd), SkBits2Float(0x4282bb2c));  // 214.9f, 65.3629f, 215.6f, 65.3628f, 216.3f, 65.3656f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x4282bc96), SkBits2Float(0x4359b333), SkBits2Float(0x4282be3c), SkBits2Float(0x435a6666), SkBits2Float(0x4282c043));  // 217, 65.3683f, 217.7f, 65.3716f, 218.4f, 65.3755f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x4282c249), SkBits2Float(0x435bcccd), SkBits2Float(0x4282c50f), SkBits2Float(0x435c8000), SkBits2Float(0x4282c752));  // 219.1f, 65.3795f, 219.8f, 65.3849f, 220.5f, 65.3893f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x4282c995), SkBits2Float(0x435de666), SkBits2Float(0x4282cc2d), SkBits2Float(0x435e999a), SkBits2Float(0x4282cdd4));  // 221.2f, 65.3937f, 221.9f, 65.3988f, 222.6f, 65.402f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x4282cf7a), SkBits2Float(0x43600000), SkBits2Float(0x4282d211), SkBits2Float(0x4360b333), SkBits2Float(0x4282d138));  // 223.3f, 65.4052f, 224, 65.4103f, 224.7f, 65.4086f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x4282d05f), SkBits2Float(0x4362199a), SkBits2Float(0x4282cd7f), SkBits2Float(0x4362cccd), SkBits2Float(0x4282c8bd));  // 225.4f, 65.407f, 226.1f, 65.4014f, 226.8f, 65.3921f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x4282c3fb), SkBits2Float(0x43643333), SkBits2Float(0x4282b8dc), SkBits2Float(0x4364e666), SkBits2Float(0x4282b4aa));  // 227.5f, 65.3828f, 228.2f, 65.3611f, 228.9f, 65.3529f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x4282b079), SkBits2Float(0x43664ccd), SkBits2Float(0x4282af57), SkBits2Float(0x43670000), SkBits2Float(0x4282af94));  // 229.6f, 65.3447f, 230.3f, 65.3425f, 231, 65.3429f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x4282afd1), SkBits2Float(0x43686666), SkBits2Float(0x4282b38b), SkBits2Float(0x4369199a), SkBits2Float(0x4282b616));  // 231.7f, 65.3434f, 232.4f, 65.3507f, 233.1f, 65.3556f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x4282b8a1), SkBits2Float(0x436a8000), SkBits2Float(0x4282bc7d), SkBits2Float(0x436b3333), SkBits2Float(0x4282bed7));  // 233.8f, 65.3606f, 234.5f, 65.3681f, 235.2f, 65.3727f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x4282c131), SkBits2Float(0x436c999a), SkBits2Float(0x4282c10c), SkBits2Float(0x436d4ccd), SkBits2Float(0x4282c434));  // 235.9f, 65.3773f, 236.6f, 65.377f, 237.3f, 65.3832f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x4282c75c), SkBits2Float(0x436eb333), SkBits2Float(0x4282cb8d), SkBits2Float(0x436f6666), SkBits2Float(0x4282d1c5));  // 238, 65.3894f, 238.7f, 65.3976f, 239.4f, 65.4097f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x4282d7fd), SkBits2Float(0x4370cccd), SkBits2Float(0x4282e22a), SkBits2Float(0x43718000), SkBits2Float(0x4282e983));  // 240.1f, 65.4219f, 240.8f, 65.4417f, 241.5f, 65.4561f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x4282f0dc), SkBits2Float(0x4372e666), SkBits2Float(0x4282fa30), SkBits2Float(0x4373999a), SkBits2Float(0x4282fddc));  // 242.2f, 65.4704f, 242.9f, 65.4886f, 243.6f, 65.4958f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42830189), SkBits2Float(0x43750000), SkBits2Float(0x4282fec1), SkBits2Float(0x4375b333), SkBits2Float(0x4282ff8e));  // 244.3f, 65.503f, 245, 65.4976f, 245.7f, 65.4991f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x4283005c), SkBits2Float(0x4377199a), SkBits2Float(0x42830015), SkBits2Float(0x4377cccd), SkBits2Float(0x428302ac));  // 246.4f, 65.5007f, 247.1f, 65.5002f, 247.8f, 65.5052f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42830543), SkBits2Float(0x43793333), SkBits2Float(0x42830b0b), SkBits2Float(0x4379e666), SkBits2Float(0x42830f18));  // 248.5f, 65.5103f, 249.2f, 65.5216f, 249.9f, 65.5295f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42831324), SkBits2Float(0x437b4ccd), SkBits2Float(0x428316b9), SkBits2Float(0x437c0000), SkBits2Float(0x42831af7));  // 250.6f, 65.5374f, 251.3f, 65.5444f, 252, 65.5527f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42831f34), SkBits2Float(0x437d6666), SkBits2Float(0x428327af), SkBits2Float(0x437e199a), SkBits2Float(0x42832888));  // 252.7f, 65.5609f, 253.4f, 65.5775f, 254.1f, 65.5792f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42832961), SkBits2Float(0x437f8000), SkBits2Float(0x42832e8f), SkBits2Float(0x4380199a), SkBits2Float(0x4283200d));  // 254.8f, 65.5808f, 255.5f, 65.5909f, 256.2f, 65.5626f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x4283118c), SkBits2Float(0x4380cccd), SkBits2Float(0x4282f101), SkBits2Float(0x43812666), SkBits2Float(0x4282d17e));  // 256.9f, 65.5343f, 257.6f, 65.4707f, 258.3f, 65.4092f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x4282b1fc), SkBits2Float(0x4381d99a), SkBits2Float(0x42827d60), SkBits2Float(0x43823333), SkBits2Float(0x428262fe));  // 259, 65.3476f, 259.7f, 65.2449f, 260.4f, 65.1933f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x4282489c), SkBits2Float(0x4382e666), SkBits2Float(0x42823b2a), SkBits2Float(0x43834000), SkBits2Float(0x42823333));  // 261.1f, 65.1418f, 261.8f, 65.1156f, 262.5f, 65.1f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42822b3c), SkBits2Float(0x4383f333), SkBits2Float(0x42823333), SkBits2Float(0x43844ccd), SkBits2Float(0x42823333));  // 263.2f, 65.0844f, 263.9f, 65.1f, 264.6f, 65.1f
+path.lineTo(SkBits2Float(0x43844ccd), SkBits2Float(0x42823333));  // 264.6f, 65.1f
+path.lineTo(SkBits2Float(0x431d8000), SkBits2Float(0x42823333));  // 157.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x4282356b), SkBits2Float(0x438e7333), SkBits2Float(0x42823d2c), SkBits2Float(0x438ecccd), SkBits2Float(0x42824086));  // 284.2f, 65.1043f, 284.9f, 65.1195f, 285.6f, 65.126f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x428243e0), SkBits2Float(0x438f8000), SkBits2Float(0x42824988), SkBits2Float(0x438fd99a), SkBits2Float(0x4282474f));  // 286.3f, 65.1326f, 287, 65.1436f, 287.7f, 65.1393f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42824516), SkBits2Float(0x43908ccd), SkBits2Float(0x4282368d), SkBits2Float(0x4390e666), SkBits2Float(0x42823333));  // 288.4f, 65.1349f, 289.1f, 65.1065f, 289.8f, 65.1f
+path.lineTo(SkBits2Float(0x4390e666), SkBits2Float(0x42823333));  // 289.8f, 65.1f
+path.lineTo(SkBits2Float(0x438dc000), SkBits2Float(0x42823333));  // 283.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.cubicTo(SkBits2Float(0x4399a666), SkBits2Float(0x42823334), SkBits2Float(0x439a0000), SkBits2Float(0x42822e24), SkBits2Float(0x439a599a), SkBits2Float(0x4282333c));  // 307.3f, 65.1f, 308, 65.0901f, 308.7f, 65.1001f
+path.cubicTo(SkBits2Float(0x439ab333), SkBits2Float(0x42823854), SkBits2Float(0x439b0ccd), SkBits2Float(0x428247d2), SkBits2Float(0x439b6666), SkBits2Float(0x428251c2));  // 309.4f, 65.11f, 310.1f, 65.1403f, 310.8f, 65.1597f
+path.cubicTo(SkBits2Float(0x439bc000), SkBits2Float(0x42825bb2), SkBits2Float(0x439c199a), SkBits2Float(0x428264e1), SkBits2Float(0x439c7333), SkBits2Float(0x42826edd));  // 311.5f, 65.1791f, 312.2f, 65.197f, 312.9f, 65.2165f
+path.cubicTo(SkBits2Float(0x439ccccd), SkBits2Float(0x428278d9), SkBits2Float(0x439d2666), SkBits2Float(0x428282d5), SkBits2Float(0x439d8000), SkBits2Float(0x42828daa));  // 313.6f, 65.236f, 314.3f, 65.2555f, 315, 65.2767f
+path.cubicTo(SkBits2Float(0x439dd99a), SkBits2Float(0x4282987f), SkBits2Float(0x439e3333), SkBits2Float(0x4282a5a2), SkBits2Float(0x439e8ccd), SkBits2Float(0x4282afdb));  // 315.7f, 65.2978f, 316.4f, 65.3235f, 317.1f, 65.3435f
+path.cubicTo(SkBits2Float(0x439ee666), SkBits2Float(0x4282ba13), SkBits2Float(0x439f4000), SkBits2Float(0x4282c3ec), SkBits2Float(0x439f999a), SkBits2Float(0x4282cafd));  // 317.8f, 65.3634f, 318.5f, 65.3827f, 319.2f, 65.3965f
+path.cubicTo(SkBits2Float(0x439ff333), SkBits2Float(0x4282d20d), SkBits2Float(0x43a04ccd), SkBits2Float(0x4282d6db), SkBits2Float(0x43a0a666), SkBits2Float(0x4282da40));  // 319.9f, 65.4103f, 320.6f, 65.4196f, 321.3f, 65.4263f
+path.cubicTo(SkBits2Float(0x43a10000), SkBits2Float(0x4282dda4), SkBits2Float(0x43a1599a), SkBits2Float(0x4282ded1), SkBits2Float(0x43a1b333), SkBits2Float(0x4282df56));  // 322, 65.4329f, 322.7f, 65.4352f, 323.4f, 65.4362f
+path.cubicTo(SkBits2Float(0x43a20ccd), SkBits2Float(0x4282dfdb), SkBits2Float(0x43a26666), SkBits2Float(0x4282de42), SkBits2Float(0x43a2c000), SkBits2Float(0x4282dd5d));  // 324.1f, 65.4372f, 324.8f, 65.4341f, 325.5f, 65.4324f
+path.cubicTo(SkBits2Float(0x43a3199a), SkBits2Float(0x4282dc78), SkBits2Float(0x43a37333), SkBits2Float(0x4282d884), SkBits2Float(0x43a3cccd), SkBits2Float(0x4282d9f9));  // 326.2f, 65.4306f, 326.9f, 65.4229f, 327.6f, 65.4257f
+path.cubicTo(SkBits2Float(0x43a42666), SkBits2Float(0x4282db6f), SkBits2Float(0x43a48000), SkBits2Float(0x4282e0a8), SkBits2Float(0x43a4d99a), SkBits2Float(0x4282e61e));  // 328.3f, 65.4286f, 329, 65.4388f, 329.7f, 65.4494f
+path.cubicTo(SkBits2Float(0x43a53333), SkBits2Float(0x4282eb95), SkBits2Float(0x43a58ccd), SkBits2Float(0x4282f833), SkBits2Float(0x43a5e666), SkBits2Float(0x4282fabf));  // 330.4f, 65.4601f, 331.1f, 65.4848f, 331.8f, 65.4897f
+path.cubicTo(SkBits2Float(0x43a64000), SkBits2Float(0x4282fd4a), SkBits2Float(0x43a6999a), SkBits2Float(0x4282fb20), SkBits2Float(0x43a6f333), SkBits2Float(0x4282f562));  // 332.5f, 65.4947f, 333.2f, 65.4905f, 333.9f, 65.4793f
+path.cubicTo(SkBits2Float(0x43a74ccd), SkBits2Float(0x4282efa3), SkBits2Float(0x43a7a666), SkBits2Float(0x4282e6bd), SkBits2Float(0x43a80000), SkBits2Float(0x4282d847));  // 334.6f, 65.468f, 335.3f, 65.4507f, 336, 65.4224f
+path.cubicTo(SkBits2Float(0x43a8599a), SkBits2Float(0x4282c9d1), SkBits2Float(0x43a8b333), SkBits2Float(0x4282b47a), SkBits2Float(0x43a90ccd), SkBits2Float(0x42829e9f));  // 336.7f, 65.3942f, 337.4f, 65.3525f, 338.1f, 65.3098f
+path.cubicTo(SkBits2Float(0x43a96666), SkBits2Float(0x428288c4), SkBits2Float(0x43a9c000), SkBits2Float(0x42825c97), SkBits2Float(0x43aa199a), SkBits2Float(0x42825526));  // 338.8f, 65.2671f, 339.5f, 65.1808f, 340.2f, 65.1663f
+path.cubicTo(SkBits2Float(0x43aa7333), SkBits2Float(0x42824db6), SkBits2Float(0x43aacccd), SkBits2Float(0x42825867), SkBits2Float(0x43ab2666), SkBits2Float(0x428271fb));  // 340.9f, 65.1518f, 341.6f, 65.1727f, 342.3f, 65.2226f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42828b8e), SkBits2Float(0x43abd99a), SkBits2Float(0x4282cd10), SkBits2Float(0x43ac3333), SkBits2Float(0x4282ee99));  // 343, 65.2726f, 343.7f, 65.4005f, 344.4f, 65.466f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42831022), SkBits2Float(0x43ace666), SkBits2Float(0x42832d56), SkBits2Float(0x43ad4000), SkBits2Float(0x42833b2f));  // 345.1f, 65.5315f, 345.8f, 65.5885f, 346.5f, 65.6156f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42834908), SkBits2Float(0x43adf333), SkBits2Float(0x428342d2), SkBits2Float(0x43ae4ccd), SkBits2Float(0x428341b1));  // 347.2f, 65.6426f, 347.9f, 65.6305f, 348.6f, 65.6283f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42834090), SkBits2Float(0x43af0000), SkBits2Float(0x42833a4a), SkBits2Float(0x43af599a), SkBits2Float(0x42833467));  // 349.3f, 65.6261f, 350, 65.6138f, 350.7f, 65.6023f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42832e83), SkBits2Float(0x43b00ccd), SkBits2Float(0x428324e7), SkBits2Float(0x43b06666), SkBits2Float(0x42831e5b));  // 351.4f, 65.5908f, 352.1f, 65.5721f, 352.8f, 65.5593f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x428317cf), SkBits2Float(0x43b1199a), SkBits2Float(0x4283131a), SkBits2Float(0x43b17333), SkBits2Float(0x42830d1f));  // 353.5f, 65.5465f, 354.2f, 65.5373f, 354.9f, 65.5256f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42830724), SkBits2Float(0x43b22666), SkBits2Float(0x42830067), SkBits2Float(0x43b28000), SkBits2Float(0x4282fa78));  // 355.6f, 65.5139f, 356.3f, 65.5008f, 357, 65.4892f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x4282f488), SkBits2Float(0x43b33333), SkBits2Float(0x4282eb71), SkBits2Float(0x43b38ccd), SkBits2Float(0x4282e983));  // 357.7f, 65.4776f, 358.4f, 65.4598f, 359.1f, 65.4561f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x4282e794), SkBits2Float(0x43b44000), SkBits2Float(0x4282ea97), SkBits2Float(0x43b4999a), SkBits2Float(0x4282eee0));  // 359.8f, 65.4523f, 360.5f, 65.4582f, 361.2f, 65.4666f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x4282f329), SkBits2Float(0x43b54ccd), SkBits2Float(0x4282febf), SkBits2Float(0x43b5a666), SkBits2Float(0x42830339));  // 361.9f, 65.4749f, 362.6f, 65.4976f, 363.3f, 65.5063f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x428307b3), SkBits2Float(0x43b6599a), SkBits2Float(0x42830ae8), SkBits2Float(0x43b6b333), SkBits2Float(0x428309bb));  // 364, 65.515f, 364.7f, 65.5213f, 365.4f, 65.519f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x4283088e), SkBits2Float(0x43b76666), SkBits2Float(0x42830219), SkBits2Float(0x43b7c000), SkBits2Float(0x4282fc2a));  // 366.1f, 65.5167f, 366.8f, 65.5041f, 367.5f, 65.4925f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x4282f63b), SkBits2Float(0x43b87333), SkBits2Float(0x4282ec93), SkBits2Float(0x43b8cccd), SkBits2Float(0x4282e61e));  // 368.2f, 65.4809f, 368.9f, 65.4621f, 369.6f, 65.4494f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x4282dfaa), SkBits2Float(0x43b98000), SkBits2Float(0x4282d91c), SkBits2Float(0x43b9d99a), SkBits2Float(0x4282d570));  // 370.3f, 65.4368f, 371, 65.424f, 371.7f, 65.4169f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x4282d1c3), SkBits2Float(0x43ba8ccd), SkBits2Float(0x4282d2b6), SkBits2Float(0x43bae666), SkBits2Float(0x4282d013));  // 372.4f, 65.4097f, 373.1f, 65.4115f, 373.8f, 65.4064f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x4282cd70), SkBits2Float(0x43bb999a), SkBits2Float(0x4282ce26), SkBits2Float(0x43bbf333), SkBits2Float(0x4282c5a0));  // 374.5f, 65.4012f, 375.2f, 65.4026f, 375.9f, 65.386f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x4282bd19), SkBits2Float(0x43bca666), SkBits2Float(0x4282aa59), SkBits2Float(0x43bd0000), SkBits2Float(0x42829ced));  // 376.6f, 65.3693f, 377.3f, 65.3327f, 378, 65.3065f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42828f81), SkBits2Float(0x43bdb333), SkBits2Float(0x42827f68), SkBits2Float(0x43be0ccd), SkBits2Float(0x42827518));  // 378.7f, 65.2803f, 379.4f, 65.2488f, 380.1f, 65.2287f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42826ac8), SkBits2Float(0x43bec000), SkBits2Float(0x428268f1), SkBits2Float(0x43bf199a), SkBits2Float(0x42825f0d));  // 380.8f, 65.2086f, 381.5f, 65.205f, 382.2f, 65.1856f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42825528), SkBits2Float(0x43bfcccd), SkBits2Float(0x4282410d), SkBits2Float(0x43c02666), SkBits2Float(0x428239be));  // 382.9f, 65.1663f, 383.6f, 65.1271f, 384.3f, 65.1128f
+path.lineTo(SkBits2Float(0x43c02666), SkBits2Float(0x42823333));  // 384.3f, 65.1f
+path.lineTo(SkBits2Float(0x43994ccd), SkBits2Float(0x42823333));  // 306.6f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42823333), SkBits2Float(0x43c2f333), SkBits2Float(0x42822c75), SkBits2Float(0x43c34ccd), SkBits2Float(0x42823333));  // 389.2f, 65.1f, 389.9f, 65.0868f, 390.6f, 65.1f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x428239f1), SkBits2Float(0x43c40000), SkBits2Float(0x428243dd), SkBits2Float(0x43c4599a), SkBits2Float(0x42825ba8));  // 391.3f, 65.1132f, 392, 65.1325f, 392.7f, 65.179f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42827373), SkBits2Float(0x43c50ccd), SkBits2Float(0x4282a819), SkBits2Float(0x43c56666), SkBits2Float(0x4282c1f5));  // 393.4f, 65.2255f, 394.1f, 65.3283f, 394.8f, 65.3788f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x4282dbd0), SkBits2Float(0x43c6199a), SkBits2Float(0x4282ee83), SkBits2Float(0x43c67333), SkBits2Float(0x4282f6cd));  // 395.5f, 65.4293f, 396.2f, 65.4658f, 396.9f, 65.482f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x4282ff17), SkBits2Float(0x43c72666), SkBits2Float(0x4282f4d1), SkBits2Float(0x43c78000), SkBits2Float(0x4282f3af));  // 397.6f, 65.4982f, 398.3f, 65.4782f, 399, 65.4759f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x4282f28e), SkBits2Float(0x43c83333), SkBits2Float(0x4282f13e), SkBits2Float(0x43c88ccd), SkBits2Float(0x4282f005));  // 399.7f, 65.4737f, 400.4f, 65.4712f, 401.1f, 65.4688f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x4282eecc), SkBits2Float(0x43c94000), SkBits2Float(0x4282eed9), SkBits2Float(0x43c9999a), SkBits2Float(0x4282ec5a));  // 401.8f, 65.4664f, 402.5f, 65.4665f, 403.2f, 65.4616f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x4282e9da), SkBits2Float(0x43ca4ccd), SkBits2Float(0x4282e71d), SkBits2Float(0x43caa666), SkBits2Float(0x4282e108));  // 403.9f, 65.4567f, 404.6f, 65.4514f, 405.3f, 65.4395f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x4282daf4), SkBits2Float(0x43cb599a), SkBits2Float(0x4282ce0b), SkBits2Float(0x43cbb333), SkBits2Float(0x4282c7df));  // 406, 65.4276f, 406.7f, 65.4024f, 407.4f, 65.3904f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x4282c1b3), SkBits2Float(0x43cc6666), SkBits2Float(0x4282bf7c), SkBits2Float(0x43ccc000), SkBits2Float(0x4282bc00));  // 408.1f, 65.3783f, 408.8f, 65.374f, 409.5f, 65.3672f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x4282b884), SkBits2Float(0x43cd7333), SkBits2Float(0x4282bb96), SkBits2Float(0x43cdcccd), SkBits2Float(0x4282b2f8));  // 410.2f, 65.3604f, 410.9f, 65.3664f, 411.6f, 65.3495f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x4282aa5a), SkBits2Float(0x43ce8000), SkBits2Float(0x428295f5), SkBits2Float(0x43ced99a), SkBits2Float(0x4282884d));  // 412.3f, 65.3327f, 413, 65.2929f, 413.7f, 65.2662f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42827aa4), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42826b01), SkBits2Float(0x43cfe666), SkBits2Float(0x42826105));  // 414.4f, 65.2395f, 415.1f, 65.209f, 415.8f, 65.1895f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42825709), SkBits2Float(0x43d0999a), SkBits2Float(0x42824ec0), SkBits2Float(0x43d0f333), SkBits2Float(0x42824c65));  // 416.5f, 65.17f, 417.2f, 65.1538f, 417.9f, 65.1492f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42824a0b), SkBits2Float(0x43d1a666), SkBits2Float(0x42824c1f), SkBits2Float(0x43d20000), SkBits2Float(0x428252e7));  // 418.6f, 65.1446f, 419.3f, 65.1487f, 420, 65.1619f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x428259b0), SkBits2Float(0x43d2b333), SkBits2Float(0x428269b3), SkBits2Float(0x43d30ccd), SkBits2Float(0x42827518));  // 420.7f, 65.1752f, 421.4f, 65.2064f, 422.1f, 65.2287f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x4282807e), SkBits2Float(0x43d3c000), SkBits2Float(0x42829021), SkBits2Float(0x43d4199a), SkBits2Float(0x42829749));  // 422.8f, 65.251f, 423.5f, 65.2815f, 424.2f, 65.2955f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42829e72), SkBits2Float(0x43d4cccd), SkBits2Float(0x4282a08f), SkBits2Float(0x43d52666), SkBits2Float(0x4282a00a));  // 424.9f, 65.3095f, 425.6f, 65.3136f, 426.3f, 65.3126f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42829f85), SkBits2Float(0x43d5d99a), SkBits2Float(0x42829626), SkBits2Float(0x43d63333), SkBits2Float(0x4282942c));  // 427, 65.3116f, 427.7f, 65.2933f, 428.4f, 65.2894f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42829231), SkBits2Float(0x43d6e666), SkBits2Float(0x42828e79), SkBits2Float(0x43d74000), SkBits2Float(0x4282942c));  // 429.1f, 65.2855f, 429.8f, 65.2783f, 430.5f, 65.2894f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x428299de), SkBits2Float(0x43d7f333), SkBits2Float(0x4282ac18), SkBits2Float(0x43d84ccd), SkBits2Float(0x4282b65d));  // 431.2f, 65.3005f, 431.9f, 65.3361f, 432.6f, 65.3562f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x4282c0a1), SkBits2Float(0x43d90000), SkBits2Float(0x4282cceb), SkBits2Float(0x43d9599a), SkBits2Float(0x4282d1c5));  // 433.3f, 65.3762f, 434, 65.4002f, 434.7f, 65.4097f
+path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x4282d69f), SkBits2Float(0x43da0ccd), SkBits2Float(0x4282d3cb), SkBits2Float(0x43da6666), SkBits2Float(0x4282d377));  // 435.4f, 65.4192f, 436.1f, 65.4137f, 436.8f, 65.413f
+path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x4282d323), SkBits2Float(0x43db199a), SkBits2Float(0x4282d18a), SkBits2Float(0x43db7333), SkBits2Float(0x4282cfcc));  // 437.5f, 65.4124f, 438.2f, 65.4093f, 438.9f, 65.4059f
+path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x4282ce0e), SkBits2Float(0x43dc2666), SkBits2Float(0x4282cd41), SkBits2Float(0x43dc8000), SkBits2Float(0x4282c904));  // 439.6f, 65.4025f, 440.3f, 65.4009f, 441, 65.3926f
+path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x4282c4c7), SkBits2Float(0x43dd3333), SkBits2Float(0x4282bc94), SkBits2Float(0x43dd8ccd), SkBits2Float(0x4282b65d));  // 441.7f, 65.3843f, 442.4f, 65.3683f, 443.1f, 65.3562f
+path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x4282b025), SkBits2Float(0x43de4000), SkBits2Float(0x4282a847), SkBits2Float(0x43de999a), SkBits2Float(0x4282a3b5));  // 443.8f, 65.344f, 444.5f, 65.3287f, 445.2f, 65.3197f
+path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x42829f24), SkBits2Float(0x43df4ccd), SkBits2Float(0x42829c21), SkBits2Float(0x43dfa666), SkBits2Float(0x42829af4));  // 445.9f, 65.3108f, 446.6f, 65.3049f, 447.3f, 65.3026f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x428299c7), SkBits2Float(0x43e0599a), SkBits2Float(0x42829af4), SkBits2Float(0x43e0b333), SkBits2Float(0x42829ca6));  // 448, 65.3003f, 448.7f, 65.3026f, 449.4f, 65.306f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42829e58), SkBits2Float(0x43e16666), SkBits2Float(0x4282a31a), SkBits2Float(0x43e1c000), SkBits2Float(0x4282a521));  // 450.1f, 65.3093f, 450.8f, 65.3186f, 451.5f, 65.3225f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x4282a727), SkBits2Float(0x43e27333), SkBits2Float(0x4282a640), SkBits2Float(0x43e2cccd), SkBits2Float(0x4282a8cc));  // 452.2f, 65.3265f, 452.9f, 65.3247f, 453.6f, 65.3297f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x4282ab57), SkBits2Float(0x43e38000), SkBits2Float(0x4282adef), SkBits2Float(0x43e3d99a), SkBits2Float(0x4282b464));  // 454.3f, 65.3346f, 455, 65.3397f, 455.7f, 65.3523f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x4282bad8), SkBits2Float(0x43e48ccd), SkBits2Float(0x4282c468), SkBits2Float(0x43e4e666), SkBits2Float(0x4282cf86));  // 456.4f, 65.3649f, 457.1f, 65.3836f, 457.8f, 65.4053f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x4282daa3), SkBits2Float(0x43e5999a), SkBits2Float(0x4282e9b4), SkBits2Float(0x43e5f333), SkBits2Float(0x4282f714));  // 458.5f, 65.427f, 459.2f, 65.4565f, 459.9f, 65.4826f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42830474), SkBits2Float(0x43e6a666), SkBits2Float(0x428314c1), SkBits2Float(0x43e70000), SkBits2Float(0x42831fc7));  // 460.6f, 65.5087f, 461.3f, 65.5405f, 462, 65.5621f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42832acc), SkBits2Float(0x43e7b333), SkBits2Float(0x4283338f), SkBits2Float(0x43e80ccd), SkBits2Float(0x42833936));  // 462.7f, 65.5836f, 463.4f, 65.6007f, 464.1f, 65.6117f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42833edd), SkBits2Float(0x43e8c000), SkBits2Float(0x42834090), SkBits2Float(0x43e9199a), SkBits2Float(0x428341b1));  // 464.8f, 65.6228f, 465.5f, 65.6261f, 466.2f, 65.6283f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x428342d2), SkBits2Float(0x43e9cccd), SkBits2Float(0x428340d8), SkBits2Float(0x43ea2666), SkBits2Float(0x42833fff));  // 466.9f, 65.6305f, 467.6f, 65.6266f, 468.3f, 65.625f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42833f26), SkBits2Float(0x43ead99a), SkBits2Float(0x42833e4d), SkBits2Float(0x43eb3333), SkBits2Float(0x42833c9b));  // 469, 65.6233f, 469.7f, 65.6217f, 470.4f, 65.6184f
+path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42833ae8), SkBits2Float(0x43ebe666), SkBits2Float(0x42833f32), SkBits2Float(0x43ec4000), SkBits2Float(0x428335d2));  // 471.1f, 65.6151f, 471.8f, 65.6234f, 472.5f, 65.6051f
+path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x42832c73), SkBits2Float(0x43ecf333), SkBits2Float(0x42831e2e), SkBits2Float(0x43ed4ccd), SkBits2Float(0x4283045e));  // 473.2f, 65.5868f, 473.9f, 65.5589f, 474.6f, 65.5085f
+path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x4282ea8e), SkBits2Float(0x43ee0000), SkBits2Float(0x4282bd9e), SkBits2Float(0x43ee599a), SkBits2Float(0x42829af4));  // 475.3f, 65.4581f, 476, 65.3703f, 476.7f, 65.3026f
+path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x4282784a), SkBits2Float(0x43ef0ccd), SkBits2Float(0x428245ac), SkBits2Float(0x43ef6666), SkBits2Float(0x42823461));  // 477.4f, 65.2349f, 478.1f, 65.1361f, 478.8f, 65.1023f
+path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42822316), SkBits2Float(0x43f0199a), SkBits2Float(0x42823365), SkBits2Float(0x43f07333), SkBits2Float(0x42823333));  // 479.5f, 65.0685f, 480.2f, 65.1004f, 480.9f, 65.1f
+path.lineTo(SkBits2Float(0x43f07333), SkBits2Float(0x42823333));  // 480.9f, 65.1f
+path.lineTo(SkBits2Float(0x43c24000), SkBits2Float(0x42823333));  // 388.5f, 65.1f
+path.close();
+path.moveTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42823333), SkBits2Float(0x43fcb333), SkBits2Float(0x42822ef4), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42823333));  // 504.7f, 65.1f, 505.4f, 65.0917f, 506.1f, 65.1f
+path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42823772), SkBits2Float(0x43fdc000), SkBits2Float(0x42823e7d), SkBits2Float(0x43fe199a), SkBits2Float(0x42824cac));  // 506.8f, 65.1083f, 507.5f, 65.122f, 508.2f, 65.1497f
+path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42825adb), SkBits2Float(0x43fecccd), SkBits2Float(0x42827a73), SkBits2Float(0x43ff2666), SkBits2Float(0x4282884d));  // 508.9f, 65.1775f, 509.6f, 65.2392f, 510.3f, 65.2662f
+path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x42829626), SkBits2Float(0x43ffd99a), SkBits2Float(0x42829a88), SkBits2Float(0x4400199a), SkBits2Float(0x42829fc4));  // 511, 65.2933f, 511.7f, 65.3018f, 512.4f, 65.312f
+path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x4282a4ff), SkBits2Float(0x44007333), SkBits2Float(0x4282a6e4), SkBits2Float(0x4400a000), SkBits2Float(0x4282a7b1));  // 513.1f, 65.3223f, 513.8f, 65.326f, 514.5f, 65.3275f
+path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x4282a87e), SkBits2Float(0x4400f99a), SkBits2Float(0x4282a682), SkBits2Float(0x44012666), SkBits2Float(0x4282a494));  // 515.2f, 65.3291f, 515.9f, 65.3252f, 516.6f, 65.3214f
+path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x4282a2a5), SkBits2Float(0x44018000), SkBits2Float(0x42829e44), SkBits2Float(0x4401accd), SkBits2Float(0x42829c19));  // 517.3f, 65.3177f, 518, 65.3091f, 518.7f, 65.3049f
+path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x428299ee), SkBits2Float(0x44020666), SkBits2Float(0x42829718), SkBits2Float(0x44023333), SkBits2Float(0x42829790));  // 519.4f, 65.3006f, 520.1f, 65.2951f, 520.8f, 65.296f
+path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x42829807), SkBits2Float(0x44028ccd), SkBits2Float(0x42829ae5), SkBits2Float(0x4402b99a), SkBits2Float(0x42829ee5));  // 521.5f, 65.2969f, 522.2f, 65.3025f, 522.9f, 65.3103f
+path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x4282a2e6), SkBits2Float(0x44031333), SkBits2Float(0x4282abab), SkBits2Float(0x44034000), SkBits2Float(0x4282af94));  // 523.6f, 65.3182f, 524.3f, 65.3353f, 525, 65.3429f
+path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x4282b37d), SkBits2Float(0x4403999a), SkBits2Float(0x4282b5c0), SkBits2Float(0x4403c666), SkBits2Float(0x4282b65d));  // 525.7f, 65.3506f, 526.4f, 65.355f, 527.1f, 65.3562f
+path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x4282b6f9), SkBits2Float(0x44042000), SkBits2Float(0x4282c91b), SkBits2Float(0x44044ccd), SkBits2Float(0x4282b33f));  // 527.8f, 65.3574f, 528.5f, 65.3928f, 529.2f, 65.3501f
+path.cubicTo(SkBits2Float(0x4404799a), SkBits2Float(0x42829d63), SkBits2Float(0x4404a666), SkBits2Float(0x4282488a), SkBits2Float(0x4404d333), SkBits2Float(0x42823333));  // 529.9f, 65.3074f, 530.6f, 65.1417f, 531.3f, 65.1f
+path.cubicTo(SkBits2Float(0x44050000), SkBits2Float(0x42821ddb), SkBits2Float(0x44052ccd), SkBits2Float(0x42823333), SkBits2Float(0x4405599a), SkBits2Float(0x42823333));  // 532, 65.0583f, 532.7f, 65.1f, 533.4f, 65.1f
+path.lineTo(SkBits2Float(0x4405599a), SkBits2Float(0x42823333));  // 533.4f, 65.1f
+path.lineTo(SkBits2Float(0x43fc0000), SkBits2Float(0x42823333));  // 504, 65.1f
+path.close();
+    return path;
+}
+
+static SkPath path3() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42bd0000), SkBits2Float(0x42b33333), SkBits2Float(0x42bd2573), SkBits2Float(0x42b4999a), SkBits2Float(0x42bd0000));  // 88.9f, 94.5f, 89.6f, 94.5731f, 90.3f, 94.5f
+path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42bcda8d), SkBits2Float(0x42b76666), SkBits2Float(0x42bc4598), SkBits2Float(0x42b8cccd), SkBits2Float(0x42bc1f4b));  // 91, 94.4269f, 91.7f, 94.1359f, 92.4f, 94.0611f
+path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42bbf8ff), SkBits2Float(0x42bb999a), SkBits2Float(0x42bc1b9e), SkBits2Float(0x42bd0000), SkBits2Float(0x42bc1a35));  // 93.1f, 93.9863f, 93.8f, 94.0539f, 94.5f, 94.0512f
+path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42bc18cb), SkBits2Float(0x42bfcccd), SkBits2Float(0x42bc17f2), SkBits2Float(0x42c13333), SkBits2Float(0x42bc16d0));  // 95.2f, 94.0484f, 95.9f, 94.0468f, 96.6f, 94.0446f
+path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42bc15af), SkBits2Float(0x42c40000), SkBits2Float(0x42bc13fd), SkBits2Float(0x42c56666), SkBits2Float(0x42bc136c));  // 97.3f, 94.0424f, 98, 94.039f, 98.7f, 94.0379f
+path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42bc12dc), SkBits2Float(0x42c83333), SkBits2Float(0x42bc12e7), SkBits2Float(0x42c9999a), SkBits2Float(0x42bc136c));  // 99.4f, 94.0368f, 100.1f, 94.0369f, 100.8f, 94.0379f
+path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42bc13f1), SkBits2Float(0x42cc6666), SkBits2Float(0x42bc15c8), SkBits2Float(0x42cdcccd), SkBits2Float(0x42bc168a));  // 101.5f, 94.0389f, 102.2f, 94.0425f, 102.9f, 94.044f
+path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42bc174b), SkBits2Float(0x42d0999a), SkBits2Float(0x42bc17ad), SkBits2Float(0x42d20000), SkBits2Float(0x42bc17f5));  // 103.6f, 94.0455f, 104.3f, 94.0462f, 105, 94.0468f
+path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42bc183e), SkBits2Float(0x42d4cccd), SkBits2Float(0x42bc162a), SkBits2Float(0x42d63333), SkBits2Float(0x42bc183c));  // 105.7f, 94.0473f, 106.4f, 94.0433f, 107.1f, 94.0473f
+path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42bc1a4e), SkBits2Float(0x42d90000), SkBits2Float(0x42bc1e66), SkBits2Float(0x42da6666), SkBits2Float(0x42bc2461));  // 107.8f, 94.0514f, 108.5f, 94.0594f, 109.2f, 94.0711f
+path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42bc2a5d), SkBits2Float(0x42dd3333), SkBits2Float(0x42bc33f9), SkBits2Float(0x42de999a), SkBits2Float(0x42bc3c1f));  // 109.9f, 94.0827f, 110.6f, 94.1015f, 111.3f, 94.1174f
+path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42bc4446), SkBits2Float(0x42e16666), SkBits2Float(0x42bc4cce), SkBits2Float(0x42e2cccd), SkBits2Float(0x42bc5548));  // 112, 94.1333f, 112.7f, 94.15f, 113.4f, 94.1666f
+path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42bc5dc3), SkBits2Float(0x42e5999a), SkBits2Float(0x42bc6472), SkBits2Float(0x42e70000), SkBits2Float(0x42bc6eff));  // 114.1f, 94.1831f, 114.8f, 94.1962f, 115.5f, 94.2168f
+path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42bc798b), SkBits2Float(0x42e9cccd), SkBits2Float(0x42bc8607), SkBits2Float(0x42eb3333), SkBits2Float(0x42bc9494));  // 116.2f, 94.2374f, 116.9f, 94.2618f, 117.6f, 94.2902f
+path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42bca321), SkBits2Float(0x42ee0000), SkBits2Float(0x42bcb9f8), SkBits2Float(0x42ef6666), SkBits2Float(0x42bcc64f));  // 118.3f, 94.3186f, 119, 94.3632f, 119.7f, 94.3873f
+path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42bcd2a5), SkBits2Float(0x42f23333), SkBits2Float(0x42bcdb35), SkBits2Float(0x42f3999a), SkBits2Float(0x42bcde9a));  // 120.4f, 94.4114f, 121.1f, 94.4281f, 121.8f, 94.4348f
+path.cubicTo(SkBits2Float(0x42f50000), SkBits2Float(0x42bce1fe), SkBits2Float(0x42f66666), SkBits2Float(0x42bcdd3f), SkBits2Float(0x42f7cccd), SkBits2Float(0x42bcdaa8));  // 122.5f, 94.4414f, 123.2f, 94.4321f, 123.9f, 94.4271f
+path.cubicTo(SkBits2Float(0x42f93333), SkBits2Float(0x42bcd811), SkBits2Float(0x42fa999a), SkBits2Float(0x42bcd25d), SkBits2Float(0x42fc0000), SkBits2Float(0x42bccf10));  // 124.6f, 94.422f, 125.3f, 94.4109f, 126, 94.4044f
+path.cubicTo(SkBits2Float(0x42fd6666), SkBits2Float(0x42bccbc3), SkBits2Float(0x42fecccd), SkBits2Float(0x42bcc95b), SkBits2Float(0x4300199a), SkBits2Float(0x42bcc6dc));  // 126.7f, 94.398f, 127.4f, 94.3933f, 128.1f, 94.3884f
+path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42bcc45c), SkBits2Float(0x43018000), SkBits2Float(0x42bcc0ec), SkBits2Float(0x43023333), SkBits2Float(0x42bcc013));  // 128.8f, 94.3835f, 129.5f, 94.3768f, 130.2f, 94.3751f
+path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42bcbf3a), SkBits2Float(0x4303999a), SkBits2Float(0x42bcc0b0), SkBits2Float(0x43044ccd), SkBits2Float(0x42bcc1c5));  // 130.9f, 94.3735f, 131.6f, 94.3763f, 132.3f, 94.3785f
+path.cubicTo(SkBits2Float(0x43050000), SkBits2Float(0x42bcc2db), SkBits2Float(0x4305b333), SkBits2Float(0x42bcc5bc), SkBits2Float(0x43066666), SkBits2Float(0x42bcc695));  // 133, 94.3806f, 133.7f, 94.3862f, 134.4f, 94.3879f
+path.cubicTo(SkBits2Float(0x4307199a), SkBits2Float(0x42bcc76e), SkBits2Float(0x4307cccd), SkBits2Float(0x42bcc688), SkBits2Float(0x43088000), SkBits2Float(0x42bcc6dc));  // 135.1f, 94.3895f, 135.8f, 94.3878f, 136.5f, 94.3884f
+path.cubicTo(SkBits2Float(0x43093333), SkBits2Float(0x42bcc730), SkBits2Float(0x4309e666), SkBits2Float(0x42bcc89a), SkBits2Float(0x430a999a), SkBits2Float(0x42bcc88e));  // 137.2f, 94.389f, 137.9f, 94.3918f, 138.6f, 94.3917f
+path.cubicTo(SkBits2Float(0x430b4ccd), SkBits2Float(0x42bcc882), SkBits2Float(0x430c0000), SkBits2Float(0x42bcc76e), SkBits2Float(0x430cb333), SkBits2Float(0x42bcc695));  // 139.3f, 94.3916f, 140, 94.3895f, 140.7f, 94.3879f
+path.cubicTo(SkBits2Float(0x430d6666), SkBits2Float(0x42bcc5bc), SkBits2Float(0x430e199a), SkBits2Float(0x42bcc445), SkBits2Float(0x430ecccd), SkBits2Float(0x42bcc378));  // 141.4f, 94.3862f, 142.1f, 94.3833f, 142.8f, 94.3818f
+path.cubicTo(SkBits2Float(0x430f8000), SkBits2Float(0x42bcc2aa), SkBits2Float(0x43103333), SkBits2Float(0x42bcc32f), SkBits2Float(0x4310e666), SkBits2Float(0x42bcc1c5));  // 143.5f, 94.3802f, 144.2f, 94.3812f, 144.9f, 94.3785f
+path.cubicTo(SkBits2Float(0x4311999a), SkBits2Float(0x42bcc05c), SkBits2Float(0x43124ccd), SkBits2Float(0x42bcbd88), SkBits2Float(0x43130000), SkBits2Float(0x42bcbafd));  // 145.6f, 94.3757f, 146.3f, 94.3702f, 147, 94.3652f
+path.cubicTo(SkBits2Float(0x4313b333), SkBits2Float(0x42bcb872), SkBits2Float(0x43146666), SkBits2Float(0x42bcb50e), SkBits2Float(0x4315199a), SkBits2Float(0x42bcb282));  // 147.7f, 94.3602f, 148.4f, 94.3536f, 149.1f, 94.3486f
+path.cubicTo(SkBits2Float(0x4315cccd), SkBits2Float(0x42bcaff7), SkBits2Float(0x43168000), SkBits2Float(0x42bcac56), SkBits2Float(0x43173333), SkBits2Float(0x42bcabba));  // 149.8f, 94.3437f, 150.5f, 94.3366f, 151.2f, 94.3354f
+path.cubicTo(SkBits2Float(0x4317e666), SkBits2Float(0x42bcab1d), SkBits2Float(0x4318999a), SkBits2Float(0x42bcadfe), SkBits2Float(0x43194ccd), SkBits2Float(0x42bcaed8));  // 151.9f, 94.3342f, 152.6f, 94.3398f, 153.3f, 94.3415f
+path.cubicTo(SkBits2Float(0x431a0000), SkBits2Float(0x42bcafb1), SkBits2Float(0x431ab333), SkBits2Float(0x42bcb034), SkBits2Float(0x431b6666), SkBits2Float(0x42bcb0d0));  // 154, 94.3431f, 154.7f, 94.3441f, 155.4f, 94.3453f
+path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42bcb16d), SkBits2Float(0x431ccccd), SkBits2Float(0x42bcb119), SkBits2Float(0x431d8000), SkBits2Float(0x42bcb282));  // 156.1f, 94.3465f, 156.8f, 94.3459f, 157.5f, 94.3486f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42bcb3ec), SkBits2Float(0x431ee666), SkBits2Float(0x42bcb708), SkBits2Float(0x431f999a), SkBits2Float(0x42bcb94b));  // 158.2f, 94.3514f, 158.9f, 94.3575f, 159.6f, 94.3619f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42bcbb8e), SkBits2Float(0x43210000), SkBits2Float(0x42bcbef2), SkBits2Float(0x4321b333), SkBits2Float(0x42bcc013));  // 160.3f, 94.3663f, 161, 94.3729f, 161.7f, 94.3751f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42bcc135), SkBits2Float(0x4323199a), SkBits2Float(0x42bcbfe3), SkBits2Float(0x4323cccd), SkBits2Float(0x42bcc013));  // 162.4f, 94.3774f, 163.1f, 94.3748f, 163.8f, 94.3751f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42bcc044), SkBits2Float(0x43253333), SkBits2Float(0x42bcbf19), SkBits2Float(0x4325e666), SkBits2Float(0x42bcc138));  // 164.5f, 94.3755f, 165.2f, 94.3732f, 165.9f, 94.3774f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42bcc358), SkBits2Float(0x43274ccd), SkBits2Float(0x42bcc7c6), SkBits2Float(0x43280000), SkBits2Float(0x42bcccd1));  // 166.6f, 94.3815f, 167.3f, 94.3902f, 168, 94.4f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42bcd1db), SkBits2Float(0x43296666), SkBits2Float(0x42bcd9b9), SkBits2Float(0x432a199a), SkBits2Float(0x42bcdf78));  // 168.7f, 94.4099f, 169.4f, 94.4252f, 170.1f, 94.4365f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42bce536), SkBits2Float(0x432b8000), SkBits2Float(0x42bceb84), SkBits2Float(0x432c3333), SkBits2Float(0x42bcef48));  // 170.8f, 94.4477f, 171.5f, 94.46f, 172.2f, 94.4673f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42bcf30c), SkBits2Float(0x432d999a), SkBits2Float(0x42bcf3ce), SkBits2Float(0x432e4ccd), SkBits2Float(0x42bcf611));  // 172.9f, 94.4747f, 173.6f, 94.4762f, 174.3f, 94.4806f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42bcf853), SkBits2Float(0x432fb333), SkBits2Float(0x42bcfb31), SkBits2Float(0x43306666), SkBits2Float(0x42bcfcd9));  // 175, 94.485f, 175.7f, 94.4906f, 176.4f, 94.4938f
+path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42bd0000));  // 176.4f, 94.5f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.close();
+path.moveTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x42bd0000), SkBits2Float(0x4342999a), SkBits2Float(0x42bd0549), SkBits2Float(0x43434ccd), SkBits2Float(0x42bd0000));  // 193.9f, 94.5f, 194.6f, 94.5103f, 195.3f, 94.5f
+path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x42bcfab7), SkBits2Float(0x4344b333), SkBits2Float(0x42bcec20), SkBits2Float(0x43456666), SkBits2Float(0x42bce04c));  // 196, 94.4897f, 196.7f, 94.4612f, 197.4f, 94.4381f
+path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x42bcd477), SkBits2Float(0x4346cccd), SkBits2Float(0x42bcc14e), SkBits2Float(0x43478000), SkBits2Float(0x42bcb904));  // 198.1f, 94.415f, 198.8f, 94.3775f, 199.5f, 94.3614f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42bcb0ba), SkBits2Float(0x4348e666), SkBits2Float(0x42bcaed9), SkBits2Float(0x4349999a), SkBits2Float(0x42bcae91));  // 200.2f, 94.3452f, 200.9f, 94.3415f, 201.6f, 94.341f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42bcae49), SkBits2Float(0x434b0000), SkBits2Float(0x42bcb23c), SkBits2Float(0x434bb333), SkBits2Float(0x42bcb752));  // 202.3f, 94.3404f, 203, 94.3481f, 203.7f, 94.358f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42bcbc69), SkBits2Float(0x434d199a), SkBits2Float(0x42bcc612), SkBits2Float(0x434dcccd), SkBits2Float(0x42bccd17));  // 204.4f, 94.368f, 205.1f, 94.3869f, 205.8f, 94.4006f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42bcd41c), SkBits2Float(0x434f3333), SkBits2Float(0x42bcdc5a), SkBits2Float(0x434fe666), SkBits2Float(0x42bce171));  // 206.5f, 94.4143f, 207.2f, 94.4304f, 207.9f, 94.4403f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42bce687), SkBits2Float(0x43514ccd), SkBits2Float(0x42bceb0d), SkBits2Float(0x43520000), SkBits2Float(0x42bceb9d));  // 208.6f, 94.4502f, 209.3f, 94.4591f, 210, 94.4602f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42bcec2e), SkBits2Float(0x43536666), SkBits2Float(0x42bcea70), SkBits2Float(0x4354199a), SkBits2Float(0x42bce4d5));  // 210.7f, 94.4613f, 211.4f, 94.4579f, 212.1f, 94.4469f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42bcdf39), SkBits2Float(0x43558000), SkBits2Float(0x42bcd432), SkBits2Float(0x43563333), SkBits2Float(0x42bcc9f9));  // 212.8f, 94.436f, 213.5f, 94.4144f, 214.2f, 94.3945f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42bcbfc1), SkBits2Float(0x4357999a), SkBits2Float(0x42bcb9bc), SkBits2Float(0x43584ccd), SkBits2Float(0x42bca782));  // 214.9f, 94.3745f, 215.6f, 94.3628f, 216.3f, 94.3272f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42bc9548), SkBits2Float(0x4359b333), SkBits2Float(0x42bc76fe), SkBits2Float(0x435a6666), SkBits2Float(0x42bc5c9e));  // 217, 94.2916f, 217.7f, 94.2324f, 218.4f, 94.1809f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42bc423e), SkBits2Float(0x435bcccd), SkBits2Float(0x42bc19b0), SkBits2Float(0x435c8000), SkBits2Float(0x42bc0940));  // 219.1f, 94.1294f, 219.8f, 94.0502f, 220.5f, 94.0181f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42bbf8cf), SkBits2Float(0x435de666), SkBits2Float(0x42bbfbf7), SkBits2Float(0x435e999a), SkBits2Float(0x42bbf9fc));  // 221.2f, 93.986f, 221.9f, 93.9921f, 222.6f, 93.9883f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42bbf802), SkBits2Float(0x43600000), SkBits2Float(0x42bbfad6), SkBits2Float(0x4360b333), SkBits2Float(0x42bbfd61));  // 223.3f, 93.9844f, 224, 93.9899f, 224.7f, 93.9949f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42bbffec), SkBits2Float(0x4362199a), SkBits2Float(0x42bc06fd), SkBits2Float(0x4362cccd), SkBits2Float(0x42bc0940));  // 225.4f, 93.9998f, 226.1f, 94.0136f, 226.8f, 94.0181f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42bc0b82), SkBits2Float(0x43643333), SkBits2Float(0x42bc0c13), SkBits2Float(0x4364e666), SkBits2Float(0x42bc0af2));  // 227.5f, 94.0225f, 228.2f, 94.0236f, 228.9f, 94.0214f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42bc09d0), SkBits2Float(0x43664ccd), SkBits2Float(0x42bc019e), SkBits2Float(0x43670000), SkBits2Float(0x42bc0277));  // 229.6f, 94.0192f, 230.3f, 94.0032f, 231, 94.0048f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42bc0350), SkBits2Float(0x43686666), SkBits2Float(0x42bc0b3a), SkBits2Float(0x4369199a), SkBits2Float(0x42bc1008));  // 231.7f, 94.0065f, 232.4f, 94.0219f, 233.1f, 94.0313f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42bc14d6), SkBits2Float(0x436a8000), SkBits2Float(0x42bc1d08), SkBits2Float(0x436b3333), SkBits2Float(0x42bc1f4b));  // 233.8f, 94.0407f, 234.5f, 94.0567f, 235.2f, 94.0611f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42bc218e), SkBits2Float(0x436c999a), SkBits2Float(0x42bc1e2a), SkBits2Float(0x436d4ccd), SkBits2Float(0x42bc1d99));  // 235.9f, 94.0655f, 236.6f, 94.0589f, 237.3f, 94.0578f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42bc1d08), SkBits2Float(0x436eb333), SkBits2Float(0x42bc1cc0), SkBits2Float(0x436f6666), SkBits2Float(0x42bc1be7));  // 238, 94.0567f, 238.7f, 94.0562f, 239.4f, 94.0545f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42bc1b0e), SkBits2Float(0x4370cccd), SkBits2Float(0x42bc195c), SkBits2Float(0x43718000), SkBits2Float(0x42bc1883));  // 240.1f, 94.0528f, 240.8f, 94.0495f, 241.5f, 94.0479f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42bc17aa), SkBits2Float(0x4372e666), SkBits2Float(0x42bc1719), SkBits2Float(0x4373999a), SkBits2Float(0x42bc16d0));  // 242.2f, 94.0462f, 242.9f, 94.0451f, 243.6f, 94.0446f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42bc1688), SkBits2Float(0x43750000), SkBits2Float(0x42bc1719), SkBits2Float(0x4375b333), SkBits2Float(0x42bc16d0));  // 244.3f, 94.044f, 245, 94.0451f, 245.7f, 94.0446f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42bc1688), SkBits2Float(0x4377199a), SkBits2Float(0x42bc1567), SkBits2Float(0x4377cccd), SkBits2Float(0x42bc151e));  // 246.4f, 94.044f, 247.1f, 94.0418f, 247.8f, 94.0412f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42bc14d6), SkBits2Float(0x43793333), SkBits2Float(0x42bc148e), SkBits2Float(0x4379e666), SkBits2Float(0x42bc151e));  // 248.5f, 94.0407f, 249.2f, 94.0401f, 249.9f, 94.0412f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42bc15af), SkBits2Float(0x437b4ccd), SkBits2Float(0x42bc1761), SkBits2Float(0x437c0000), SkBits2Float(0x42bc1883));  // 250.6f, 94.0424f, 251.3f, 94.0457f, 252, 94.0479f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42bc19a4), SkBits2Float(0x437d6666), SkBits2Float(0x42bc1b0e), SkBits2Float(0x437e199a), SkBits2Float(0x42bc1be7));  // 252.7f, 94.0501f, 253.4f, 94.0528f, 254.1f, 94.0545f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42bc1cc0), SkBits2Float(0x437f8000), SkBits2Float(0x42bc1d99), SkBits2Float(0x4380199a), SkBits2Float(0x42bc1d99));  // 254.8f, 94.0562f, 255.5f, 94.0578f, 256.2f, 94.0578f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42bc1d99), SkBits2Float(0x4380cccd), SkBits2Float(0x42bc1d08), SkBits2Float(0x43812666), SkBits2Float(0x42bc1be7));  // 256.9f, 94.0578f, 257.6f, 94.0567f, 258.3f, 94.0545f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42bc1ac5), SkBits2Float(0x4381d99a), SkBits2Float(0x42bc183a), SkBits2Float(0x43823333), SkBits2Float(0x42bc16d0));  // 259, 94.0523f, 259.7f, 94.0473f, 260.4f, 94.0446f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42bc1567), SkBits2Float(0x4382e666), SkBits2Float(0x42bc13fd), SkBits2Float(0x43834000), SkBits2Float(0x42bc136c));  // 261.1f, 94.0418f, 261.8f, 94.039f, 262.5f, 94.0379f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42bc12dc), SkBits2Float(0x4383f333), SkBits2Float(0x42bc1324), SkBits2Float(0x43844ccd), SkBits2Float(0x42bc136c));  // 263.2f, 94.0368f, 263.9f, 94.0374f, 264.6f, 94.0379f
+path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42bc13b5), SkBits2Float(0x43850000), SkBits2Float(0x42bc148e), SkBits2Float(0x4385599a), SkBits2Float(0x42bc151e));  // 265.3f, 94.0385f, 266, 94.0401f, 266.7f, 94.0412f
+path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42bc15af), SkBits2Float(0x43860ccd), SkBits2Float(0x42bc1688), SkBits2Float(0x43866666), SkBits2Float(0x42bc16d0));  // 267.4f, 94.0424f, 268.1f, 94.044f, 268.8f, 94.0446f
+path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42bc1719), SkBits2Float(0x4387199a), SkBits2Float(0x42bc1719), SkBits2Float(0x43877333), SkBits2Float(0x42bc16d0));  // 269.5f, 94.0451f, 270.2f, 94.0451f, 270.9f, 94.0446f
+path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42bc1688), SkBits2Float(0x43882666), SkBits2Float(0x42bc1567), SkBits2Float(0x43888000), SkBits2Float(0x42bc151e));  // 271.6f, 94.044f, 272.3f, 94.0418f, 273, 94.0412f
+path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42bc14d6), SkBits2Float(0x43893333), SkBits2Float(0x42bc151e), SkBits2Float(0x43898ccd), SkBits2Float(0x42bc151e));  // 273.7f, 94.0407f, 274.4f, 94.0412f, 275.1f, 94.0412f
+path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42bc151e), SkBits2Float(0x438a4000), SkBits2Float(0x42bc1567), SkBits2Float(0x438a999a), SkBits2Float(0x42bc151e));  // 275.8f, 94.0412f, 276.5f, 94.0418f, 277.2f, 94.0412f
+path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42bc14d6), SkBits2Float(0x438b4ccd), SkBits2Float(0x42bc136c), SkBits2Float(0x438ba666), SkBits2Float(0x42bc136c));  // 277.9f, 94.0407f, 278.6f, 94.0379f, 279.3f, 94.0379f
+path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42bc136c), SkBits2Float(0x438c599a), SkBits2Float(0x42bc152a), SkBits2Float(0x438cb333), SkBits2Float(0x42bc151e));  // 280, 94.0379f, 280.7f, 94.0413f, 281.4f, 94.0412f
+path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42bc1513), SkBits2Float(0x438d6666), SkBits2Float(0x42bc14ef), SkBits2Float(0x438dc000), SkBits2Float(0x42bc1326));  // 282.1f, 94.0412f, 282.8f, 94.0409f, 283.5f, 94.0374f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42bc115c), SkBits2Float(0x438e7333), SkBits2Float(0x42bc0c17), SkBits2Float(0x438ecccd), SkBits2Float(0x42bc0a64));  // 284.2f, 94.0339f, 284.9f, 94.0236f, 285.6f, 94.0203f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42bc08b2), SkBits2Float(0x438f8000), SkBits2Float(0x42bc0413), SkBits2Float(0x438fd99a), SkBits2Float(0x42bc08f9));  // 286.3f, 94.017f, 287, 94.008f, 287.7f, 94.0175f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42bc0dde), SkBits2Float(0x43908ccd), SkBits2Float(0x42bc1476), SkBits2Float(0x4390e666), SkBits2Float(0x42bc27c6));  // 288.4f, 94.0271f, 289.1f, 94.04f, 289.8f, 94.0777f
+path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42bc3b15), SkBits2Float(0x4391999a), SkBits2Float(0x42bc5916), SkBits2Float(0x4391f333), SkBits2Float(0x42bc7cd6));  // 290.5f, 94.1154f, 291.2f, 94.174f, 291.9f, 94.2438f
+path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42bca096), SkBits2Float(0x4392a666), SkBits2Float(0x42bce868), SkBits2Float(0x43930000), SkBits2Float(0x42bcfe45));  // 292.6f, 94.3136f, 293.3f, 94.4539f, 294, 94.4966f
+path.cubicTo(SkBits2Float(0x4393599a), SkBits2Float(0x42bd1421), SkBits2Float(0x4393b333), SkBits2Float(0x42bcffb6), SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000));  // 294.7f, 94.5393f, 295.4f, 94.4994f, 296.1f, 94.5f
+path.lineTo(SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000));  // 296.1f, 94.5f
+path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.close();
+path.moveTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42bd0000), SkBits2Float(0x43ace666), SkBits2Float(0x42bd03a2), SkBits2Float(0x43ad4000), SkBits2Float(0x42bd0000));  // 345.1f, 94.5f, 345.8f, 94.5071f, 346.5f, 94.5f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42bcfc5e), SkBits2Float(0x43adf333), SkBits2Float(0x42bd069a), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42bcea32));  // 347.2f, 94.4929f, 347.9f, 94.5129f, 348.6f, 94.4574f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42bccdca), SkBits2Float(0x43af0000), SkBits2Float(0x42bc7b18), SkBits2Float(0x43af599a), SkBits2Float(0x42bc558f));  // 349.3f, 94.4019f, 350, 94.2404f, 350.7f, 94.1671f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42bc3005), SkBits2Float(0x43b00ccd), SkBits2Float(0x42bc16d2), SkBits2Float(0x43b06666), SkBits2Float(0x42bc08f9));  // 351.4f, 94.0938f, 352.1f, 94.0446f, 352.8f, 94.0175f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42bbfb20), SkBits2Float(0x43b1199a), SkBits2Float(0x42bc03d5), SkBits2Float(0x43b17333), SkBits2Float(0x42bc0277));  // 353.5f, 93.9905f, 354.2f, 94.0075f, 354.9f, 94.0048f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42bc0119), SkBits2Float(0x43b22666), SkBits2Float(0x42bc019e), SkBits2Float(0x43b28000), SkBits2Float(0x42bc00c5));  // 355.6f, 94.0021f, 356.3f, 94.0032f, 357, 94.0015f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42bbffec), SkBits2Float(0x43b33333), SkBits2Float(0x42bbfdf1), SkBits2Float(0x43b38ccd), SkBits2Float(0x42bbfd61));  // 357.7f, 93.9998f, 358.4f, 93.996f, 359.1f, 93.9949f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42bbfcd0), SkBits2Float(0x43b44000), SkBits2Float(0x42bbfdf1), SkBits2Float(0x43b4999a), SkBits2Float(0x42bbfd61));  // 359.8f, 93.9938f, 360.5f, 93.996f, 361.2f, 93.9949f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42bbfcd0), SkBits2Float(0x43b54ccd), SkBits2Float(0x42bbfad6), SkBits2Float(0x43b5a666), SkBits2Float(0x42bbf9fc));  // 361.9f, 93.9938f, 362.6f, 93.9899f, 363.3f, 93.9883f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42bbf923), SkBits2Float(0x43b6599a), SkBits2Float(0x42bbf8db), SkBits2Float(0x43b6b333), SkBits2Float(0x42bbf84a));  // 364, 93.9866f, 364.7f, 93.986f, 365.4f, 93.9849f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42bbf7ba), SkBits2Float(0x43b76666), SkBits2Float(0x42bbf729), SkBits2Float(0x43b7c000), SkBits2Float(0x42bbf698));  // 366.1f, 93.9838f, 366.8f, 93.9827f, 367.5f, 93.9816f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42bbf608), SkBits2Float(0x43b87333), SkBits2Float(0x42bbf52e), SkBits2Float(0x43b8cccd), SkBits2Float(0x42bbf4e6));  // 368.2f, 93.9805f, 368.9f, 93.9789f, 369.6f, 93.9783f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42bbf49e), SkBits2Float(0x43b98000), SkBits2Float(0x42bbf455), SkBits2Float(0x43b9d99a), SkBits2Float(0x42bbf4e6));  // 370.3f, 93.9778f, 371, 93.9772f, 371.7f, 93.9783f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42bbf577), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42bbf771), SkBits2Float(0x43bae666), SkBits2Float(0x42bbf84a));  // 372.4f, 93.9794f, 373.1f, 93.9833f, 373.8f, 93.9849f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42bbf923), SkBits2Float(0x43bb999a), SkBits2Float(0x42bbf9b4), SkBits2Float(0x43bbf333), SkBits2Float(0x42bbf9fc));  // 374.5f, 93.9866f, 375.2f, 93.9877f, 375.9f, 93.9883f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42bbfa45), SkBits2Float(0x43bca666), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43bd0000), SkBits2Float(0x42bbf9fc));  // 376.6f, 93.9888f, 377.3f, 93.9883f, 378, 93.9883f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43bdb333), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43be0ccd), SkBits2Float(0x42bbf9fc));  // 378.7f, 93.9883f, 379.4f, 93.9883f, 380.1f, 93.9883f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42bbf9fc), SkBits2Float(0x43bec000), SkBits2Float(0x42bbf8db), SkBits2Float(0x43bf199a), SkBits2Float(0x42bbf9fc));  // 380.8f, 93.9883f, 381.5f, 93.986f, 382.2f, 93.9883f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42bbfb1e), SkBits2Float(0x43bfcccd), SkBits2Float(0x42bbfeca), SkBits2Float(0x43c02666), SkBits2Float(0x42bc00c5));  // 382.9f, 93.9905f, 383.6f, 93.9976f, 384.3f, 94.0015f
+path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42bc02bf), SkBits2Float(0x43c0d99a), SkBits2Float(0x42bc0593), SkBits2Float(0x43c13333), SkBits2Float(0x42bc05db));  // 385, 94.0054f, 385.7f, 94.0109f, 386.4f, 94.0114f
+path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42bc0624), SkBits2Float(0x43c1e666), SkBits2Float(0x42bc0308), SkBits2Float(0x43c24000), SkBits2Float(0x42bc0277));  // 387.1f, 94.012f, 387.8f, 94.0059f, 388.5f, 94.0048f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42bc01e6), SkBits2Float(0x43c2f333), SkBits2Float(0x42bc022f), SkBits2Float(0x43c34ccd), SkBits2Float(0x42bc0277));  // 389.2f, 94.0037f, 389.9f, 94.0043f, 390.6f, 94.0048f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42bc02bf), SkBits2Float(0x43c40000), SkBits2Float(0x42bc02bf), SkBits2Float(0x43c4599a), SkBits2Float(0x42bc0429));  // 391.3f, 94.0054f, 392, 94.0054f, 392.7f, 94.0081f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42bc0593), SkBits2Float(0x43c50ccd), SkBits2Float(0x42bc08f7), SkBits2Float(0x43c56666), SkBits2Float(0x42bc0af2));  // 393.4f, 94.0109f, 394.1f, 94.0175f, 394.8f, 94.0214f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42bc0cec), SkBits2Float(0x43c6199a), SkBits2Float(0x42bc0f2f), SkBits2Float(0x43c67333), SkBits2Float(0x42bc1008));  // 395.5f, 94.0252f, 396.2f, 94.0297f, 396.9f, 94.0313f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42bc10e1), SkBits2Float(0x43c72666), SkBits2Float(0x42bc0fc0), SkBits2Float(0x43c78000), SkBits2Float(0x42bc1008));  // 397.6f, 94.033f, 398.3f, 94.0308f, 399, 94.0313f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42bc1050), SkBits2Float(0x43c83333), SkBits2Float(0x42bc1172), SkBits2Float(0x43c88ccd), SkBits2Float(0x42bc11ba));  // 399.7f, 94.0319f, 400.4f, 94.0341f, 401.1f, 94.0346f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42bc1202), SkBits2Float(0x43c94000), SkBits2Float(0x42bc11ba), SkBits2Float(0x43c9999a), SkBits2Float(0x42bc11ba));  // 401.8f, 94.0352f, 402.5f, 94.0346f, 403.2f, 94.0346f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42bc11ba), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42bc124b), SkBits2Float(0x43caa666), SkBits2Float(0x42bc11ba));  // 403.9f, 94.0346f, 404.6f, 94.0357f, 405.3f, 94.0346f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42bc1129), SkBits2Float(0x43cb599a), SkBits2Float(0x42bc0f77), SkBits2Float(0x43cbb333), SkBits2Float(0x42bc0e56));  // 406, 94.0335f, 406.7f, 94.0302f, 407.4f, 94.028f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42bc0d34), SkBits2Float(0x43cc6666), SkBits2Float(0x42bc0b82), SkBits2Float(0x43ccc000), SkBits2Float(0x42bc0af2));  // 408.1f, 94.0258f, 408.8f, 94.0225f, 409.5f, 94.0214f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42bc0a61), SkBits2Float(0x43cd7333), SkBits2Float(0x42bc0b3a), SkBits2Float(0x43cdcccd), SkBits2Float(0x42bc0af2));  // 410.2f, 94.0203f, 410.9f, 94.0219f, 411.6f, 94.0214f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42bc0aa9), SkBits2Float(0x43ce8000), SkBits2Float(0x42bc0aa9), SkBits2Float(0x43ced99a), SkBits2Float(0x42bc0940));  // 412.3f, 94.0208f, 413, 94.0208f, 413.7f, 94.0181f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42bc07d6), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42bc0502), SkBits2Float(0x43cfe666), SkBits2Float(0x42bc0277));  // 414.4f, 94.0153f, 415.1f, 94.0098f, 415.8f, 94.0048f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42bbffec), SkBits2Float(0x43d0999a), SkBits2Float(0x42bbfc88), SkBits2Float(0x43d0f333), SkBits2Float(0x42bbf9fc));  // 416.5f, 93.9998f, 417.2f, 93.9932f, 417.9f, 93.9883f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42bbf771), SkBits2Float(0x43d1a666), SkBits2Float(0x42bbf4e6), SkBits2Float(0x43d20000), SkBits2Float(0x42bbf334));  // 418.6f, 93.9833f, 419.3f, 93.9783f, 420, 93.975f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42bbf182), SkBits2Float(0x43d2b333), SkBits2Float(0x42bbee66), SkBits2Float(0x43d30ccd), SkBits2Float(0x42bbefd0));  // 420.7f, 93.9717f, 421.4f, 93.9656f, 422.1f, 93.9684f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42bbf13a), SkBits2Float(0x43d3c000), SkBits2Float(0x42bbf52e), SkBits2Float(0x43d4199a), SkBits2Float(0x42bbfbaf));  // 422.8f, 93.9711f, 423.5f, 93.9789f, 424.2f, 93.9916f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42bc022f), SkBits2Float(0x43d4cccd), SkBits2Float(0x42bc1014), SkBits2Float(0x43d52666), SkBits2Float(0x42bc16d0));  // 424.9f, 94.0043f, 425.6f, 94.0314f, 426.3f, 94.0446f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42bc1d8d), SkBits2Float(0x43d5d99a), SkBits2Float(0x42bc1de3), SkBits2Float(0x43d63333), SkBits2Float(0x42bc241b));  // 427, 94.0577f, 427.7f, 94.0584f, 428.4f, 94.0705f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42bc2a53), SkBits2Float(0x43d6e666), SkBits2Float(0x42bc30c5), SkBits2Float(0x43d74000), SkBits2Float(0x42bc3c1f));  // 429.1f, 94.0827f, 429.8f, 94.0953f, 430.5f, 94.1174f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42bc4779), SkBits2Float(0x43d7f333), SkBits2Float(0x42bc6283), SkBits2Float(0x43d84ccd), SkBits2Float(0x42bc6836));  // 431.2f, 94.1396f, 431.9f, 94.1924f, 432.6f, 94.2035f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42bc6de9), SkBits2Float(0x43d90000), SkBits2Float(0x42bc67e0), SkBits2Float(0x43d9599a), SkBits2Float(0x42bc5e50));  // 433.3f, 94.2147f, 434, 94.2029f, 434.7f, 94.1842f
+path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x42bc54c0), SkBits2Float(0x43da0ccd), SkBits2Float(0x42bc3a23), SkBits2Float(0x43da6666), SkBits2Float(0x42bc2ed5));  // 435.4f, 94.1655f, 436.1f, 94.1135f, 436.8f, 94.0915f
+path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x42bc2387), SkBits2Float(0x43db199a), SkBits2Float(0x42bc1e34), SkBits2Float(0x43db7333), SkBits2Float(0x42bc1a7b));  // 437.5f, 94.0694f, 438.2f, 94.059f, 438.9f, 94.0517f
+path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x42bc16c3), SkBits2Float(0x43dc2666), SkBits2Float(0x42bc18a6), SkBits2Float(0x43dc8000), SkBits2Float(0x42bc1883));  // 439.6f, 94.0445f, 440.3f, 94.0481f, 441, 94.0479f
+path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x42bc185f), SkBits2Float(0x43dd3333), SkBits2Float(0x42bc19f0), SkBits2Float(0x43dd8ccd), SkBits2Float(0x42bc19a8));  // 441.7f, 94.0476f, 442.4f, 94.0507f, 443.1f, 94.0501f
+path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x42bc195f), SkBits2Float(0x43de4000), SkBits2Float(0x42bc15d4), SkBits2Float(0x43de999a), SkBits2Float(0x42bc16d0));  // 443.8f, 94.0496f, 444.5f, 94.0426f, 445.2f, 94.0446f
+path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x42bc17cd), SkBits2Float(0x43df4ccd), SkBits2Float(0x42bc11a1), SkBits2Float(0x43dfa666), SkBits2Float(0x42bc1f92));  // 445.9f, 94.0465f, 446.6f, 94.0344f, 447.3f, 94.0617f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42bc2d83), SkBits2Float(0x43e0599a), SkBits2Float(0x42bc478f), SkBits2Float(0x43e0b333), SkBits2Float(0x42bc6a76));  // 448, 94.0889f, 448.7f, 94.1398f, 449.4f, 94.2079f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42bc8d5c), SkBits2Float(0x43e16666), SkBits2Float(0x42bcd80e), SkBits2Float(0x43e1c000), SkBits2Float(0x42bcf0fa));  // 450.1f, 94.2761f, 450.8f, 94.422f, 451.5f, 94.4707f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42bd09e7), SkBits2Float(0x43e27333), SkBits2Float(0x42bd0449), SkBits2Float(0x43e2cccd), SkBits2Float(0x42bd0000));  // 452.2f, 94.5193f, 452.9f, 94.5084f, 453.6f, 94.5f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42bcfbb7), SkBits2Float(0x43e38000), SkBits2Float(0x42bcebe7), SkBits2Float(0x43e3d99a), SkBits2Float(0x42bcd744));  // 454.3f, 94.4916f, 455, 94.4607f, 455.7f, 94.4204f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42bcc2a0), SkBits2Float(0x43e48ccd), SkBits2Float(0x42bc8dc8), SkBits2Float(0x43e4e666), SkBits2Float(0x42bc842c));  // 456.4f, 94.3801f, 457.1f, 94.2769f, 457.8f, 94.2581f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42bc7a90), SkBits2Float(0x43e5999a), SkBits2Float(0x42bc8f32), SkBits2Float(0x43e5f333), SkBits2Float(0x42bc9d9c));  // 458.5f, 94.2394f, 459.2f, 94.2797f, 459.9f, 94.3078f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42bcac06), SkBits2Float(0x43e6a666), SkBits2Float(0x42bcca42), SkBits2Float(0x43e70000), SkBits2Float(0x42bcdaa8));  // 460.6f, 94.336f, 461.3f, 94.395f, 462, 94.4271f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42bceb0e), SkBits2Float(0x43e7b333), SkBits2Float(0x42bcf9c7), SkBits2Float(0x43e80ccd), SkBits2Float(0x42bd0000));  // 462.7f, 94.4591f, 463.4f, 94.4878f, 464.1f, 94.5f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42bd0639), SkBits2Float(0x43e8c000), SkBits2Float(0x42bd010b), SkBits2Float(0x43e9199a), SkBits2Float(0x42bd0000));  // 464.8f, 94.5122f, 465.5f, 94.502f, 466.2f, 94.5f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42bcfef5), SkBits2Float(0x43e9cccd), SkBits2Float(0x42bcf9bb), SkBits2Float(0x43ea2666), SkBits2Float(0x42bcf9bb));  // 466.9f, 94.498f, 467.6f, 94.4878f, 468.3f, 94.4878f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42bcf9bb), SkBits2Float(0x43ead99a), SkBits2Float(0x42bcfef5), SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000));  // 469, 94.4878f, 469.7f, 94.498f, 470.4f, 94.5f
+path.lineTo(SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000));  // 470.4f, 94.5f
+path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.close();
+    return path;
+}
+
+static SkPath path4() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42bd0000), SkBits2Float(0x42b33333), SkBits2Float(0x42bcda8d), SkBits2Float(0x42b4999a), SkBits2Float(0x42bd0000));  // 88.9f, 94.5f, 89.6f, 94.4269f, 90.3f, 94.5f
+path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42bd2573), SkBits2Float(0x42b76666), SkBits2Float(0x42bdba68), SkBits2Float(0x42b8cccd), SkBits2Float(0x42bde0b5));  // 91, 94.5731f, 91.7f, 94.8641f, 92.4f, 94.9389f
+path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42be0701), SkBits2Float(0x42bb999a), SkBits2Float(0x42bde462), SkBits2Float(0x42bd0000), SkBits2Float(0x42bde5cb));  // 93.1f, 95.0137f, 93.8f, 94.9461f, 94.5f, 94.9488f
+path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42bde735), SkBits2Float(0x42bfcccd), SkBits2Float(0x42bde80e), SkBits2Float(0x42c13333), SkBits2Float(0x42bde930));  // 95.2f, 94.9516f, 95.9f, 94.9532f, 96.6f, 94.9554f
+path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42bdea51), SkBits2Float(0x42c40000), SkBits2Float(0x42bdec03), SkBits2Float(0x42c56666), SkBits2Float(0x42bdec94));  // 97.3f, 94.9576f, 98, 94.961f, 98.7f, 94.9621f
+path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42bded24), SkBits2Float(0x42c83333), SkBits2Float(0x42bded19), SkBits2Float(0x42c9999a), SkBits2Float(0x42bdec94));  // 99.4f, 94.9632f, 100.1f, 94.9631f, 100.8f, 94.9621f
+path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42bdec0f), SkBits2Float(0x42cc6666), SkBits2Float(0x42bdea38), SkBits2Float(0x42cdcccd), SkBits2Float(0x42bde976));  // 101.5f, 94.9611f, 102.2f, 94.9575f, 102.9f, 94.956f
+path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42bde8b5), SkBits2Float(0x42d0999a), SkBits2Float(0x42bde853), SkBits2Float(0x42d20000), SkBits2Float(0x42bde80b));  // 103.6f, 94.9545f, 104.3f, 94.9538f, 105, 94.9532f
+path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42bde7c2), SkBits2Float(0x42d4cccd), SkBits2Float(0x42bde9d6), SkBits2Float(0x42d63333), SkBits2Float(0x42bde7c4));  // 105.7f, 94.9527f, 106.4f, 94.9567f, 107.1f, 94.9527f
+path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42bde5b2), SkBits2Float(0x42d90000), SkBits2Float(0x42bde19a), SkBits2Float(0x42da6666), SkBits2Float(0x42bddb9f));  // 107.8f, 94.9486f, 108.5f, 94.9406f, 109.2f, 94.9289f
+path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42bdd5a3), SkBits2Float(0x42dd3333), SkBits2Float(0x42bdcc07), SkBits2Float(0x42de999a), SkBits2Float(0x42bdc3e1));  // 109.9f, 94.9173f, 110.6f, 94.8985f, 111.3f, 94.8826f
+path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42bdbbba), SkBits2Float(0x42e16666), SkBits2Float(0x42bdb332), SkBits2Float(0x42e2cccd), SkBits2Float(0x42bdaab8));  // 112, 94.8667f, 112.7f, 94.85f, 113.4f, 94.8334f
+path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42bda23d), SkBits2Float(0x42e5999a), SkBits2Float(0x42bd9b8e), SkBits2Float(0x42e70000), SkBits2Float(0x42bd9101));  // 114.1f, 94.8169f, 114.8f, 94.8038f, 115.5f, 94.7832f
+path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42bd8675), SkBits2Float(0x42e9cccd), SkBits2Float(0x42bd79f9), SkBits2Float(0x42eb3333), SkBits2Float(0x42bd6b6c));  // 116.2f, 94.7626f, 116.9f, 94.7382f, 117.6f, 94.7098f
+path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42bd5cdf), SkBits2Float(0x42ee0000), SkBits2Float(0x42bd4608), SkBits2Float(0x42ef6666), SkBits2Float(0x42bd39b1));  // 118.3f, 94.6814f, 119, 94.6368f, 119.7f, 94.6127f
+path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42bd2d5b), SkBits2Float(0x42f23333), SkBits2Float(0x42bd24cb), SkBits2Float(0x42f3999a), SkBits2Float(0x42bd2166));  // 120.4f, 94.5886f, 121.1f, 94.5719f, 121.8f, 94.5652f
+path.cubicTo(SkBits2Float(0x42f50000), SkBits2Float(0x42bd1e02), SkBits2Float(0x42f66666), SkBits2Float(0x42bd22c1), SkBits2Float(0x42f7cccd), SkBits2Float(0x42bd2558));  // 122.5f, 94.5586f, 123.2f, 94.5679f, 123.9f, 94.5729f
+path.cubicTo(SkBits2Float(0x42f93333), SkBits2Float(0x42bd27ef), SkBits2Float(0x42fa999a), SkBits2Float(0x42bd2da3), SkBits2Float(0x42fc0000), SkBits2Float(0x42bd30f0));  // 124.6f, 94.578f, 125.3f, 94.5891f, 126, 94.5956f
+path.cubicTo(SkBits2Float(0x42fd6666), SkBits2Float(0x42bd343d), SkBits2Float(0x42fecccd), SkBits2Float(0x42bd36a5), SkBits2Float(0x4300199a), SkBits2Float(0x42bd3924));  // 126.7f, 94.602f, 127.4f, 94.6067f, 128.1f, 94.6116f
+path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42bd3ba4), SkBits2Float(0x43018000), SkBits2Float(0x42bd3f14), SkBits2Float(0x43023333), SkBits2Float(0x42bd3fed));  // 128.8f, 94.6165f, 129.5f, 94.6232f, 130.2f, 94.6249f
+path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42bd40c6), SkBits2Float(0x4303999a), SkBits2Float(0x42bd3f50), SkBits2Float(0x43044ccd), SkBits2Float(0x42bd3e3b));  // 130.9f, 94.6265f, 131.6f, 94.6237f, 132.3f, 94.6215f
+path.cubicTo(SkBits2Float(0x43050000), SkBits2Float(0x42bd3d25), SkBits2Float(0x4305b333), SkBits2Float(0x42bd3a44), SkBits2Float(0x43066666), SkBits2Float(0x42bd396b));  // 133, 94.6194f, 133.7f, 94.6138f, 134.4f, 94.6121f
+path.cubicTo(SkBits2Float(0x4307199a), SkBits2Float(0x42bd3892), SkBits2Float(0x4307cccd), SkBits2Float(0x42bd3978), SkBits2Float(0x43088000), SkBits2Float(0x42bd3924));  // 135.1f, 94.6105f, 135.8f, 94.6122f, 136.5f, 94.6116f
+path.cubicTo(SkBits2Float(0x43093333), SkBits2Float(0x42bd38d0), SkBits2Float(0x4309e666), SkBits2Float(0x42bd3766), SkBits2Float(0x430a999a), SkBits2Float(0x42bd3772));  // 137.2f, 94.611f, 137.9f, 94.6082f, 138.6f, 94.6083f
+path.cubicTo(SkBits2Float(0x430b4ccd), SkBits2Float(0x42bd377e), SkBits2Float(0x430c0000), SkBits2Float(0x42bd3892), SkBits2Float(0x430cb333), SkBits2Float(0x42bd396b));  // 139.3f, 94.6084f, 140, 94.6105f, 140.7f, 94.6121f
+path.cubicTo(SkBits2Float(0x430d6666), SkBits2Float(0x42bd3a44), SkBits2Float(0x430e199a), SkBits2Float(0x42bd3bbb), SkBits2Float(0x430ecccd), SkBits2Float(0x42bd3c88));  // 141.4f, 94.6138f, 142.1f, 94.6167f, 142.8f, 94.6182f
+path.cubicTo(SkBits2Float(0x430f8000), SkBits2Float(0x42bd3d56), SkBits2Float(0x43103333), SkBits2Float(0x42bd3cd1), SkBits2Float(0x4310e666), SkBits2Float(0x42bd3e3b));  // 143.5f, 94.6198f, 144.2f, 94.6188f, 144.9f, 94.6215f
+path.cubicTo(SkBits2Float(0x4311999a), SkBits2Float(0x42bd3fa4), SkBits2Float(0x43124ccd), SkBits2Float(0x42bd4278), SkBits2Float(0x43130000), SkBits2Float(0x42bd4503));  // 145.6f, 94.6243f, 146.3f, 94.6298f, 147, 94.6348f
+path.cubicTo(SkBits2Float(0x4313b333), SkBits2Float(0x42bd478e), SkBits2Float(0x43146666), SkBits2Float(0x42bd4af2), SkBits2Float(0x4315199a), SkBits2Float(0x42bd4d7e));  // 147.7f, 94.6398f, 148.4f, 94.6464f, 149.1f, 94.6514f
+path.cubicTo(SkBits2Float(0x4315cccd), SkBits2Float(0x42bd5009), SkBits2Float(0x43168000), SkBits2Float(0x42bd53aa), SkBits2Float(0x43173333), SkBits2Float(0x42bd5446));  // 149.8f, 94.6563f, 150.5f, 94.6634f, 151.2f, 94.6646f
+path.cubicTo(SkBits2Float(0x4317e666), SkBits2Float(0x42bd54e3), SkBits2Float(0x4318999a), SkBits2Float(0x42bd5202), SkBits2Float(0x43194ccd), SkBits2Float(0x42bd5128));  // 151.9f, 94.6658f, 152.6f, 94.6602f, 153.3f, 94.6585f
+path.cubicTo(SkBits2Float(0x431a0000), SkBits2Float(0x42bd504f), SkBits2Float(0x431ab333), SkBits2Float(0x42bd4fcc), SkBits2Float(0x431b6666), SkBits2Float(0x42bd4f30));  // 154, 94.6569f, 154.7f, 94.6559f, 155.4f, 94.6547f
+path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42bd4e93), SkBits2Float(0x431ccccd), SkBits2Float(0x42bd4ee7), SkBits2Float(0x431d8000), SkBits2Float(0x42bd4d7e));  // 156.1f, 94.6535f, 156.8f, 94.6541f, 157.5f, 94.6514f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42bd4c14), SkBits2Float(0x431ee666), SkBits2Float(0x42bd48f8), SkBits2Float(0x431f999a), SkBits2Float(0x42bd46b5));  // 158.2f, 94.6486f, 158.9f, 94.6425f, 159.6f, 94.6381f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42bd4472), SkBits2Float(0x43210000), SkBits2Float(0x42bd410e), SkBits2Float(0x4321b333), SkBits2Float(0x42bd3fed));  // 160.3f, 94.6337f, 161, 94.6271f, 161.7f, 94.6249f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42bd3ecb), SkBits2Float(0x4323199a), SkBits2Float(0x42bd401d), SkBits2Float(0x4323cccd), SkBits2Float(0x42bd3fed));  // 162.4f, 94.6226f, 163.1f, 94.6252f, 163.8f, 94.6249f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42bd3fbc), SkBits2Float(0x43253333), SkBits2Float(0x42bd40e7), SkBits2Float(0x4325e666), SkBits2Float(0x42bd3ec8));  // 164.5f, 94.6245f, 165.2f, 94.6268f, 165.9f, 94.6226f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42bd3ca8), SkBits2Float(0x43274ccd), SkBits2Float(0x42bd383a), SkBits2Float(0x43280000), SkBits2Float(0x42bd332f));  // 166.6f, 94.6185f, 167.3f, 94.6098f, 168, 94.6f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42bd2e25), SkBits2Float(0x43296666), SkBits2Float(0x42bd2647), SkBits2Float(0x432a199a), SkBits2Float(0x42bd2088));  // 168.7f, 94.5901f, 169.4f, 94.5748f, 170.1f, 94.5635f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42bd1aca), SkBits2Float(0x432b8000), SkBits2Float(0x42bd147c), SkBits2Float(0x432c3333), SkBits2Float(0x42bd10b8));  // 170.8f, 94.5523f, 171.5f, 94.54f, 172.2f, 94.5327f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42bd0cf4), SkBits2Float(0x432d999a), SkBits2Float(0x42bd0c32), SkBits2Float(0x432e4ccd), SkBits2Float(0x42bd09ef));  // 172.9f, 94.5253f, 173.6f, 94.5238f, 174.3f, 94.5194f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42bd07ad), SkBits2Float(0x432fb333), SkBits2Float(0x42bd04cf), SkBits2Float(0x43306666), SkBits2Float(0x42bd0327));  // 175, 94.515f, 175.7f, 94.5094f, 176.4f, 94.5062f
+path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42bd0000));  // 176.4f, 94.5f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42bd0000));  // 88.2f, 94.5f
+path.close();
+path.moveTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.cubicTo(SkBits2Float(0x4341e666), SkBits2Float(0x42bd0000), SkBits2Float(0x4342999a), SkBits2Float(0x42bcfab7), SkBits2Float(0x43434ccd), SkBits2Float(0x42bd0000));  // 193.9f, 94.5f, 194.6f, 94.4897f, 195.3f, 94.5f
+path.cubicTo(SkBits2Float(0x43440000), SkBits2Float(0x42bd0549), SkBits2Float(0x4344b333), SkBits2Float(0x42bd13e0), SkBits2Float(0x43456666), SkBits2Float(0x42bd1fb4));  // 196, 94.5103f, 196.7f, 94.5388f, 197.4f, 94.5619f
+path.cubicTo(SkBits2Float(0x4346199a), SkBits2Float(0x42bd2b89), SkBits2Float(0x4346cccd), SkBits2Float(0x42bd3eb2), SkBits2Float(0x43478000), SkBits2Float(0x42bd46fc));  // 198.1f, 94.585f, 198.8f, 94.6225f, 199.5f, 94.6386f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42bd4f46), SkBits2Float(0x4348e666), SkBits2Float(0x42bd5127), SkBits2Float(0x4349999a), SkBits2Float(0x42bd516f));  // 200.2f, 94.6548f, 200.9f, 94.6585f, 201.6f, 94.659f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42bd51b7), SkBits2Float(0x434b0000), SkBits2Float(0x42bd4dc4), SkBits2Float(0x434bb333), SkBits2Float(0x42bd48ae));  // 202.3f, 94.6596f, 203, 94.6519f, 203.7f, 94.642f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42bd4397), SkBits2Float(0x434d199a), SkBits2Float(0x42bd39ee), SkBits2Float(0x434dcccd), SkBits2Float(0x42bd32e9));  // 204.4f, 94.632f, 205.1f, 94.6131f, 205.8f, 94.5994f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42bd2be4), SkBits2Float(0x434f3333), SkBits2Float(0x42bd23a6), SkBits2Float(0x434fe666), SkBits2Float(0x42bd1e8f));  // 206.5f, 94.5857f, 207.2f, 94.5696f, 207.9f, 94.5597f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42bd1979), SkBits2Float(0x43514ccd), SkBits2Float(0x42bd14f3), SkBits2Float(0x43520000), SkBits2Float(0x42bd1463));  // 208.6f, 94.5498f, 209.3f, 94.5409f, 210, 94.5398f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42bd13d2), SkBits2Float(0x43536666), SkBits2Float(0x42bd1590), SkBits2Float(0x4354199a), SkBits2Float(0x42bd1b2b));  // 210.7f, 94.5387f, 211.4f, 94.5421f, 212.1f, 94.5531f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42bd20c7), SkBits2Float(0x43558000), SkBits2Float(0x42bd2bce), SkBits2Float(0x43563333), SkBits2Float(0x42bd3607));  // 212.8f, 94.564f, 213.5f, 94.5856f, 214.2f, 94.6055f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42bd403f), SkBits2Float(0x4357999a), SkBits2Float(0x42bd4644), SkBits2Float(0x43584ccd), SkBits2Float(0x42bd587e));  // 214.9f, 94.6255f, 215.6f, 94.6372f, 216.3f, 94.6728f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42bd6ab8), SkBits2Float(0x4359b333), SkBits2Float(0x42bd8902), SkBits2Float(0x435a6666), SkBits2Float(0x42bda362));  // 217, 94.7084f, 217.7f, 94.7676f, 218.4f, 94.8191f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42bdbdc2), SkBits2Float(0x435bcccd), SkBits2Float(0x42bde650), SkBits2Float(0x435c8000), SkBits2Float(0x42bdf6c0));  // 219.1f, 94.8706f, 219.8f, 94.9498f, 220.5f, 94.9819f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42be0731), SkBits2Float(0x435de666), SkBits2Float(0x42be0409), SkBits2Float(0x435e999a), SkBits2Float(0x42be0604));  // 221.2f, 95.014f, 221.9f, 95.0079f, 222.6f, 95.0117f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42be07fe), SkBits2Float(0x43600000), SkBits2Float(0x42be052a), SkBits2Float(0x4360b333), SkBits2Float(0x42be029f));  // 223.3f, 95.0156f, 224, 95.0101f, 224.7f, 95.0051f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42be0014), SkBits2Float(0x4362199a), SkBits2Float(0x42bdf903), SkBits2Float(0x4362cccd), SkBits2Float(0x42bdf6c0));  // 225.4f, 95.0002f, 226.1f, 94.9864f, 226.8f, 94.9819f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42bdf47e), SkBits2Float(0x43643333), SkBits2Float(0x42bdf3ed), SkBits2Float(0x4364e666), SkBits2Float(0x42bdf50e));  // 227.5f, 94.9775f, 228.2f, 94.9764f, 228.9f, 94.9786f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42bdf630), SkBits2Float(0x43664ccd), SkBits2Float(0x42bdfe62), SkBits2Float(0x43670000), SkBits2Float(0x42bdfd89));  // 229.6f, 94.9808f, 230.3f, 94.9968f, 231, 94.9952f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42bdfcb0), SkBits2Float(0x43686666), SkBits2Float(0x42bdf4c6), SkBits2Float(0x4369199a), SkBits2Float(0x42bdeff8));  // 231.7f, 94.9935f, 232.4f, 94.9781f, 233.1f, 94.9687f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42bdeb2a), SkBits2Float(0x436a8000), SkBits2Float(0x42bde2f8), SkBits2Float(0x436b3333), SkBits2Float(0x42bde0b5));  // 233.8f, 94.9593f, 234.5f, 94.9433f, 235.2f, 94.9389f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42bdde72), SkBits2Float(0x436c999a), SkBits2Float(0x42bde1d6), SkBits2Float(0x436d4ccd), SkBits2Float(0x42bde267));  // 235.9f, 94.9345f, 236.6f, 94.9411f, 237.3f, 94.9422f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42bde2f8), SkBits2Float(0x436eb333), SkBits2Float(0x42bde340), SkBits2Float(0x436f6666), SkBits2Float(0x42bde419));  // 238, 94.9433f, 238.7f, 94.9438f, 239.4f, 94.9455f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42bde4f2), SkBits2Float(0x4370cccd), SkBits2Float(0x42bde6a4), SkBits2Float(0x43718000), SkBits2Float(0x42bde77d));  // 240.1f, 94.9472f, 240.8f, 94.9505f, 241.5f, 94.9521f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42bde856), SkBits2Float(0x4372e666), SkBits2Float(0x42bde8e7), SkBits2Float(0x4373999a), SkBits2Float(0x42bde930));  // 242.2f, 94.9538f, 242.9f, 94.9549f, 243.6f, 94.9554f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42bde978), SkBits2Float(0x43750000), SkBits2Float(0x42bde8e7), SkBits2Float(0x4375b333), SkBits2Float(0x42bde930));  // 244.3f, 94.956f, 245, 94.9549f, 245.7f, 94.9554f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42bde978), SkBits2Float(0x4377199a), SkBits2Float(0x42bdea99), SkBits2Float(0x4377cccd), SkBits2Float(0x42bdeae2));  // 246.4f, 94.956f, 247.1f, 94.9582f, 247.8f, 94.9588f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42bdeb2a), SkBits2Float(0x43793333), SkBits2Float(0x42bdeb72), SkBits2Float(0x4379e666), SkBits2Float(0x42bdeae2));  // 248.5f, 94.9593f, 249.2f, 94.9599f, 249.9f, 94.9588f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42bdea51), SkBits2Float(0x437b4ccd), SkBits2Float(0x42bde89f), SkBits2Float(0x437c0000), SkBits2Float(0x42bde77d));  // 250.6f, 94.9576f, 251.3f, 94.9543f, 252, 94.9521f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42bde65c), SkBits2Float(0x437d6666), SkBits2Float(0x42bde4f2), SkBits2Float(0x437e199a), SkBits2Float(0x42bde419));  // 252.7f, 94.9499f, 253.4f, 94.9472f, 254.1f, 94.9455f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42bde340), SkBits2Float(0x437f8000), SkBits2Float(0x42bde267), SkBits2Float(0x4380199a), SkBits2Float(0x42bde267));  // 254.8f, 94.9438f, 255.5f, 94.9422f, 256.2f, 94.9422f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42bde267), SkBits2Float(0x4380cccd), SkBits2Float(0x42bde2f8), SkBits2Float(0x43812666), SkBits2Float(0x42bde419));  // 256.9f, 94.9422f, 257.6f, 94.9433f, 258.3f, 94.9455f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42bde53b), SkBits2Float(0x4381d99a), SkBits2Float(0x42bde7c6), SkBits2Float(0x43823333), SkBits2Float(0x42bde930));  // 259, 94.9477f, 259.7f, 94.9527f, 260.4f, 94.9554f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42bdea99), SkBits2Float(0x4382e666), SkBits2Float(0x42bdec03), SkBits2Float(0x43834000), SkBits2Float(0x42bdec94));  // 261.1f, 94.9582f, 261.8f, 94.961f, 262.5f, 94.9621f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42bded24), SkBits2Float(0x4383f333), SkBits2Float(0x42bdecdc), SkBits2Float(0x43844ccd), SkBits2Float(0x42bdec94));  // 263.2f, 94.9632f, 263.9f, 94.9626f, 264.6f, 94.9621f
+path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42bdec4b), SkBits2Float(0x43850000), SkBits2Float(0x42bdeb72), SkBits2Float(0x4385599a), SkBits2Float(0x42bdeae2));  // 265.3f, 94.9615f, 266, 94.9599f, 266.7f, 94.9588f
+path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42bdea51), SkBits2Float(0x43860ccd), SkBits2Float(0x42bde978), SkBits2Float(0x43866666), SkBits2Float(0x42bde930));  // 267.4f, 94.9576f, 268.1f, 94.956f, 268.8f, 94.9554f
+path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42bde8e7), SkBits2Float(0x4387199a), SkBits2Float(0x42bde8e7), SkBits2Float(0x43877333), SkBits2Float(0x42bde930));  // 269.5f, 94.9549f, 270.2f, 94.9549f, 270.9f, 94.9554f
+path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42bde978), SkBits2Float(0x43882666), SkBits2Float(0x42bdea99), SkBits2Float(0x43888000), SkBits2Float(0x42bdeae2));  // 271.6f, 94.956f, 272.3f, 94.9582f, 273, 94.9588f
+path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42bdeb2a), SkBits2Float(0x43893333), SkBits2Float(0x42bdeae2), SkBits2Float(0x43898ccd), SkBits2Float(0x42bdeae2));  // 273.7f, 94.9593f, 274.4f, 94.9588f, 275.1f, 94.9588f
+path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42bdeae2), SkBits2Float(0x438a4000), SkBits2Float(0x42bdea99), SkBits2Float(0x438a999a), SkBits2Float(0x42bdeae2));  // 275.8f, 94.9588f, 276.5f, 94.9582f, 277.2f, 94.9588f
+path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42bdeb2a), SkBits2Float(0x438b4ccd), SkBits2Float(0x42bdec94), SkBits2Float(0x438ba666), SkBits2Float(0x42bdec94));  // 277.9f, 94.9593f, 278.6f, 94.9621f, 279.3f, 94.9621f
+path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42bdec94), SkBits2Float(0x438c599a), SkBits2Float(0x42bdead6), SkBits2Float(0x438cb333), SkBits2Float(0x42bdeae2));  // 280, 94.9621f, 280.7f, 94.9587f, 281.4f, 94.9588f
+path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42bdeaed), SkBits2Float(0x438d6666), SkBits2Float(0x42bdeb11), SkBits2Float(0x438dc000), SkBits2Float(0x42bdecda));  // 282.1f, 94.9588f, 282.8f, 94.9591f, 283.5f, 94.9626f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42bdeea4), SkBits2Float(0x438e7333), SkBits2Float(0x42bdf3e9), SkBits2Float(0x438ecccd), SkBits2Float(0x42bdf59c));  // 284.2f, 94.9661f, 284.9f, 94.9764f, 285.6f, 94.9797f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42bdf74e), SkBits2Float(0x438f8000), SkBits2Float(0x42bdfbed), SkBits2Float(0x438fd99a), SkBits2Float(0x42bdf707));  // 286.3f, 94.983f, 287, 94.992f, 287.7f, 94.9825f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42bdf222), SkBits2Float(0x43908ccd), SkBits2Float(0x42bdeb8a), SkBits2Float(0x4390e666), SkBits2Float(0x42bdd83a));  // 288.4f, 94.9729f, 289.1f, 94.96f, 289.8f, 94.9223f
+path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42bdc4eb), SkBits2Float(0x4391999a), SkBits2Float(0x42bda6ea), SkBits2Float(0x4391f333), SkBits2Float(0x42bd832a));  // 290.5f, 94.8846f, 291.2f, 94.826f, 291.9f, 94.7562f
+path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42bd5f6a), SkBits2Float(0x4392a666), SkBits2Float(0x42bd1798), SkBits2Float(0x43930000), SkBits2Float(0x42bd01bb));  // 292.6f, 94.6864f, 293.3f, 94.5461f, 294, 94.5034f
+path.cubicTo(SkBits2Float(0x4393599a), SkBits2Float(0x42bcebdf), SkBits2Float(0x4393b333), SkBits2Float(0x42bd004a), SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000));  // 294.7f, 94.4607f, 295.4f, 94.5006f, 296.1f, 94.5f
+path.lineTo(SkBits2Float(0x43940ccd), SkBits2Float(0x42bd0000));  // 296.1f, 94.5f
+path.lineTo(SkBits2Float(0x43413333), SkBits2Float(0x42bd0000));  // 193.2f, 94.5f
+path.close();
+path.moveTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42bd0000), SkBits2Float(0x43ace666), SkBits2Float(0x42bcfc5e), SkBits2Float(0x43ad4000), SkBits2Float(0x42bd0000));  // 345.1f, 94.5f, 345.8f, 94.4929f, 346.5f, 94.5f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42bd03a2), SkBits2Float(0x43adf333), SkBits2Float(0x42bcf966), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42bd15ce));  // 347.2f, 94.5071f, 347.9f, 94.4871f, 348.6f, 94.5426f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42bd3236), SkBits2Float(0x43af0000), SkBits2Float(0x42bd84e8), SkBits2Float(0x43af599a), SkBits2Float(0x42bdaa71));  // 349.3f, 94.5981f, 350, 94.7596f, 350.7f, 94.8329f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42bdcffb), SkBits2Float(0x43b00ccd), SkBits2Float(0x42bde92e), SkBits2Float(0x43b06666), SkBits2Float(0x42bdf707));  // 351.4f, 94.9062f, 352.1f, 94.9554f, 352.8f, 94.9825f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42be04e0), SkBits2Float(0x43b1199a), SkBits2Float(0x42bdfc2b), SkBits2Float(0x43b17333), SkBits2Float(0x42bdfd89));  // 353.5f, 95.0095f, 354.2f, 94.9925f, 354.9f, 94.9952f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42bdfee7), SkBits2Float(0x43b22666), SkBits2Float(0x42bdfe62), SkBits2Float(0x43b28000), SkBits2Float(0x42bdff3b));  // 355.6f, 94.9979f, 356.3f, 94.9968f, 357, 94.9985f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42be0014), SkBits2Float(0x43b33333), SkBits2Float(0x42be020f), SkBits2Float(0x43b38ccd), SkBits2Float(0x42be029f));  // 357.7f, 95.0002f, 358.4f, 95.004f, 359.1f, 95.0051f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42be0330), SkBits2Float(0x43b44000), SkBits2Float(0x42be020f), SkBits2Float(0x43b4999a), SkBits2Float(0x42be029f));  // 359.8f, 95.0062f, 360.5f, 95.004f, 361.2f, 95.0051f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42be0330), SkBits2Float(0x43b54ccd), SkBits2Float(0x42be052a), SkBits2Float(0x43b5a666), SkBits2Float(0x42be0604));  // 361.9f, 95.0062f, 362.6f, 95.0101f, 363.3f, 95.0117f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42be06dd), SkBits2Float(0x43b6599a), SkBits2Float(0x42be0725), SkBits2Float(0x43b6b333), SkBits2Float(0x42be07b6));  // 364, 95.0134f, 364.7f, 95.014f, 365.4f, 95.0151f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42be0846), SkBits2Float(0x43b76666), SkBits2Float(0x42be08d7), SkBits2Float(0x43b7c000), SkBits2Float(0x42be0968));  // 366.1f, 95.0162f, 366.8f, 95.0173f, 367.5f, 95.0184f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42be09f8), SkBits2Float(0x43b87333), SkBits2Float(0x42be0ad2), SkBits2Float(0x43b8cccd), SkBits2Float(0x42be0b1a));  // 368.2f, 95.0195f, 368.9f, 95.0211f, 369.6f, 95.0217f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42be0b62), SkBits2Float(0x43b98000), SkBits2Float(0x42be0bab), SkBits2Float(0x43b9d99a), SkBits2Float(0x42be0b1a));  // 370.3f, 95.0222f, 371, 95.0228f, 371.7f, 95.0217f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42be0a89), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42be088f), SkBits2Float(0x43bae666), SkBits2Float(0x42be07b6));  // 372.4f, 95.0206f, 373.1f, 95.0167f, 373.8f, 95.0151f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42be06dd), SkBits2Float(0x43bb999a), SkBits2Float(0x42be064c), SkBits2Float(0x43bbf333), SkBits2Float(0x42be0604));  // 374.5f, 95.0134f, 375.2f, 95.0123f, 375.9f, 95.0117f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42be05bb), SkBits2Float(0x43bca666), SkBits2Float(0x42be0604), SkBits2Float(0x43bd0000), SkBits2Float(0x42be0604));  // 376.6f, 95.0112f, 377.3f, 95.0117f, 378, 95.0117f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42be0604), SkBits2Float(0x43bdb333), SkBits2Float(0x42be0604), SkBits2Float(0x43be0ccd), SkBits2Float(0x42be0604));  // 378.7f, 95.0117f, 379.4f, 95.0117f, 380.1f, 95.0117f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42be0604), SkBits2Float(0x43bec000), SkBits2Float(0x42be0725), SkBits2Float(0x43bf199a), SkBits2Float(0x42be0604));  // 380.8f, 95.0117f, 381.5f, 95.014f, 382.2f, 95.0117f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42be04e2), SkBits2Float(0x43bfcccd), SkBits2Float(0x42be0136), SkBits2Float(0x43c02666), SkBits2Float(0x42bdff3b));  // 382.9f, 95.0095f, 383.6f, 95.0024f, 384.3f, 94.9985f
+path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42bdfd41), SkBits2Float(0x43c0d99a), SkBits2Float(0x42bdfa6d), SkBits2Float(0x43c13333), SkBits2Float(0x42bdfa25));  // 385, 94.9946f, 385.7f, 94.9891f, 386.4f, 94.9886f
+path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42bdf9dc), SkBits2Float(0x43c1e666), SkBits2Float(0x42bdfcf8), SkBits2Float(0x43c24000), SkBits2Float(0x42bdfd89));  // 387.1f, 94.988f, 387.8f, 94.9941f, 388.5f, 94.9952f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42bdfe1a), SkBits2Float(0x43c2f333), SkBits2Float(0x42bdfdd1), SkBits2Float(0x43c34ccd), SkBits2Float(0x42bdfd89));  // 389.2f, 94.9963f, 389.9f, 94.9957f, 390.6f, 94.9952f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42bdfd41), SkBits2Float(0x43c40000), SkBits2Float(0x42bdfd41), SkBits2Float(0x43c4599a), SkBits2Float(0x42bdfbd7));  // 391.3f, 94.9946f, 392, 94.9946f, 392.7f, 94.9919f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42bdfa6d), SkBits2Float(0x43c50ccd), SkBits2Float(0x42bdf709), SkBits2Float(0x43c56666), SkBits2Float(0x42bdf50e));  // 393.4f, 94.9891f, 394.1f, 94.9825f, 394.8f, 94.9786f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42bdf314), SkBits2Float(0x43c6199a), SkBits2Float(0x42bdf0d1), SkBits2Float(0x43c67333), SkBits2Float(0x42bdeff8));  // 395.5f, 94.9748f, 396.2f, 94.9703f, 396.9f, 94.9687f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42bdef1f), SkBits2Float(0x43c72666), SkBits2Float(0x42bdf040), SkBits2Float(0x43c78000), SkBits2Float(0x42bdeff8));  // 397.6f, 94.967f, 398.3f, 94.9692f, 399, 94.9687f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42bdefb0), SkBits2Float(0x43c83333), SkBits2Float(0x42bdee8e), SkBits2Float(0x43c88ccd), SkBits2Float(0x42bdee46));  // 399.7f, 94.9681f, 400.4f, 94.9659f, 401.1f, 94.9654f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42bdedfe), SkBits2Float(0x43c94000), SkBits2Float(0x42bdee46), SkBits2Float(0x43c9999a), SkBits2Float(0x42bdee46));  // 401.8f, 94.9648f, 402.5f, 94.9654f, 403.2f, 94.9654f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42bdee46), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42bdedb5), SkBits2Float(0x43caa666), SkBits2Float(0x42bdee46));  // 403.9f, 94.9654f, 404.6f, 94.9643f, 405.3f, 94.9654f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42bdeed7), SkBits2Float(0x43cb599a), SkBits2Float(0x42bdf089), SkBits2Float(0x43cbb333), SkBits2Float(0x42bdf1aa));  // 406, 94.9665f, 406.7f, 94.9698f, 407.4f, 94.972f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42bdf2cc), SkBits2Float(0x43cc6666), SkBits2Float(0x42bdf47e), SkBits2Float(0x43ccc000), SkBits2Float(0x42bdf50e));  // 408.1f, 94.9742f, 408.8f, 94.9775f, 409.5f, 94.9786f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42bdf59f), SkBits2Float(0x43cd7333), SkBits2Float(0x42bdf4c6), SkBits2Float(0x43cdcccd), SkBits2Float(0x42bdf50e));  // 410.2f, 94.9797f, 410.9f, 94.9781f, 411.6f, 94.9786f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42bdf557), SkBits2Float(0x43ce8000), SkBits2Float(0x42bdf557), SkBits2Float(0x43ced99a), SkBits2Float(0x42bdf6c0));  // 412.3f, 94.9792f, 413, 94.9792f, 413.7f, 94.9819f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42bdf82a), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42bdfafe), SkBits2Float(0x43cfe666), SkBits2Float(0x42bdfd89));  // 414.4f, 94.9847f, 415.1f, 94.9902f, 415.8f, 94.9952f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42be0014), SkBits2Float(0x43d0999a), SkBits2Float(0x42be0378), SkBits2Float(0x43d0f333), SkBits2Float(0x42be0604));  // 416.5f, 95.0002f, 417.2f, 95.0068f, 417.9f, 95.0117f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42be088f), SkBits2Float(0x43d1a666), SkBits2Float(0x42be0b1a), SkBits2Float(0x43d20000), SkBits2Float(0x42be0ccc));  // 418.6f, 95.0167f, 419.3f, 95.0217f, 420, 95.025f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42be0e7e), SkBits2Float(0x43d2b333), SkBits2Float(0x42be119a), SkBits2Float(0x43d30ccd), SkBits2Float(0x42be1030));  // 420.7f, 95.0283f, 421.4f, 95.0344f, 422.1f, 95.0316f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42be0ec6), SkBits2Float(0x43d3c000), SkBits2Float(0x42be0ad2), SkBits2Float(0x43d4199a), SkBits2Float(0x42be0451));  // 422.8f, 95.0289f, 423.5f, 95.0211f, 424.2f, 95.0084f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42bdfdd1), SkBits2Float(0x43d4cccd), SkBits2Float(0x42bdefec), SkBits2Float(0x43d52666), SkBits2Float(0x42bde930));  // 424.9f, 94.9957f, 425.6f, 94.9686f, 426.3f, 94.9554f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42bde273), SkBits2Float(0x43d5d99a), SkBits2Float(0x42bde21d), SkBits2Float(0x43d63333), SkBits2Float(0x42bddbe5));  // 427, 94.9423f, 427.7f, 94.9416f, 428.4f, 94.9295f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42bdd5ad), SkBits2Float(0x43d6e666), SkBits2Float(0x42bdcf3b), SkBits2Float(0x43d74000), SkBits2Float(0x42bdc3e1));  // 429.1f, 94.9173f, 429.8f, 94.9047f, 430.5f, 94.8826f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42bdb887), SkBits2Float(0x43d7f333), SkBits2Float(0x42bd9d7d), SkBits2Float(0x43d84ccd), SkBits2Float(0x42bd97ca));  // 431.2f, 94.8604f, 431.9f, 94.8076f, 432.6f, 94.7965f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42bd9217), SkBits2Float(0x43d90000), SkBits2Float(0x42bd9820), SkBits2Float(0x43d9599a), SkBits2Float(0x42bda1b0));  // 433.3f, 94.7853f, 434, 94.7971f, 434.7f, 94.8158f
+path.cubicTo(SkBits2Float(0x43d9b333), SkBits2Float(0x42bdab40), SkBits2Float(0x43da0ccd), SkBits2Float(0x42bdc5dd), SkBits2Float(0x43da6666), SkBits2Float(0x42bdd12b));  // 435.4f, 94.8345f, 436.1f, 94.8865f, 436.8f, 94.9085f
+path.cubicTo(SkBits2Float(0x43dac000), SkBits2Float(0x42bddc79), SkBits2Float(0x43db199a), SkBits2Float(0x42bde1cc), SkBits2Float(0x43db7333), SkBits2Float(0x42bde585));  // 437.5f, 94.9306f, 438.2f, 94.941f, 438.9f, 94.9483f
+path.cubicTo(SkBits2Float(0x43dbcccd), SkBits2Float(0x42bde93d), SkBits2Float(0x43dc2666), SkBits2Float(0x42bde75a), SkBits2Float(0x43dc8000), SkBits2Float(0x42bde77d));  // 439.6f, 94.9555f, 440.3f, 94.9519f, 441, 94.9521f
+path.cubicTo(SkBits2Float(0x43dcd99a), SkBits2Float(0x42bde7a1), SkBits2Float(0x43dd3333), SkBits2Float(0x42bde610), SkBits2Float(0x43dd8ccd), SkBits2Float(0x42bde658));  // 441.7f, 94.9524f, 442.4f, 94.9493f, 443.1f, 94.9499f
+path.cubicTo(SkBits2Float(0x43dde666), SkBits2Float(0x42bde6a1), SkBits2Float(0x43de4000), SkBits2Float(0x42bdea2c), SkBits2Float(0x43de999a), SkBits2Float(0x42bde930));  // 443.8f, 94.9504f, 444.5f, 94.9574f, 445.2f, 94.9554f
+path.cubicTo(SkBits2Float(0x43def333), SkBits2Float(0x42bde833), SkBits2Float(0x43df4ccd), SkBits2Float(0x42bdee5f), SkBits2Float(0x43dfa666), SkBits2Float(0x42bde06e));  // 445.9f, 94.9535f, 446.6f, 94.9656f, 447.3f, 94.9383f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42bdd27d), SkBits2Float(0x43e0599a), SkBits2Float(0x42bdb871), SkBits2Float(0x43e0b333), SkBits2Float(0x42bd958a));  // 448, 94.9111f, 448.7f, 94.8602f, 449.4f, 94.7921f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42bd72a4), SkBits2Float(0x43e16666), SkBits2Float(0x42bd27f2), SkBits2Float(0x43e1c000), SkBits2Float(0x42bd0f06));  // 450.1f, 94.7239f, 450.8f, 94.578f, 451.5f, 94.5293f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42bcf619), SkBits2Float(0x43e27333), SkBits2Float(0x42bcfbb7), SkBits2Float(0x43e2cccd), SkBits2Float(0x42bd0000));  // 452.2f, 94.4807f, 452.9f, 94.4916f, 453.6f, 94.5f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42bd0449), SkBits2Float(0x43e38000), SkBits2Float(0x42bd1419), SkBits2Float(0x43e3d99a), SkBits2Float(0x42bd28bc));  // 454.3f, 94.5084f, 455, 94.5393f, 455.7f, 94.5796f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42bd3d60), SkBits2Float(0x43e48ccd), SkBits2Float(0x42bd7238), SkBits2Float(0x43e4e666), SkBits2Float(0x42bd7bd4));  // 456.4f, 94.6199f, 457.1f, 94.7231f, 457.8f, 94.7419f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42bd8570), SkBits2Float(0x43e5999a), SkBits2Float(0x42bd70ce), SkBits2Float(0x43e5f333), SkBits2Float(0x42bd6264));  // 458.5f, 94.7606f, 459.2f, 94.7203f, 459.9f, 94.6922f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42bd53fa), SkBits2Float(0x43e6a666), SkBits2Float(0x42bd35be), SkBits2Float(0x43e70000), SkBits2Float(0x42bd2558));  // 460.6f, 94.664f, 461.3f, 94.605f, 462, 94.5729f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42bd14f2), SkBits2Float(0x43e7b333), SkBits2Float(0x42bd0639), SkBits2Float(0x43e80ccd), SkBits2Float(0x42bd0000));  // 462.7f, 94.5409f, 463.4f, 94.5122f, 464.1f, 94.5f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42bcf9c7), SkBits2Float(0x43e8c000), SkBits2Float(0x42bcfef5), SkBits2Float(0x43e9199a), SkBits2Float(0x42bd0000));  // 464.8f, 94.4878f, 465.5f, 94.498f, 466.2f, 94.5f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42bd010b), SkBits2Float(0x43e9cccd), SkBits2Float(0x42bd0645), SkBits2Float(0x43ea2666), SkBits2Float(0x42bd0645));  // 466.9f, 94.502f, 467.6f, 94.5122f, 468.3f, 94.5122f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42bd0645), SkBits2Float(0x43ead99a), SkBits2Float(0x42bd010b), SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000));  // 469, 94.5122f, 469.7f, 94.502f, 470.4f, 94.5f
+path.lineTo(SkBits2Float(0x43eb3333), SkBits2Float(0x42bd0000));  // 470.4f, 94.5f
+path.lineTo(SkBits2Float(0x43ac3333), SkBits2Float(0x42bd0000));  // 344.4f, 94.5f
+path.close();
+    return path;
+}
+
+static SkPath path5() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42c9999a), SkBits2Float(0x42b33333), SkBits2Float(0x42c9b407), SkBits2Float(0x42b4999a), SkBits2Float(0x42c9999a));  // 88.9f, 100.8f, 89.6f, 100.852f, 90.3f, 100.8f
+path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42c97f2c), SkBits2Float(0x42b76666), SkBits2Float(0x42c91521), SkBits2Float(0x42b8cccd), SkBits2Float(0x42c8fb07));  // 91, 100.748f, 91.7f, 100.541f, 92.4f, 100.49f
+path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42c8e0ee), SkBits2Float(0x42bb999a), SkBits2Float(0x42c8fd00), SkBits2Float(0x42bd0000), SkBits2Float(0x42c8fd00));  // 93.1f, 100.439f, 93.8f, 100.494f, 94.5f, 100.494f
+path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42c8fd00), SkBits2Float(0x42bfcccd), SkBits2Float(0x42c8facb), SkBits2Float(0x42c13333), SkBits2Float(0x42c8fb07));  // 95.2f, 100.494f, 95.9f, 100.49f, 96.6f, 100.49f
+path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42c8fb44), SkBits2Float(0x42c40000), SkBits2Float(0x42c8fd4a), SkBits2Float(0x42c56666), SkBits2Float(0x42c8fe6c));  // 97.3f, 100.491f, 98, 100.495f, 98.7f, 100.497f
+path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42c8ff8d), SkBits2Float(0x42c83333), SkBits2Float(0x42c90218), SkBits2Float(0x42c9999a), SkBits2Float(0x42c901d0));  // 99.4f, 100.499f, 100.1f, 100.504f, 100.8f, 100.504f
+path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42c90187), SkBits2Float(0x42cc6666), SkBits2Float(0x42c8ff39), SkBits2Float(0x42cdcccd), SkBits2Float(0x42c8fcb9));  // 101.5f, 100.503f, 102.2f, 100.498f, 102.9f, 100.494f
+path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42c8fa3a), SkBits2Float(0x42d0999a), SkBits2Float(0x42c8f364), SkBits2Float(0x42d20000), SkBits2Float(0x42c8f2d3));  // 103.6f, 100.489f, 104.3f, 100.475f, 105, 100.474f
+path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42c8f243), SkBits2Float(0x42d4cccd), SkBits2Float(0x42c8f402), SkBits2Float(0x42d63333), SkBits2Float(0x42c8f955));  // 105.7f, 100.473f, 106.4f, 100.477f, 107.1f, 100.487f
+path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42c8fea8), SkBits2Float(0x42d90000), SkBits2Float(0x42c90daf), SkBits2Float(0x42da6666), SkBits2Float(0x42c912c5));  // 107.8f, 100.497f, 108.5f, 100.527f, 109.2f, 100.537f
+path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42c917db), SkBits2Float(0x42dd3333), SkBits2Float(0x42c918f1), SkBits2Float(0x42de999a), SkBits2Float(0x42c917db));  // 109.9f, 100.547f, 110.6f, 100.549f, 111.3f, 100.547f
+path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42c916c6), SkBits2Float(0x42e16666), SkBits2Float(0x42c90ac2), SkBits2Float(0x42e2cccd), SkBits2Float(0x42c90c43));  // 112, 100.544f, 112.7f, 100.521f, 113.4f, 100.524f
+path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42c90dc4), SkBits2Float(0x42e5999a), SkBits2Float(0x42c91420), SkBits2Float(0x42e70000), SkBits2Float(0x42c920e3));  // 114.1f, 100.527f, 114.8f, 100.539f, 115.5f, 100.564f
+path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42c92da7), SkBits2Float(0x42e9cccd), SkBits2Float(0x42c946ab), SkBits2Float(0x42eb3333), SkBits2Float(0x42c958d9));  // 116.2f, 100.589f, 116.9f, 100.638f, 117.6f, 100.674f
+path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42c96b07), SkBits2Float(0x42ee0000), SkBits2Float(0x42c9832d), SkBits2Float(0x42ef6666), SkBits2Float(0x42c98df8));  // 118.3f, 100.709f, 119, 100.756f, 119.7f, 100.777f
+path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42c998c3), SkBits2Float(0x42f23333), SkBits2Float(0x42c997a9), SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a));  // 120.4f, 100.798f, 121.1f, 100.796f, 121.8f, 100.8f
+path.lineTo(SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a));  // 121.8f, 100.8f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a));  // 128.1f, 100.8f
+path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c99825));  // 128.1f, 100.797f
+path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42c99756), SkBits2Float(0x43018000), SkBits2Float(0x42c99482), SkBits2Float(0x43023333), SkBits2Float(0x42c994c1));  // 128.8f, 100.796f, 129.5f, 100.79f, 130.2f, 100.791f
+path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42c994ff), SkBits2Float(0x4303999a), SkBits2Float(0x42c998cb), SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a));  // 130.9f, 100.791f, 131.6f, 100.798f, 132.3f, 100.8f
+path.lineTo(SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a));  // 132.3f, 100.8f
+path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a));  // 128.1f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42c9999a), SkBits2Float(0x431ccccd), SkBits2Float(0x42c99dcd), SkBits2Float(0x431d8000), SkBits2Float(0x42c9999a));  // 156.1f, 100.8f, 156.8f, 100.808f, 157.5f, 100.8f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42c99567), SkBits2Float(0x431ee666), SkBits2Float(0x42c98d21), SkBits2Float(0x431f999a), SkBits2Float(0x42c98067));  // 158.2f, 100.792f, 158.9f, 100.776f, 159.6f, 100.751f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42c973ae), SkBits2Float(0x43210000), SkBits2Float(0x42c95aa1), SkBits2Float(0x4321b333), SkBits2Float(0x42c94d41));  // 160.3f, 100.726f, 161, 100.677f, 161.7f, 100.651f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42c93fe1), SkBits2Float(0x4323199a), SkBits2Float(0x42c935e5), SkBits2Float(0x4323cccd), SkBits2Float(0x42c93026));  // 162.4f, 100.625f, 163.1f, 100.605f, 163.8f, 100.594f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42c92a68), SkBits2Float(0x43253333), SkBits2Float(0x42c92b66), SkBits2Float(0x4325e666), SkBits2Float(0x42c92ac9));  // 164.5f, 100.583f, 165.2f, 100.585f, 165.9f, 100.584f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42c92a2d), SkBits2Float(0x43274ccd), SkBits2Float(0x42c92759), SkBits2Float(0x43280000), SkBits2Float(0x42c92c7b));  // 166.6f, 100.582f, 167.3f, 100.577f, 168, 100.587f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42c9319e), SkBits2Float(0x43296666), SkBits2Float(0x42c93d57), SkBits2Float(0x432a199a), SkBits2Float(0x42c94996));  // 168.7f, 100.597f, 169.4f, 100.62f, 170.1f, 100.644f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42c955d5), SkBits2Float(0x432b8000), SkBits2Float(0x42c968e8), SkBits2Float(0x432c3333), SkBits2Float(0x42c975f4));  // 170.8f, 100.668f, 171.5f, 100.705f, 172.2f, 100.73f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42c98300), SkBits2Float(0x432d999a), SkBits2Float(0x42c991ed), SkBits2Float(0x432e4ccd), SkBits2Float(0x42c997de));  // 172.9f, 100.756f, 173.6f, 100.785f, 174.3f, 100.797f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42c99dcf), SkBits2Float(0x432fb333), SkBits2Float(0x42c99950), SkBits2Float(0x43306666), SkBits2Float(0x42c9999a));  // 175, 100.808f, 175.7f, 100.799f, 176.4f, 100.8f
+path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42c9999a));  // 176.4f, 100.8f
+path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42c9999a), SkBits2Float(0x4348e666), SkBits2Float(0x42c99cf4), SkBits2Float(0x4349999a), SkBits2Float(0x42c9999a));  // 200.2f, 100.8f, 200.9f, 100.807f, 201.6f, 100.8f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42c99640), SkBits2Float(0x434b0000), SkBits2Float(0x42c98bf3), SkBits2Float(0x434bb333), SkBits2Float(0x42c9857d));  // 202.3f, 100.793f, 203, 100.773f, 203.7f, 100.761f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42c97f08), SkBits2Float(0x434d199a), SkBits2Float(0x42c9771f), SkBits2Float(0x434dcccd), SkBits2Float(0x42c972d6));  // 204.4f, 100.748f, 205.1f, 100.733f, 205.8f, 100.724f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42c96e8d), SkBits2Float(0x434f3333), SkBits2Float(0x42c96dc2), SkBits2Float(0x434fe666), SkBits2Float(0x42c96bc7));  // 206.5f, 100.716f, 207.2f, 100.714f, 207.9f, 100.711f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42c969cd), SkBits2Float(0x43514ccd), SkBits2Float(0x42c967c5), SkBits2Float(0x43520000), SkBits2Float(0x42c966f7));  // 208.6f, 100.707f, 209.3f, 100.703f, 210, 100.701f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42c9662a), SkBits2Float(0x43536666), SkBits2Float(0x42c966af), SkBits2Float(0x4354199a), SkBits2Float(0x42c966f7));  // 210.7f, 100.7f, 211.4f, 100.701f, 212.1f, 100.701f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42c96740), SkBits2Float(0x43558000), SkBits2Float(0x42c96b40), SkBits2Float(0x43563333), SkBits2Float(0x42c968a9));  // 212.8f, 100.702f, 213.5f, 100.709f, 214.2f, 100.704f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42c96612), SkBits2Float(0x4357999a), SkBits2Float(0x42c9624e), SkBits2Float(0x43584ccd), SkBits2Float(0x42c9576e));  // 214.9f, 100.699f, 215.6f, 100.692f, 216.3f, 100.671f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42c94c8d), SkBits2Float(0x4359b333), SkBits2Float(0x42c935e7), SkBits2Float(0x435a6666), SkBits2Float(0x42c92765));  // 217, 100.65f, 217.7f, 100.605f, 218.4f, 100.577f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42c918e4), SkBits2Float(0x435bcccd), SkBits2Float(0x42c9097b), SkBits2Float(0x435c8000), SkBits2Float(0x42c90064));  // 219.1f, 100.549f, 219.8f, 100.519f, 220.5f, 100.501f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42c8f74d), SkBits2Float(0x435de666), SkBits2Float(0x42c8f840), SkBits2Float(0x435e999a), SkBits2Float(0x42c8f0db));  // 221.2f, 100.483f, 221.9f, 100.485f, 222.6f, 100.47f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42c8e976), SkBits2Float(0x43600000), SkBits2Float(0x42c8df18), SkBits2Float(0x4360b333), SkBits2Float(0x42c8d407));  // 223.3f, 100.456f, 224, 100.436f, 224.7f, 100.414f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42c8c8f5), SkBits2Float(0x4362199a), SkBits2Float(0x42c8b92f), SkBits2Float(0x4362cccd), SkBits2Float(0x42c8ae71));  // 225.4f, 100.392f, 226.1f, 100.362f, 226.8f, 100.341f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42c8a3b4), SkBits2Float(0x43643333), SkBits2Float(0x42c89926), SkBits2Float(0x4364e666), SkBits2Float(0x42c89396));  // 227.5f, 100.32f, 228.2f, 100.299f, 228.9f, 100.288f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42c88e07), SkBits2Float(0x43664ccd), SkBits2Float(0x42c88baa), SkBits2Float(0x43670000), SkBits2Float(0x42c88d14));  // 229.6f, 100.277f, 230.3f, 100.273f, 231, 100.276f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42c88e7e), SkBits2Float(0x43686666), SkBits2Float(0x42c89675), SkBits2Float(0x4369199a), SkBits2Float(0x42c89c11));  // 231.7f, 100.278f, 232.4f, 100.294f, 233.1f, 100.305f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42c8a1ac), SkBits2Float(0x436a8000), SkBits2Float(0x42c8a9ea), SkBits2Float(0x436b3333), SkBits2Float(0x42c8aeb8));  // 233.8f, 100.316f, 234.5f, 100.332f, 235.2f, 100.341f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42c8b386), SkBits2Float(0x436c999a), SkBits2Float(0x42c8b733), SkBits2Float(0x436d4ccd), SkBits2Float(0x42c8b8e5));  // 235.9f, 100.351f, 236.6f, 100.358f, 237.3f, 100.361f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42c8ba97), SkBits2Float(0x436eb333), SkBits2Float(0x42c8b9be), SkBits2Float(0x436f6666), SkBits2Float(0x42c8b8e5));  // 238, 100.364f, 238.7f, 100.363f, 239.4f, 100.361f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42c8b80c), SkBits2Float(0x4370cccd), SkBits2Float(0x42c8b45f), SkBits2Float(0x43718000), SkBits2Float(0x42c8b3ce));  // 240.1f, 100.359f, 240.8f, 100.352f, 241.5f, 100.351f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42c8b33e), SkBits2Float(0x4372e666), SkBits2Float(0x42c8b4f0), SkBits2Float(0x4373999a), SkBits2Float(0x42c8b580));  // 242.2f, 100.35f, 242.9f, 100.353f, 243.6f, 100.354f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42c8b611), SkBits2Float(0x43750000), SkBits2Float(0x42c8b6ea), SkBits2Float(0x4375b333), SkBits2Float(0x42c8b733));  // 244.3f, 100.356f, 245, 100.357f, 245.7f, 100.358f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42c8b77b), SkBits2Float(0x4377199a), SkBits2Float(0x42c8b77b), SkBits2Float(0x4377cccd), SkBits2Float(0x42c8b733));  // 246.4f, 100.358f, 247.1f, 100.358f, 247.8f, 100.358f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42c8b6ea), SkBits2Float(0x43793333), SkBits2Float(0x42c8b5c9), SkBits2Float(0x4379e666), SkBits2Float(0x42c8b580));  // 248.5f, 100.357f, 249.2f, 100.355f, 249.9f, 100.354f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42c8b538), SkBits2Float(0x437b4ccd), SkBits2Float(0x42c8b538), SkBits2Float(0x437c0000), SkBits2Float(0x42c8b580));  // 250.6f, 100.354f, 251.3f, 100.354f, 252, 100.354f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42c8b5c9), SkBits2Float(0x437d6666), SkBits2Float(0x42c8b6ea), SkBits2Float(0x437e199a), SkBits2Float(0x42c8b733));  // 252.7f, 100.355f, 253.4f, 100.357f, 254.1f, 100.358f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42c8b77b), SkBits2Float(0x437f8000), SkBits2Float(0x42c8b77b), SkBits2Float(0x4380199a), SkBits2Float(0x42c8b733));  // 254.8f, 100.358f, 255.5f, 100.358f, 256.2f, 100.358f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42c8b6ea), SkBits2Float(0x4380cccd), SkBits2Float(0x42c8b5c9), SkBits2Float(0x43812666), SkBits2Float(0x42c8b580));  // 256.9f, 100.357f, 257.6f, 100.355f, 258.3f, 100.354f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42c8b538), SkBits2Float(0x4381d99a), SkBits2Float(0x42c8b580), SkBits2Float(0x43823333), SkBits2Float(0x42c8b580));  // 259, 100.354f, 259.7f, 100.354f, 260.4f, 100.354f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42c8b580), SkBits2Float(0x4382e666), SkBits2Float(0x42c8b580), SkBits2Float(0x43834000), SkBits2Float(0x42c8b580));  // 261.1f, 100.354f, 261.8f, 100.354f, 262.5f, 100.354f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42c8b580), SkBits2Float(0x4383f333), SkBits2Float(0x42c8b5c9), SkBits2Float(0x43844ccd), SkBits2Float(0x42c8b580));  // 263.2f, 100.354f, 263.9f, 100.355f, 264.6f, 100.354f
+path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42c8b538), SkBits2Float(0x43850000), SkBits2Float(0x42c8b417), SkBits2Float(0x4385599a), SkBits2Float(0x42c8b3ce));  // 265.3f, 100.354f, 266, 100.352f, 266.7f, 100.351f
+path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42c8b386), SkBits2Float(0x43860ccd), SkBits2Float(0x42c8b386), SkBits2Float(0x43866666), SkBits2Float(0x42c8b3ce));  // 267.4f, 100.351f, 268.1f, 100.351f, 268.8f, 100.351f
+path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42c8b417), SkBits2Float(0x4387199a), SkBits2Float(0x42c8b4f0), SkBits2Float(0x43877333), SkBits2Float(0x42c8b580));  // 269.5f, 100.352f, 270.2f, 100.353f, 270.9f, 100.354f
+path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42c8b611), SkBits2Float(0x43882666), SkBits2Float(0x42c8b6ea), SkBits2Float(0x43888000), SkBits2Float(0x42c8b733));  // 271.6f, 100.356f, 272.3f, 100.357f, 273, 100.358f
+path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42c8b77b), SkBits2Float(0x43893333), SkBits2Float(0x42c8b6a2), SkBits2Float(0x43898ccd), SkBits2Float(0x42c8b733));  // 273.7f, 100.358f, 274.4f, 100.357f, 275.1f, 100.358f
+path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42c8b7c3), SkBits2Float(0x438a4000), SkBits2Float(0x42c8ba97), SkBits2Float(0x438a999a), SkBits2Float(0x42c8ba97));  // 275.8f, 100.359f, 276.5f, 100.364f, 277.2f, 100.364f
+path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42c8ba97), SkBits2Float(0x438b4ccd), SkBits2Float(0x42c8ba5a), SkBits2Float(0x438ba666), SkBits2Float(0x42c8b733));  // 277.9f, 100.364f, 278.6f, 100.364f, 279.3f, 100.358f
+path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42c8b40b), SkBits2Float(0x438c599a), SkBits2Float(0x42c8aad1), SkBits2Float(0x438cb333), SkBits2Float(0x42c8a7a9));  // 280, 100.352f, 280.7f, 100.334f, 281.4f, 100.327f
+path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42c8a481), SkBits2Float(0x438d6666), SkBits2Float(0x42c89f23), SkBits2Float(0x438dc000), SkBits2Float(0x42c8a445));  // 282.1f, 100.321f, 282.8f, 100.311f, 283.5f, 100.321f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42c8a967), SkBits2Float(0x438e7333), SkBits2Float(0x42c8b67f), SkBits2Float(0x438ecccd), SkBits2Float(0x42c8c676));  // 284.2f, 100.331f, 284.9f, 100.356f, 285.6f, 100.388f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42c8d66d), SkBits2Float(0x438f8000), SkBits2Float(0x42c8ecb3), SkBits2Float(0x438fd99a), SkBits2Float(0x42c9040f));  // 286.3f, 100.419f, 287, 100.462f, 287.7f, 100.508f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42c91b6b), SkBits2Float(0x43908ccd), SkBits2Float(0x42c939b1), SkBits2Float(0x4390e666), SkBits2Float(0x42c9529e));  // 288.4f, 100.554f, 289.1f, 100.613f, 289.8f, 100.661f
+path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42c96b8a), SkBits2Float(0x4391999a), SkBits2Float(0x42c98dc5), SkBits2Float(0x4391f333), SkBits2Float(0x42c9999a));  // 290.5f, 100.71f, 291.2f, 100.777f, 291.9f, 100.8f
+path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42c9a56e), SkBits2Float(0x4392a666), SkBits2Float(0x42c9999a), SkBits2Float(0x43930000), SkBits2Float(0x42c9999a));  // 292.6f, 100.823f, 293.3f, 100.8f, 294, 100.8f
+path.lineTo(SkBits2Float(0x43930000), SkBits2Float(0x42c9999a));  // 294, 100.8f
+path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42c9999a), SkBits2Float(0x43abd99a), SkBits2Float(0x42c9a526), SkBits2Float(0x43ac3333), SkBits2Float(0x42c9999a));  // 343, 100.8f, 343.7f, 100.823f, 344.4f, 100.8f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42c98e0d), SkBits2Float(0x43ace666), SkBits2Float(0x42c9760b), SkBits2Float(0x43ad4000), SkBits2Float(0x42c95450));  // 345.1f, 100.777f, 345.8f, 100.731f, 346.5f, 100.665f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42c93295), SkBits2Float(0x43adf333), SkBits2Float(0x42c8ebfd), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42c8cf37));  // 347.2f, 100.599f, 347.9f, 100.461f, 348.6f, 100.405f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42c8b270), SkBits2Float(0x43af0000), SkBits2Float(0x42c8afe7), SkBits2Float(0x43af599a), SkBits2Float(0x42c8a7a9));  // 349.3f, 100.349f, 350, 100.344f, 350.7f, 100.327f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42c89f6b), SkBits2Float(0x43b00ccd), SkBits2Float(0x42c8a08b), SkBits2Float(0x43b06666), SkBits2Float(0x42c89dc3));  // 351.4f, 100.311f, 352.1f, 100.314f, 352.8f, 100.308f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42c89afb), SkBits2Float(0x43b1199a), SkBits2Float(0x42c89864), SkBits2Float(0x43b17333), SkBits2Float(0x42c896fa));  // 353.5f, 100.303f, 354.2f, 100.298f, 354.9f, 100.295f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42c89591), SkBits2Float(0x43b22666), SkBits2Float(0x42c89591), SkBits2Float(0x43b28000), SkBits2Float(0x42c89548));  // 355.6f, 100.292f, 356.3f, 100.292f, 357, 100.292f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42c89500), SkBits2Float(0x43b33333), SkBits2Float(0x42c89500), SkBits2Float(0x43b38ccd), SkBits2Float(0x42c89548));  // 357.7f, 100.291f, 358.4f, 100.291f, 359.1f, 100.292f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42c89591), SkBits2Float(0x43b44000), SkBits2Float(0x42c896b2), SkBits2Float(0x43b4999a), SkBits2Float(0x42c896fa));  // 359.8f, 100.292f, 360.5f, 100.294f, 361.2f, 100.295f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42c89743), SkBits2Float(0x43b54ccd), SkBits2Float(0x42c896fa), SkBits2Float(0x43b5a666), SkBits2Float(0x42c896fa));  // 361.9f, 100.295f, 362.6f, 100.295f, 363.3f, 100.295f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42c896fa), SkBits2Float(0x43b6599a), SkBits2Float(0x42c89743), SkBits2Float(0x43b6b333), SkBits2Float(0x42c896fa));  // 364, 100.295f, 364.7f, 100.295f, 365.4f, 100.295f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42c896b2), SkBits2Float(0x43b76666), SkBits2Float(0x42c89591), SkBits2Float(0x43b7c000), SkBits2Float(0x42c89548));  // 366.1f, 100.294f, 366.8f, 100.292f, 367.5f, 100.292f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42c89500), SkBits2Float(0x43b87333), SkBits2Float(0x42c89548), SkBits2Float(0x43b8cccd), SkBits2Float(0x42c89548));  // 368.2f, 100.291f, 368.9f, 100.292f, 369.6f, 100.292f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42c89548), SkBits2Float(0x43b98000), SkBits2Float(0x42c89548), SkBits2Float(0x43b9d99a), SkBits2Float(0x42c89548));  // 370.3f, 100.292f, 371, 100.292f, 371.7f, 100.292f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42c89548), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42c894b7), SkBits2Float(0x43bae666), SkBits2Float(0x42c89548));  // 372.4f, 100.292f, 373.1f, 100.29f, 373.8f, 100.292f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42c895d9), SkBits2Float(0x43bb999a), SkBits2Float(0x42c897d3), SkBits2Float(0x43bbf333), SkBits2Float(0x42c898ac));  // 374.5f, 100.293f, 375.2f, 100.297f, 375.9f, 100.298f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42c89985), SkBits2Float(0x43bca666), SkBits2Float(0x42c89a16), SkBits2Float(0x43bd0000), SkBits2Float(0x42c89a5f));  // 376.6f, 100.3f, 377.3f, 100.301f, 378, 100.302f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42c89aa7), SkBits2Float(0x43bdb333), SkBits2Float(0x42c89aa7), SkBits2Float(0x43be0ccd), SkBits2Float(0x42c89a5f));  // 378.7f, 100.302f, 379.4f, 100.302f, 380.1f, 100.302f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42c89a16), SkBits2Float(0x43bec000), SkBits2Float(0x42c8993d), SkBits2Float(0x43bf199a), SkBits2Float(0x42c898ac));  // 380.8f, 100.301f, 381.5f, 100.299f, 382.2f, 100.298f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42c8981c), SkBits2Float(0x43bfcccd), SkBits2Float(0x42c8978b), SkBits2Float(0x43c02666), SkBits2Float(0x42c896fa));  // 382.9f, 100.297f, 383.6f, 100.296f, 384.3f, 100.295f
+path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42c8966a), SkBits2Float(0x43c0d99a), SkBits2Float(0x42c8946f), SkBits2Float(0x43c13333), SkBits2Float(0x42c89548));  // 385, 100.294f, 385.7f, 100.29f, 386.4f, 100.292f
+path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42c89621), SkBits2Float(0x43c1e666), SkBits2Float(0x42c898f5), SkBits2Float(0x43c24000), SkBits2Float(0x42c89c11));  // 387.1f, 100.293f, 387.8f, 100.299f, 388.5f, 100.305f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42c89f2d), SkBits2Float(0x43c2f333), SkBits2Float(0x42c8a5ad), SkBits2Float(0x43c34ccd), SkBits2Float(0x42c8a7ef));  // 389.2f, 100.311f, 389.9f, 100.324f, 390.6f, 100.328f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42c8aa32), SkBits2Float(0x43c40000), SkBits2Float(0x42c8a9a2), SkBits2Float(0x43c4599a), SkBits2Float(0x42c8a9a2));  // 391.3f, 100.332f, 392, 100.331f, 392.7f, 100.331f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42c8a9a2), SkBits2Float(0x43c50ccd), SkBits2Float(0x42c8a8c9), SkBits2Float(0x43c56666), SkBits2Float(0x42c8a7ef));  // 393.4f, 100.331f, 394.1f, 100.33f, 394.8f, 100.328f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42c8a716), SkBits2Float(0x43c6199a), SkBits2Float(0x42c8a5ad), SkBits2Float(0x43c67333), SkBits2Float(0x42c8a48b));  // 395.5f, 100.326f, 396.2f, 100.324f, 396.9f, 100.321f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42c8a36a), SkBits2Float(0x43c72666), SkBits2Float(0x42c8a291), SkBits2Float(0x43c78000), SkBits2Float(0x42c8a127));  // 397.6f, 100.319f, 398.3f, 100.318f, 399, 100.315f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42c89fbd), SkBits2Float(0x43c83333), SkBits2Float(0x42c89dc3), SkBits2Float(0x43c88ccd), SkBits2Float(0x42c89c11));  // 399.7f, 100.312f, 400.4f, 100.308f, 401.1f, 100.305f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42c89a5f), SkBits2Float(0x43c94000), SkBits2Float(0x42c898ac), SkBits2Float(0x43c9999a), SkBits2Float(0x42c896fa));  // 401.8f, 100.302f, 402.5f, 100.298f, 403.2f, 100.295f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42c89548), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42c89305), SkBits2Float(0x43caa666), SkBits2Float(0x42c891e4));  // 403.9f, 100.292f, 404.6f, 100.287f, 405.3f, 100.285f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42c890c3), SkBits2Float(0x43cb599a), SkBits2Float(0x42c88ec8), SkBits2Float(0x43cbb333), SkBits2Float(0x42c89032));  // 406, 100.283f, 406.7f, 100.279f, 407.4f, 100.282f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42c8919c), SkBits2Float(0x43cc6666), SkBits2Float(0x42c89864), SkBits2Float(0x43ccc000), SkBits2Float(0x42c89a5f));  // 408.1f, 100.284f, 408.8f, 100.298f, 409.5f, 100.302f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42c89c59), SkBits2Float(0x43cd7333), SkBits2Float(0x42c89dff), SkBits2Float(0x43cdcccd), SkBits2Float(0x42c89c11));  // 410.2f, 100.305f, 410.9f, 100.309f, 411.6f, 100.305f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42c89a22), SkBits2Float(0x43ce8000), SkBits2Float(0x42c8919a), SkBits2Float(0x43ced99a), SkBits2Float(0x42c88ec6));  // 412.3f, 100.301f, 413, 100.284f, 413.7f, 100.279f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42c88bf3), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42c88b70), SkBits2Float(0x43cfe666), SkBits2Float(0x42c88b1b));  // 414.4f, 100.273f, 415.1f, 100.272f, 415.8f, 100.272f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42c88ac7), SkBits2Float(0x43d0999a), SkBits2Float(0x42c88bac), SkBits2Float(0x43d0f333), SkBits2Float(0x42c88cce));  // 416.5f, 100.271f, 417.2f, 100.273f, 417.9f, 100.275f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42c88def), SkBits2Float(0x43d1a666), SkBits2Float(0x42c89032), SkBits2Float(0x43d20000), SkBits2Float(0x42c891e4));  // 418.6f, 100.277f, 419.3f, 100.282f, 420, 100.285f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42c89396), SkBits2Float(0x43d2b333), SkBits2Float(0x42c89621), SkBits2Float(0x43d30ccd), SkBits2Float(0x42c896fa));  // 420.7f, 100.288f, 421.4f, 100.293f, 422.1f, 100.295f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42c897d3), SkBits2Float(0x43d3c000), SkBits2Float(0x42c89810), SkBits2Float(0x43d4199a), SkBits2Float(0x42c896fa));  // 422.8f, 100.297f, 423.5f, 100.297f, 424.2f, 100.295f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42c895e5), SkBits2Float(0x43d4cccd), SkBits2Float(0x42c88d99), SkBits2Float(0x43d52666), SkBits2Float(0x42c89078));  // 424.9f, 100.293f, 425.6f, 100.277f, 426.3f, 100.282f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42c89358), SkBits2Float(0x43d5d99a), SkBits2Float(0x42c88f0f), SkBits2Float(0x43d63333), SkBits2Float(0x42c8a836));  // 427, 100.288f, 427.7f, 100.279f, 428.4f, 100.329f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42c8c15e), SkBits2Float(0x43d6e666), SkBits2Float(0x42c8ff2a), SkBits2Float(0x43d74000), SkBits2Float(0x42c92765));  // 429.1f, 100.378f, 429.8f, 100.498f, 430.5f, 100.577f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42c94fa0), SkBits2Float(0x43d7f333), SkBits2Float(0x42c98691), SkBits2Float(0x43d84ccd), SkBits2Float(0x42c9999a));  // 431.2f, 100.656f, 431.9f, 100.763f, 432.6f, 100.8f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42c9aca2), SkBits2Float(0x43d90000), SkBits2Float(0x42c9999a), SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a));  // 433.3f, 100.837f, 434, 100.8f, 434.7f, 100.8f
+path.lineTo(SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a));  // 434.7f, 100.8f
+path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42c99877), SkBits2Float(0x43e0599a), SkBits2Float(0x42c99312), SkBits2Float(0x43e0b333), SkBits2Float(0x42c992c8));  // 448, 100.798f, 448.7f, 100.787f, 449.4f, 100.787f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42c9927e), SkBits2Float(0x43e16666), SkBits2Float(0x42c996bb), SkBits2Float(0x43e1c000), SkBits2Float(0x42c997de));  // 450.1f, 100.786f, 450.8f, 100.794f, 451.5f, 100.797f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42c99901), SkBits2Float(0x43e27333), SkBits2Float(0x42c9a0aa), SkBits2Float(0x43e2cccd), SkBits2Float(0x42c9999a));  // 452.2f, 100.799f, 452.9f, 100.814f, 453.6f, 100.8f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42c99289), SkBits2Float(0x43e38000), SkBits2Float(0x42c97cb2), SkBits2Float(0x43e3d99a), SkBits2Float(0x42c96d79));  // 454.3f, 100.786f, 455, 100.744f, 455.7f, 100.714f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42c95e40), SkBits2Float(0x43e48ccd), SkBits2Float(0x42c943a3), SkBits2Float(0x43e4e666), SkBits2Float(0x42c93e44));  // 456.4f, 100.684f, 457.1f, 100.632f, 457.8f, 100.622f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42c938e6), SkBits2Float(0x43e5999a), SkBits2Float(0x42c949f4), SkBits2Float(0x43e5f333), SkBits2Float(0x42c94d41));  // 458.5f, 100.611f, 459.2f, 100.644f, 459.9f, 100.651f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42c9508e), SkBits2Float(0x43e6a666), SkBits2Float(0x42c94fce), SkBits2Float(0x43e70000), SkBits2Float(0x42c95211));  // 460.6f, 100.657f, 461.3f, 100.656f, 462, 100.66f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42c95454), SkBits2Float(0x43e7b333), SkBits2Float(0x42c9595c), SkBits2Float(0x43e80ccd), SkBits2Float(0x42c95ad2));  // 462.7f, 100.665f, 463.4f, 100.675f, 464.1f, 100.677f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42c95c47), SkBits2Float(0x43e8c000), SkBits2Float(0x42c959f9), SkBits2Float(0x43e9199a), SkBits2Float(0x42c95ad2));  // 464.8f, 100.68f, 465.5f, 100.676f, 466.2f, 100.677f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42c95bab), SkBits2Float(0x43e9cccd), SkBits2Float(0x42c95d20), SkBits2Float(0x43ea2666), SkBits2Float(0x42c95fe8));  // 466.9f, 100.679f, 467.6f, 100.682f, 468.3f, 100.687f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42c962b0), SkBits2Float(0x43ead99a), SkBits2Float(0x42c96743), SkBits2Float(0x43eb3333), SkBits2Float(0x42c96b80));  // 469, 100.693f, 469.7f, 100.702f, 470.4f, 100.71f
+path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42c96fbe), SkBits2Float(0x43ebe666), SkBits2Float(0x42c9754c), SkBits2Float(0x43ec4000), SkBits2Float(0x42c97958));  // 471.1f, 100.718f, 471.8f, 100.729f, 472.5f, 100.737f
+path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x42c97d65), SkBits2Float(0x43ecf333), SkBits2Float(0x42c97ef2), SkBits2Float(0x43ed4ccd), SkBits2Float(0x42c983cb));  // 473.2f, 100.745f, 473.9f, 100.748f, 474.6f, 100.757f
+path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x42c988a5), SkBits2Float(0x43ee0000), SkBits2Float(0x42c992d0), SkBits2Float(0x43ee599a), SkBits2Float(0x42c99673));  // 475.3f, 100.767f, 476, 100.787f, 476.7f, 100.794f
+path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x42c99a15), SkBits2Float(0x43ef0ccd), SkBits2Float(0x42c99bdc), SkBits2Float(0x43ef6666), SkBits2Float(0x42c9999a));  // 477.4f, 100.801f, 478.1f, 100.804f, 478.8f, 100.8f
+path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42c99757), SkBits2Float(0x43f0199a), SkBits2Float(0x42c99426), SkBits2Float(0x43f07333), SkBits2Float(0x42c988e2));  // 479.5f, 100.796f, 480.2f, 100.789f, 480.9f, 100.767f
+path.cubicTo(SkBits2Float(0x43f0cccd), SkBits2Float(0x42c97d9e), SkBits2Float(0x43f12666), SkBits2Float(0x42c96356), SkBits2Float(0x43f18000), SkBits2Float(0x42c95602));  // 481.6f, 100.745f, 482.3f, 100.694f, 483, 100.668f
+path.cubicTo(SkBits2Float(0x43f1d99a), SkBits2Float(0x42c948ae), SkBits2Float(0x43f23333), SkBits2Float(0x42c93ee3), SkBits2Float(0x43f28ccd), SkBits2Float(0x42c938e7));  // 483.7f, 100.642f, 484.4f, 100.623f, 485.1f, 100.611f
+path.cubicTo(SkBits2Float(0x43f2e666), SkBits2Float(0x42c932ec), SkBits2Float(0x43f34000), SkBits2Float(0x42c93741), SkBits2Float(0x43f3999a), SkBits2Float(0x42c9321f));  // 485.8f, 100.599f, 486.5f, 100.608f, 487.2f, 100.598f
+path.cubicTo(SkBits2Float(0x43f3f333), SkBits2Float(0x42c92cfd), SkBits2Float(0x43f44ccd), SkBits2Float(0x42c922f5), SkBits2Float(0x43f4a666), SkBits2Float(0x42c91a1b));  // 487.9f, 100.588f, 488.6f, 100.568f, 489.3f, 100.551f
+path.cubicTo(SkBits2Float(0x43f50000), SkBits2Float(0x42c91140), SkBits2Float(0x43f5599a), SkBits2Float(0x42c904ad), SkBits2Float(0x43f5b333), SkBits2Float(0x42c8fd00));  // 490, 100.534f, 490.7f, 100.509f, 491.4f, 100.494f
+path.cubicTo(SkBits2Float(0x43f60ccd), SkBits2Float(0x42c8f553), SkBits2Float(0x43f66666), SkBits2Float(0x42c8ef7b), SkBits2Float(0x43f6c000), SkBits2Float(0x42c8ec0b));  // 492.1f, 100.479f, 492.8f, 100.468f, 493.5f, 100.461f
+path.cubicTo(SkBits2Float(0x43f7199a), SkBits2Float(0x42c8e89b), SkBits2Float(0x43f77333), SkBits2Float(0x42c8e981), SkBits2Float(0x43f7cccd), SkBits2Float(0x42c8e860));  // 494.2f, 100.454f, 494.9f, 100.456f, 495.6f, 100.454f
+path.cubicTo(SkBits2Float(0x43f82666), SkBits2Float(0x42c8e73f), SkBits2Float(0x43f88000), SkBits2Float(0x42c8e658), SkBits2Float(0x43f8d99a), SkBits2Float(0x42c8e542));  // 496.3f, 100.452f, 497, 100.45f, 497.7f, 100.448f
+path.cubicTo(SkBits2Float(0x43f93333), SkBits2Float(0x42c8e42d), SkBits2Float(0x43f98ccd), SkBits2Float(0x42c8e348), SkBits2Float(0x43f9e666), SkBits2Float(0x42c8e1de));  // 498.4f, 100.446f, 499.1f, 100.444f, 499.8f, 100.441f
+path.cubicTo(SkBits2Float(0x43fa4000), SkBits2Float(0x42c8e074), SkBits2Float(0x43fa999a), SkBits2Float(0x42c8df53), SkBits2Float(0x43faf333), SkBits2Float(0x42c8dcc8));  // 500.5f, 100.438f, 501.2f, 100.436f, 501.9f, 100.431f
+path.cubicTo(SkBits2Float(0x43fb4ccd), SkBits2Float(0x42c8da3d), SkBits2Float(0x43fba666), SkBits2Float(0x42c8d5c3), SkBits2Float(0x43fc0000), SkBits2Float(0x42c8d29b));  // 502.6f, 100.426f, 503.3f, 100.418f, 504, 100.411f
+path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42c8cf73), SkBits2Float(0x43fcb333), SkBits2Float(0x42c8ccf6), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42c8c9da));  // 504.7f, 100.405f, 505.4f, 100.4f, 506.1f, 100.394f
+path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42c8c6be), SkBits2Float(0x43fdc000), SkBits2Float(0x42c8c310), SkBits2Float(0x43fe199a), SkBits2Float(0x42c8bff4));  // 506.8f, 100.388f, 507.5f, 100.381f, 508.2f, 100.375f
+path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42c8bcd8), SkBits2Float(0x43fecccd), SkBits2Float(0x42c8b8f0), SkBits2Float(0x43ff2666), SkBits2Float(0x42c8b733));  // 508.9f, 100.369f, 509.6f, 100.361f, 510.3f, 100.358f
+path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x42c8b575), SkBits2Float(0x43ffd99a), SkBits2Float(0x42c8b45f), SkBits2Float(0x4400199a), SkBits2Float(0x42c8b580));  // 511, 100.354f, 511.7f, 100.352f, 512.4f, 100.354f
+path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x42c8b6a2), SkBits2Float(0x44007333), SkBits2Float(0x42c8bb28), SkBits2Float(0x4400a000), SkBits2Float(0x42c8bdfb));  // 513.1f, 100.357f, 513.8f, 100.366f, 514.5f, 100.371f
+path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x42c8c0cf), SkBits2Float(0x4400f99a), SkBits2Float(0x42c8c31d), SkBits2Float(0x44012666), SkBits2Float(0x42c8c676));  // 515.2f, 100.377f, 515.9f, 100.381f, 516.6f, 100.388f
+path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x42c8c9ce), SkBits2Float(0x44018000), SkBits2Float(0x42c8caa9), SkBits2Float(0x4401accd), SkBits2Float(0x42c8d20e));  // 517.3f, 100.394f, 518, 100.396f, 518.7f, 100.41f
+path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x42c8d973), SkBits2Float(0x44020666), SkBits2Float(0x42c8e446), SkBits2Float(0x44023333), SkBits2Float(0x42c8f2d3));  // 519.4f, 100.425f, 520.1f, 100.446f, 520.8f, 100.474f
+path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x42c90161), SkBits2Float(0x44028ccd), SkBits2Float(0x42c917c0), SkBits2Float(0x4402b99a), SkBits2Float(0x42c9295e));  // 521.5f, 100.503f, 522.2f, 100.546f, 522.9f, 100.581f
+path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x42c93afb), SkBits2Float(0x44031333), SkBits2Float(0x42c94c14), SkBits2Float(0x44034000), SkBits2Float(0x42c95c84));  // 523.6f, 100.615f, 524.3f, 100.649f, 525, 100.681f
+path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x42c96cf4), SkBits2Float(0x4403999a), SkBits2Float(0x42c981d1), SkBits2Float(0x4403c666), SkBits2Float(0x42c98bff));  // 525.7f, 100.713f, 526.4f, 100.754f, 527.1f, 100.773f
+path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x42c9962e), SkBits2Float(0x44042000), SkBits2Float(0x42c99755), SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a));  // 527.8f, 100.793f, 528.5f, 100.796f, 529.2f, 100.8f
+path.lineTo(SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a));  // 529.2f, 100.8f
+path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.close();
+    return path;
+}
+
+static SkPath path6() {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.cubicTo(SkBits2Float(0x42b1cccd), SkBits2Float(0x42c9999a), SkBits2Float(0x42b33333), SkBits2Float(0x42c97f2d), SkBits2Float(0x42b4999a), SkBits2Float(0x42c9999a));  // 88.9f, 100.8f, 89.6f, 100.748f, 90.3f, 100.8f
+path.cubicTo(SkBits2Float(0x42b60000), SkBits2Float(0x42c9b408), SkBits2Float(0x42b76666), SkBits2Float(0x42ca1e13), SkBits2Float(0x42b8cccd), SkBits2Float(0x42ca382d));  // 91, 100.852f, 91.7f, 101.059f, 92.4f, 101.11f
+path.cubicTo(SkBits2Float(0x42ba3333), SkBits2Float(0x42ca5246), SkBits2Float(0x42bb999a), SkBits2Float(0x42ca3634), SkBits2Float(0x42bd0000), SkBits2Float(0x42ca3634));  // 93.1f, 101.161f, 93.8f, 101.106f, 94.5f, 101.106f
+path.cubicTo(SkBits2Float(0x42be6666), SkBits2Float(0x42ca3634), SkBits2Float(0x42bfcccd), SkBits2Float(0x42ca3869), SkBits2Float(0x42c13333), SkBits2Float(0x42ca382d));  // 95.2f, 101.106f, 95.9f, 101.11f, 96.6f, 101.11f
+path.cubicTo(SkBits2Float(0x42c2999a), SkBits2Float(0x42ca37f0), SkBits2Float(0x42c40000), SkBits2Float(0x42ca35ea), SkBits2Float(0x42c56666), SkBits2Float(0x42ca34c8));  // 97.3f, 101.109f, 98, 101.105f, 98.7f, 101.103f
+path.cubicTo(SkBits2Float(0x42c6cccd), SkBits2Float(0x42ca33a7), SkBits2Float(0x42c83333), SkBits2Float(0x42ca311c), SkBits2Float(0x42c9999a), SkBits2Float(0x42ca3164));  // 99.4f, 101.101f, 100.1f, 101.096f, 100.8f, 101.096f
+path.cubicTo(SkBits2Float(0x42cb0000), SkBits2Float(0x42ca31ad), SkBits2Float(0x42cc6666), SkBits2Float(0x42ca33fb), SkBits2Float(0x42cdcccd), SkBits2Float(0x42ca367b));  // 101.5f, 101.097f, 102.2f, 101.102f, 102.9f, 101.106f
+path.cubicTo(SkBits2Float(0x42cf3333), SkBits2Float(0x42ca38fa), SkBits2Float(0x42d0999a), SkBits2Float(0x42ca3fd0), SkBits2Float(0x42d20000), SkBits2Float(0x42ca4061));  // 103.6f, 101.111f, 104.3f, 101.125f, 105, 101.126f
+path.cubicTo(SkBits2Float(0x42d36666), SkBits2Float(0x42ca40f1), SkBits2Float(0x42d4cccd), SkBits2Float(0x42ca3f32), SkBits2Float(0x42d63333), SkBits2Float(0x42ca39df));  // 105.7f, 101.127f, 106.4f, 101.123f, 107.1f, 101.113f
+path.cubicTo(SkBits2Float(0x42d7999a), SkBits2Float(0x42ca348c), SkBits2Float(0x42d90000), SkBits2Float(0x42ca2585), SkBits2Float(0x42da6666), SkBits2Float(0x42ca206f));  // 107.8f, 101.103f, 108.5f, 101.073f, 109.2f, 101.063f
+path.cubicTo(SkBits2Float(0x42dbcccd), SkBits2Float(0x42ca1b59), SkBits2Float(0x42dd3333), SkBits2Float(0x42ca1a43), SkBits2Float(0x42de999a), SkBits2Float(0x42ca1b59));  // 109.9f, 101.053f, 110.6f, 101.051f, 111.3f, 101.053f
+path.cubicTo(SkBits2Float(0x42e00000), SkBits2Float(0x42ca1c6e), SkBits2Float(0x42e16666), SkBits2Float(0x42ca2872), SkBits2Float(0x42e2cccd), SkBits2Float(0x42ca26f1));  // 112, 101.056f, 112.7f, 101.079f, 113.4f, 101.076f
+path.cubicTo(SkBits2Float(0x42e43333), SkBits2Float(0x42ca2570), SkBits2Float(0x42e5999a), SkBits2Float(0x42ca1f14), SkBits2Float(0x42e70000), SkBits2Float(0x42ca1251));  // 114.1f, 101.073f, 114.8f, 101.061f, 115.5f, 101.036f
+path.cubicTo(SkBits2Float(0x42e86666), SkBits2Float(0x42ca058d), SkBits2Float(0x42e9cccd), SkBits2Float(0x42c9ec89), SkBits2Float(0x42eb3333), SkBits2Float(0x42c9da5b));  // 116.2f, 101.011f, 116.9f, 100.962f, 117.6f, 100.926f
+path.cubicTo(SkBits2Float(0x42ec999a), SkBits2Float(0x42c9c82d), SkBits2Float(0x42ee0000), SkBits2Float(0x42c9b007), SkBits2Float(0x42ef6666), SkBits2Float(0x42c9a53c));  // 118.3f, 100.891f, 119, 100.844f, 119.7f, 100.823f
+path.cubicTo(SkBits2Float(0x42f0cccd), SkBits2Float(0x42c99a71), SkBits2Float(0x42f23333), SkBits2Float(0x42c99b8b), SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a));  // 120.4f, 100.802f, 121.1f, 100.804f, 121.8f, 100.8f
+path.lineTo(SkBits2Float(0x42f3999a), SkBits2Float(0x42c9999a));  // 121.8f, 100.8f
+path.lineTo(SkBits2Float(0x42b06666), SkBits2Float(0x42c9999a));  // 88.2f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a));  // 128.1f, 100.8f
+path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c99b0f));  // 128.1f, 100.803f
+path.cubicTo(SkBits2Float(0x4300cccd), SkBits2Float(0x42c99bde), SkBits2Float(0x43018000), SkBits2Float(0x42c99eb2), SkBits2Float(0x43023333), SkBits2Float(0x42c99e73));  // 128.8f, 100.804f, 129.5f, 100.81f, 130.2f, 100.809f
+path.cubicTo(SkBits2Float(0x4302e666), SkBits2Float(0x42c99e35), SkBits2Float(0x4303999a), SkBits2Float(0x42c99a69), SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a));  // 130.9f, 100.809f, 131.6f, 100.802f, 132.3f, 100.8f
+path.lineTo(SkBits2Float(0x43044ccd), SkBits2Float(0x42c9999a));  // 132.3f, 100.8f
+path.lineTo(SkBits2Float(0x4300199a), SkBits2Float(0x42c9999a));  // 128.1f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.cubicTo(SkBits2Float(0x431c199a), SkBits2Float(0x42c9999a), SkBits2Float(0x431ccccd), SkBits2Float(0x42c99567), SkBits2Float(0x431d8000), SkBits2Float(0x42c9999a));  // 156.1f, 100.8f, 156.8f, 100.792f, 157.5f, 100.8f
+path.cubicTo(SkBits2Float(0x431e3333), SkBits2Float(0x42c99dcd), SkBits2Float(0x431ee666), SkBits2Float(0x42c9a613), SkBits2Float(0x431f999a), SkBits2Float(0x42c9b2cd));  // 158.2f, 100.808f, 158.9f, 100.824f, 159.6f, 100.849f
+path.cubicTo(SkBits2Float(0x43204ccd), SkBits2Float(0x42c9bf86), SkBits2Float(0x43210000), SkBits2Float(0x42c9d893), SkBits2Float(0x4321b333), SkBits2Float(0x42c9e5f3));  // 160.3f, 100.874f, 161, 100.923f, 161.7f, 100.949f
+path.cubicTo(SkBits2Float(0x43226666), SkBits2Float(0x42c9f353), SkBits2Float(0x4323199a), SkBits2Float(0x42c9fd4f), SkBits2Float(0x4323cccd), SkBits2Float(0x42ca030e));  // 162.4f, 100.975f, 163.1f, 100.995f, 163.8f, 101.006f
+path.cubicTo(SkBits2Float(0x43248000), SkBits2Float(0x42ca08cc), SkBits2Float(0x43253333), SkBits2Float(0x42ca07ce), SkBits2Float(0x4325e666), SkBits2Float(0x42ca086b));  // 164.5f, 101.017f, 165.2f, 101.015f, 165.9f, 101.016f
+path.cubicTo(SkBits2Float(0x4326999a), SkBits2Float(0x42ca0907), SkBits2Float(0x43274ccd), SkBits2Float(0x42ca0bdb), SkBits2Float(0x43280000), SkBits2Float(0x42ca06b9));  // 166.6f, 101.018f, 167.3f, 101.023f, 168, 101.013f
+path.cubicTo(SkBits2Float(0x4328b333), SkBits2Float(0x42ca0196), SkBits2Float(0x43296666), SkBits2Float(0x42c9f5dd), SkBits2Float(0x432a199a), SkBits2Float(0x42c9e99e));  // 168.7f, 101.003f, 169.4f, 100.98f, 170.1f, 100.956f
+path.cubicTo(SkBits2Float(0x432acccd), SkBits2Float(0x42c9dd5f), SkBits2Float(0x432b8000), SkBits2Float(0x42c9ca4c), SkBits2Float(0x432c3333), SkBits2Float(0x42c9bd40));  // 170.8f, 100.932f, 171.5f, 100.895f, 172.2f, 100.87f
+path.cubicTo(SkBits2Float(0x432ce666), SkBits2Float(0x42c9b034), SkBits2Float(0x432d999a), SkBits2Float(0x42c9a147), SkBits2Float(0x432e4ccd), SkBits2Float(0x42c99b56));  // 172.9f, 100.844f, 173.6f, 100.815f, 174.3f, 100.803f
+path.cubicTo(SkBits2Float(0x432f0000), SkBits2Float(0x42c99565), SkBits2Float(0x432fb333), SkBits2Float(0x42c999e4), SkBits2Float(0x43306666), SkBits2Float(0x42c9999a));  // 175, 100.792f, 175.7f, 100.801f, 176.4f, 100.8f
+path.lineTo(SkBits2Float(0x43306666), SkBits2Float(0x42c9999a));  // 176.4f, 100.8f
+path.lineTo(SkBits2Float(0x431b6666), SkBits2Float(0x42c9999a));  // 155.4f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.cubicTo(SkBits2Float(0x43483333), SkBits2Float(0x42c9999a), SkBits2Float(0x4348e666), SkBits2Float(0x42c99640), SkBits2Float(0x4349999a), SkBits2Float(0x42c9999a));  // 200.2f, 100.8f, 200.9f, 100.793f, 201.6f, 100.8f
+path.cubicTo(SkBits2Float(0x434a4ccd), SkBits2Float(0x42c99cf4), SkBits2Float(0x434b0000), SkBits2Float(0x42c9a741), SkBits2Float(0x434bb333), SkBits2Float(0x42c9adb7));  // 202.3f, 100.807f, 203, 100.827f, 203.7f, 100.839f
+path.cubicTo(SkBits2Float(0x434c6666), SkBits2Float(0x42c9b42c), SkBits2Float(0x434d199a), SkBits2Float(0x42c9bc15), SkBits2Float(0x434dcccd), SkBits2Float(0x42c9c05e));  // 204.4f, 100.852f, 205.1f, 100.867f, 205.8f, 100.876f
+path.cubicTo(SkBits2Float(0x434e8000), SkBits2Float(0x42c9c4a7), SkBits2Float(0x434f3333), SkBits2Float(0x42c9c572), SkBits2Float(0x434fe666), SkBits2Float(0x42c9c76d));  // 206.5f, 100.884f, 207.2f, 100.886f, 207.9f, 100.89f
+path.cubicTo(SkBits2Float(0x4350999a), SkBits2Float(0x42c9c967), SkBits2Float(0x43514ccd), SkBits2Float(0x42c9cb6f), SkBits2Float(0x43520000), SkBits2Float(0x42c9cc3d));  // 208.6f, 100.893f, 209.3f, 100.897f, 210, 100.899f
+path.cubicTo(SkBits2Float(0x4352b333), SkBits2Float(0x42c9cd0a), SkBits2Float(0x43536666), SkBits2Float(0x42c9cc85), SkBits2Float(0x4354199a), SkBits2Float(0x42c9cc3d));  // 210.7f, 100.9f, 211.4f, 100.899f, 212.1f, 100.899f
+path.cubicTo(SkBits2Float(0x4354cccd), SkBits2Float(0x42c9cbf4), SkBits2Float(0x43558000), SkBits2Float(0x42c9c7f4), SkBits2Float(0x43563333), SkBits2Float(0x42c9ca8b));  // 212.8f, 100.898f, 213.5f, 100.891f, 214.2f, 100.896f
+path.cubicTo(SkBits2Float(0x4356e666), SkBits2Float(0x42c9cd22), SkBits2Float(0x4357999a), SkBits2Float(0x42c9d0e6), SkBits2Float(0x43584ccd), SkBits2Float(0x42c9dbc6));  // 214.9f, 100.901f, 215.6f, 100.908f, 216.3f, 100.929f
+path.cubicTo(SkBits2Float(0x43590000), SkBits2Float(0x42c9e6a7), SkBits2Float(0x4359b333), SkBits2Float(0x42c9fd4d), SkBits2Float(0x435a6666), SkBits2Float(0x42ca0bcf));  // 217, 100.95f, 217.7f, 100.995f, 218.4f, 101.023f
+path.cubicTo(SkBits2Float(0x435b199a), SkBits2Float(0x42ca1a50), SkBits2Float(0x435bcccd), SkBits2Float(0x42ca29b9), SkBits2Float(0x435c8000), SkBits2Float(0x42ca32d0));  // 219.1f, 101.051f, 219.8f, 101.081f, 220.5f, 101.099f
+path.cubicTo(SkBits2Float(0x435d3333), SkBits2Float(0x42ca3be7), SkBits2Float(0x435de666), SkBits2Float(0x42ca3af4), SkBits2Float(0x435e999a), SkBits2Float(0x42ca4259));  // 221.2f, 101.117f, 221.9f, 101.115f, 222.6f, 101.13f
+path.cubicTo(SkBits2Float(0x435f4ccd), SkBits2Float(0x42ca49be), SkBits2Float(0x43600000), SkBits2Float(0x42ca541c), SkBits2Float(0x4360b333), SkBits2Float(0x42ca5f2d));  // 223.3f, 101.144f, 224, 101.164f, 224.7f, 101.186f
+path.cubicTo(SkBits2Float(0x43616666), SkBits2Float(0x42ca6a3f), SkBits2Float(0x4362199a), SkBits2Float(0x42ca7a05), SkBits2Float(0x4362cccd), SkBits2Float(0x42ca84c3));  // 225.4f, 101.208f, 226.1f, 101.238f, 226.8f, 101.259f
+path.cubicTo(SkBits2Float(0x43638000), SkBits2Float(0x42ca8f80), SkBits2Float(0x43643333), SkBits2Float(0x42ca9a0e), SkBits2Float(0x4364e666), SkBits2Float(0x42ca9f9e));  // 227.5f, 101.28f, 228.2f, 101.301f, 228.9f, 101.312f
+path.cubicTo(SkBits2Float(0x4365999a), SkBits2Float(0x42caa52d), SkBits2Float(0x43664ccd), SkBits2Float(0x42caa78a), SkBits2Float(0x43670000), SkBits2Float(0x42caa620));  // 229.6f, 101.323f, 230.3f, 101.327f, 231, 101.324f
+path.cubicTo(SkBits2Float(0x4367b333), SkBits2Float(0x42caa4b6), SkBits2Float(0x43686666), SkBits2Float(0x42ca9cbf), SkBits2Float(0x4369199a), SkBits2Float(0x42ca9723));  // 231.7f, 101.322f, 232.4f, 101.306f, 233.1f, 101.295f
+path.cubicTo(SkBits2Float(0x4369cccd), SkBits2Float(0x42ca9188), SkBits2Float(0x436a8000), SkBits2Float(0x42ca894a), SkBits2Float(0x436b3333), SkBits2Float(0x42ca847c));  // 233.8f, 101.284f, 234.5f, 101.268f, 235.2f, 101.259f
+path.cubicTo(SkBits2Float(0x436be666), SkBits2Float(0x42ca7fae), SkBits2Float(0x436c999a), SkBits2Float(0x42ca7c01), SkBits2Float(0x436d4ccd), SkBits2Float(0x42ca7a4f));  // 235.9f, 101.249f, 236.6f, 101.242f, 237.3f, 101.239f
+path.cubicTo(SkBits2Float(0x436e0000), SkBits2Float(0x42ca789d), SkBits2Float(0x436eb333), SkBits2Float(0x42ca7976), SkBits2Float(0x436f6666), SkBits2Float(0x42ca7a4f));  // 238, 101.236f, 238.7f, 101.237f, 239.4f, 101.239f
+path.cubicTo(SkBits2Float(0x4370199a), SkBits2Float(0x42ca7b28), SkBits2Float(0x4370cccd), SkBits2Float(0x42ca7ed5), SkBits2Float(0x43718000), SkBits2Float(0x42ca7f66));  // 240.1f, 101.241f, 240.8f, 101.248f, 241.5f, 101.249f
+path.cubicTo(SkBits2Float(0x43723333), SkBits2Float(0x42ca7ff6), SkBits2Float(0x4372e666), SkBits2Float(0x42ca7e44), SkBits2Float(0x4373999a), SkBits2Float(0x42ca7db4));  // 242.2f, 101.25f, 242.9f, 101.247f, 243.6f, 101.246f
+path.cubicTo(SkBits2Float(0x43744ccd), SkBits2Float(0x42ca7d23), SkBits2Float(0x43750000), SkBits2Float(0x42ca7c4a), SkBits2Float(0x4375b333), SkBits2Float(0x42ca7c01));  // 244.3f, 101.244f, 245, 101.243f, 245.7f, 101.242f
+path.cubicTo(SkBits2Float(0x43766666), SkBits2Float(0x42ca7bb9), SkBits2Float(0x4377199a), SkBits2Float(0x42ca7bb9), SkBits2Float(0x4377cccd), SkBits2Float(0x42ca7c01));  // 246.4f, 101.242f, 247.1f, 101.242f, 247.8f, 101.242f
+path.cubicTo(SkBits2Float(0x43788000), SkBits2Float(0x42ca7c4a), SkBits2Float(0x43793333), SkBits2Float(0x42ca7d6b), SkBits2Float(0x4379e666), SkBits2Float(0x42ca7db4));  // 248.5f, 101.243f, 249.2f, 101.245f, 249.9f, 101.246f
+path.cubicTo(SkBits2Float(0x437a999a), SkBits2Float(0x42ca7dfc), SkBits2Float(0x437b4ccd), SkBits2Float(0x42ca7dfc), SkBits2Float(0x437c0000), SkBits2Float(0x42ca7db4));  // 250.6f, 101.246f, 251.3f, 101.246f, 252, 101.246f
+path.cubicTo(SkBits2Float(0x437cb333), SkBits2Float(0x42ca7d6b), SkBits2Float(0x437d6666), SkBits2Float(0x42ca7c4a), SkBits2Float(0x437e199a), SkBits2Float(0x42ca7c01));  // 252.7f, 101.245f, 253.4f, 101.243f, 254.1f, 101.242f
+path.cubicTo(SkBits2Float(0x437ecccd), SkBits2Float(0x42ca7bb9), SkBits2Float(0x437f8000), SkBits2Float(0x42ca7bb9), SkBits2Float(0x4380199a), SkBits2Float(0x42ca7c01));  // 254.8f, 101.242f, 255.5f, 101.242f, 256.2f, 101.242f
+path.cubicTo(SkBits2Float(0x43807333), SkBits2Float(0x42ca7c4a), SkBits2Float(0x4380cccd), SkBits2Float(0x42ca7d6b), SkBits2Float(0x43812666), SkBits2Float(0x42ca7db4));  // 256.9f, 101.243f, 257.6f, 101.245f, 258.3f, 101.246f
+path.cubicTo(SkBits2Float(0x43818000), SkBits2Float(0x42ca7dfc), SkBits2Float(0x4381d99a), SkBits2Float(0x42ca7db4), SkBits2Float(0x43823333), SkBits2Float(0x42ca7db4));  // 259, 101.246f, 259.7f, 101.246f, 260.4f, 101.246f
+path.cubicTo(SkBits2Float(0x43828ccd), SkBits2Float(0x42ca7db4), SkBits2Float(0x4382e666), SkBits2Float(0x42ca7db4), SkBits2Float(0x43834000), SkBits2Float(0x42ca7db4));  // 261.1f, 101.246f, 261.8f, 101.246f, 262.5f, 101.246f
+path.cubicTo(SkBits2Float(0x4383999a), SkBits2Float(0x42ca7db4), SkBits2Float(0x4383f333), SkBits2Float(0x42ca7d6b), SkBits2Float(0x43844ccd), SkBits2Float(0x42ca7db4));  // 263.2f, 101.246f, 263.9f, 101.245f, 264.6f, 101.246f
+path.cubicTo(SkBits2Float(0x4384a666), SkBits2Float(0x42ca7dfc), SkBits2Float(0x43850000), SkBits2Float(0x42ca7f1d), SkBits2Float(0x4385599a), SkBits2Float(0x42ca7f66));  // 265.3f, 101.246f, 266, 101.248f, 266.7f, 101.249f
+path.cubicTo(SkBits2Float(0x4385b333), SkBits2Float(0x42ca7fae), SkBits2Float(0x43860ccd), SkBits2Float(0x42ca7fae), SkBits2Float(0x43866666), SkBits2Float(0x42ca7f66));  // 267.4f, 101.249f, 268.1f, 101.249f, 268.8f, 101.249f
+path.cubicTo(SkBits2Float(0x4386c000), SkBits2Float(0x42ca7f1d), SkBits2Float(0x4387199a), SkBits2Float(0x42ca7e44), SkBits2Float(0x43877333), SkBits2Float(0x42ca7db4));  // 269.5f, 101.248f, 270.2f, 101.247f, 270.9f, 101.246f
+path.cubicTo(SkBits2Float(0x4387cccd), SkBits2Float(0x42ca7d23), SkBits2Float(0x43882666), SkBits2Float(0x42ca7c4a), SkBits2Float(0x43888000), SkBits2Float(0x42ca7c01));  // 271.6f, 101.244f, 272.3f, 101.243f, 273, 101.242f
+path.cubicTo(SkBits2Float(0x4388d99a), SkBits2Float(0x42ca7bb9), SkBits2Float(0x43893333), SkBits2Float(0x42ca7c92), SkBits2Float(0x43898ccd), SkBits2Float(0x42ca7c01));  // 273.7f, 101.242f, 274.4f, 101.243f, 275.1f, 101.242f
+path.cubicTo(SkBits2Float(0x4389e666), SkBits2Float(0x42ca7b71), SkBits2Float(0x438a4000), SkBits2Float(0x42ca789d), SkBits2Float(0x438a999a), SkBits2Float(0x42ca789d));  // 275.8f, 101.241f, 276.5f, 101.236f, 277.2f, 101.236f
+path.cubicTo(SkBits2Float(0x438af333), SkBits2Float(0x42ca789d), SkBits2Float(0x438b4ccd), SkBits2Float(0x42ca78da), SkBits2Float(0x438ba666), SkBits2Float(0x42ca7c01));  // 277.9f, 101.236f, 278.6f, 101.236f, 279.3f, 101.242f
+path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x42ca7f29), SkBits2Float(0x438c599a), SkBits2Float(0x42ca8863), SkBits2Float(0x438cb333), SkBits2Float(0x42ca8b8b));  // 280, 101.248f, 280.7f, 101.266f, 281.4f, 101.273f
+path.cubicTo(SkBits2Float(0x438d0ccd), SkBits2Float(0x42ca8eb3), SkBits2Float(0x438d6666), SkBits2Float(0x42ca9411), SkBits2Float(0x438dc000), SkBits2Float(0x42ca8eef));  // 282.1f, 101.279f, 282.8f, 101.289f, 283.5f, 101.279f
+path.cubicTo(SkBits2Float(0x438e199a), SkBits2Float(0x42ca89cd), SkBits2Float(0x438e7333), SkBits2Float(0x42ca7cb5), SkBits2Float(0x438ecccd), SkBits2Float(0x42ca6cbe));  // 284.2f, 101.269f, 284.9f, 101.244f, 285.6f, 101.212f
+path.cubicTo(SkBits2Float(0x438f2666), SkBits2Float(0x42ca5cc7), SkBits2Float(0x438f8000), SkBits2Float(0x42ca4681), SkBits2Float(0x438fd99a), SkBits2Float(0x42ca2f25));  // 286.3f, 101.181f, 287, 101.138f, 287.7f, 101.092f
+path.cubicTo(SkBits2Float(0x43903333), SkBits2Float(0x42ca17c9), SkBits2Float(0x43908ccd), SkBits2Float(0x42c9f983), SkBits2Float(0x4390e666), SkBits2Float(0x42c9e096));  // 288.4f, 101.046f, 289.1f, 100.987f, 289.8f, 100.939f
+path.cubicTo(SkBits2Float(0x43914000), SkBits2Float(0x42c9c7aa), SkBits2Float(0x4391999a), SkBits2Float(0x42c9a56f), SkBits2Float(0x4391f333), SkBits2Float(0x42c9999a));  // 290.5f, 100.89f, 291.2f, 100.823f, 291.9f, 100.8f
+path.cubicTo(SkBits2Float(0x43924ccd), SkBits2Float(0x42c98dc6), SkBits2Float(0x4392a666), SkBits2Float(0x42c9999a), SkBits2Float(0x43930000), SkBits2Float(0x42c9999a));  // 292.6f, 100.777f, 293.3f, 100.8f, 294, 100.8f
+path.lineTo(SkBits2Float(0x43930000), SkBits2Float(0x42c9999a));  // 294, 100.8f
+path.lineTo(SkBits2Float(0x43478000), SkBits2Float(0x42c9999a));  // 199.5f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.cubicTo(SkBits2Float(0x43ab8000), SkBits2Float(0x42c9999a), SkBits2Float(0x43abd99a), SkBits2Float(0x42c98e0e), SkBits2Float(0x43ac3333), SkBits2Float(0x42c9999a));  // 343, 100.8f, 343.7f, 100.777f, 344.4f, 100.8f
+path.cubicTo(SkBits2Float(0x43ac8ccd), SkBits2Float(0x42c9a527), SkBits2Float(0x43ace666), SkBits2Float(0x42c9bd29), SkBits2Float(0x43ad4000), SkBits2Float(0x42c9dee4));  // 345.1f, 100.823f, 345.8f, 100.869f, 346.5f, 100.935f
+path.cubicTo(SkBits2Float(0x43ad999a), SkBits2Float(0x42ca009f), SkBits2Float(0x43adf333), SkBits2Float(0x42ca4737), SkBits2Float(0x43ae4ccd), SkBits2Float(0x42ca63fd));  // 347.2f, 101.001f, 347.9f, 101.139f, 348.6f, 101.195f
+path.cubicTo(SkBits2Float(0x43aea666), SkBits2Float(0x42ca80c4), SkBits2Float(0x43af0000), SkBits2Float(0x42ca834d), SkBits2Float(0x43af599a), SkBits2Float(0x42ca8b8b));  // 349.3f, 101.251f, 350, 101.256f, 350.7f, 101.273f
+path.cubicTo(SkBits2Float(0x43afb333), SkBits2Float(0x42ca93c9), SkBits2Float(0x43b00ccd), SkBits2Float(0x42ca92a9), SkBits2Float(0x43b06666), SkBits2Float(0x42ca9571));  // 351.4f, 101.289f, 352.1f, 101.286f, 352.8f, 101.292f
+path.cubicTo(SkBits2Float(0x43b0c000), SkBits2Float(0x42ca9839), SkBits2Float(0x43b1199a), SkBits2Float(0x42ca9ad0), SkBits2Float(0x43b17333), SkBits2Float(0x42ca9c3a));  // 353.5f, 101.297f, 354.2f, 101.302f, 354.9f, 101.305f
+path.cubicTo(SkBits2Float(0x43b1cccd), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b22666), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b28000), SkBits2Float(0x42ca9dec));  // 355.6f, 101.308f, 356.3f, 101.308f, 357, 101.308f
+path.cubicTo(SkBits2Float(0x43b2d99a), SkBits2Float(0x42ca9e34), SkBits2Float(0x43b33333), SkBits2Float(0x42ca9e34), SkBits2Float(0x43b38ccd), SkBits2Float(0x42ca9dec));  // 357.7f, 101.309f, 358.4f, 101.309f, 359.1f, 101.308f
+path.cubicTo(SkBits2Float(0x43b3e666), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b44000), SkBits2Float(0x42ca9c82), SkBits2Float(0x43b4999a), SkBits2Float(0x42ca9c3a));  // 359.8f, 101.308f, 360.5f, 101.306f, 361.2f, 101.305f
+path.cubicTo(SkBits2Float(0x43b4f333), SkBits2Float(0x42ca9bf1), SkBits2Float(0x43b54ccd), SkBits2Float(0x42ca9c3a), SkBits2Float(0x43b5a666), SkBits2Float(0x42ca9c3a));  // 361.9f, 101.305f, 362.6f, 101.305f, 363.3f, 101.305f
+path.cubicTo(SkBits2Float(0x43b60000), SkBits2Float(0x42ca9c3a), SkBits2Float(0x43b6599a), SkBits2Float(0x42ca9bf1), SkBits2Float(0x43b6b333), SkBits2Float(0x42ca9c3a));  // 364, 101.305f, 364.7f, 101.305f, 365.4f, 101.305f
+path.cubicTo(SkBits2Float(0x43b70ccd), SkBits2Float(0x42ca9c82), SkBits2Float(0x43b76666), SkBits2Float(0x42ca9da3), SkBits2Float(0x43b7c000), SkBits2Float(0x42ca9dec));  // 366.1f, 101.306f, 366.8f, 101.308f, 367.5f, 101.308f
+path.cubicTo(SkBits2Float(0x43b8199a), SkBits2Float(0x42ca9e34), SkBits2Float(0x43b87333), SkBits2Float(0x42ca9dec), SkBits2Float(0x43b8cccd), SkBits2Float(0x42ca9dec));  // 368.2f, 101.309f, 368.9f, 101.308f, 369.6f, 101.308f
+path.cubicTo(SkBits2Float(0x43b92666), SkBits2Float(0x42ca9dec), SkBits2Float(0x43b98000), SkBits2Float(0x42ca9dec), SkBits2Float(0x43b9d99a), SkBits2Float(0x42ca9dec));  // 370.3f, 101.308f, 371, 101.308f, 371.7f, 101.308f
+path.cubicTo(SkBits2Float(0x43ba3333), SkBits2Float(0x42ca9dec), SkBits2Float(0x43ba8ccd), SkBits2Float(0x42ca9e7d), SkBits2Float(0x43bae666), SkBits2Float(0x42ca9dec));  // 372.4f, 101.308f, 373.1f, 101.31f, 373.8f, 101.308f
+path.cubicTo(SkBits2Float(0x43bb4000), SkBits2Float(0x42ca9d5b), SkBits2Float(0x43bb999a), SkBits2Float(0x42ca9b61), SkBits2Float(0x43bbf333), SkBits2Float(0x42ca9a88));  // 374.5f, 101.307f, 375.2f, 101.303f, 375.9f, 101.302f
+path.cubicTo(SkBits2Float(0x43bc4ccd), SkBits2Float(0x42ca99af), SkBits2Float(0x43bca666), SkBits2Float(0x42ca991e), SkBits2Float(0x43bd0000), SkBits2Float(0x42ca98d5));  // 376.6f, 101.3f, 377.3f, 101.299f, 378, 101.299f
+path.cubicTo(SkBits2Float(0x43bd599a), SkBits2Float(0x42ca988d), SkBits2Float(0x43bdb333), SkBits2Float(0x42ca988d), SkBits2Float(0x43be0ccd), SkBits2Float(0x42ca98d5));  // 378.7f, 101.298f, 379.4f, 101.298f, 380.1f, 101.299f
+path.cubicTo(SkBits2Float(0x43be6666), SkBits2Float(0x42ca991e), SkBits2Float(0x43bec000), SkBits2Float(0x42ca99f7), SkBits2Float(0x43bf199a), SkBits2Float(0x42ca9a88));  // 380.8f, 101.299f, 381.5f, 101.301f, 382.2f, 101.302f
+path.cubicTo(SkBits2Float(0x43bf7333), SkBits2Float(0x42ca9b18), SkBits2Float(0x43bfcccd), SkBits2Float(0x42ca9ba9), SkBits2Float(0x43c02666), SkBits2Float(0x42ca9c3a));  // 382.9f, 101.303f, 383.6f, 101.304f, 384.3f, 101.305f
+path.cubicTo(SkBits2Float(0x43c08000), SkBits2Float(0x42ca9cca), SkBits2Float(0x43c0d99a), SkBits2Float(0x42ca9ec5), SkBits2Float(0x43c13333), SkBits2Float(0x42ca9dec));  // 385, 101.306f, 385.7f, 101.31f, 386.4f, 101.308f
+path.cubicTo(SkBits2Float(0x43c18ccd), SkBits2Float(0x42ca9d13), SkBits2Float(0x43c1e666), SkBits2Float(0x42ca9a3f), SkBits2Float(0x43c24000), SkBits2Float(0x42ca9723));  // 387.1f, 101.307f, 387.8f, 101.301f, 388.5f, 101.295f
+path.cubicTo(SkBits2Float(0x43c2999a), SkBits2Float(0x42ca9407), SkBits2Float(0x43c2f333), SkBits2Float(0x42ca8d87), SkBits2Float(0x43c34ccd), SkBits2Float(0x42ca8b45));  // 389.2f, 101.289f, 389.9f, 101.276f, 390.6f, 101.272f
+path.cubicTo(SkBits2Float(0x43c3a666), SkBits2Float(0x42ca8902), SkBits2Float(0x43c40000), SkBits2Float(0x42ca8992), SkBits2Float(0x43c4599a), SkBits2Float(0x42ca8992));  // 391.3f, 101.268f, 392, 101.269f, 392.7f, 101.269f
+path.cubicTo(SkBits2Float(0x43c4b333), SkBits2Float(0x42ca8992), SkBits2Float(0x43c50ccd), SkBits2Float(0x42ca8a6b), SkBits2Float(0x43c56666), SkBits2Float(0x42ca8b45));  // 393.4f, 101.269f, 394.1f, 101.27f, 394.8f, 101.272f
+path.cubicTo(SkBits2Float(0x43c5c000), SkBits2Float(0x42ca8c1e), SkBits2Float(0x43c6199a), SkBits2Float(0x42ca8d87), SkBits2Float(0x43c67333), SkBits2Float(0x42ca8ea9));  // 395.5f, 101.274f, 396.2f, 101.276f, 396.9f, 101.279f
+path.cubicTo(SkBits2Float(0x43c6cccd), SkBits2Float(0x42ca8fca), SkBits2Float(0x43c72666), SkBits2Float(0x42ca90a3), SkBits2Float(0x43c78000), SkBits2Float(0x42ca920d));  // 397.6f, 101.281f, 398.3f, 101.282f, 399, 101.285f
+path.cubicTo(SkBits2Float(0x43c7d99a), SkBits2Float(0x42ca9377), SkBits2Float(0x43c83333), SkBits2Float(0x42ca9571), SkBits2Float(0x43c88ccd), SkBits2Float(0x42ca9723));  // 399.7f, 101.288f, 400.4f, 101.292f, 401.1f, 101.295f
+path.cubicTo(SkBits2Float(0x43c8e666), SkBits2Float(0x42ca98d5), SkBits2Float(0x43c94000), SkBits2Float(0x42ca9a88), SkBits2Float(0x43c9999a), SkBits2Float(0x42ca9c3a));  // 401.8f, 101.299f, 402.5f, 101.302f, 403.2f, 101.305f
+path.cubicTo(SkBits2Float(0x43c9f333), SkBits2Float(0x42ca9dec), SkBits2Float(0x43ca4ccd), SkBits2Float(0x42caa02f), SkBits2Float(0x43caa666), SkBits2Float(0x42caa150));  // 403.9f, 101.308f, 404.6f, 101.313f, 405.3f, 101.315f
+path.cubicTo(SkBits2Float(0x43cb0000), SkBits2Float(0x42caa271), SkBits2Float(0x43cb599a), SkBits2Float(0x42caa46c), SkBits2Float(0x43cbb333), SkBits2Float(0x42caa302));  // 406, 101.317f, 406.7f, 101.321f, 407.4f, 101.318f
+path.cubicTo(SkBits2Float(0x43cc0ccd), SkBits2Float(0x42caa198), SkBits2Float(0x43cc6666), SkBits2Float(0x42ca9ad0), SkBits2Float(0x43ccc000), SkBits2Float(0x42ca98d5));  // 408.1f, 101.316f, 408.8f, 101.302f, 409.5f, 101.299f
+path.cubicTo(SkBits2Float(0x43cd199a), SkBits2Float(0x42ca96db), SkBits2Float(0x43cd7333), SkBits2Float(0x42ca9535), SkBits2Float(0x43cdcccd), SkBits2Float(0x42ca9723));  // 410.2f, 101.295f, 410.9f, 101.291f, 411.6f, 101.295f
+path.cubicTo(SkBits2Float(0x43ce2666), SkBits2Float(0x42ca9912), SkBits2Float(0x43ce8000), SkBits2Float(0x42caa19a), SkBits2Float(0x43ced99a), SkBits2Float(0x42caa46e));  // 412.3f, 101.299f, 413, 101.316f, 413.7f, 101.321f
+path.cubicTo(SkBits2Float(0x43cf3333), SkBits2Float(0x42caa741), SkBits2Float(0x43cf8ccd), SkBits2Float(0x42caa7c4), SkBits2Float(0x43cfe666), SkBits2Float(0x42caa819));  // 414.4f, 101.327f, 415.1f, 101.328f, 415.8f, 101.328f
+path.cubicTo(SkBits2Float(0x43d04000), SkBits2Float(0x42caa86d), SkBits2Float(0x43d0999a), SkBits2Float(0x42caa788), SkBits2Float(0x43d0f333), SkBits2Float(0x42caa666));  // 416.5f, 101.329f, 417.2f, 101.327f, 417.9f, 101.325f
+path.cubicTo(SkBits2Float(0x43d14ccd), SkBits2Float(0x42caa545), SkBits2Float(0x43d1a666), SkBits2Float(0x42caa302), SkBits2Float(0x43d20000), SkBits2Float(0x42caa150));  // 418.6f, 101.323f, 419.3f, 101.318f, 420, 101.315f
+path.cubicTo(SkBits2Float(0x43d2599a), SkBits2Float(0x42ca9f9e), SkBits2Float(0x43d2b333), SkBits2Float(0x42ca9d13), SkBits2Float(0x43d30ccd), SkBits2Float(0x42ca9c3a));  // 420.7f, 101.312f, 421.4f, 101.307f, 422.1f, 101.305f
+path.cubicTo(SkBits2Float(0x43d36666), SkBits2Float(0x42ca9b61), SkBits2Float(0x43d3c000), SkBits2Float(0x42ca9b24), SkBits2Float(0x43d4199a), SkBits2Float(0x42ca9c3a));  // 422.8f, 101.303f, 423.5f, 101.303f, 424.2f, 101.305f
+path.cubicTo(SkBits2Float(0x43d47333), SkBits2Float(0x42ca9d4f), SkBits2Float(0x43d4cccd), SkBits2Float(0x42caa59b), SkBits2Float(0x43d52666), SkBits2Float(0x42caa2bc));  // 424.9f, 101.307f, 425.6f, 101.323f, 426.3f, 101.318f
+path.cubicTo(SkBits2Float(0x43d58000), SkBits2Float(0x42ca9fdc), SkBits2Float(0x43d5d99a), SkBits2Float(0x42caa425), SkBits2Float(0x43d63333), SkBits2Float(0x42ca8afe));  // 427, 101.312f, 427.7f, 101.321f, 428.4f, 101.271f
+path.cubicTo(SkBits2Float(0x43d68ccd), SkBits2Float(0x42ca71d6), SkBits2Float(0x43d6e666), SkBits2Float(0x42ca340a), SkBits2Float(0x43d74000), SkBits2Float(0x42ca0bcf));  // 429.1f, 101.222f, 429.8f, 101.102f, 430.5f, 101.023f
+path.cubicTo(SkBits2Float(0x43d7999a), SkBits2Float(0x42c9e394), SkBits2Float(0x43d7f333), SkBits2Float(0x42c9aca3), SkBits2Float(0x43d84ccd), SkBits2Float(0x42c9999a));  // 431.2f, 100.944f, 431.9f, 100.837f, 432.6f, 100.8f
+path.cubicTo(SkBits2Float(0x43d8a666), SkBits2Float(0x42c98692), SkBits2Float(0x43d90000), SkBits2Float(0x42c9999a), SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a));  // 433.3f, 100.763f, 434, 100.8f, 434.7f, 100.8f
+path.lineTo(SkBits2Float(0x43d9599a), SkBits2Float(0x42c9999a));  // 434.7f, 100.8f
+path.lineTo(SkBits2Float(0x43ab2666), SkBits2Float(0x42c9999a));  // 342.3f, 100.8f
+path.close();
+path.moveTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.cubicTo(SkBits2Float(0x43e00000), SkBits2Float(0x42c99abd), SkBits2Float(0x43e0599a), SkBits2Float(0x42c9a022), SkBits2Float(0x43e0b333), SkBits2Float(0x42c9a06c));  // 448, 100.802f, 448.7f, 100.813f, 449.4f, 100.813f
+path.cubicTo(SkBits2Float(0x43e10ccd), SkBits2Float(0x42c9a0b6), SkBits2Float(0x43e16666), SkBits2Float(0x42c99c79), SkBits2Float(0x43e1c000), SkBits2Float(0x42c99b56));  // 450.1f, 100.814f, 450.8f, 100.806f, 451.5f, 100.803f
+path.cubicTo(SkBits2Float(0x43e2199a), SkBits2Float(0x42c99a33), SkBits2Float(0x43e27333), SkBits2Float(0x42c9928a), SkBits2Float(0x43e2cccd), SkBits2Float(0x42c9999a));  // 452.2f, 100.801f, 452.9f, 100.786f, 453.6f, 100.8f
+path.cubicTo(SkBits2Float(0x43e32666), SkBits2Float(0x42c9a0ab), SkBits2Float(0x43e38000), SkBits2Float(0x42c9b682), SkBits2Float(0x43e3d99a), SkBits2Float(0x42c9c5bb));  // 454.3f, 100.814f, 455, 100.856f, 455.7f, 100.886f
+path.cubicTo(SkBits2Float(0x43e43333), SkBits2Float(0x42c9d4f4), SkBits2Float(0x43e48ccd), SkBits2Float(0x42c9ef91), SkBits2Float(0x43e4e666), SkBits2Float(0x42c9f4f0));  // 456.4f, 100.916f, 457.1f, 100.968f, 457.8f, 100.978f
+path.cubicTo(SkBits2Float(0x43e54000), SkBits2Float(0x42c9fa4e), SkBits2Float(0x43e5999a), SkBits2Float(0x42c9e940), SkBits2Float(0x43e5f333), SkBits2Float(0x42c9e5f3));  // 458.5f, 100.989f, 459.2f, 100.956f, 459.9f, 100.949f
+path.cubicTo(SkBits2Float(0x43e64ccd), SkBits2Float(0x42c9e2a6), SkBits2Float(0x43e6a666), SkBits2Float(0x42c9e366), SkBits2Float(0x43e70000), SkBits2Float(0x42c9e123));  // 460.6f, 100.943f, 461.3f, 100.944f, 462, 100.94f
+path.cubicTo(SkBits2Float(0x43e7599a), SkBits2Float(0x42c9dee0), SkBits2Float(0x43e7b333), SkBits2Float(0x42c9d9d8), SkBits2Float(0x43e80ccd), SkBits2Float(0x42c9d862));  // 462.7f, 100.935f, 463.4f, 100.925f, 464.1f, 100.923f
+path.cubicTo(SkBits2Float(0x43e86666), SkBits2Float(0x42c9d6ed), SkBits2Float(0x43e8c000), SkBits2Float(0x42c9d93b), SkBits2Float(0x43e9199a), SkBits2Float(0x42c9d862));  // 464.8f, 100.92f, 465.5f, 100.924f, 466.2f, 100.923f
+path.cubicTo(SkBits2Float(0x43e97333), SkBits2Float(0x42c9d789), SkBits2Float(0x43e9cccd), SkBits2Float(0x42c9d614), SkBits2Float(0x43ea2666), SkBits2Float(0x42c9d34c));  // 466.9f, 100.921f, 467.6f, 100.918f, 468.3f, 100.913f
+path.cubicTo(SkBits2Float(0x43ea8000), SkBits2Float(0x42c9d084), SkBits2Float(0x43ead99a), SkBits2Float(0x42c9cbf1), SkBits2Float(0x43eb3333), SkBits2Float(0x42c9c7b4));  // 469, 100.907f, 469.7f, 100.898f, 470.4f, 100.89f
+path.cubicTo(SkBits2Float(0x43eb8ccd), SkBits2Float(0x42c9c376), SkBits2Float(0x43ebe666), SkBits2Float(0x42c9bde8), SkBits2Float(0x43ec4000), SkBits2Float(0x42c9b9dc));  // 471.1f, 100.882f, 471.8f, 100.871f, 472.5f, 100.863f
+path.cubicTo(SkBits2Float(0x43ec999a), SkBits2Float(0x42c9b5cf), SkBits2Float(0x43ecf333), SkBits2Float(0x42c9b442), SkBits2Float(0x43ed4ccd), SkBits2Float(0x42c9af69));  // 473.2f, 100.855f, 473.9f, 100.852f, 474.6f, 100.843f
+path.cubicTo(SkBits2Float(0x43eda666), SkBits2Float(0x42c9aa8f), SkBits2Float(0x43ee0000), SkBits2Float(0x42c9a064), SkBits2Float(0x43ee599a), SkBits2Float(0x42c99cc1));  // 475.3f, 100.833f, 476, 100.813f, 476.7f, 100.806f
+path.cubicTo(SkBits2Float(0x43eeb333), SkBits2Float(0x42c9991f), SkBits2Float(0x43ef0ccd), SkBits2Float(0x42c99758), SkBits2Float(0x43ef6666), SkBits2Float(0x42c9999a));  // 477.4f, 100.799f, 478.1f, 100.796f, 478.8f, 100.8f
+path.cubicTo(SkBits2Float(0x43efc000), SkBits2Float(0x42c99bdd), SkBits2Float(0x43f0199a), SkBits2Float(0x42c99f0e), SkBits2Float(0x43f07333), SkBits2Float(0x42c9aa52));  // 479.5f, 100.804f, 480.2f, 100.811f, 480.9f, 100.833f
+path.cubicTo(SkBits2Float(0x43f0cccd), SkBits2Float(0x42c9b596), SkBits2Float(0x43f12666), SkBits2Float(0x42c9cfde), SkBits2Float(0x43f18000), SkBits2Float(0x42c9dd32));  // 481.6f, 100.855f, 482.3f, 100.906f, 483, 100.932f
+path.cubicTo(SkBits2Float(0x43f1d99a), SkBits2Float(0x42c9ea86), SkBits2Float(0x43f23333), SkBits2Float(0x42c9f451), SkBits2Float(0x43f28ccd), SkBits2Float(0x42c9fa4d));  // 483.7f, 100.958f, 484.4f, 100.977f, 485.1f, 100.989f
+path.cubicTo(SkBits2Float(0x43f2e666), SkBits2Float(0x42ca0048), SkBits2Float(0x43f34000), SkBits2Float(0x42c9fbf3), SkBits2Float(0x43f3999a), SkBits2Float(0x42ca0115));  // 485.8f, 101.001f, 486.5f, 100.992f, 487.2f, 101.002f
+path.cubicTo(SkBits2Float(0x43f3f333), SkBits2Float(0x42ca0637), SkBits2Float(0x43f44ccd), SkBits2Float(0x42ca103f), SkBits2Float(0x43f4a666), SkBits2Float(0x42ca1919));  // 487.9f, 101.012f, 488.6f, 101.032f, 489.3f, 101.049f
+path.cubicTo(SkBits2Float(0x43f50000), SkBits2Float(0x42ca21f4), SkBits2Float(0x43f5599a), SkBits2Float(0x42ca2e87), SkBits2Float(0x43f5b333), SkBits2Float(0x42ca3634));  // 490, 101.066f, 490.7f, 101.091f, 491.4f, 101.106f
+path.cubicTo(SkBits2Float(0x43f60ccd), SkBits2Float(0x42ca3de1), SkBits2Float(0x43f66666), SkBits2Float(0x42ca43b9), SkBits2Float(0x43f6c000), SkBits2Float(0x42ca4729));  // 492.1f, 101.121f, 492.8f, 101.132f, 493.5f, 101.139f
+path.cubicTo(SkBits2Float(0x43f7199a), SkBits2Float(0x42ca4a99), SkBits2Float(0x43f77333), SkBits2Float(0x42ca49b3), SkBits2Float(0x43f7cccd), SkBits2Float(0x42ca4ad4));  // 494.2f, 101.146f, 494.9f, 101.144f, 495.6f, 101.146f
+path.cubicTo(SkBits2Float(0x43f82666), SkBits2Float(0x42ca4bf5), SkBits2Float(0x43f88000), SkBits2Float(0x42ca4cdc), SkBits2Float(0x43f8d99a), SkBits2Float(0x42ca4df2));  // 496.3f, 101.148f, 497, 101.15f, 497.7f, 101.152f
+path.cubicTo(SkBits2Float(0x43f93333), SkBits2Float(0x42ca4f07), SkBits2Float(0x43f98ccd), SkBits2Float(0x42ca4fec), SkBits2Float(0x43f9e666), SkBits2Float(0x42ca5156));  // 498.4f, 101.154f, 499.1f, 101.156f, 499.8f, 101.159f
+path.cubicTo(SkBits2Float(0x43fa4000), SkBits2Float(0x42ca52c0), SkBits2Float(0x43fa999a), SkBits2Float(0x42ca53e1), SkBits2Float(0x43faf333), SkBits2Float(0x42ca566c));  // 500.5f, 101.162f, 501.2f, 101.164f, 501.9f, 101.169f
+path.cubicTo(SkBits2Float(0x43fb4ccd), SkBits2Float(0x42ca58f7), SkBits2Float(0x43fba666), SkBits2Float(0x42ca5d71), SkBits2Float(0x43fc0000), SkBits2Float(0x42ca6099));  // 502.6f, 101.174f, 503.3f, 101.183f, 504, 101.189f
+path.cubicTo(SkBits2Float(0x43fc599a), SkBits2Float(0x42ca63c1), SkBits2Float(0x43fcb333), SkBits2Float(0x42ca663e), SkBits2Float(0x43fd0ccd), SkBits2Float(0x42ca695a));  // 504.7f, 101.195f, 505.4f, 101.2f, 506.1f, 101.206f
+path.cubicTo(SkBits2Float(0x43fd6666), SkBits2Float(0x42ca6c76), SkBits2Float(0x43fdc000), SkBits2Float(0x42ca7024), SkBits2Float(0x43fe199a), SkBits2Float(0x42ca7340));  // 506.8f, 101.212f, 507.5f, 101.219f, 508.2f, 101.225f
+path.cubicTo(SkBits2Float(0x43fe7333), SkBits2Float(0x42ca765c), SkBits2Float(0x43fecccd), SkBits2Float(0x42ca7a44), SkBits2Float(0x43ff2666), SkBits2Float(0x42ca7c01));  // 508.9f, 101.231f, 509.6f, 101.239f, 510.3f, 101.242f
+path.cubicTo(SkBits2Float(0x43ff8000), SkBits2Float(0x42ca7dbf), SkBits2Float(0x43ffd99a), SkBits2Float(0x42ca7ed5), SkBits2Float(0x4400199a), SkBits2Float(0x42ca7db4));  // 511, 101.246f, 511.7f, 101.248f, 512.4f, 101.246f
+path.cubicTo(SkBits2Float(0x44004666), SkBits2Float(0x42ca7c92), SkBits2Float(0x44007333), SkBits2Float(0x42ca780c), SkBits2Float(0x4400a000), SkBits2Float(0x42ca7539));  // 513.1f, 101.243f, 513.8f, 101.234f, 514.5f, 101.229f
+path.cubicTo(SkBits2Float(0x4400cccd), SkBits2Float(0x42ca7265), SkBits2Float(0x4400f99a), SkBits2Float(0x42ca7017), SkBits2Float(0x44012666), SkBits2Float(0x42ca6cbe));  // 515.2f, 101.223f, 515.9f, 101.219f, 516.6f, 101.212f
+path.cubicTo(SkBits2Float(0x44015333), SkBits2Float(0x42ca6966), SkBits2Float(0x44018000), SkBits2Float(0x42ca688b), SkBits2Float(0x4401accd), SkBits2Float(0x42ca6126));  // 517.3f, 101.206f, 518, 101.204f, 518.7f, 101.19f
+path.cubicTo(SkBits2Float(0x4401d99a), SkBits2Float(0x42ca59c1), SkBits2Float(0x44020666), SkBits2Float(0x42ca4eee), SkBits2Float(0x44023333), SkBits2Float(0x42ca4061));  // 519.4f, 101.175f, 520.1f, 101.154f, 520.8f, 101.126f
+path.cubicTo(SkBits2Float(0x44026000), SkBits2Float(0x42ca31d3), SkBits2Float(0x44028ccd), SkBits2Float(0x42ca1b74), SkBits2Float(0x4402b99a), SkBits2Float(0x42ca09d6));  // 521.5f, 101.097f, 522.2f, 101.054f, 522.9f, 101.019f
+path.cubicTo(SkBits2Float(0x4402e666), SkBits2Float(0x42c9f839), SkBits2Float(0x44031333), SkBits2Float(0x42c9e720), SkBits2Float(0x44034000), SkBits2Float(0x42c9d6b0));  // 523.6f, 100.985f, 524.3f, 100.951f, 525, 100.919f
+path.cubicTo(SkBits2Float(0x44036ccd), SkBits2Float(0x42c9c640), SkBits2Float(0x4403999a), SkBits2Float(0x42c9b163), SkBits2Float(0x4403c666), SkBits2Float(0x42c9a735));  // 525.7f, 100.887f, 526.4f, 100.846f, 527.1f, 100.827f
+path.cubicTo(SkBits2Float(0x4403f333), SkBits2Float(0x42c99d06), SkBits2Float(0x44042000), SkBits2Float(0x42c99bdf), SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a));  // 527.8f, 100.807f, 528.5f, 100.804f, 529.2f, 100.8f
+path.lineTo(SkBits2Float(0x44044ccd), SkBits2Float(0x42c9999a));  // 529.2f, 100.8f
+path.lineTo(SkBits2Float(0x43dfa666), SkBits2Float(0x42c9999a));  // 447.3f, 100.8f
+path.close();
+    return path;
+}
+
+static void issue3651_1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path = path1();
+    SkPath pathB = path2();
+    testPathOpCheck(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename, FLAGS_runFail);
+}
+
+static void issue3651_2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path = path3();
+    SkPath pathB = path4();
+    testPathOp(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename);
+}
+
+static void issue3651_3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path = path5();
+    SkPath pathB = path6();
+    testPathOp(reporter, path, pathB, SkPathOp::kUnion_SkPathOp, filename);
+}
+
+static void issue3651_4(skiatest::Reporter* reporter, const char* filename) {
+SkPath path;
+path.moveTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.cubicTo(SkBits2Float(0x4205eeef), SkBits2Float(0x43346666), SkBits2Float(0x4208aaab), SkBits2Float(0x4334780f), SkBits2Float(0x420b6666), SkBits2Float(0x43346666));  // 33.4833f, 180.4f, 34.1667f, 180.469f, 34.85f, 180.4f
+path.cubicTo(SkBits2Float(0x420e2222), SkBits2Float(0x433454be), SkBits2Float(0x4210ddde), SkBits2Float(0x43340d56), SkBits2Float(0x4213999a), SkBits2Float(0x4333fc72));  // 35.5333f, 180.331f, 36.2167f, 180.052f, 36.9f, 179.986f
+path.cubicTo(SkBits2Float(0x42165555), SkBits2Float(0x4333eb8e), SkBits2Float(0x42191111), SkBits2Float(0x4333fbf4), SkBits2Float(0x421bcccd), SkBits2Float(0x4334010f));  // 37.5833f, 179.92f, 38.2667f, 179.984f, 38.95f, 180.004f
+path.cubicTo(SkBits2Float(0x421e8889), SkBits2Float(0x4334062b), SkBits2Float(0x42214444), SkBits2Float(0x43341213), SkBits2Float(0x42240000), SkBits2Float(0x43341b17));  // 39.6333f, 180.024f, 40.3167f, 180.071f, 41, 180.106f
+path.cubicTo(SkBits2Float(0x4226bbbc), SkBits2Float(0x4334241b), SkBits2Float(0x42297777), SkBits2Float(0x4334339e), SkBits2Float(0x422c3333), SkBits2Float(0x43343728));  // 41.6833f, 180.141f, 42.3667f, 180.202f, 43.05f, 180.215f
+path.cubicTo(SkBits2Float(0x422eeeef), SkBits2Float(0x43343ab2), SkBits2Float(0x4231aaab), SkBits2Float(0x4334337f), SkBits2Float(0x42346666), SkBits2Float(0x43343054));  // 43.7333f, 180.229f, 44.4167f, 180.201f, 45.1f, 180.189f
+path.cubicTo(SkBits2Float(0x42372222), SkBits2Float(0x43342d28), SkBits2Float(0x4239ddde), SkBits2Float(0x43342281), SkBits2Float(0x423c999a), SkBits2Float(0x43342423));  // 45.7833f, 180.176f, 46.4667f, 180.135f, 47.15f, 180.141f
+path.cubicTo(SkBits2Float(0x423f5555), SkBits2Float(0x433425c5), SkBits2Float(0x42421111), SkBits2Float(0x43343381), SkBits2Float(0x4244cccd), SkBits2Float(0x43343a1f));  // 47.8333f, 180.148f, 48.5167f, 180.201f, 49.2f, 180.227f
+path.cubicTo(SkBits2Float(0x42478889), SkBits2Float(0x433440be), SkBits2Float(0x424a4444), SkBits2Float(0x43344d5d), SkBits2Float(0x424d0000), SkBits2Float(0x43344bdb));  // 49.8833f, 180.253f, 50.5667f, 180.302f, 51.25f, 180.296f
+path.cubicTo(SkBits2Float(0x424fbbbc), SkBits2Float(0x43344a59), SkBits2Float(0x42527777), SkBits2Float(0x43342ca7), SkBits2Float(0x42553333), SkBits2Float(0x43343113));  // 51.9333f, 180.29f, 52.6167f, 180.174f, 53.3f, 180.192f
+path.cubicTo(SkBits2Float(0x4257eeef), SkBits2Float(0x43343580), SkBits2Float(0x425aaaab), SkBits2Float(0x4334654b), SkBits2Float(0x425d6666), SkBits2Float(0x43346666));  // 53.9833f, 180.209f, 54.6667f, 180.396f, 55.35f, 180.4f
+path.cubicTo(SkBits2Float(0x42602222), SkBits2Float(0x43346782), SkBits2Float(0x4262ddde), SkBits2Float(0x43343ee4), SkBits2Float(0x4265999a), SkBits2Float(0x433437ba));  // 56.0333f, 180.404f, 56.7167f, 180.246f, 57.4f, 180.218f
+path.cubicTo(SkBits2Float(0x42685555), SkBits2Float(0x4334308f), SkBits2Float(0x426b1111), SkBits2Float(0x43343ce3), SkBits2Float(0x426dcccd), SkBits2Float(0x43343b69));  // 58.0833f, 180.19f, 58.7667f, 180.238f, 59.45f, 180.232f
+path.cubicTo(SkBits2Float(0x42708889), SkBits2Float(0x433439ef), SkBits2Float(0x42734444), SkBits2Float(0x433437d8), SkBits2Float(0x42760000), SkBits2Float(0x43342edc));  // 60.1333f, 180.226f, 60.8167f, 180.218f, 61.5f, 180.183f
+path.cubicTo(SkBits2Float(0x4278bbbc), SkBits2Float(0x433425df), SkBits2Float(0x427b7777), SkBits2Float(0x43340d4f), SkBits2Float(0x427e3333), SkBits2Float(0x4334057e));  // 62.1833f, 180.148f, 62.8667f, 180.052f, 63.55f, 180.021f
+path.cubicTo(SkBits2Float(0x42807777), SkBits2Float(0x4333fdad), SkBits2Float(0x4281d555), SkBits2Float(0x4333fb55), SkBits2Float(0x42833333), SkBits2Float(0x4333fff3));  // 64.2333f, 179.991f, 64.9167f, 179.982f, 65.6f, 180
+path.cubicTo(SkBits2Float(0x42849111), SkBits2Float(0x43340492), SkBits2Float(0x4285eeef), SkBits2Float(0x43341020), SkBits2Float(0x42874ccd), SkBits2Float(0x43342133));  // 66.2833f, 180.018f, 66.9667f, 180.063f, 67.65f, 180.13f
+path.cubicTo(SkBits2Float(0x4288aaab), SkBits2Float(0x43343246), SkBits2Float(0x428a0889), SkBits2Float(0x43345ade), SkBits2Float(0x428b6666), SkBits2Float(0x43346666));  // 68.3333f, 180.196f, 69.0167f, 180.355f, 69.7f, 180.4f
+path.cubicTo(SkBits2Float(0x428cc444), SkBits2Float(0x433471ef), SkBits2Float(0x428e2222), SkBits2Float(0x433467af), SkBits2Float(0x428f8000), SkBits2Float(0x43346666));  // 70.3833f, 180.445f, 71.0667f, 180.405f, 71.75f, 180.4f
+path.cubicTo(SkBits2Float(0x4290ddde), SkBits2Float(0x4334651e), SkBits2Float(0x42923bbc), SkBits2Float(0x43346442), SkBits2Float(0x4293999a), SkBits2Float(0x43345eb2));  // 72.4333f, 180.395f, 73.1167f, 180.392f, 73.8f, 180.37f
+path.cubicTo(SkBits2Float(0x4294f777), SkBits2Float(0x43345922), SkBits2Float(0x42965555), SkBits2Float(0x43344bb4), SkBits2Float(0x4297b333), SkBits2Float(0x43344506));  // 74.4833f, 180.348f, 75.1667f, 180.296f, 75.85f, 180.27f
+path.cubicTo(SkBits2Float(0x42991111), SkBits2Float(0x43343e58), SkBits2Float(0x429a6eef), SkBits2Float(0x433438a6), SkBits2Float(0x429bcccd), SkBits2Float(0x4334369e));  // 76.5333f, 180.244f, 77.2167f, 180.221f, 77.9f, 180.213f
+path.cubicTo(SkBits2Float(0x429d2aab), SkBits2Float(0x43343496), SkBits2Float(0x429e8889), SkBits2Float(0x43343fb3), SkBits2Float(0x429fe666), SkBits2Float(0x433438d5));  // 78.5833f, 180.205f, 79.2667f, 180.249f, 79.95f, 180.222f
+path.cubicTo(SkBits2Float(0x42a14444), SkBits2Float(0x433431f8), SkBits2Float(0x42a2a222), SkBits2Float(0x433411e5), SkBits2Float(0x42a40000), SkBits2Float(0x43340d6e));  // 80.6333f, 180.195f, 81.3167f, 180.07f, 82, 180.052f
+path.cubicTo(SkBits2Float(0x42a55dde), SkBits2Float(0x433408f8), SkBits2Float(0x42a6bbbc), SkBits2Float(0x43341ae2), SkBits2Float(0x42a8199a), SkBits2Float(0x43341e0e));  // 82.6833f, 180.035f, 83.3667f, 180.105f, 84.05f, 180.117f
+path.cubicTo(SkBits2Float(0x42a97777), SkBits2Float(0x43342139), SkBits2Float(0x42aad555), SkBits2Float(0x433427a8), SkBits2Float(0x42ac3333), SkBits2Float(0x43342073));  // 84.7333f, 180.13f, 85.4167f, 180.155f, 86.1f, 180.127f
+path.cubicTo(SkBits2Float(0x42ad9111), SkBits2Float(0x4334193f), SkBits2Float(0x42aeeeef), SkBits2Float(0x4333fa48), SkBits2Float(0x42b04ccd), SkBits2Float(0x4333f2d5));  // 86.7833f, 180.099f, 87.4667f, 179.978f, 88.15f, 179.949f
+path.cubicTo(SkBits2Float(0x42b1aaab), SkBits2Float(0x4333eb62), SkBits2Float(0x42b30889), SkBits2Float(0x4333f0fd), SkBits2Float(0x42b46666), SkBits2Float(0x4333f3c3));  // 88.8333f, 179.919f, 89.5167f, 179.941f, 90.2f, 179.952f
+path.cubicTo(SkBits2Float(0x42b5c444), SkBits2Float(0x4333f688), SkBits2Float(0x42b72222), SkBits2Float(0x4333f7ca), SkBits2Float(0x42b88000), SkBits2Float(0x43340375));  // 90.8833f, 179.963f, 91.5667f, 179.968f, 92.25f, 180.014f
+path.cubicTo(SkBits2Float(0x42b9ddde), SkBits2Float(0x43340f1f), SkBits2Float(0x42bb3bbc), SkBits2Float(0x43342b53), SkBits2Float(0x42bc999a), SkBits2Float(0x433439c3));  // 92.9333f, 180.059f, 93.6167f, 180.169f, 94.3f, 180.226f
+path.cubicTo(SkBits2Float(0x42bdf777), SkBits2Float(0x43344833), SkBits2Float(0x42bf5555), SkBits2Float(0x43345473), SkBits2Float(0x42c0b333), SkBits2Float(0x43345a15));  // 94.9833f, 180.282f, 95.6667f, 180.33f, 96.35f, 180.352f
+path.cubicTo(SkBits2Float(0x42c21111), SkBits2Float(0x43345fb6), SkBits2Float(0x42c36eef), SkBits2Float(0x433467f5), SkBits2Float(0x42c4cccd), SkBits2Float(0x43345b8d));  // 97.0333f, 180.374f, 97.7167f, 180.406f, 98.4f, 180.358f
+path.cubicTo(SkBits2Float(0x42c62aab), SkBits2Float(0x43344f25), SkBits2Float(0x42c78889), SkBits2Float(0x43342bb0), SkBits2Float(0x42c8e666), SkBits2Float(0x43340fa6));  // 99.0833f, 180.309f, 99.7667f, 180.171f, 100.45f, 180.061f
+path.cubicTo(SkBits2Float(0x42ca4444), SkBits2Float(0x4333f39b), SkBits2Float(0x42cba222), SkBits2Float(0x4333c2e0), SkBits2Float(0x42cd0000), SkBits2Float(0x4333b34d));  // 101.133f, 179.952f, 101.817f, 179.761f, 102.5f, 179.7f
+path.cubicTo(SkBits2Float(0x42ce5dde), SkBits2Float(0x4333a3b9), SkBits2Float(0x42cfbbbc), SkBits2Float(0x4333b115), SkBits2Float(0x42d1199a), SkBits2Float(0x4333b231));  // 103.183f, 179.64f, 103.867f, 179.692f, 104.55f, 179.696f
+path.cubicTo(SkBits2Float(0x42d27777), SkBits2Float(0x4333b34d), SkBits2Float(0x42d3d555), SkBits2Float(0x4333b6a0), SkBits2Float(0x42d53333), SkBits2Float(0x4333b9f3));  // 105.233f, 179.7f, 105.917f, 179.713f, 106.6f, 179.726f
+path.cubicTo(SkBits2Float(0x42d69111), SkBits2Float(0x4333bd46), SkBits2Float(0x42d7eeef), SkBits2Float(0x4333c308), SkBits2Float(0x42d94ccd), SkBits2Float(0x4333c624));  // 107.283f, 179.739f, 107.967f, 179.762f, 108.65f, 179.774f
+path.cubicTo(SkBits2Float(0x42daaaab), SkBits2Float(0x4333c940), SkBits2Float(0x42dc0889), SkBits2Float(0x4333b41c), SkBits2Float(0x42dd6666), SkBits2Float(0x4333cc9c));  // 109.333f, 179.786f, 110.017f, 179.704f, 110.7f, 179.799f
+path.cubicTo(SkBits2Float(0x42dec444), SkBits2Float(0x4333e51d), SkBits2Float(0x42e02222), SkBits2Float(0x43343f85), SkBits2Float(0x42e18000), SkBits2Float(0x43345927));  // 111.383f, 179.895f, 112.067f, 180.248f, 112.75f, 180.348f
+path.cubicTo(SkBits2Float(0x42e2ddde), SkBits2Float(0x433472c9), SkBits2Float(0x42e43bbc), SkBits2Float(0x43346431), SkBits2Float(0x42e5999a), SkBits2Float(0x43346666));  // 113.433f, 180.448f, 114.117f, 180.391f, 114.8f, 180.4f
+path.cubicTo(SkBits2Float(0x42e6f777), SkBits2Float(0x4334689c), SkBits2Float(0x42e85555), SkBits2Float(0x43346666), SkBits2Float(0x42e9b333), SkBits2Float(0x43346666));  // 115.483f, 180.409f, 116.167f, 180.4f, 116.85f, 180.4f
+path.lineTo(SkBits2Float(0x42e9b333), SkBits2Float(0x43346666));  // 116.85f, 180.4f
+path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.close();
+path.moveTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.cubicTo(SkBits2Float(0x4305eeef), SkBits2Float(0x43346666), SkBits2Float(0x43069dde), SkBits2Float(0x43347a6e), SkBits2Float(0x43074ccd), SkBits2Float(0x43346666));  // 133.933f, 180.4f, 134.617f, 180.478f, 135.3f, 180.4f
+path.cubicTo(SkBits2Float(0x4307fbbc), SkBits2Float(0x4334525f), SkBits2Float(0x4308aaab), SkBits2Float(0x43340a40), SkBits2Float(0x4309599a), SkBits2Float(0x4333ee38));  // 135.983f, 180.322f, 136.667f, 180.04f, 137.35f, 179.931f
+path.cubicTo(SkBits2Float(0x430a0889), SkBits2Float(0x4333d230), SkBits2Float(0x430ab777), SkBits2Float(0x4333c68b), SkBits2Float(0x430b6666), SkBits2Float(0x4333be34));  // 138.033f, 179.821f, 138.717f, 179.776f, 139.4f, 179.743f
+path.cubicTo(SkBits2Float(0x430c1555), SkBits2Float(0x4333b5dc), SkBits2Float(0x430cc444), SkBits2Float(0x4333bc82), SkBits2Float(0x430d7333), SkBits2Float(0x4333bc2b));  // 140.083f, 179.71f, 140.767f, 179.736f, 141.45f, 179.735f
+path.cubicTo(SkBits2Float(0x430e2222), SkBits2Float(0x4333bbd4), SkBits2Float(0x430ed111), SkBits2Float(0x4333bd76), SkBits2Float(0x430f8000), SkBits2Float(0x4333bc2b));  // 142.133f, 179.734f, 142.817f, 179.74f, 143.5f, 179.735f
+path.cubicTo(SkBits2Float(0x43102eef), SkBits2Float(0x4333bae0), SkBits2Float(0x4310ddde), SkBits2Float(0x4333b72e), SkBits2Float(0x43118ccd), SkBits2Float(0x4333b469));  // 144.183f, 179.73f, 144.867f, 179.716f, 145.55f, 179.705f
+path.cubicTo(SkBits2Float(0x43123bbc), SkBits2Float(0x4333b1a3), SkBits2Float(0x4312eaab), SkBits2Float(0x4333ad34), SkBits2Float(0x4313999a), SkBits2Float(0x4333ab8b));  // 146.233f, 179.694f, 146.917f, 179.677f, 147.6f, 179.67f
+path.cubicTo(SkBits2Float(0x43144889), SkBits2Float(0x4333a9e1), SkBits2Float(0x4314f777), SkBits2Float(0x4333aa97), SkBits2Float(0x4315a666), SkBits2Float(0x4333aa6f));  // 148.283f, 179.664f, 148.967f, 179.666f, 149.65f, 179.666f
+path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x4333aa48), SkBits2Float(0x43170444), SkBits2Float(0x4333aac5), SkBits2Float(0x4317b333), SkBits2Float(0x4333aa9d));  // 150.333f, 179.665f, 151.017f, 179.667f, 151.7f, 179.666f
+path.cubicTo(SkBits2Float(0x43186222), SkBits2Float(0x4333aa76), SkBits2Float(0x43191111), SkBits2Float(0x4333a962), SkBits2Float(0x4319c000), SkBits2Float(0x4333a982));  // 152.383f, 179.666f, 153.067f, 179.662f, 153.75f, 179.662f
+path.cubicTo(SkBits2Float(0x431a6eef), SkBits2Float(0x4333a9a2), SkBits2Float(0x431b1dde), SkBits2Float(0x4333ab0e), SkBits2Float(0x431bcccd), SkBits2Float(0x4333ab5d));  // 154.433f, 179.663f, 155.117f, 179.668f, 155.8f, 179.669f
+path.cubicTo(SkBits2Float(0x431c7bbc), SkBits2Float(0x4333abac), SkBits2Float(0x431d2aab), SkBits2Float(0x4333ab84), SkBits2Float(0x431dd99a), SkBits2Float(0x4333ab5d));  // 156.483f, 179.671f, 157.167f, 179.67f, 157.85f, 179.669f
+path.cubicTo(SkBits2Float(0x431e8889), SkBits2Float(0x4333ab35), SkBits2Float(0x431f3777), SkBits2Float(0x4333aa8f), SkBits2Float(0x431fe666), SkBits2Float(0x4333aa6f));  // 158.533f, 179.669f, 159.217f, 179.666f, 159.9f, 179.666f
+path.cubicTo(SkBits2Float(0x43209555), SkBits2Float(0x4333aa4f), SkBits2Float(0x43214444), SkBits2Float(0x4333a9b1), SkBits2Float(0x4321f333), SkBits2Float(0x4333aa9d));  // 160.583f, 179.665f, 161.267f, 179.663f, 161.95f, 179.666f
+path.cubicTo(SkBits2Float(0x4322a222), SkBits2Float(0x4333ab8a), SkBits2Float(0x43235111), SkBits2Float(0x4333aeb6), SkBits2Float(0x43240000), SkBits2Float(0x4333affa));  // 162.633f, 179.67f, 163.317f, 179.682f, 164, 179.687f
+path.cubicTo(SkBits2Float(0x4324aeef), SkBits2Float(0x4333b13d), SkBits2Float(0x43255dde), SkBits2Float(0x4333b1a3), SkBits2Float(0x43260ccd), SkBits2Float(0x4333b231));  // 164.683f, 179.692f, 165.367f, 179.694f, 166.05f, 179.696f
+path.cubicTo(SkBits2Float(0x4326bbbc), SkBits2Float(0x4333b2bf), SkBits2Float(0x43276aab), SkBits2Float(0x4333b439), SkBits2Float(0x4328199a), SkBits2Float(0x4333b34d));  // 166.733f, 179.698f, 167.417f, 179.704f, 168.1f, 179.7f
+path.cubicTo(SkBits2Float(0x4328c889), SkBits2Float(0x4333b260), SkBits2Float(0x43297777), SkBits2Float(0x4333ae48), SkBits2Float(0x432a2666), SkBits2Float(0x4333aca7));  // 168.783f, 179.697f, 169.467f, 179.681f, 170.15f, 179.674f
+path.cubicTo(SkBits2Float(0x432ad555), SkBits2Float(0x4333ab05), SkBits2Float(0x432b8444), SkBits2Float(0x4333a9d8), SkBits2Float(0x432c3333), SkBits2Float(0x4333a982));  // 170.833f, 179.668f, 171.517f, 179.663f, 172.2f, 179.662f
+path.cubicTo(SkBits2Float(0x432ce222), SkBits2Float(0x4333a92b), SkBits2Float(0x432d9111), SkBits2Float(0x4333a63e), SkBits2Float(0x432e4000), SkBits2Float(0x4333aa9d));  // 172.883f, 179.661f, 173.567f, 179.649f, 174.25f, 179.666f
+path.cubicTo(SkBits2Float(0x432eeeef), SkBits2Float(0x4333aefd), SkBits2Float(0x432f9dde), SkBits2Float(0x4333aacf), SkBits2Float(0x43304ccd), SkBits2Float(0x4333c3bf));  // 174.933f, 179.684f, 175.617f, 179.667f, 176.3f, 179.765f
+path.cubicTo(SkBits2Float(0x4330fbbc), SkBits2Float(0x4333dcae), SkBits2Float(0x4331aaab), SkBits2Float(0x433427ba), SkBits2Float(0x4332599a), SkBits2Float(0x4334403b));  // 176.983f, 179.862f, 177.667f, 180.155f, 178.35f, 180.251f
+path.cubicTo(SkBits2Float(0x43330889), SkBits2Float(0x433458bc), SkBits2Float(0x4333b777), SkBits2Float(0x43345065), SkBits2Float(0x43346666), SkBits2Float(0x433456c2));  // 179.033f, 180.347f, 179.717f, 180.314f, 180.4f, 180.339f
+path.cubicTo(SkBits2Float(0x43351555), SkBits2Float(0x43345d1e), SkBits2Float(0x4335c444), SkBits2Float(0x433463cb), SkBits2Float(0x43367333), SkBits2Float(0x43346666));  // 181.083f, 180.364f, 181.767f, 180.39f, 182.45f, 180.4f
+path.cubicTo(SkBits2Float(0x43372222), SkBits2Float(0x43346902), SkBits2Float(0x4337d111), SkBits2Float(0x43346666), SkBits2Float(0x43388000), SkBits2Float(0x43346666));  // 183.133f, 180.41f, 183.817f, 180.4f, 184.5f, 180.4f
+path.lineTo(SkBits2Float(0x43388000), SkBits2Float(0x43346666));  // 184.5f, 180.4f
+path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.close();
+path.moveTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.cubicTo(SkBits2Float(0x433b3bbc), SkBits2Float(0x433465db), SkBits2Float(0x433beaab), SkBits2Float(0x433463ac), SkBits2Float(0x433c999a), SkBits2Float(0x43346321));  // 187.233f, 180.398f, 187.917f, 180.389f, 188.6f, 180.387f
+path.cubicTo(SkBits2Float(0x433d4889), SkBits2Float(0x43346295), SkBits2Float(0x433df777), SkBits2Float(0x43346295), SkBits2Float(0x433ea666), SkBits2Float(0x43346321));  // 189.283f, 180.385f, 189.967f, 180.385f, 190.65f, 180.387f
+path.cubicTo(SkBits2Float(0x433f5555), SkBits2Float(0x433463ac), SkBits2Float(0x43400444), SkBits2Float(0x433465db), SkBits2Float(0x4340b333), SkBits2Float(0x43346666));  // 191.333f, 180.389f, 192.017f, 180.398f, 192.7f, 180.4f
+path.lineTo(SkBits2Float(0x4340b333), SkBits2Float(0x43346666));  // 192.7f, 180.4f
+path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.close();
+SkPath pathA = path;
+path.reset();
+path.moveTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.cubicTo(SkBits2Float(0x4205eeef), SkBits2Float(0x43346666), SkBits2Float(0x4208aaab), SkBits2Float(0x433454bd), SkBits2Float(0x420b6666), SkBits2Float(0x43346666));  // 33.4833f, 180.4f, 34.1667f, 180.331f, 34.85f, 180.4f
+path.cubicTo(SkBits2Float(0x420e2222), SkBits2Float(0x4334780e), SkBits2Float(0x4210ddde), SkBits2Float(0x4334bf76), SkBits2Float(0x4213999a), SkBits2Float(0x4334d05a));  // 35.5333f, 180.469f, 36.2167f, 180.748f, 36.9f, 180.814f
+path.cubicTo(SkBits2Float(0x42165555), SkBits2Float(0x4334e13e), SkBits2Float(0x42191111), SkBits2Float(0x4334d0d8), SkBits2Float(0x421bcccd), SkBits2Float(0x4334cbbd));  // 37.5833f, 180.88f, 38.2667f, 180.816f, 38.95f, 180.796f
+path.cubicTo(SkBits2Float(0x421e8889), SkBits2Float(0x4334c6a1), SkBits2Float(0x42214444), SkBits2Float(0x4334bab9), SkBits2Float(0x42240000), SkBits2Float(0x4334b1b5));  // 39.6333f, 180.776f, 40.3167f, 180.729f, 41, 180.694f
+path.cubicTo(SkBits2Float(0x4226bbbc), SkBits2Float(0x4334a8b1), SkBits2Float(0x42297777), SkBits2Float(0x4334992e), SkBits2Float(0x422c3333), SkBits2Float(0x433495a4));  // 41.6833f, 180.659f, 42.3667f, 180.598f, 43.05f, 180.585f
+path.cubicTo(SkBits2Float(0x422eeeef), SkBits2Float(0x4334921a), SkBits2Float(0x4231aaab), SkBits2Float(0x4334994d), SkBits2Float(0x42346666), SkBits2Float(0x43349c78));  // 43.7333f, 180.571f, 44.4167f, 180.599f, 45.1f, 180.611f
+path.cubicTo(SkBits2Float(0x42372222), SkBits2Float(0x43349fa4), SkBits2Float(0x4239ddde), SkBits2Float(0x4334aa4b), SkBits2Float(0x423c999a), SkBits2Float(0x4334a8a9));  // 45.7833f, 180.624f, 46.4667f, 180.665f, 47.15f, 180.659f
+path.cubicTo(SkBits2Float(0x423f5555), SkBits2Float(0x4334a707), SkBits2Float(0x42421111), SkBits2Float(0x4334994b), SkBits2Float(0x4244cccd), SkBits2Float(0x433492ad));  // 47.8333f, 180.652f, 48.5167f, 180.599f, 49.2f, 180.573f
+path.cubicTo(SkBits2Float(0x42478889), SkBits2Float(0x43348c0e), SkBits2Float(0x424a4444), SkBits2Float(0x43347f6f), SkBits2Float(0x424d0000), SkBits2Float(0x433480f1));  // 49.8833f, 180.547f, 50.5667f, 180.498f, 51.25f, 180.504f
+path.cubicTo(SkBits2Float(0x424fbbbc), SkBits2Float(0x43348273), SkBits2Float(0x42527777), SkBits2Float(0x4334a025), SkBits2Float(0x42553333), SkBits2Float(0x43349bb9));  // 51.9333f, 180.51f, 52.6167f, 180.626f, 53.3f, 180.608f
+path.cubicTo(SkBits2Float(0x4257eeef), SkBits2Float(0x4334974c), SkBits2Float(0x425aaaab), SkBits2Float(0x43346781), SkBits2Float(0x425d6666), SkBits2Float(0x43346666));  // 53.9833f, 180.591f, 54.6667f, 180.404f, 55.35f, 180.4f
+path.cubicTo(SkBits2Float(0x42602222), SkBits2Float(0x4334654a), SkBits2Float(0x4262ddde), SkBits2Float(0x43348de8), SkBits2Float(0x4265999a), SkBits2Float(0x43349512));  // 56.0333f, 180.396f, 56.7167f, 180.554f, 57.4f, 180.582f
+path.cubicTo(SkBits2Float(0x42685555), SkBits2Float(0x43349c3d), SkBits2Float(0x426b1111), SkBits2Float(0x43348fe9), SkBits2Float(0x426dcccd), SkBits2Float(0x43349163));  // 58.0833f, 180.61f, 58.7667f, 180.562f, 59.45f, 180.568f
+path.cubicTo(SkBits2Float(0x42708889), SkBits2Float(0x433492dd), SkBits2Float(0x42734444), SkBits2Float(0x433494f4), SkBits2Float(0x42760000), SkBits2Float(0x43349df0));  // 60.1333f, 180.574f, 60.8167f, 180.582f, 61.5f, 180.617f
+path.cubicTo(SkBits2Float(0x4278bbbc), SkBits2Float(0x4334a6ed), SkBits2Float(0x427b7777), SkBits2Float(0x4334bf7d), SkBits2Float(0x427e3333), SkBits2Float(0x4334c74e));  // 62.1833f, 180.652f, 62.8667f, 180.748f, 63.55f, 180.779f
+path.cubicTo(SkBits2Float(0x42807777), SkBits2Float(0x4334cf1f), SkBits2Float(0x4281d555), SkBits2Float(0x4334d177), SkBits2Float(0x42833333), SkBits2Float(0x4334ccd9));  // 64.2333f, 180.809f, 64.9167f, 180.818f, 65.6f, 180.8f
+path.cubicTo(SkBits2Float(0x42849111), SkBits2Float(0x4334c83a), SkBits2Float(0x4285eeef), SkBits2Float(0x4334bcac), SkBits2Float(0x42874ccd), SkBits2Float(0x4334ab99));  // 66.2833f, 180.782f, 66.9667f, 180.737f, 67.65f, 180.67f
+path.cubicTo(SkBits2Float(0x4288aaab), SkBits2Float(0x43349a86), SkBits2Float(0x428a0889), SkBits2Float(0x433471ee), SkBits2Float(0x428b6666), SkBits2Float(0x43346666));  // 68.3333f, 180.604f, 69.0167f, 180.445f, 69.7f, 180.4f
+path.cubicTo(SkBits2Float(0x428cc444), SkBits2Float(0x43345add), SkBits2Float(0x428e2222), SkBits2Float(0x4334651d), SkBits2Float(0x428f8000), SkBits2Float(0x43346666));  // 70.3833f, 180.355f, 71.0667f, 180.395f, 71.75f, 180.4f
+path.cubicTo(SkBits2Float(0x4290ddde), SkBits2Float(0x433467ae), SkBits2Float(0x42923bbc), SkBits2Float(0x4334688a), SkBits2Float(0x4293999a), SkBits2Float(0x43346e1a));  // 72.4333f, 180.405f, 73.1167f, 180.408f, 73.8f, 180.43f
+path.cubicTo(SkBits2Float(0x4294f777), SkBits2Float(0x433473aa), SkBits2Float(0x42965555), SkBits2Float(0x43348118), SkBits2Float(0x4297b333), SkBits2Float(0x433487c6));  // 74.4833f, 180.452f, 75.1667f, 180.504f, 75.85f, 180.53f
+path.cubicTo(SkBits2Float(0x42991111), SkBits2Float(0x43348e74), SkBits2Float(0x429a6eef), SkBits2Float(0x43349426), SkBits2Float(0x429bcccd), SkBits2Float(0x4334962e));  // 76.5333f, 180.556f, 77.2167f, 180.579f, 77.9f, 180.587f
+path.cubicTo(SkBits2Float(0x429d2aab), SkBits2Float(0x43349836), SkBits2Float(0x429e8889), SkBits2Float(0x43348d19), SkBits2Float(0x429fe666), SkBits2Float(0x433493f7));  // 78.5833f, 180.595f, 79.2667f, 180.551f, 79.95f, 180.578f
+path.cubicTo(SkBits2Float(0x42a14444), SkBits2Float(0x43349ad4), SkBits2Float(0x42a2a222), SkBits2Float(0x4334bae7), SkBits2Float(0x42a40000), SkBits2Float(0x4334bf5e));  // 80.6333f, 180.605f, 81.3167f, 180.73f, 82, 180.748f
+path.cubicTo(SkBits2Float(0x42a55dde), SkBits2Float(0x4334c3d4), SkBits2Float(0x42a6bbbc), SkBits2Float(0x4334b1ea), SkBits2Float(0x42a8199a), SkBits2Float(0x4334aebe));  // 82.6833f, 180.765f, 83.3667f, 180.695f, 84.05f, 180.683f
+path.cubicTo(SkBits2Float(0x42a97777), SkBits2Float(0x4334ab93), SkBits2Float(0x42aad555), SkBits2Float(0x4334a524), SkBits2Float(0x42ac3333), SkBits2Float(0x4334ac59));  // 84.7333f, 180.67f, 85.4167f, 180.645f, 86.1f, 180.673f
+path.cubicTo(SkBits2Float(0x42ad9111), SkBits2Float(0x4334b38d), SkBits2Float(0x42aeeeef), SkBits2Float(0x4334d284), SkBits2Float(0x42b04ccd), SkBits2Float(0x4334d9f7));  // 86.7833f, 180.701f, 87.4667f, 180.822f, 88.15f, 180.851f
+path.cubicTo(SkBits2Float(0x42b1aaab), SkBits2Float(0x4334e16a), SkBits2Float(0x42b30889), SkBits2Float(0x4334dbcf), SkBits2Float(0x42b46666), SkBits2Float(0x4334d909));  // 88.8333f, 180.881f, 89.5167f, 180.859f, 90.2f, 180.848f
+path.cubicTo(SkBits2Float(0x42b5c444), SkBits2Float(0x4334d644), SkBits2Float(0x42b72222), SkBits2Float(0x4334d502), SkBits2Float(0x42b88000), SkBits2Float(0x4334c957));  // 90.8833f, 180.837f, 91.5667f, 180.832f, 92.25f, 180.786f
+path.cubicTo(SkBits2Float(0x42b9ddde), SkBits2Float(0x4334bdad), SkBits2Float(0x42bb3bbc), SkBits2Float(0x4334a179), SkBits2Float(0x42bc999a), SkBits2Float(0x43349309));  // 92.9333f, 180.741f, 93.6167f, 180.631f, 94.3f, 180.574f
+path.cubicTo(SkBits2Float(0x42bdf777), SkBits2Float(0x43348499), SkBits2Float(0x42bf5555), SkBits2Float(0x43347859), SkBits2Float(0x42c0b333), SkBits2Float(0x433472b7));  // 94.9833f, 180.518f, 95.6667f, 180.47f, 96.35f, 180.448f
+path.cubicTo(SkBits2Float(0x42c21111), SkBits2Float(0x43346d16), SkBits2Float(0x42c36eef), SkBits2Float(0x433464d7), SkBits2Float(0x42c4cccd), SkBits2Float(0x4334713f));  // 97.0333f, 180.426f, 97.7167f, 180.394f, 98.4f, 180.442f
+path.cubicTo(SkBits2Float(0x42c62aab), SkBits2Float(0x43347da7), SkBits2Float(0x42c78889), SkBits2Float(0x4334a11c), SkBits2Float(0x42c8e666), SkBits2Float(0x4334bd26));  // 99.0833f, 180.491f, 99.7667f, 180.629f, 100.45f, 180.739f
+path.cubicTo(SkBits2Float(0x42ca4444), SkBits2Float(0x4334d931), SkBits2Float(0x42cba222), SkBits2Float(0x433509ec), SkBits2Float(0x42cd0000), SkBits2Float(0x4335197f));  // 101.133f, 180.848f, 101.817f, 181.039f, 102.5f, 181.1f
+path.cubicTo(SkBits2Float(0x42ce5dde), SkBits2Float(0x43352913), SkBits2Float(0x42cfbbbc), SkBits2Float(0x43351bb7), SkBits2Float(0x42d1199a), SkBits2Float(0x43351a9b));  // 103.183f, 181.16f, 103.867f, 181.108f, 104.55f, 181.104f
+path.cubicTo(SkBits2Float(0x42d27777), SkBits2Float(0x4335197f), SkBits2Float(0x42d3d555), SkBits2Float(0x4335162c), SkBits2Float(0x42d53333), SkBits2Float(0x433512d9));  // 105.233f, 181.1f, 105.917f, 181.087f, 106.6f, 181.074f
+path.cubicTo(SkBits2Float(0x42d69111), SkBits2Float(0x43350f86), SkBits2Float(0x42d7eeef), SkBits2Float(0x433509c4), SkBits2Float(0x42d94ccd), SkBits2Float(0x433506a8));  // 107.283f, 181.061f, 107.967f, 181.038f, 108.65f, 181.026f
+path.cubicTo(SkBits2Float(0x42daaaab), SkBits2Float(0x4335038c), SkBits2Float(0x42dc0889), SkBits2Float(0x433518b0), SkBits2Float(0x42dd6666), SkBits2Float(0x43350030));  // 109.333f, 181.014f, 110.017f, 181.096f, 110.7f, 181.001f
+path.cubicTo(SkBits2Float(0x42dec444), SkBits2Float(0x4334e7af), SkBits2Float(0x42e02222), SkBits2Float(0x43348d47), SkBits2Float(0x42e18000), SkBits2Float(0x433473a5));  // 111.383f, 180.905f, 112.067f, 180.552f, 112.75f, 180.452f
+path.cubicTo(SkBits2Float(0x42e2ddde), SkBits2Float(0x43345a03), SkBits2Float(0x42e43bbc), SkBits2Float(0x4334689b), SkBits2Float(0x42e5999a), SkBits2Float(0x43346666));  // 113.433f, 180.352f, 114.117f, 180.409f, 114.8f, 180.4f
+path.cubicTo(SkBits2Float(0x42e6f777), SkBits2Float(0x43346430), SkBits2Float(0x42e85555), SkBits2Float(0x43346666), SkBits2Float(0x42e9b333), SkBits2Float(0x43346666));  // 115.483f, 180.391f, 116.167f, 180.4f, 116.85f, 180.4f
+path.lineTo(SkBits2Float(0x42e9b333), SkBits2Float(0x43346666));  // 116.85f, 180.4f
+path.lineTo(SkBits2Float(0x42033333), SkBits2Float(0x43346666));  // 32.8f, 180.4f
+path.close();
+path.moveTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.cubicTo(SkBits2Float(0x4305eeef), SkBits2Float(0x43346666), SkBits2Float(0x43069dde), SkBits2Float(0x4334525e), SkBits2Float(0x43074ccd), SkBits2Float(0x43346666));  // 133.933f, 180.4f, 134.617f, 180.322f, 135.3f, 180.4f
+path.cubicTo(SkBits2Float(0x4307fbbc), SkBits2Float(0x43347a6d), SkBits2Float(0x4308aaab), SkBits2Float(0x4334c28c), SkBits2Float(0x4309599a), SkBits2Float(0x4334de94));  // 135.983f, 180.478f, 136.667f, 180.76f, 137.35f, 180.869f
+path.cubicTo(SkBits2Float(0x430a0889), SkBits2Float(0x4334fa9c), SkBits2Float(0x430ab777), SkBits2Float(0x43350641), SkBits2Float(0x430b6666), SkBits2Float(0x43350e98));  // 138.033f, 180.979f, 138.717f, 181.024f, 139.4f, 181.057f
+path.cubicTo(SkBits2Float(0x430c1555), SkBits2Float(0x433516f0), SkBits2Float(0x430cc444), SkBits2Float(0x4335104a), SkBits2Float(0x430d7333), SkBits2Float(0x433510a1));  // 140.083f, 181.09f, 140.767f, 181.064f, 141.45f, 181.065f
+path.cubicTo(SkBits2Float(0x430e2222), SkBits2Float(0x433510f8), SkBits2Float(0x430ed111), SkBits2Float(0x43350f56), SkBits2Float(0x430f8000), SkBits2Float(0x433510a1));  // 142.133f, 181.066f, 142.817f, 181.06f, 143.5f, 181.065f
+path.cubicTo(SkBits2Float(0x43102eef), SkBits2Float(0x433511ec), SkBits2Float(0x4310ddde), SkBits2Float(0x4335159e), SkBits2Float(0x43118ccd), SkBits2Float(0x43351863));  // 144.183f, 181.07f, 144.867f, 181.084f, 145.55f, 181.095f
+path.cubicTo(SkBits2Float(0x43123bbc), SkBits2Float(0x43351b29), SkBits2Float(0x4312eaab), SkBits2Float(0x43351f98), SkBits2Float(0x4313999a), SkBits2Float(0x43352141));  // 146.233f, 181.106f, 146.917f, 181.123f, 147.6f, 181.13f
+path.cubicTo(SkBits2Float(0x43144889), SkBits2Float(0x433522eb), SkBits2Float(0x4314f777), SkBits2Float(0x43352235), SkBits2Float(0x4315a666), SkBits2Float(0x4335225d));  // 148.283f, 181.136f, 148.967f, 181.134f, 149.65f, 181.134f
+path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x43352284), SkBits2Float(0x43170444), SkBits2Float(0x43352207), SkBits2Float(0x4317b333), SkBits2Float(0x4335222f));  // 150.333f, 181.135f, 151.017f, 181.133f, 151.7f, 181.134f
+path.cubicTo(SkBits2Float(0x43186222), SkBits2Float(0x43352256), SkBits2Float(0x43191111), SkBits2Float(0x4335236a), SkBits2Float(0x4319c000), SkBits2Float(0x4335234a));  // 152.383f, 181.134f, 153.067f, 181.138f, 153.75f, 181.138f
+path.cubicTo(SkBits2Float(0x431a6eef), SkBits2Float(0x4335232a), SkBits2Float(0x431b1dde), SkBits2Float(0x433521be), SkBits2Float(0x431bcccd), SkBits2Float(0x4335216f));  // 154.433f, 181.137f, 155.117f, 181.132f, 155.8f, 181.131f
+path.cubicTo(SkBits2Float(0x431c7bbc), SkBits2Float(0x43352120), SkBits2Float(0x431d2aab), SkBits2Float(0x43352148), SkBits2Float(0x431dd99a), SkBits2Float(0x4335216f));  // 156.483f, 181.129f, 157.167f, 181.13f, 157.85f, 181.131f
+path.cubicTo(SkBits2Float(0x431e8889), SkBits2Float(0x43352197), SkBits2Float(0x431f3777), SkBits2Float(0x4335223d), SkBits2Float(0x431fe666), SkBits2Float(0x4335225d));  // 158.533f, 181.131f, 159.217f, 181.134f, 159.9f, 181.134f
+path.cubicTo(SkBits2Float(0x43209555), SkBits2Float(0x4335227d), SkBits2Float(0x43214444), SkBits2Float(0x4335231b), SkBits2Float(0x4321f333), SkBits2Float(0x4335222f));  // 160.583f, 181.135f, 161.267f, 181.137f, 161.95f, 181.134f
+path.cubicTo(SkBits2Float(0x4322a222), SkBits2Float(0x43352142), SkBits2Float(0x43235111), SkBits2Float(0x43351e16), SkBits2Float(0x43240000), SkBits2Float(0x43351cd2));  // 162.633f, 181.13f, 163.317f, 181.118f, 164, 181.113f
+path.cubicTo(SkBits2Float(0x4324aeef), SkBits2Float(0x43351b8f), SkBits2Float(0x43255dde), SkBits2Float(0x43351b29), SkBits2Float(0x43260ccd), SkBits2Float(0x43351a9b));  // 164.683f, 181.108f, 165.367f, 181.106f, 166.05f, 181.104f
+path.cubicTo(SkBits2Float(0x4326bbbc), SkBits2Float(0x43351a0d), SkBits2Float(0x43276aab), SkBits2Float(0x43351893), SkBits2Float(0x4328199a), SkBits2Float(0x4335197f));  // 166.733f, 181.102f, 167.417f, 181.096f, 168.1f, 181.1f
+path.cubicTo(SkBits2Float(0x4328c889), SkBits2Float(0x43351a6c), SkBits2Float(0x43297777), SkBits2Float(0x43351e84), SkBits2Float(0x432a2666), SkBits2Float(0x43352025));  // 168.783f, 181.103f, 169.467f, 181.119f, 170.15f, 181.126f
+path.cubicTo(SkBits2Float(0x432ad555), SkBits2Float(0x433521c7), SkBits2Float(0x432b8444), SkBits2Float(0x433522f4), SkBits2Float(0x432c3333), SkBits2Float(0x4335234a));  // 170.833f, 181.132f, 171.517f, 181.137f, 172.2f, 181.138f
+path.cubicTo(SkBits2Float(0x432ce222), SkBits2Float(0x433523a1), SkBits2Float(0x432d9111), SkBits2Float(0x4335268e), SkBits2Float(0x432e4000), SkBits2Float(0x4335222f));  // 172.883f, 181.139f, 173.567f, 181.151f, 174.25f, 181.134f
+path.cubicTo(SkBits2Float(0x432eeeef), SkBits2Float(0x43351dcf), SkBits2Float(0x432f9dde), SkBits2Float(0x433521fd), SkBits2Float(0x43304ccd), SkBits2Float(0x4335090d));  // 174.933f, 181.116f, 175.617f, 181.133f, 176.3f, 181.035f
+path.cubicTo(SkBits2Float(0x4330fbbc), SkBits2Float(0x4334f01e), SkBits2Float(0x4331aaab), SkBits2Float(0x4334a512), SkBits2Float(0x4332599a), SkBits2Float(0x43348c91));  // 176.983f, 180.938f, 177.667f, 180.645f, 178.35f, 180.549f
+path.cubicTo(SkBits2Float(0x43330889), SkBits2Float(0x43347410), SkBits2Float(0x4333b777), SkBits2Float(0x43347c67), SkBits2Float(0x43346666), SkBits2Float(0x4334760a));  // 179.033f, 180.453f, 179.717f, 180.486f, 180.4f, 180.461f
+path.cubicTo(SkBits2Float(0x43351555), SkBits2Float(0x43346fae), SkBits2Float(0x4335c444), SkBits2Float(0x43346901), SkBits2Float(0x43367333), SkBits2Float(0x43346666));  // 181.083f, 180.436f, 181.767f, 180.41f, 182.45f, 180.4f
+path.cubicTo(SkBits2Float(0x43372222), SkBits2Float(0x433463ca), SkBits2Float(0x4337d111), SkBits2Float(0x43346666), SkBits2Float(0x43388000), SkBits2Float(0x43346666));  // 183.133f, 180.39f, 183.817f, 180.4f, 184.5f, 180.4f
+path.lineTo(SkBits2Float(0x43388000), SkBits2Float(0x43346666));  // 184.5f, 180.4f
+path.lineTo(SkBits2Float(0x43054000), SkBits2Float(0x43346666));  // 133.25f, 180.4f
+path.close();
+path.moveTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.cubicTo(SkBits2Float(0x433b3bbc), SkBits2Float(0x433466f1), SkBits2Float(0x433beaab), SkBits2Float(0x43346920), SkBits2Float(0x433c999a), SkBits2Float(0x433469ab));  // 187.233f, 180.402f, 187.917f, 180.411f, 188.6f, 180.413f
+path.cubicTo(SkBits2Float(0x433d4889), SkBits2Float(0x43346a37), SkBits2Float(0x433df777), SkBits2Float(0x43346a37), SkBits2Float(0x433ea666), SkBits2Float(0x433469ab));  // 189.283f, 180.415f, 189.967f, 180.415f, 190.65f, 180.413f
+path.cubicTo(SkBits2Float(0x433f5555), SkBits2Float(0x43346920), SkBits2Float(0x43400444), SkBits2Float(0x433466f1), SkBits2Float(0x4340b333), SkBits2Float(0x43346666));  // 191.333f, 180.411f, 192.017f, 180.402f, 192.7f, 180.4f
+path.lineTo(SkBits2Float(0x4340b333), SkBits2Float(0x43346666));  // 192.7f, 180.4f
+path.lineTo(SkBits2Float(0x433a8ccd), SkBits2Float(0x43346666));  // 186.55f, 180.4f
+path.close();
+    testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename);
+}
+
+static void issue3651_5(skiatest::Reporter* reporter, const char* filename) {
+SkPath path;
+path.moveTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.cubicTo(SkBits2Float(0x41300000), SkBits2Float(0x4380b333), SkBits2Float(0x4141999a), SkBits2Float(0x4380ba9e), SkBits2Float(0x41533333), SkBits2Float(0x4380b333));  // 11, 257.4f, 12.1f, 257.458f, 13.2f, 257.4f
+path.cubicTo(SkBits2Float(0x4164cccd), SkBits2Float(0x4380abc8), SkBits2Float(0x41766666), SkBits2Float(0x43809a93), SkBits2Float(0x41840000), SkBits2Float(0x438086b0));  // 14.3f, 257.342f, 15.4f, 257.208f, 16.5f, 257.052f
+path.cubicTo(SkBits2Float(0x418ccccd), SkBits2Float(0x438072cc), SkBits2Float(0x4195999a), SkBits2Float(0x4380498b), SkBits2Float(0x419e6666), SkBits2Float(0x43803bdc));  // 17.6f, 256.897f, 18.7f, 256.575f, 19.8f, 256.468f
+path.cubicTo(SkBits2Float(0x41a73333), SkBits2Float(0x43802e2d), SkBits2Float(0x41b00000), SkBits2Float(0x438026f3), SkBits2Float(0x41b8cccd), SkBits2Float(0x43803498));  // 20.9f, 256.361f, 22, 256.304f, 23.1f, 256.411f
+path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x4380423d), SkBits2Float(0x41ca6666), SkBits2Float(0x4380789f), SkBits2Float(0x41d33333), SkBits2Float(0x43808db9));  // 24.2f, 256.517f, 25.3f, 256.942f, 26.4f, 257.107f
+path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380a2d3), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b36f), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333));  // 27.5f, 257.272f, 28.6f, 257.402f, 29.7f, 257.4f
+path.cubicTo(SkBits2Float(0x41f66666), SkBits2Float(0x4380b2f7), SkBits2Float(0x41ff3333), SkBits2Float(0x4380a1a6), SkBits2Float(0x42040000), SkBits2Float(0x43808c51));  // 30.8f, 257.398f, 31.9f, 257.263f, 33, 257.096f
+path.cubicTo(SkBits2Float(0x42086666), SkBits2Float(0x438076fc), SkBits2Float(0x420ccccd), SkBits2Float(0x43803f7a), SkBits2Float(0x42113333), SkBits2Float(0x43803333));  // 34.1f, 256.93f, 35.2f, 256.496f, 36.3f, 256.4f
+path.cubicTo(SkBits2Float(0x4215999a), SkBits2Float(0x438026ed), SkBits2Float(0x421a0000), SkBits2Float(0x43802d56), SkBits2Float(0x421e6666), SkBits2Float(0x438042ab));  // 37.4f, 256.304f, 38.5f, 256.354f, 39.6f, 256.521f
+path.cubicTo(SkBits2Float(0x4222cccd), SkBits2Float(0x43805800), SkBits2Float(0x42273333), SkBits2Float(0x4380a072), SkBits2Float(0x422b999a), SkBits2Float(0x4380b333));  // 40.7f, 256.688f, 41.8f, 257.253f, 42.9f, 257.4f
+path.cubicTo(SkBits2Float(0x42300000), SkBits2Float(0x4380c5f5), SkBits2Float(0x42346666), SkBits2Float(0x4380b333), SkBits2Float(0x4238cccd), SkBits2Float(0x4380b333));  // 44, 257.547f, 45.1f, 257.4f, 46.2f, 257.4f
+path.cubicTo(SkBits2Float(0x423d3333), SkBits2Float(0x4380b333), SkBits2Float(0x4241999a), SkBits2Float(0x4380c1e4), SkBits2Float(0x42460000), SkBits2Float(0x4380b333));  // 47.3f, 257.4f, 48.4f, 257.515f, 49.5f, 257.4f
+path.cubicTo(SkBits2Float(0x424a6666), SkBits2Float(0x4380a482), SkBits2Float(0x424ecccd), SkBits2Float(0x43807063), SkBits2Float(0x42533333), SkBits2Float(0x43805b0e));  // 50.6f, 257.285f, 51.7f, 256.878f, 52.8f, 256.711f
+path.cubicTo(SkBits2Float(0x4257999a), SkBits2Float(0x438045b8), SkBits2Float(0x425c0000), SkBits2Float(0x438039d8), SkBits2Float(0x42606666), SkBits2Float(0x43803333));  // 53.9f, 256.545f, 55, 256.452f, 56.1f, 256.4f
+path.cubicTo(SkBits2Float(0x4264cccd), SkBits2Float(0x43802c8f), SkBits2Float(0x42693333), SkBits2Float(0x43803333), SkBits2Float(0x426d999a), SkBits2Float(0x43803333));  // 57.2f, 256.348f, 58.3f, 256.4f, 59.4f, 256.4f
+path.cubicTo(SkBits2Float(0x42720000), SkBits2Float(0x43803333), SkBits2Float(0x42766666), SkBits2Float(0x43802bdb), SkBits2Float(0x427acccd), SkBits2Float(0x43803333));  // 60.5f, 256.4f, 61.6f, 256.343f, 62.7f, 256.4f
+path.cubicTo(SkBits2Float(0x427f3333), SkBits2Float(0x43803a8c), SkBits2Float(0x4281cccd), SkBits2Float(0x438049f1), SkBits2Float(0x42840000), SkBits2Float(0x43805f47));  // 63.8f, 256.457f, 64.9f, 256.578f, 66, 256.744f
+path.cubicTo(SkBits2Float(0x42863333), SkBits2Float(0x4380749c), SkBits2Float(0x42886666), SkBits2Float(0x4380a536), SkBits2Float(0x428a999a), SkBits2Float(0x4380b333));  // 67.1f, 256.911f, 68.2f, 257.291f, 69.3f, 257.4f
+path.cubicTo(SkBits2Float(0x428ccccd), SkBits2Float(0x4380c130), SkBits2Float(0x428f0000), SkBits2Float(0x4380b333), SkBits2Float(0x42913333), SkBits2Float(0x4380b333));  // 70.4f, 257.509f, 71.5f, 257.4f, 72.6f, 257.4f
+path.cubicTo(SkBits2Float(0x42936666), SkBits2Float(0x4380b333), SkBits2Float(0x4295999a), SkBits2Float(0x4380bb17), SkBits2Float(0x4297cccd), SkBits2Float(0x4380b333));  // 73.7f, 257.4f, 74.8f, 257.462f, 75.9f, 257.4f
+path.cubicTo(SkBits2Float(0x429a0000), SkBits2Float(0x4380ab50), SkBits2Float(0x429c3333), SkBits2Float(0x438083df), SkBits2Float(0x429e6666), SkBits2Float(0x438083df));  // 77, 257.338f, 78.1f, 257.03f, 79.2f, 257.03f
+path.cubicTo(SkBits2Float(0x42a0999a), SkBits2Float(0x438083df), SkBits2Float(0x42a2cccd), SkBits2Float(0x4380ab50), SkBits2Float(0x42a50000), SkBits2Float(0x4380b333));  // 80.3f, 257.03f, 81.4f, 257.338f, 82.5f, 257.4f
+path.cubicTo(SkBits2Float(0x42a73333), SkBits2Float(0x4380bb17), SkBits2Float(0x42a96666), SkBits2Float(0x4380b333), SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333));  // 83.6f, 257.462f, 84.7f, 257.4f, 85.8f, 257.4f
+path.lineTo(SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333));  // 85.8f, 257.4f
+path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.close();
+SkPath pathA = path;
+path.reset();
+path.moveTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.cubicTo(SkBits2Float(0x41300000), SkBits2Float(0x4380b333), SkBits2Float(0x4141999a), SkBits2Float(0x4380abc8), SkBits2Float(0x41533333), SkBits2Float(0x4380b333));  // 11, 257.4f, 12.1f, 257.342f, 13.2f, 257.4f
+path.cubicTo(SkBits2Float(0x4164cccd), SkBits2Float(0x4380ba9e), SkBits2Float(0x41766666), SkBits2Float(0x4380cbd3), SkBits2Float(0x41840000), SkBits2Float(0x4380dfb6));  // 14.3f, 257.458f, 15.4f, 257.592f, 16.5f, 257.748f
+path.cubicTo(SkBits2Float(0x418ccccd), SkBits2Float(0x4380f39a), SkBits2Float(0x4195999a), SkBits2Float(0x43811cdb), SkBits2Float(0x419e6666), SkBits2Float(0x43812a8a));  // 17.6f, 257.903f, 18.7f, 258.225f, 19.8f, 258.332f
+path.cubicTo(SkBits2Float(0x41a73333), SkBits2Float(0x43813839), SkBits2Float(0x41b00000), SkBits2Float(0x43813f73), SkBits2Float(0x41b8cccd), SkBits2Float(0x438131ce));  // 20.9f, 258.439f, 22, 258.496f, 23.1f, 258.389f
+path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x43812429), SkBits2Float(0x41ca6666), SkBits2Float(0x4380edc7), SkBits2Float(0x41d33333), SkBits2Float(0x4380d8ad));  // 24.2f, 258.283f, 25.3f, 257.858f, 26.4f, 257.693f
+path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380c393), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b2f7), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333));  // 27.5f, 257.528f, 28.6f, 257.398f, 29.7f, 257.4f
+path.cubicTo(SkBits2Float(0x41f66666), SkBits2Float(0x4380b36f), SkBits2Float(0x41ff3333), SkBits2Float(0x4380c4c0), SkBits2Float(0x42040000), SkBits2Float(0x4380da15));  // 30.8f, 257.402f, 31.9f, 257.537f, 33, 257.704f
+path.cubicTo(SkBits2Float(0x42086666), SkBits2Float(0x4380ef6a), SkBits2Float(0x420ccccd), SkBits2Float(0x438126ec), SkBits2Float(0x42113333), SkBits2Float(0x43813333));  // 34.1f, 257.87f, 35.2f, 258.304f, 36.3f, 258.4f
+path.cubicTo(SkBits2Float(0x4215999a), SkBits2Float(0x43813f79), SkBits2Float(0x421a0000), SkBits2Float(0x43813910), SkBits2Float(0x421e6666), SkBits2Float(0x438123bb));  // 37.4f, 258.496f, 38.5f, 258.446f, 39.6f, 258.279f
+path.cubicTo(SkBits2Float(0x4222cccd), SkBits2Float(0x43810e66), SkBits2Float(0x42273333), SkBits2Float(0x4380c5f4), SkBits2Float(0x422b999a), SkBits2Float(0x4380b333));  // 40.7f, 258.112f, 41.8f, 257.547f, 42.9f, 257.4f
+path.cubicTo(SkBits2Float(0x42300000), SkBits2Float(0x4380a071), SkBits2Float(0x42346666), SkBits2Float(0x4380b333), SkBits2Float(0x4238cccd), SkBits2Float(0x4380b333));  // 44, 257.253f, 45.1f, 257.4f, 46.2f, 257.4f
+path.cubicTo(SkBits2Float(0x423d3333), SkBits2Float(0x4380b333), SkBits2Float(0x4241999a), SkBits2Float(0x4380a482), SkBits2Float(0x42460000), SkBits2Float(0x4380b333));  // 47.3f, 257.4f, 48.4f, 257.285f, 49.5f, 257.4f
+path.cubicTo(SkBits2Float(0x424a6666), SkBits2Float(0x4380c1e4), SkBits2Float(0x424ecccd), SkBits2Float(0x4380f603), SkBits2Float(0x42533333), SkBits2Float(0x43810b58));  // 50.6f, 257.515f, 51.7f, 257.922f, 52.8f, 258.089f
+path.cubicTo(SkBits2Float(0x4257999a), SkBits2Float(0x438120ae), SkBits2Float(0x425c0000), SkBits2Float(0x43812c8e), SkBits2Float(0x42606666), SkBits2Float(0x43813333));  // 53.9f, 258.255f, 55, 258.348f, 56.1f, 258.4f
+path.cubicTo(SkBits2Float(0x4264cccd), SkBits2Float(0x438139d7), SkBits2Float(0x42693333), SkBits2Float(0x43813333), SkBits2Float(0x426d999a), SkBits2Float(0x43813333));  // 57.2f, 258.452f, 58.3f, 258.4f, 59.4f, 258.4f
+path.cubicTo(SkBits2Float(0x42720000), SkBits2Float(0x43813333), SkBits2Float(0x42766666), SkBits2Float(0x43813a8b), SkBits2Float(0x427acccd), SkBits2Float(0x43813333));  // 60.5f, 258.4f, 61.6f, 258.457f, 62.7f, 258.4f
+path.cubicTo(SkBits2Float(0x427f3333), SkBits2Float(0x43812bda), SkBits2Float(0x4281cccd), SkBits2Float(0x43811c75), SkBits2Float(0x42840000), SkBits2Float(0x4381071f));  // 63.8f, 258.343f, 64.9f, 258.222f, 66, 258.056f
+path.cubicTo(SkBits2Float(0x42863333), SkBits2Float(0x4380f1ca), SkBits2Float(0x42886666), SkBits2Float(0x4380c130), SkBits2Float(0x428a999a), SkBits2Float(0x4380b333));  // 67.1f, 257.889f, 68.2f, 257.509f, 69.3f, 257.4f
+path.cubicTo(SkBits2Float(0x428ccccd), SkBits2Float(0x4380a536), SkBits2Float(0x428f0000), SkBits2Float(0x4380b333), SkBits2Float(0x42913333), SkBits2Float(0x4380b333));  // 70.4f, 257.291f, 71.5f, 257.4f, 72.6f, 257.4f
+path.cubicTo(SkBits2Float(0x42936666), SkBits2Float(0x4380b333), SkBits2Float(0x4295999a), SkBits2Float(0x4380ab4f), SkBits2Float(0x4297cccd), SkBits2Float(0x4380b333));  // 73.7f, 257.4f, 74.8f, 257.338f, 75.9f, 257.4f
+path.cubicTo(SkBits2Float(0x429a0000), SkBits2Float(0x4380bb16), SkBits2Float(0x429c3333), SkBits2Float(0x4380e287), SkBits2Float(0x429e6666), SkBits2Float(0x4380e287));  // 77, 257.462f, 78.1f, 257.77f, 79.2f, 257.77f
+path.cubicTo(SkBits2Float(0x42a0999a), SkBits2Float(0x4380e287), SkBits2Float(0x42a2cccd), SkBits2Float(0x4380bb16), SkBits2Float(0x42a50000), SkBits2Float(0x4380b333));  // 80.3f, 257.77f, 81.4f, 257.462f, 82.5f, 257.4f
+path.cubicTo(SkBits2Float(0x42a73333), SkBits2Float(0x4380ab4f), SkBits2Float(0x42a96666), SkBits2Float(0x4380b333), SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333));  // 83.6f, 257.338f, 84.7f, 257.4f, 85.8f, 257.4f
+path.lineTo(SkBits2Float(0x42ab999a), SkBits2Float(0x4380b333));  // 85.8f, 257.4f
+path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.close();
+    testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename);
+}
+
+static void issue3651_6(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x4380423d), SkBits2Float(0x41ca6666), SkBits2Float(0x4380789f), SkBits2Float(0x41d33333), SkBits2Float(0x43808db9));  // 24.2f, 256.517f, 25.3f, 256.942f, 26.4f, 257.107f
+path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380a2d3), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b36f), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333));  // 27.5f, 257.272f, 28.6f, 257.402f, 29.7f, 257.4f
+path.lineTo(SkBits2Float(0x411e6666), SkBits2Float(0x4380b333));  // 9.9f, 257.4f
+path.close();
+SkPath pathA = path;
+path.reset();
+path.cubicTo(SkBits2Float(0x41c1999a), SkBits2Float(0x43812429), SkBits2Float(0x41ca6666), SkBits2Float(0x4380edc7), SkBits2Float(0x41d33333), SkBits2Float(0x4380d8ad));  // 24.2f, 258.283f, 25.3f, 257.858f, 26.4f, 257.693f
+path.cubicTo(SkBits2Float(0x41dc0000), SkBits2Float(0x4380c393), SkBits2Float(0x41e4cccd), SkBits2Float(0x4380b2f7), SkBits2Float(0x41ed999a), SkBits2Float(0x4380b333));  // 27.5f, 257.528f, 28.6f, 257.398f, 29.7f, 257.4f
+    testPathOp(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename);
+}
+
+static void issue3651_7(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+path.moveTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.cubicTo(SkBits2Float(0x4289ddde), SkBits2Float(0x4295999a), SkBits2Float(0x428b5555), SkBits2Float(0x4295ab91), SkBits2Float(0x428ccccd), SkBits2Float(0x4295999a));  // 68.9333f, 74.8f, 69.6667f, 74.8351f, 70.4f, 74.8f
+path.cubicTo(SkBits2Float(0x428e4444), SkBits2Float(0x429587a2), SkBits2Float(0x428fbbbc), SkBits2Float(0x42954339), SkBits2Float(0x42913333), SkBits2Float(0x42952dcf));  // 71.1333f, 74.7649f, 71.8667f, 74.6313f, 72.6f, 74.5895f
+path.cubicTo(SkBits2Float(0x4292aaab), SkBits2Float(0x42951865), SkBits2Float(0x42942222), SkBits2Float(0x42951dec), SkBits2Float(0x4295999a), SkBits2Float(0x4295191f));  // 73.3333f, 74.5476f, 74.0667f, 74.5584f, 74.8f, 74.5491f
+path.cubicTo(SkBits2Float(0x42971111), SkBits2Float(0x42951452), SkBits2Float(0x42988889), SkBits2Float(0x4294fb9a), SkBits2Float(0x429a0000), SkBits2Float(0x42951101));  // 75.5333f, 74.5397f, 76.2667f, 74.4914f, 77, 74.5332f
+path.cubicTo(SkBits2Float(0x429b7777), SkBits2Float(0x42952667), SkBits2Float(0x429ceeef), SkBits2Float(0x429582c2), SkBits2Float(0x429e6666), SkBits2Float(0x42959986));  // 77.7333f, 74.575f, 78.4667f, 74.7554f, 79.2f, 74.7999f
+path.cubicTo(SkBits2Float(0x429fddde), SkBits2Float(0x4295b04b), SkBits2Float(0x42a15555), SkBits2Float(0x42959996), SkBits2Float(0x42a2cccd), SkBits2Float(0x4295999a));  // 79.9333f, 74.8443f, 80.6667f, 74.8f, 81.4f, 74.8f
+path.cubicTo(SkBits2Float(0x42a44444), SkBits2Float(0x4295999d), SkBits2Float(0x42a5bbbc), SkBits2Float(0x42959e78), SkBits2Float(0x42a73333), SkBits2Float(0x4295999a));  // 82.1333f, 74.8f, 82.8667f, 74.8095f, 83.6f, 74.8f
+path.cubicTo(SkBits2Float(0x42a8aaab), SkBits2Float(0x429594bb), SkBits2Float(0x42aa2222), SkBits2Float(0x42957c62), SkBits2Float(0x42ab999a), SkBits2Float(0x42957c62));  // 84.3333f, 74.7905f, 85.0667f, 74.7429f, 85.8f, 74.7429f
+path.cubicTo(SkBits2Float(0x42ad1111), SkBits2Float(0x42957c62), SkBits2Float(0x42ae8889), SkBits2Float(0x429594bb), SkBits2Float(0x42b00000), SkBits2Float(0x4295999a));  // 86.5333f, 74.7429f, 87.2667f, 74.7905f, 88, 74.8f
+path.cubicTo(SkBits2Float(0x42b17777), SkBits2Float(0x42959e78), SkBits2Float(0x42b2eeef), SkBits2Float(0x4295ac2d), SkBits2Float(0x42b46666), SkBits2Float(0x4295999a));  // 88.7333f, 74.8095f, 89.4667f, 74.8363f, 90.2f, 74.8f
+path.cubicTo(SkBits2Float(0x42b5ddde), SkBits2Float(0x42958706), SkBits2Float(0x42b75555), SkBits2Float(0x42953b44), SkBits2Float(0x42b8cccd), SkBits2Float(0x42952a25));  // 90.9333f, 74.7637f, 91.6667f, 74.6158f, 92.4f, 74.5823f
+path.cubicTo(SkBits2Float(0x42ba4444), SkBits2Float(0x42951906), SkBits2Float(0x42bbbbbc), SkBits2Float(0x42952e4b), SkBits2Float(0x42bd3333), SkBits2Float(0x429532e0));  // 93.1333f, 74.5489f, 93.8667f, 74.5904f, 94.6f, 74.5994f
+path.cubicTo(SkBits2Float(0x42beaaab), SkBits2Float(0x42953775), SkBits2Float(0x42c02222), SkBits2Float(0x42954fda), SkBits2Float(0x42c1999a), SkBits2Float(0x429545a4));  // 95.3333f, 74.6083f, 96.0667f, 74.656f, 96.8f, 74.636f
+path.cubicTo(SkBits2Float(0x42c31111), SkBits2Float(0x42953b6d), SkBits2Float(0x42c48889), SkBits2Float(0x42950c1f), SkBits2Float(0x42c60000), SkBits2Float(0x4294f599));  // 97.5333f, 74.6161f, 98.2667f, 74.5237f, 99, 74.4797f
+path.cubicTo(SkBits2Float(0x42c77777), SkBits2Float(0x4294df14), SkBits2Float(0x42c8eeef), SkBits2Float(0x4294c8f4), SkBits2Float(0x42ca6666), SkBits2Float(0x4294be85));  // 99.7333f, 74.4357f, 100.467f, 74.3925f, 101.2f, 74.3721f
+path.cubicTo(SkBits2Float(0x42cbddde), SkBits2Float(0x4294b417), SkBits2Float(0x42cd5555), SkBits2Float(0x4294ba72), SkBits2Float(0x42cecccd), SkBits2Float(0x4294b703));  // 101.933f, 74.3517f, 102.667f, 74.3642f, 103.4f, 74.3574f
+path.cubicTo(SkBits2Float(0x42d04444), SkBits2Float(0x4294b395), SkBits2Float(0x42d1bbbc), SkBits2Float(0x4294b0ca), SkBits2Float(0x42d33333), SkBits2Float(0x4294a9ec));  // 104.133f, 74.3507f, 104.867f, 74.3453f, 105.6f, 74.3319f
+path.cubicTo(SkBits2Float(0x42d4aaab), SkBits2Float(0x4294a30e), SkBits2Float(0x42d62222), SkBits2Float(0x429493f5), SkBits2Float(0x42d7999a), SkBits2Float(0x42948dd1));  // 106.333f, 74.3185f, 107.067f, 74.289f, 107.8f, 74.277f
+path.cubicTo(SkBits2Float(0x42d91111), SkBits2Float(0x429487ae), SkBits2Float(0x42da8889), SkBits2Float(0x42947f24), SkBits2Float(0x42dc0000), SkBits2Float(0x42948517));  // 108.533f, 74.265f, 109.267f, 74.2483f, 110, 74.2599f
+path.cubicTo(SkBits2Float(0x42dd7777), SkBits2Float(0x42948b0a), SkBits2Float(0x42deeeef), SkBits2Float(0x42949db5), SkBits2Float(0x42e06666), SkBits2Float(0x4294b185));  // 110.733f, 74.2716f, 111.467f, 74.308f, 112.2f, 74.3467f
+path.cubicTo(SkBits2Float(0x42e1ddde), SkBits2Float(0x4294c556), SkBits2Float(0x42e35555), SkBits2Float(0x4294ed7d), SkBits2Float(0x42e4cccd), SkBits2Float(0x4294fbfa));  // 112.933f, 74.3854f, 113.667f, 74.4638f, 114.4f, 74.4921f
+path.cubicTo(SkBits2Float(0x42e64444), SkBits2Float(0x42950a77), SkBits2Float(0x42e7bbbc), SkBits2Float(0x42950d94), SkBits2Float(0x42e93333), SkBits2Float(0x42950875));  // 115.133f, 74.5204f, 115.867f, 74.5265f, 116.6f, 74.5165f
+path.cubicTo(SkBits2Float(0x42eaaaab), SkBits2Float(0x42950356), SkBits2Float(0x42ec2222), SkBits2Float(0x4294e77a), SkBits2Float(0x42ed999a), SkBits2Float(0x4294dd40));  // 117.333f, 74.5065f, 118.067f, 74.4521f, 118.8f, 74.4321f
+path.cubicTo(SkBits2Float(0x42ef1111), SkBits2Float(0x4294d305), SkBits2Float(0x42f08889), SkBits2Float(0x4294c50a), SkBits2Float(0x42f20000), SkBits2Float(0x4294cb18));  // 119.533f, 74.4121f, 120.267f, 74.3848f, 121, 74.3967f
+path.cubicTo(SkBits2Float(0x42f37777), SkBits2Float(0x4294d125), SkBits2Float(0x42f4eeef), SkBits2Float(0x4294ece7), SkBits2Float(0x42f66666), SkBits2Float(0x42950190));  // 121.733f, 74.4085f, 122.467f, 74.4627f, 123.2f, 74.5031f
+path.cubicTo(SkBits2Float(0x42f7ddde), SkBits2Float(0x42951638), SkBits2Float(0x42f95555), SkBits2Float(0x42953e63), SkBits2Float(0x42facccd), SkBits2Float(0x4295470b));  // 123.933f, 74.5434f, 124.667f, 74.6218f, 125.4f, 74.6388f
+path.cubicTo(SkBits2Float(0x42fc4444), SkBits2Float(0x42954fb3), SkBits2Float(0x42fdbbbc), SkBits2Float(0x4295478d), SkBits2Float(0x42ff3333), SkBits2Float(0x4295357f));  // 126.133f, 74.6557f, 126.867f, 74.6397f, 127.6f, 74.6045f
+path.cubicTo(SkBits2Float(0x43005555), SkBits2Float(0x42952371), SkBits2Float(0x43011111), SkBits2Float(0x4294fc35), SkBits2Float(0x4301cccd), SkBits2Float(0x4294dab7));  // 128.333f, 74.5692f, 129.067f, 74.4926f, 129.8f, 74.4272f
+path.cubicTo(SkBits2Float(0x43028889), SkBits2Float(0x4294b93a), SkBits2Float(0x43034444), SkBits2Float(0x429480ff), SkBits2Float(0x43040000), SkBits2Float(0x42946c8f));  // 130.533f, 74.3618f, 131.267f, 74.2519f, 132, 74.212f
+path.cubicTo(SkBits2Float(0x4304bbbc), SkBits2Float(0x4294581e), SkBits2Float(0x43057777), SkBits2Float(0x42945d0d), SkBits2Float(0x43063333), SkBits2Float(0x42946014));  // 132.733f, 74.1721f, 133.467f, 74.1817f, 134.2f, 74.1877f
+path.cubicTo(SkBits2Float(0x4306eeef), SkBits2Float(0x4294631a), SkBits2Float(0x4307aaab), SkBits2Float(0x42947340), SkBits2Float(0x43086666), SkBits2Float(0x42947eb7));  // 134.933f, 74.1936f, 135.667f, 74.2251f, 136.4f, 74.2475f
+path.cubicTo(SkBits2Float(0x43092222), SkBits2Float(0x42948a2d), SkBits2Float(0x4309ddde), SkBits2Float(0x42949abf), SkBits2Float(0x430a999a), SkBits2Float(0x4294a4db));  // 137.133f, 74.2699f, 137.867f, 74.3022f, 138.6f, 74.322f
+path.cubicTo(SkBits2Float(0x430b5555), SkBits2Float(0x4294aef8), SkBits2Float(0x430c1111), SkBits2Float(0x4294bfa8), SkBits2Float(0x430ccccd), SkBits2Float(0x4294bb61));  // 139.333f, 74.3417f, 140.067f, 74.3743f, 140.8f, 74.366f
+path.cubicTo(SkBits2Float(0x430d8889), SkBits2Float(0x4294b71a), SkBits2Float(0x430e4444), SkBits2Float(0x429490b9), SkBits2Float(0x430f0000), SkBits2Float(0x42948b32));  // 141.533f, 74.3576f, 142.267f, 74.2827f, 143, 74.2719f
+path.cubicTo(SkBits2Float(0x430fbbbc), SkBits2Float(0x429485ab), SkBits2Float(0x43107777), SkBits2Float(0x429483e4), SkBits2Float(0x43113333), SkBits2Float(0x42949a35));  // 143.733f, 74.2611f, 144.467f, 74.2576f, 145.2f, 74.3012f
+path.cubicTo(SkBits2Float(0x4311eeef), SkBits2Float(0x4294b086), SkBits2Float(0x4312aaab), SkBits2Float(0x4294f894), SkBits2Float(0x43136666), SkBits2Float(0x42951118));  // 145.933f, 74.3448f, 146.667f, 74.4855f, 147.4f, 74.5334f
+path.cubicTo(SkBits2Float(0x43142222), SkBits2Float(0x4295299b), SkBits2Float(0x4314ddde), SkBits2Float(0x4295342c), SkBits2Float(0x4315999a), SkBits2Float(0x42952d4a));  // 148.133f, 74.5813f, 148.867f, 74.6019f, 149.6f, 74.5885f
+path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x42952668), SkBits2Float(0x43171111), SkBits2Float(0x4294f257), SkBits2Float(0x4317cccd), SkBits2Float(0x4294e7cf));  // 150.333f, 74.575f, 151.067f, 74.4733f, 151.8f, 74.4528f
+path.cubicTo(SkBits2Float(0x43188889), SkBits2Float(0x4294dd46), SkBits2Float(0x43194444), SkBits2Float(0x4294e9b3), SkBits2Float(0x431a0000), SkBits2Float(0x4294ee18));  // 152.533f, 74.4322f, 153.267f, 74.4564f, 154, 74.465f
+path.cubicTo(SkBits2Float(0x431abbbc), SkBits2Float(0x4294f27d), SkBits2Float(0x431b7777), SkBits2Float(0x42950422), SkBits2Float(0x431c3333), SkBits2Float(0x4295022c));  // 154.733f, 74.4736f, 155.467f, 74.5081f, 156.2f, 74.5042f
+path.cubicTo(SkBits2Float(0x431ceeef), SkBits2Float(0x42950035), SkBits2Float(0x431daaab), SkBits2Float(0x4294e324), SkBits2Float(0x431e6666), SkBits2Float(0x4294e250));  // 156.933f, 74.5004f, 157.667f, 74.4436f, 158.4f, 74.442f
+path.cubicTo(SkBits2Float(0x431f2222), SkBits2Float(0x4294e17c), SkBits2Float(0x431fddde), SkBits2Float(0x4294e5a1), SkBits2Float(0x4320999a), SkBits2Float(0x4294fd32));  // 159.133f, 74.4404f, 159.867f, 74.4485f, 160.6f, 74.4945f
+path.cubicTo(SkBits2Float(0x43215555), SkBits2Float(0x429514c4), SkBits2Float(0x43221111), SkBits2Float(0x42955c02), SkBits2Float(0x4322cccd), SkBits2Float(0x42956fb8));  // 161.333f, 74.5406f, 162.067f, 74.6797f, 162.8f, 74.7182f
+path.cubicTo(SkBits2Float(0x43238889), SkBits2Float(0x4295836e), SkBits2Float(0x43244444), SkBits2Float(0x429586fb), SkBits2Float(0x43250000), SkBits2Float(0x42957379));  // 163.533f, 74.7567f, 164.267f, 74.7636f, 165, 74.7255f
+path.cubicTo(SkBits2Float(0x4325bbbc), SkBits2Float(0x42955ff7), SkBits2Float(0x43267777), SkBits2Float(0x42950a52), SkBits2Float(0x43273333), SkBits2Float(0x4294faaa));  // 165.733f, 74.6874f, 166.467f, 74.5202f, 167.2f, 74.4896f
+path.cubicTo(SkBits2Float(0x4327eeef), SkBits2Float(0x4294eb03), SkBits2Float(0x4328aaab), SkBits2Float(0x4294fb0f), SkBits2Float(0x43296666), SkBits2Float(0x4295158c));  // 167.933f, 74.459f, 168.667f, 74.4903f, 169.4f, 74.5421f
+path.cubicTo(SkBits2Float(0x432a2222), SkBits2Float(0x4295300a), SkBits2Float(0x432addde), SkBits2Float(0x42958397), SkBits2Float(0x432b999a), SkBits2Float(0x4295999a));  // 170.133f, 74.5938f, 170.867f, 74.757f, 171.6f, 74.8f
+path.cubicTo(SkBits2Float(0x432c5555), SkBits2Float(0x4295af9c), SkBits2Float(0x432d1111), SkBits2Float(0x4295ace7), SkBits2Float(0x432dcccd), SkBits2Float(0x4295999a));  // 172.333f, 74.843f, 173.067f, 74.8377f, 173.8f, 74.8f
+path.cubicTo(SkBits2Float(0x432e8889), SkBits2Float(0x4295864c), SkBits2Float(0x432f4444), SkBits2Float(0x4295555a), SkBits2Float(0x43300000), SkBits2Float(0x429525c8));  // 174.533f, 74.7623f, 175.267f, 74.6667f, 176, 74.5738f
+path.cubicTo(SkBits2Float(0x4330bbbc), SkBits2Float(0x4294f636), SkBits2Float(0x43317777), SkBits2Float(0x42949a53), SkBits2Float(0x43323333), SkBits2Float(0x42947c2e));  // 176.733f, 74.4809f, 177.467f, 74.3014f, 178.2f, 74.2425f
+path.cubicTo(SkBits2Float(0x4332eeef), SkBits2Float(0x42945e0a), SkBits2Float(0x4333aaab), SkBits2Float(0x42946377), SkBits2Float(0x43346666), SkBits2Float(0x429470ec));  // 178.933f, 74.1837f, 179.667f, 74.1943f, 180.4f, 74.2206f
+path.cubicTo(SkBits2Float(0x43352222), SkBits2Float(0x42947e61), SkBits2Float(0x4335ddde), SkBits2Float(0x4294b4b7), SkBits2Float(0x4336999a), SkBits2Float(0x4294ccec));  // 181.133f, 74.2468f, 181.867f, 74.353f, 182.6f, 74.4002f
+path.cubicTo(SkBits2Float(0x43375555), SkBits2Float(0x4294e522), SkBits2Float(0x43381111), SkBits2Float(0x4294f1c6), SkBits2Float(0x4338cccd), SkBits2Float(0x4295022c));  // 183.333f, 74.4475f, 184.067f, 74.4722f, 184.8f, 74.5042f
+path.cubicTo(SkBits2Float(0x43398889), SkBits2Float(0x42951291), SkBits2Float(0x433a4444), SkBits2Float(0x42952241), SkBits2Float(0x433b0000), SkBits2Float(0x42952f4d));  // 185.533f, 74.5363f, 186.267f, 74.5669f, 187, 74.5924f
+path.cubicTo(SkBits2Float(0x433bbbbc), SkBits2Float(0x42953c5a), SkBits2Float(0x433c7777), SkBits2Float(0x42955a4b), SkBits2Float(0x433d3333), SkBits2Float(0x42955079));  // 187.733f, 74.6179f, 188.467f, 74.6764f, 189.2f, 74.6572f
+path.cubicTo(SkBits2Float(0x433deeef), SkBits2Float(0x429546a7), SkBits2Float(0x433eaaab), SkBits2Float(0x4294fc01), SkBits2Float(0x433f6666), SkBits2Float(0x4294f461));  // 189.933f, 74.638f, 190.667f, 74.4922f, 191.4f, 74.4773f
+path.cubicTo(SkBits2Float(0x43402222), SkBits2Float(0x4294ecc1), SkBits2Float(0x4340ddde), SkBits2Float(0x42950f1f), SkBits2Float(0x4341999a), SkBits2Float(0x429522bb));  // 192.133f, 74.4624f, 192.867f, 74.5295f, 193.6f, 74.5678f
+path.cubicTo(SkBits2Float(0x43425555), SkBits2Float(0x42953657), SkBits2Float(0x43431111), SkBits2Float(0x4295616b), SkBits2Float(0x4343cccd), SkBits2Float(0x42956a0b));  // 194.333f, 74.6061f, 195.067f, 74.6903f, 195.8f, 74.7071f
+path.cubicTo(SkBits2Float(0x43448889), SkBits2Float(0x429572ab), SkBits2Float(0x43454444), SkBits2Float(0x4295608a), SkBits2Float(0x43460000), SkBits2Float(0x4295567c));  // 196.533f, 74.724f, 197.267f, 74.6886f, 198, 74.6689f
+path.cubicTo(SkBits2Float(0x4346bbbc), SkBits2Float(0x42954c6e), SkBits2Float(0x43477777), SkBits2Float(0x42953beb), SkBits2Float(0x43483333), SkBits2Float(0x42952db8));  // 198.733f, 74.6493f, 199.467f, 74.617f, 200.2f, 74.5893f
+path.cubicTo(SkBits2Float(0x4348eeef), SkBits2Float(0x42951f85), SkBits2Float(0x4349aaab), SkBits2Float(0x42950a92), SkBits2Float(0x434a6666), SkBits2Float(0x4295014a));  // 200.933f, 74.5616f, 201.667f, 74.5206f, 202.4f, 74.5025f
+path.cubicTo(SkBits2Float(0x434b2222), SkBits2Float(0x4294f802), SkBits2Float(0x434bddde), SkBits2Float(0x4294f9c8), SkBits2Float(0x434c999a), SkBits2Float(0x4294f607));  // 203.133f, 74.4844f, 203.867f, 74.4879f, 204.6f, 74.4805f
+path.cubicTo(SkBits2Float(0x434d5555), SkBits2Float(0x4294f246), SkBits2Float(0x434e1111), SkBits2Float(0x4294f080), SkBits2Float(0x434ecccd), SkBits2Float(0x4294eac5));  // 205.333f, 74.4732f, 206.067f, 74.4697f, 206.8f, 74.4585f
+path.cubicTo(SkBits2Float(0x434f8889), SkBits2Float(0x4294e509), SkBits2Float(0x43504444), SkBits2Float(0x4294dd6d), SkBits2Float(0x43510000), SkBits2Float(0x4294d3a3));  // 207.533f, 74.4473f, 208.267f, 74.4325f, 209, 74.4134f
+path.cubicTo(SkBits2Float(0x4351bbbc), SkBits2Float(0x4294c9d9), SkBits2Float(0x43527777), SkBits2Float(0x4294b931), SkBits2Float(0x43533333), SkBits2Float(0x4294b007));  // 209.733f, 74.3942f, 210.467f, 74.3617f, 211.2f, 74.3438f
+path.cubicTo(SkBits2Float(0x4353eeef), SkBits2Float(0x4294a6dd), SkBits2Float(0x4354aaab), SkBits2Float(0x4294a02f), SkBits2Float(0x43556666), SkBits2Float(0x42949ca6));  // 211.933f, 74.3259f, 212.667f, 74.3129f, 213.4f, 74.306f
+path.cubicTo(SkBits2Float(0x43562222), SkBits2Float(0x4294991d), SkBits2Float(0x4356ddde), SkBits2Float(0x42949775), SkBits2Float(0x4357999a), SkBits2Float(0x42949ad1));  // 214.133f, 74.299f, 214.867f, 74.2958f, 215.6f, 74.3024f
+path.cubicTo(SkBits2Float(0x43585555), SkBits2Float(0x42949e2e), SkBits2Float(0x43591111), SkBits2Float(0x4294a9b4), SkBits2Float(0x4359cccd), SkBits2Float(0x4294b0d2));  // 216.333f, 74.3089f, 217.067f, 74.3315f, 217.8f, 74.3454f
+path.cubicTo(SkBits2Float(0x435a8889), SkBits2Float(0x4294b7ef), SkBits2Float(0x435b4444), SkBits2Float(0x4294d2c3), SkBits2Float(0x435c0000), SkBits2Float(0x4294c582));  // 218.533f, 74.3592f, 219.267f, 74.4116f, 220, 74.3858f
+path.cubicTo(SkBits2Float(0x435cbbbc), SkBits2Float(0x4294b841), SkBits2Float(0x435d7777), SkBits2Float(0x42947919), SkBits2Float(0x435e3333), SkBits2Float(0x4294614c));  // 220.733f, 74.3599f, 221.467f, 74.2365f, 222.2f, 74.19f
+path.cubicTo(SkBits2Float(0x435eeeef), SkBits2Float(0x4294497f), SkBits2Float(0x435faaab), SkBits2Float(0x42943521), SkBits2Float(0x43606666), SkBits2Float(0x429436b3));  // 222.933f, 74.1435f, 223.667f, 74.1038f, 224.4f, 74.1068f
+path.cubicTo(SkBits2Float(0x43612222), SkBits2Float(0x42943845), SkBits2Float(0x4361ddde), SkBits2Float(0x42945996), SkBits2Float(0x4362999a), SkBits2Float(0x42946aba));  // 225.133f, 74.1099f, 225.867f, 74.175f, 226.6f, 74.2085f
+path.cubicTo(SkBits2Float(0x43635555), SkBits2Float(0x42947bdd), SkBits2Float(0x43641111), SkBits2Float(0x4294973f), SkBits2Float(0x4364cccd), SkBits2Float(0x42949d88));  // 227.333f, 74.2419f, 228.067f, 74.2954f, 228.8f, 74.3077f
+path.cubicTo(SkBits2Float(0x43658889), SkBits2Float(0x4294a3d2), SkBits2Float(0x43664444), SkBits2Float(0x429495a6), SkBits2Float(0x43670000), SkBits2Float(0x42949071));  // 229.533f, 74.32f, 230.267f, 74.2923f, 231, 74.2821f
+path.cubicTo(SkBits2Float(0x4367bbbc), SkBits2Float(0x42948b3c), SkBits2Float(0x43687777), SkBits2Float(0x429480b0), SkBits2Float(0x43693333), SkBits2Float(0x42947e49));  // 231.733f, 74.2719f, 232.467f, 74.2513f, 233.2f, 74.2467f
+path.cubicTo(SkBits2Float(0x4369eeef), SkBits2Float(0x42947be3), SkBits2Float(0x436aaaab), SkBits2Float(0x42947a88), SkBits2Float(0x436b6666), SkBits2Float(0x4294820a));  // 233.933f, 74.242f, 234.667f, 74.2393f, 235.4f, 74.254f
+path.cubicTo(SkBits2Float(0x436c2222), SkBits2Float(0x4294898c), SkBits2Float(0x436cddde), SkBits2Float(0x42949eee), SkBits2Float(0x436d999a), SkBits2Float(0x4294ab53));  // 236.133f, 74.2686f, 236.867f, 74.3104f, 237.6f, 74.3346f
+path.cubicTo(SkBits2Float(0x436e5555), SkBits2Float(0x4294b7b8), SkBits2Float(0x436f1111), SkBits2Float(0x4294c2bb), SkBits2Float(0x436fcccd), SkBits2Float(0x4294cc67));  // 238.333f, 74.3588f, 239.067f, 74.3803f, 239.8f, 74.3992f
+path.cubicTo(SkBits2Float(0x43708889), SkBits2Float(0x4294d614), SkBits2Float(0x43714444), SkBits2Float(0x4294d5a0), SkBits2Float(0x43720000), SkBits2Float(0x4294e55e));  // 240.533f, 74.4181f, 241.267f, 74.4172f, 242, 74.448f
+path.cubicTo(SkBits2Float(0x4372bbbc), SkBits2Float(0x4294f51b), SkBits2Float(0x43737777), SkBits2Float(0x429516d0), SkBits2Float(0x43743333), SkBits2Float(0x42952ad9));  // 242.733f, 74.4787f, 243.467f, 74.5446f, 244.2f, 74.5837f
+path.cubicTo(SkBits2Float(0x4374eeef), SkBits2Float(0x42953ee1), SkBits2Float(0x4375aaab), SkBits2Float(0x42954f2d), SkBits2Float(0x43766666), SkBits2Float(0x42955d90));  // 244.933f, 74.6228f, 245.667f, 74.6546f, 246.4f, 74.6827f
+path.cubicTo(SkBits2Float(0x43772222), SkBits2Float(0x42956bf3), SkBits2Float(0x4377ddde), SkBits2Float(0x4295772b), SkBits2Float(0x4378999a), SkBits2Float(0x4295812d));  // 247.133f, 74.7108f, 247.867f, 74.7327f, 248.6f, 74.7523f
+path.cubicTo(SkBits2Float(0x43795555), SkBits2Float(0x42958b2e), SkBits2Float(0x437a1111), SkBits2Float(0x42959587), SkBits2Float(0x437acccd), SkBits2Float(0x4295999a));  // 249.333f, 74.7718f, 250.067f, 74.792f, 250.8f, 74.8f
+path.cubicTo(SkBits2Float(0x437b8889), SkBits2Float(0x42959dac), SkBits2Float(0x437c4444), SkBits2Float(0x4295999a), SkBits2Float(0x437d0000), SkBits2Float(0x4295999a));  // 251.533f, 74.808f, 252.267f, 74.8f, 253, 74.8f
+path.lineTo(SkBits2Float(0x437d0000), SkBits2Float(0x4295999a));  // 253, 74.8f
+path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.close();
+SkPath pathA = path;
+path.reset();
+
+path.moveTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.cubicTo(SkBits2Float(0x4289ddde), SkBits2Float(0x4295999a), SkBits2Float(0x428b5555), SkBits2Float(0x429587a3), SkBits2Float(0x428ccccd), SkBits2Float(0x4295999a));  // 68.9333f, 74.8f, 69.6667f, 74.7649f, 70.4f, 74.8f
+path.cubicTo(SkBits2Float(0x428e4444), SkBits2Float(0x4295ab92), SkBits2Float(0x428fbbbc), SkBits2Float(0x4295effb), SkBits2Float(0x42913333), SkBits2Float(0x42960565));  // 71.1333f, 74.8351f, 71.8667f, 74.9687f, 72.6f, 75.0105f
+path.cubicTo(SkBits2Float(0x4292aaab), SkBits2Float(0x42961acf), SkBits2Float(0x42942222), SkBits2Float(0x42961548), SkBits2Float(0x4295999a), SkBits2Float(0x42961a15));  // 73.3333f, 75.0524f, 74.0667f, 75.0416f, 74.8f, 75.0509f
+path.cubicTo(SkBits2Float(0x42971111), SkBits2Float(0x42961ee2), SkBits2Float(0x42988889), SkBits2Float(0x4296379a), SkBits2Float(0x429a0000), SkBits2Float(0x42962233));  // 75.5333f, 75.0603f, 76.2667f, 75.1086f, 77, 75.0668f
+path.cubicTo(SkBits2Float(0x429b7777), SkBits2Float(0x42960ccd), SkBits2Float(0x429ceeef), SkBits2Float(0x4295b072), SkBits2Float(0x429e6666), SkBits2Float(0x429599ae));  // 77.7333f, 75.025f, 78.4667f, 74.8446f, 79.2f, 74.8002f
+path.cubicTo(SkBits2Float(0x429fddde), SkBits2Float(0x429582e9), SkBits2Float(0x42a15555), SkBits2Float(0x4295999e), SkBits2Float(0x42a2cccd), SkBits2Float(0x4295999a));  // 79.9333f, 74.7557f, 80.6667f, 74.8f, 81.4f, 74.8f
+path.cubicTo(SkBits2Float(0x42a44444), SkBits2Float(0x42959997), SkBits2Float(0x42a5bbbc), SkBits2Float(0x429594bc), SkBits2Float(0x42a73333), SkBits2Float(0x4295999a));  // 82.1333f, 74.8f, 82.8667f, 74.7905f, 83.6f, 74.8f
+path.cubicTo(SkBits2Float(0x42a8aaab), SkBits2Float(0x42959e79), SkBits2Float(0x42aa2222), SkBits2Float(0x4295b6d2), SkBits2Float(0x42ab999a), SkBits2Float(0x4295b6d2));  // 84.3333f, 74.8095f, 85.0667f, 74.8571f, 85.8f, 74.8571f
+path.cubicTo(SkBits2Float(0x42ad1111), SkBits2Float(0x4295b6d2), SkBits2Float(0x42ae8889), SkBits2Float(0x42959e79), SkBits2Float(0x42b00000), SkBits2Float(0x4295999a));  // 86.5333f, 74.8571f, 87.2667f, 74.8095f, 88, 74.8f
+path.cubicTo(SkBits2Float(0x42b17777), SkBits2Float(0x429594bc), SkBits2Float(0x42b2eeef), SkBits2Float(0x42958707), SkBits2Float(0x42b46666), SkBits2Float(0x4295999a));  // 88.7333f, 74.7905f, 89.4667f, 74.7637f, 90.2f, 74.8f
+path.cubicTo(SkBits2Float(0x42b5ddde), SkBits2Float(0x4295ac2e), SkBits2Float(0x42b75555), SkBits2Float(0x4295f7f0), SkBits2Float(0x42b8cccd), SkBits2Float(0x4296090f));  // 90.9333f, 74.8363f, 91.6667f, 74.9843f, 92.4f, 75.0177f
+path.cubicTo(SkBits2Float(0x42ba4444), SkBits2Float(0x42961a2e), SkBits2Float(0x42bbbbbc), SkBits2Float(0x429604e9), SkBits2Float(0x42bd3333), SkBits2Float(0x42960054));  // 93.1333f, 75.0511f, 93.8667f, 75.0096f, 94.6f, 75.0006f
+path.cubicTo(SkBits2Float(0x42beaaab), SkBits2Float(0x4295fbbf), SkBits2Float(0x42c02222), SkBits2Float(0x4295e35a), SkBits2Float(0x42c1999a), SkBits2Float(0x4295ed90));  // 95.3333f, 74.9917f, 96.0667f, 74.944f, 96.8f, 74.964f
+path.cubicTo(SkBits2Float(0x42c31111), SkBits2Float(0x4295f7c7), SkBits2Float(0x42c48889), SkBits2Float(0x42962715), SkBits2Float(0x42c60000), SkBits2Float(0x42963d9b));  // 97.5333f, 74.9839f, 98.2667f, 75.0763f, 99, 75.1203f
+path.cubicTo(SkBits2Float(0x42c77777), SkBits2Float(0x42965420), SkBits2Float(0x42c8eeef), SkBits2Float(0x42966a40), SkBits2Float(0x42ca6666), SkBits2Float(0x429674af));  // 99.7333f, 75.1643f, 100.467f, 75.2075f, 101.2f, 75.2279f
+path.cubicTo(SkBits2Float(0x42cbddde), SkBits2Float(0x42967f1d), SkBits2Float(0x42cd5555), SkBits2Float(0x429678c2), SkBits2Float(0x42cecccd), SkBits2Float(0x42967c31));  // 101.933f, 75.2483f, 102.667f, 75.2359f, 103.4f, 75.2426f
+path.cubicTo(SkBits2Float(0x42d04444), SkBits2Float(0x42967f9f), SkBits2Float(0x42d1bbbc), SkBits2Float(0x4296826a), SkBits2Float(0x42d33333), SkBits2Float(0x42968948));  // 104.133f, 75.2493f, 104.867f, 75.2547f, 105.6f, 75.2681f
+path.cubicTo(SkBits2Float(0x42d4aaab), SkBits2Float(0x42969026), SkBits2Float(0x42d62222), SkBits2Float(0x42969f3f), SkBits2Float(0x42d7999a), SkBits2Float(0x4296a563));  // 106.333f, 75.2815f, 107.067f, 75.311f, 107.8f, 75.323f
+path.cubicTo(SkBits2Float(0x42d91111), SkBits2Float(0x4296ab86), SkBits2Float(0x42da8889), SkBits2Float(0x4296b410), SkBits2Float(0x42dc0000), SkBits2Float(0x4296ae1d));  // 108.533f, 75.335f, 109.267f, 75.3517f, 110, 75.3401f
+path.cubicTo(SkBits2Float(0x42dd7777), SkBits2Float(0x4296a82a), SkBits2Float(0x42deeeef), SkBits2Float(0x4296957f), SkBits2Float(0x42e06666), SkBits2Float(0x429681af));  // 110.733f, 75.3284f, 111.467f, 75.292f, 112.2f, 75.2533f
+path.cubicTo(SkBits2Float(0x42e1ddde), SkBits2Float(0x42966dde), SkBits2Float(0x42e35555), SkBits2Float(0x429645b7), SkBits2Float(0x42e4cccd), SkBits2Float(0x4296373a));  // 112.933f, 75.2146f, 113.667f, 75.1362f, 114.4f, 75.1079f
+path.cubicTo(SkBits2Float(0x42e64444), SkBits2Float(0x429628bd), SkBits2Float(0x42e7bbbc), SkBits2Float(0x429625a0), SkBits2Float(0x42e93333), SkBits2Float(0x42962abf));  // 115.133f, 75.0796f, 115.867f, 75.0735f, 116.6f, 75.0835f
+path.cubicTo(SkBits2Float(0x42eaaaab), SkBits2Float(0x42962fde), SkBits2Float(0x42ec2222), SkBits2Float(0x42964bba), SkBits2Float(0x42ed999a), SkBits2Float(0x429655f4));  // 117.333f, 75.0935f, 118.067f, 75.1479f, 118.8f, 75.1679f
+path.cubicTo(SkBits2Float(0x42ef1111), SkBits2Float(0x4296602f), SkBits2Float(0x42f08889), SkBits2Float(0x42966e2a), SkBits2Float(0x42f20000), SkBits2Float(0x4296681c));  // 119.533f, 75.1879f, 120.267f, 75.2152f, 121, 75.2033f
+path.cubicTo(SkBits2Float(0x42f37777), SkBits2Float(0x4296620f), SkBits2Float(0x42f4eeef), SkBits2Float(0x4296464d), SkBits2Float(0x42f66666), SkBits2Float(0x429631a4));  // 121.733f, 75.1915f, 122.467f, 75.1373f, 123.2f, 75.097f
+path.cubicTo(SkBits2Float(0x42f7ddde), SkBits2Float(0x42961cfc), SkBits2Float(0x42f95555), SkBits2Float(0x4295f4d1), SkBits2Float(0x42facccd), SkBits2Float(0x4295ec29));  // 123.933f, 75.0566f, 124.667f, 74.9782f, 125.4f, 74.9613f
+path.cubicTo(SkBits2Float(0x42fc4444), SkBits2Float(0x4295e381), SkBits2Float(0x42fdbbbc), SkBits2Float(0x4295eba7), SkBits2Float(0x42ff3333), SkBits2Float(0x4295fdb5));  // 126.133f, 74.9443f, 126.867f, 74.9603f, 127.6f, 74.9955f
+path.cubicTo(SkBits2Float(0x43005555), SkBits2Float(0x42960fc3), SkBits2Float(0x43011111), SkBits2Float(0x429636ff), SkBits2Float(0x4301cccd), SkBits2Float(0x4296587d));  // 128.333f, 75.0308f, 129.067f, 75.1074f, 129.8f, 75.1728f
+path.cubicTo(SkBits2Float(0x43028889), SkBits2Float(0x429679fa), SkBits2Float(0x43034444), SkBits2Float(0x4296b235), SkBits2Float(0x43040000), SkBits2Float(0x4296c6a5));  // 130.533f, 75.2382f, 131.267f, 75.3481f, 132, 75.388f
+path.cubicTo(SkBits2Float(0x4304bbbc), SkBits2Float(0x4296db16), SkBits2Float(0x43057777), SkBits2Float(0x4296d627), SkBits2Float(0x43063333), SkBits2Float(0x4296d320));  // 132.733f, 75.4279f, 133.467f, 75.4183f, 134.2f, 75.4124f
+path.cubicTo(SkBits2Float(0x4306eeef), SkBits2Float(0x4296d01a), SkBits2Float(0x4307aaab), SkBits2Float(0x4296bff4), SkBits2Float(0x43086666), SkBits2Float(0x4296b47d));  // 134.933f, 75.4064f, 135.667f, 75.3749f, 136.4f, 75.3525f
+path.cubicTo(SkBits2Float(0x43092222), SkBits2Float(0x4296a907), SkBits2Float(0x4309ddde), SkBits2Float(0x42969875), SkBits2Float(0x430a999a), SkBits2Float(0x42968e59));  // 137.133f, 75.3301f, 137.867f, 75.2978f, 138.6f, 75.278f
+path.cubicTo(SkBits2Float(0x430b5555), SkBits2Float(0x4296843c), SkBits2Float(0x430c1111), SkBits2Float(0x4296738c), SkBits2Float(0x430ccccd), SkBits2Float(0x429677d3));  // 139.333f, 75.2583f, 140.067f, 75.2257f, 140.8f, 75.234f
+path.cubicTo(SkBits2Float(0x430d8889), SkBits2Float(0x42967c1a), SkBits2Float(0x430e4444), SkBits2Float(0x4296a27b), SkBits2Float(0x430f0000), SkBits2Float(0x4296a802));  // 141.533f, 75.2424f, 142.267f, 75.3173f, 143, 75.3281f
+path.cubicTo(SkBits2Float(0x430fbbbc), SkBits2Float(0x4296ad89), SkBits2Float(0x43107777), SkBits2Float(0x4296af50), SkBits2Float(0x43113333), SkBits2Float(0x429698ff));  // 143.733f, 75.3389f, 144.467f, 75.3424f, 145.2f, 75.2988f
+path.cubicTo(SkBits2Float(0x4311eeef), SkBits2Float(0x429682ae), SkBits2Float(0x4312aaab), SkBits2Float(0x42963aa0), SkBits2Float(0x43136666), SkBits2Float(0x4296221c));  // 145.933f, 75.2552f, 146.667f, 75.1145f, 147.4f, 75.0666f
+path.cubicTo(SkBits2Float(0x43142222), SkBits2Float(0x42960999), SkBits2Float(0x4314ddde), SkBits2Float(0x4295ff08), SkBits2Float(0x4315999a), SkBits2Float(0x429605ea));  // 148.133f, 75.0187f, 148.867f, 74.9981f, 149.6f, 75.0116f
+path.cubicTo(SkBits2Float(0x43165555), SkBits2Float(0x42960ccc), SkBits2Float(0x43171111), SkBits2Float(0x429640dd), SkBits2Float(0x4317cccd), SkBits2Float(0x42964b65));  // 150.333f, 75.025f, 151.067f, 75.1267f, 151.8f, 75.1473f
+path.cubicTo(SkBits2Float(0x43188889), SkBits2Float(0x429655ee), SkBits2Float(0x43194444), SkBits2Float(0x42964981), SkBits2Float(0x431a0000), SkBits2Float(0x4296451c));  // 152.533f, 75.1678f, 153.267f, 75.1436f, 154, 75.135f
+path.cubicTo(SkBits2Float(0x431abbbc), SkBits2Float(0x429640b7), SkBits2Float(0x431b7777), SkBits2Float(0x42962f12), SkBits2Float(0x431c3333), SkBits2Float(0x42963108));  // 154.733f, 75.1264f, 155.467f, 75.0919f, 156.2f, 75.0958f
+path.cubicTo(SkBits2Float(0x431ceeef), SkBits2Float(0x429632ff), SkBits2Float(0x431daaab), SkBits2Float(0x42965010), SkBits2Float(0x431e6666), SkBits2Float(0x429650e4));  // 156.933f, 75.0996f, 157.667f, 75.1564f, 158.4f, 75.158f
+path.cubicTo(SkBits2Float(0x431f2222), SkBits2Float(0x429651b8), SkBits2Float(0x431fddde), SkBits2Float(0x42964d93), SkBits2Float(0x4320999a), SkBits2Float(0x42963602));  // 159.133f, 75.1596f, 159.867f, 75.1515f, 160.6f, 75.1055f
+path.cubicTo(SkBits2Float(0x43215555), SkBits2Float(0x42961e70), SkBits2Float(0x43221111), SkBits2Float(0x4295d732), SkBits2Float(0x4322cccd), SkBits2Float(0x4295c37c));  // 161.333f, 75.0594f, 162.067f, 74.9203f, 162.8f, 74.8818f
+path.cubicTo(SkBits2Float(0x43238889), SkBits2Float(0x4295afc6), SkBits2Float(0x43244444), SkBits2Float(0x4295ac39), SkBits2Float(0x43250000), SkBits2Float(0x4295bfbb));  // 163.533f, 74.8433f, 164.267f, 74.8364f, 165, 74.8745f
+path.cubicTo(SkBits2Float(0x4325bbbc), SkBits2Float(0x4295d33d), SkBits2Float(0x43267777), SkBits2Float(0x429628e2), SkBits2Float(0x43273333), SkBits2Float(0x4296388a));  // 165.733f, 74.9126f, 166.467f, 75.0798f, 167.2f, 75.1104f
+path.cubicTo(SkBits2Float(0x4327eeef), SkBits2Float(0x42964831), SkBits2Float(0x4328aaab), SkBits2Float(0x42963825), SkBits2Float(0x43296666), SkBits2Float(0x42961da8));  // 167.933f, 75.141f, 168.667f, 75.1097f, 169.4f, 75.0579f
+path.cubicTo(SkBits2Float(0x432a2222), SkBits2Float(0x4296032a), SkBits2Float(0x432addde), SkBits2Float(0x4295af9d), SkBits2Float(0x432b999a), SkBits2Float(0x4295999a));  // 170.133f, 75.0062f, 170.867f, 74.843f, 171.6f, 74.8f
+path.cubicTo(SkBits2Float(0x432c5555), SkBits2Float(0x42958398), SkBits2Float(0x432d1111), SkBits2Float(0x4295864d), SkBits2Float(0x432dcccd), SkBits2Float(0x4295999a));  // 172.333f, 74.757f, 173.067f, 74.7623f, 173.8f, 74.8f
+path.cubicTo(SkBits2Float(0x432e8889), SkBits2Float(0x4295ace8), SkBits2Float(0x432f4444), SkBits2Float(0x4295ddda), SkBits2Float(0x43300000), SkBits2Float(0x42960d6c));  // 174.533f, 74.8377f, 175.267f, 74.9333f, 176, 75.0262f
+path.cubicTo(SkBits2Float(0x4330bbbc), SkBits2Float(0x42963cfe), SkBits2Float(0x43317777), SkBits2Float(0x429698e1), SkBits2Float(0x43323333), SkBits2Float(0x4296b706));  // 176.733f, 75.1191f, 177.467f, 75.2986f, 178.2f, 75.3575f
+path.cubicTo(SkBits2Float(0x4332eeef), SkBits2Float(0x4296d52a), SkBits2Float(0x4333aaab), SkBits2Float(0x4296cfbd), SkBits2Float(0x43346666), SkBits2Float(0x4296c248));  // 178.933f, 75.4163f, 179.667f, 75.4057f, 180.4f, 75.3795f
+path.cubicTo(SkBits2Float(0x43352222), SkBits2Float(0x4296b4d3), SkBits2Float(0x4335ddde), SkBits2Float(0x42967e7d), SkBits2Float(0x4336999a), SkBits2Float(0x42966648));  // 181.133f, 75.3532f, 181.867f, 75.247f, 182.6f, 75.1998f
+path.cubicTo(SkBits2Float(0x43375555), SkBits2Float(0x42964e12), SkBits2Float(0x43381111), SkBits2Float(0x4296416e), SkBits2Float(0x4338cccd), SkBits2Float(0x42963108));  // 183.333f, 75.1525f, 184.067f, 75.1278f, 184.8f, 75.0958f
+path.cubicTo(SkBits2Float(0x43398889), SkBits2Float(0x429620a3), SkBits2Float(0x433a4444), SkBits2Float(0x429610f3), SkBits2Float(0x433b0000), SkBits2Float(0x429603e7));  // 185.533f, 75.0637f, 186.267f, 75.0331f, 187, 75.0076f
+path.cubicTo(SkBits2Float(0x433bbbbc), SkBits2Float(0x4295f6da), SkBits2Float(0x433c7777), SkBits2Float(0x4295d8e9), SkBits2Float(0x433d3333), SkBits2Float(0x4295e2bb));  // 187.733f, 74.9821f, 188.467f, 74.9237f, 189.2f, 74.9428f
+path.cubicTo(SkBits2Float(0x433deeef), SkBits2Float(0x4295ec8d), SkBits2Float(0x433eaaab), SkBits2Float(0x42963733), SkBits2Float(0x433f6666), SkBits2Float(0x42963ed3));  // 189.933f, 74.962f, 190.667f, 75.1078f, 191.4f, 75.1227f
+path.cubicTo(SkBits2Float(0x43402222), SkBits2Float(0x42964673), SkBits2Float(0x4340ddde), SkBits2Float(0x42962415), SkBits2Float(0x4341999a), SkBits2Float(0x42961079));  // 192.133f, 75.1376f, 192.867f, 75.0705f, 193.6f, 75.0322f
+path.cubicTo(SkBits2Float(0x43425555), SkBits2Float(0x4295fcdd), SkBits2Float(0x43431111), SkBits2Float(0x4295d1c9), SkBits2Float(0x4343cccd), SkBits2Float(0x4295c929));  // 194.333f, 74.9939f, 195.067f, 74.9097f, 195.8f, 74.8929f
+path.cubicTo(SkBits2Float(0x43448889), SkBits2Float(0x4295c089), SkBits2Float(0x43454444), SkBits2Float(0x4295d2aa), SkBits2Float(0x43460000), SkBits2Float(0x4295dcb8));  // 196.533f, 74.876f, 197.267f, 74.9115f, 198, 74.9311f
+path.cubicTo(SkBits2Float(0x4346bbbc), SkBits2Float(0x4295e6c6), SkBits2Float(0x43477777), SkBits2Float(0x4295f749), SkBits2Float(0x43483333), SkBits2Float(0x4296057c));  // 198.733f, 74.9507f, 199.467f, 74.983f, 200.2f, 75.0107f
+path.cubicTo(SkBits2Float(0x4348eeef), SkBits2Float(0x429613af), SkBits2Float(0x4349aaab), SkBits2Float(0x429628a2), SkBits2Float(0x434a6666), SkBits2Float(0x429631ea));  // 200.933f, 75.0384f, 201.667f, 75.0794f, 202.4f, 75.0975f
+path.cubicTo(SkBits2Float(0x434b2222), SkBits2Float(0x42963b32), SkBits2Float(0x434bddde), SkBits2Float(0x4296396c), SkBits2Float(0x434c999a), SkBits2Float(0x42963d2d));  // 203.133f, 75.1156f, 203.867f, 75.1122f, 204.6f, 75.1195f
+path.cubicTo(SkBits2Float(0x434d5555), SkBits2Float(0x429640ee), SkBits2Float(0x434e1111), SkBits2Float(0x429642b4), SkBits2Float(0x434ecccd), SkBits2Float(0x4296486f));  // 205.333f, 75.1268f, 206.067f, 75.1303f, 206.8f, 75.1415f
+path.cubicTo(SkBits2Float(0x434f8889), SkBits2Float(0x42964e2b), SkBits2Float(0x43504444), SkBits2Float(0x429655c7), SkBits2Float(0x43510000), SkBits2Float(0x42965f91));  // 207.533f, 75.1527f, 208.267f, 75.1675f, 209, 75.1867f
+path.cubicTo(SkBits2Float(0x4351bbbc), SkBits2Float(0x4296695b), SkBits2Float(0x43527777), SkBits2Float(0x42967a03), SkBits2Float(0x43533333), SkBits2Float(0x4296832d));  // 209.733f, 75.2058f, 210.467f, 75.2383f, 211.2f, 75.2562f
+path.cubicTo(SkBits2Float(0x4353eeef), SkBits2Float(0x42968c57), SkBits2Float(0x4354aaab), SkBits2Float(0x42969305), SkBits2Float(0x43556666), SkBits2Float(0x4296968e));  // 211.933f, 75.2741f, 212.667f, 75.2871f, 213.4f, 75.2941f
+path.cubicTo(SkBits2Float(0x43562222), SkBits2Float(0x42969a17), SkBits2Float(0x4356ddde), SkBits2Float(0x42969bbf), SkBits2Float(0x4357999a), SkBits2Float(0x42969863));  // 214.133f, 75.301f, 214.867f, 75.3042f, 215.6f, 75.2976f
+path.cubicTo(SkBits2Float(0x43585555), SkBits2Float(0x42969506), SkBits2Float(0x43591111), SkBits2Float(0x42968980), SkBits2Float(0x4359cccd), SkBits2Float(0x42968262));  // 216.333f, 75.2911f, 217.067f, 75.2686f, 217.8f, 75.2547f
+path.cubicTo(SkBits2Float(0x435a8889), SkBits2Float(0x42967b45), SkBits2Float(0x435b4444), SkBits2Float(0x42966071), SkBits2Float(0x435c0000), SkBits2Float(0x42966db2));  // 218.533f, 75.2408f, 219.267f, 75.1884f, 220, 75.2142f
+path.cubicTo(SkBits2Float(0x435cbbbc), SkBits2Float(0x42967af3), SkBits2Float(0x435d7777), SkBits2Float(0x4296ba1b), SkBits2Float(0x435e3333), SkBits2Float(0x4296d1e8));  // 220.733f, 75.2401f, 221.467f, 75.3635f, 222.2f, 75.41f
+path.cubicTo(SkBits2Float(0x435eeeef), SkBits2Float(0x4296e9b5), SkBits2Float(0x435faaab), SkBits2Float(0x4296fe13), SkBits2Float(0x43606666), SkBits2Float(0x4296fc81));  // 222.933f, 75.4565f, 223.667f, 75.4962f, 224.4f, 75.4932f
+path.cubicTo(SkBits2Float(0x43612222), SkBits2Float(0x4296faef), SkBits2Float(0x4361ddde), SkBits2Float(0x4296d99e), SkBits2Float(0x4362999a), SkBits2Float(0x4296c87a));  // 225.133f, 75.4901f, 225.867f, 75.425f, 226.6f, 75.3916f
+path.cubicTo(SkBits2Float(0x43635555), SkBits2Float(0x4296b757), SkBits2Float(0x43641111), SkBits2Float(0x42969bf5), SkBits2Float(0x4364cccd), SkBits2Float(0x429695ac));  // 227.333f, 75.3581f, 228.067f, 75.3046f, 228.8f, 75.2923f
+path.cubicTo(SkBits2Float(0x43658889), SkBits2Float(0x42968f62), SkBits2Float(0x43664444), SkBits2Float(0x42969d8e), SkBits2Float(0x43670000), SkBits2Float(0x4296a2c3));  // 229.533f, 75.28f, 230.267f, 75.3077f, 231, 75.3179f
+path.cubicTo(SkBits2Float(0x4367bbbc), SkBits2Float(0x4296a7f8), SkBits2Float(0x43687777), SkBits2Float(0x4296b284), SkBits2Float(0x43693333), SkBits2Float(0x4296b4eb));  // 231.733f, 75.3281f, 232.467f, 75.3487f, 233.2f, 75.3534f
+path.cubicTo(SkBits2Float(0x4369eeef), SkBits2Float(0x4296b751), SkBits2Float(0x436aaaab), SkBits2Float(0x4296b8ac), SkBits2Float(0x436b6666), SkBits2Float(0x4296b12a));  // 233.933f, 75.358f, 234.667f, 75.3607f, 235.4f, 75.346f
+path.cubicTo(SkBits2Float(0x436c2222), SkBits2Float(0x4296a9a8), SkBits2Float(0x436cddde), SkBits2Float(0x42969446), SkBits2Float(0x436d999a), SkBits2Float(0x429687e1));  // 236.133f, 75.3314f, 236.867f, 75.2896f, 237.6f, 75.2654f
+path.cubicTo(SkBits2Float(0x436e5555), SkBits2Float(0x42967b7c), SkBits2Float(0x436f1111), SkBits2Float(0x42967079), SkBits2Float(0x436fcccd), SkBits2Float(0x429666cd));  // 238.333f, 75.2412f, 239.067f, 75.2197f, 239.8f, 75.2008f
+path.cubicTo(SkBits2Float(0x43708889), SkBits2Float(0x42965d20), SkBits2Float(0x43714444), SkBits2Float(0x42965d94), SkBits2Float(0x43720000), SkBits2Float(0x42964dd6));  // 240.533f, 75.1819f, 241.267f, 75.1828f, 242, 75.152f
+path.cubicTo(SkBits2Float(0x4372bbbc), SkBits2Float(0x42963e19), SkBits2Float(0x43737777), SkBits2Float(0x42961c64), SkBits2Float(0x43743333), SkBits2Float(0x4296085b));  // 242.733f, 75.1213f, 243.467f, 75.0555f, 244.2f, 75.0163f
+path.cubicTo(SkBits2Float(0x4374eeef), SkBits2Float(0x4295f453), SkBits2Float(0x4375aaab), SkBits2Float(0x4295e407), SkBits2Float(0x43766666), SkBits2Float(0x4295d5a4));  // 244.933f, 74.9772f, 245.667f, 74.9454f, 246.4f, 74.9173f
+path.cubicTo(SkBits2Float(0x43772222), SkBits2Float(0x4295c741), SkBits2Float(0x4377ddde), SkBits2Float(0x4295bc09), SkBits2Float(0x4378999a), SkBits2Float(0x4295b207));  // 247.133f, 74.8892f, 247.867f, 74.8673f, 248.6f, 74.8477f
+path.cubicTo(SkBits2Float(0x43795555), SkBits2Float(0x4295a806), SkBits2Float(0x437a1111), SkBits2Float(0x42959dad), SkBits2Float(0x437acccd), SkBits2Float(0x4295999a));  // 249.333f, 74.8282f, 250.067f, 74.808f, 250.8f, 74.8f
+path.cubicTo(SkBits2Float(0x437b8889), SkBits2Float(0x42959588), SkBits2Float(0x437c4444), SkBits2Float(0x4295999a), SkBits2Float(0x437d0000), SkBits2Float(0x4295999a));  // 251.533f, 74.7921f, 252.267f, 74.8f, 253, 74.8f
+path.lineTo(SkBits2Float(0x437d0000), SkBits2Float(0x4295999a));  // 253, 74.8f
+path.lineTo(SkBits2Float(0x42886666), SkBits2Float(0x4295999a));  // 68.2f, 74.8f
+path.close();
+    testPathOpCheck(reporter, pathA, path, SkPathOp::kUnion_SkPathOp, filename, FLAGS_runFail);
+}
+
+
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
+
+static struct TestDesc tests[] = {
+    TEST(issue3651_7),
+    TEST(issue3651_6),
+    TEST(issue3651_5),
+    TEST(issue3651_4),
+    TEST(issue3651_1),
+    TEST(issue3651_2),
+    TEST(issue3651_3),
+};
+
+static const size_t testCount = SK_ARRAY_COUNT(tests);
+
+static bool runReverse = false;
+
+DEF_TEST(PathOpsIssue3651, reporter) {
+#if DEBUG_SHOW_TEST_NAME
+    strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
+#endif
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
+}
diff --git a/tests/PathOpsLineIntersectionTest.cpp b/tests/PathOpsLineIntersectionTest.cpp
index 105187b..bc0259c 100644
--- a/tests/PathOpsLineIntersectionTest.cpp
+++ b/tests/PathOpsLineIntersectionTest.cpp
@@ -11,6 +11,9 @@
 
 // FIXME: add tests for intersecting, non-intersecting, degenerate, coincident
 static const SkDLine tests[][2] = {
+{{{{0.00010360032320022583, 1.0172703415155411}, {0.00014114845544099808, 1.0200891587883234}}},
+ {{{0.00010259449481964111, 1.017270140349865}, {0.00018215179443359375, 1.022890567779541}}}},
+
 #if 0
     // these do intersect at a pair of points, but not close enough for check results liking
     {{{{365.848175,5081.15186}, {368,5103}}}, {{{367.967712,5102.61084}, {368.278717,5105.71045}}}},
@@ -82,10 +85,13 @@
 static const size_t coincidentTests_count = SK_ARRAY_COUNT(coincidentTests);
 
 static void check_results(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2,
-                          const SkIntersections& ts) {
+                          const SkIntersections& ts, bool nearAllowed) {
     for (int i = 0; i < ts.used(); ++i) {
         SkDPoint result1 = line1.ptAtT(ts[0][i]);
         SkDPoint result2 = line2.ptAtT(ts[1][i]);
+        if (nearAllowed && result1.roughlyEqual(result2)) {
+            continue;
+        }
         if (!result1.approximatelyEqual(result2) && !ts.nearlySame(i)) {
             REPORTER_ASSERT(reporter, ts.used() != 1);
             result2 = line2.ptAtT(ts[1][i ^ 1]);
@@ -98,14 +104,16 @@
     }
 }
 
-static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2) {
+static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2,
+        bool nearAllowed) {
     SkASSERT(ValidLine(line1));
     SkASSERT(ValidLine(line2));
     SkIntersections i;
+    i.allowNear(nearAllowed);
     int pts = i.intersect(line1, line2);
     REPORTER_ASSERT(reporter, pts);
     REPORTER_ASSERT(reporter, pts == i.used());
-    check_results(reporter, line1, line2, i);
+    check_results(reporter, line1, line2, i, nearAllowed);
     if (line1[0] == line1[1] || line2[0] == line2[1]) {
         return;
     }
@@ -114,28 +122,28 @@
         double right = SkTMax(line1[0].fX, line1[1].fX);
         SkIntersections ts;
         ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left);
-        check_results(reporter, line2, line1, ts);
+        check_results(reporter, line2, line1, ts, nearAllowed);
     }
     if (line2[0].fY == line2[1].fY) {
         double left = SkTMin(line2[0].fX, line2[1].fX);
         double right = SkTMax(line2[0].fX, line2[1].fX);
         SkIntersections ts;
         ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left);
-        check_results(reporter, line1, line2, ts);
+        check_results(reporter, line1, line2, ts, nearAllowed);
     }
     if (line1[0].fX == line1[1].fX) {
         double top = SkTMin(line1[0].fY, line1[1].fY);
         double bottom = SkTMax(line1[0].fY, line1[1].fY);
         SkIntersections ts;
         ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top);
-        check_results(reporter, line2, line1, ts);
+        check_results(reporter, line2, line1, ts, nearAllowed);
     }
     if (line2[0].fX == line2[1].fX) {
         double top = SkTMin(line2[0].fY, line2[1].fY);
         double bottom = SkTMax(line2[0].fY, line2[1].fY);
         SkIntersections ts;
         ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top);
-        check_results(reporter, line1, line2, ts);
+        check_results(reporter, line1, line2, ts, nearAllowed);
     }
     reporter->bumpTestCount();
 }
@@ -148,7 +156,7 @@
     int pts = ts.intersect(line1, line2);
     REPORTER_ASSERT(reporter, pts == 2);
     REPORTER_ASSERT(reporter, pts == ts.used());
-    check_results(reporter, line1, line2, ts);
+    check_results(reporter, line1, line2, ts, false);
     if (line1[0] == line1[1] || line2[0] == line2[1]) {
         return;
     }
@@ -159,7 +167,7 @@
         ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line2, line1, ts);
+        check_results(reporter, line2, line1, ts, false);
     }
     if (line2[0].fY == line2[1].fY) {
         double left = SkTMin(line2[0].fX, line2[1].fX);
@@ -168,7 +176,7 @@
         ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line1, line2, ts);
+        check_results(reporter, line1, line2, ts, false);
     }
     if (line1[0].fX == line1[1].fX) {
         double top = SkTMin(line1[0].fY, line1[1].fY);
@@ -177,7 +185,7 @@
         ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line2, line1, ts);
+        check_results(reporter, line2, line1, ts, false);
     }
     if (line2[0].fX == line2[1].fX) {
         double top = SkTMin(line2[0].fY, line2[1].fY);
@@ -186,7 +194,7 @@
         ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top);
         REPORTER_ASSERT(reporter, pts == 2);
         REPORTER_ASSERT(reporter, pts == ts.used());
-        check_results(reporter, line1, line2, ts);
+        check_results(reporter, line1, line2, ts, false);
     }
     reporter->bumpTestCount();
 }
@@ -201,7 +209,7 @@
     for (index = 0; index < tests_count; ++index) {
         const SkDLine& line1 = tests[index][0];
         const SkDLine& line2 = tests[index][1];
-        testOne(reporter, line1, line2);
+        testOne(reporter, line1, line2, true);
     }
     for (index = 0; index < noIntersect_count; ++index) {
         const SkDLine& line1 = noIntersect[index][0];
@@ -217,8 +225,13 @@
 DEF_TEST(PathOpsLineIntersectionOneOff, reporter) {
     int index = 0;
     SkASSERT(index < (int) tests_count);
-    testOne(reporter, tests[index][0], tests[index][1]);
-    testOne(reporter, tests[1][0], tests[1][1]);
+    testOne(reporter, tests[index][0], tests[index][1], true);
+}
+
+DEF_TEST(PathOpsLineIntersectionExactOneOff, reporter) {
+    int index = 0;
+    SkASSERT(index < (int) tests_count);
+    testOne(reporter, tests[index][0], tests[index][1], false);
 }
 
 DEF_TEST(PathOpsLineIntersectionOneCoincident, reporter) {
diff --git a/tests/PathOpsOpCircleThreadedTest.cpp b/tests/PathOpsOpCircleThreadedTest.cpp
new file mode 100644
index 0000000..0bb75cc
--- /dev/null
+++ b/tests/PathOpsOpCircleThreadedTest.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsExtendedTest.h"
+#include "PathOpsThreadedCommon.h"
+
+static void testOpCirclesMain(PathOpsThreadState* data) {
+        SkASSERT(data);
+    PathOpsThreadState& state = *data;
+    char pathStr[1024];
+    bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter?
+    if (progress) {
+        sk_bzero(pathStr, sizeof(pathStr));
+    }
+
+    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;
+        if (progress) {
+            char* str = pathStr;
+            const int loopNo = 4;
+            str += sprintf(str, "static void circlesOp%d(skiatest::Reporter* reporter,"
+                    " const char* filename) {\n", loopNo);
+            str += sprintf(str, "    SkPath path, pathB;\n");
+            str += sprintf(str, "    path.setFillType(SkPath::k%s_FillType);\n",
+                    e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
+                    ? "EvenOdd" : "?UNDEFINED");
+            str += sprintf(str, "    path.addCircle(%d, %d, %d, %s);\n", state.fA, state.fB,
+                    state.fC, state.fD ? "SkPath::kCW_Direction" : "SkPath::kCCW_Direction");
+            str += sprintf(str, "    pathB.setFillType(SkPath::k%s_FillType);\n",
+                    f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
+                    ? "EvenOdd" : "?UNDEFINED");
+            str += sprintf(str, "    pathB.addCircle(%d, %d, %d, %s);\n", a, b,
+                    c, d ? "SkPath::kCW_Direction" : "SkPath::kCCW_Direction");
+            str += sprintf(str, "    testPathOp(reporter, path, pathB, kDifference_SkPathOp,"
+                    " filename);\n");
+            str += sprintf(str, "}\n");
+        }
+        pathA.setFillType((SkPath::FillType) e);
+        pathA.addCircle(SkIntToScalar(state.fA), SkIntToScalar(state.fB), SkIntToScalar(state.fC),
+                state.fD ? SkPath::kCW_Direction : SkPath::kCCW_Direction);
+        pathB.setFillType((SkPath::FillType) f);
+        pathB.addCircle(SkIntToScalar(a), SkIntToScalar(b), SkIntToScalar(c),
+                d ? SkPath::kCW_Direction : SkPath::kCCW_Direction);
+        for (int op = 0 ; op <= kXOR_SkPathOp; ++op)    {
+            if (progress) {
+                outputProgress(state.fPathStr, pathStr, (SkPathOp) op);
+            }
+            testPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "circles");
+        }
+    }
+                    }
+                }
+            }
+        }
+    }
+}
+
+DEF_TEST(PathOpsOpCircleThreaded, reporter) {
+    initializeTests(reporter, "circleOp");
+    PathOpsThreadedTestRunner testRunner(reporter);
+    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 = 0; d < 2; ++d) {
+                    *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
+                            (&testOpCirclesMain, a, b, c, d, &testRunner));
+                }
+            }
+            if (!reporter->allowExtendedTest()) goto finish;
+        }
+    }
+finish:
+    testRunner.render();
+    ShowTestArray("circleOp");
+}
diff --git a/tests/PathOpsOpCubicThreadedTest.cpp b/tests/PathOpsOpCubicThreadedTest.cpp
index 751ccc5..12eb71b 100644
--- a/tests/PathOpsOpCubicThreadedTest.cpp
+++ b/tests/PathOpsOpCubicThreadedTest.cpp
@@ -7,13 +7,15 @@
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
 
+static int loopNo = 158;
+
 static void testOpCubicsMain(PathOpsThreadState* data) {
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
     SkASSERT(data);
     PathOpsThreadState& state = *data;
-    char pathStr[1024];  // gdb: set print elements 400
+    char pathStr[1024];
     bool progress = state.fReporter->verbose(); // FIXME: break out into its own parameter?
     if (progress) {
         sk_bzero(pathStr, sizeof(pathStr));
@@ -25,23 +27,6 @@
                     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;
-        if (progress) {
-            char* str = pathStr;
-            str += sprintf(str, "    path.setFillType(SkPath::k%s_FillType);\n",
-                    e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
-                    ? "EvenOdd" : "?UNDEFINED");
-            str += sprintf(str, "    path.moveTo(%d,%d);\n", state.fA, state.fB);
-            str += sprintf(str, "    path.cubicTo(%d,%d, %d,%d, %d,%d);\n", state.fC, state.fD,
-                    b, a, d, c);
-            str += sprintf(str, "    path.close();\n");
-            str += sprintf(str, "    pathB.setFillType(SkPath::k%s_FillType);\n",
-                    f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
-                    ? "EvenOdd" : "?UNDEFINED");
-            str += sprintf(str, "    pathB.moveTo(%d,%d);\n", a, b);
-            str += sprintf(str, "    pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d,
-                    state.fB, state.fA, state.fD, state.fC);
-            str += sprintf(str, "    pathB.close();\n");
-        }
         pathA.setFillType((SkPath::FillType) e);
         pathA.moveTo(SkIntToScalar(state.fA), SkIntToScalar(state.fB));
         pathA.cubicTo(SkIntToScalar(state.fC), SkIntToScalar(state.fD), SkIntToScalar(b),
@@ -52,14 +37,43 @@
         pathB.cubicTo(SkIntToScalar(c), SkIntToScalar(d), SkIntToScalar(state.fB),
                 SkIntToScalar(state.fA), SkIntToScalar(state.fD), SkIntToScalar(state.fC));
         pathB.close();
-        for (int op = 0 ; op <= kXOR_PathOp; ++op)    {
+        for (int op = 0 ; op <= kXOR_SkPathOp; ++op)    {
             if (progress) {
                 outputProgress(state.fPathStr, pathStr, (SkPathOp) op);
             }
-            testThreadedPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "cubics");
+            if (progress) {
+                char* str = pathStr;
+                str += sprintf(str, "static void cubicOp%d(skiatest::Reporter* reporter,"
+                        " const char* filename) {\n", loopNo);
+                str += sprintf(str, "    SkPath path, pathB;\n");
+                str += sprintf(str, "    path.setFillType(SkPath::k%s_FillType);\n",
+                        e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
+                        ? "EvenOdd" : "?UNDEFINED");
+                str += sprintf(str, "    path.moveTo(%d,%d);\n", state.fA, state.fB);
+                str += sprintf(str, "    path.cubicTo(%d,%d, %d,%d, %d,%d);\n", state.fC, state.fD,
+                        b, a, d, c);
+                str += sprintf(str, "    path.close();\n");
+                str += sprintf(str, "    pathB.setFillType(SkPath::k%s_FillType);\n",
+                        f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
+                        ? "EvenOdd" : "?UNDEFINED");
+                str += sprintf(str, "    pathB.moveTo(%d,%d);\n", a, b);
+                str += sprintf(str, "    pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d,
+                        state.fB, state.fA, state.fD, state.fC);
+                str += sprintf(str, "    pathB.close();\n");
+                str += sprintf(str, "    testPathOp(reporter, path, pathB, %s, filename);\n",
+                        SkPathOpsDebug::OpStr((SkPathOp) op));
+                str += sprintf(str, "}\n");
+            }
+            if (!testPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "cubics")) {
+                if (progress) {
+                    ++loopNo;
+                    goto skipToNext;
+                }
+            }
         }
     }
                     }
+skipToNext: ;
                 }
             }
         }
@@ -82,5 +96,5 @@
     }
 finish:
     testRunner.render();
-    ShowTestArray();
+    ShowTestArray("cubicOp");
 }
diff --git a/tests/PathOpsOpLoopThreadedTest.cpp b/tests/PathOpsOpLoopThreadedTest.cpp
index c50e23b..3f6d081 100755
--- a/tests/PathOpsOpLoopThreadedTest.cpp
+++ b/tests/PathOpsOpLoopThreadedTest.cpp
@@ -7,6 +7,24 @@
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
 
+static int add_point(char* str, SkScalar x, SkScalar y) {
+    int result;
+    int asInt = SkScalarRoundToInt(x);
+    if (SkIntToScalar(asInt) == x) {
+        result = sprintf(str, "%d", asInt);
+    } else {
+        result = sprintf(str, "%1.9gf", x);
+    }
+    result += sprintf(str + result, ",");
+    asInt = SkScalarRoundToInt(y);
+    if (SkIntToScalar(asInt) == y) {
+        result += sprintf(str + result, "%d", asInt);
+    } else {
+        result += sprintf(str + result, "%1.9gf", y);
+    }
+    return result;
+}
+
 static void testOpLoopsMain(PathOpsThreadState* data) {
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
@@ -35,14 +53,27 @@
         SkPath pathA, pathB;
         if (progress) {
             char* str = pathStr;
+            const int loopNo = 17;
+            str += sprintf(str, "static void loop%d(skiatest::Reporter* reporter,"
+                    " const char* filename) {\n", loopNo);
+            str += sprintf(str, "    SkPath path, pathB;\n");
             str += sprintf(str, "    path.moveTo(%d,%d);\n", a, b);
-            str += sprintf(str, "    path.cubicTo(%d,%d, %1.9gf,%1.9gf, %1.9gf,%1.9gf);\n",
-                    c, d, endC.fX, endC.fY, endD.fX, endD.fY);
+            str += sprintf(str, "    path.cubicTo(%d,%d, ", c, d);
+            str += add_point(str, endC.fX, endC.fY);
+            str += sprintf(str, ", ");
+            str += add_point(str, endD.fX, endD.fY);
+            str += sprintf(str, ");\n");
             str += sprintf(str, "    path.close();\n");
             str += sprintf(str, "    pathB.moveTo(%d,%d);\n", c, d);
-            str += sprintf(str, "    pathB.cubicTo(%1.9gf,%1.9gf, %1.9gf,%1.9gf, %d,%d);\n",
-                    endC.fX, endC.fY, endD.fX, endD.fY, a, b);
+            str += sprintf(str, "    pathB.cubicTo(");
+            str += add_point(str, endC.fX, endC.fY);
+            str += sprintf(str, ", ");
+            str += add_point(str, endD.fX, endD.fY);
+            str += sprintf(str, ", %d,%d);\n", a, b);
             str += sprintf(str, "    pathB.close();\n");
+            str += sprintf(str, "    testPathOp(reporter, path, pathB, kIntersect_SkPathOp,"
+                    " filename);\n");
+            str += sprintf(str, "}\n");
         }
         pathA.moveTo(SkIntToScalar(a), SkIntToScalar(b));
         pathA.cubicTo(SkIntToScalar(c), SkIntToScalar(d), endC.fX, endC.fY, endD.fX, endD.fY);
@@ -52,9 +83,9 @@
         pathB.close();
 //        SkDebugf("%s\n", pathStr);
         if (progress) {
-            outputProgress(state.fPathStr, pathStr, kIntersect_PathOp);
+            outputProgress(state.fPathStr, pathStr, kIntersect_SkPathOp);
         }
-        testThreadedPathOp(state.fReporter, pathA, pathB, kIntersect_PathOp, "loops");
+        testPathOp(state.fReporter, pathA, pathB, kIntersect_SkPathOp, "loops");
                 }
             }
         }
@@ -62,10 +93,7 @@
 }
 
 DEF_TEST(PathOpsOpLoopsThreaded, reporter) {
-    if (!FLAGS_runFail) {
-        return;
-    }
-    initializeTests(reporter, "cubicOp");
+    initializeTests(reporter, "loopOp");
     PathOpsThreadedTestRunner testRunner(reporter);
     for (int a = 0; a < 6; ++a) {  // outermost
         for (int b = a + 1; b < 7; ++b) {
@@ -80,30 +108,5 @@
     }
 finish:
     testRunner.render();
-    ShowTestArray();
-}
-
-DEF_TEST(PathOpsOpLoops, reporter) {
-    if (!FLAGS_runFail) {
-        return;
-    }
-    initializeTests(reporter, "cubicOp");
-    PathOpsThreadState state;
-    state.fReporter = reporter;
-    SkBitmap bitmap;
-    state.fBitmap = &bitmap;
-    char pathStr[PATH_STR_SIZE];
-    state.fPathStr = pathStr;
-    for (state.fA = 0; state.fA < 6; ++state.fA) {  // outermost
-        for (state.fB = state.fA + 1; state.fB < 7; ++state.fB) {
-            for (state.fC = 0 ; state.fC < 6; ++state.fC) {
-                for (state.fD = state.fC + 1; state.fD < 7; ++state.fD) {
-                    testOpLoopsMain(&state);
-                }
-            }
-            if (!reporter->allowExtendedTest()) goto finish;
-        }
-    }
-finish:
-    ShowTestArray();
+    ShowTestArray("loopOp");
 }
diff --git a/tests/PathOpsOpRectThreadedTest.cpp b/tests/PathOpsOpRectThreadedTest.cpp
index 1b6e4e8..9195e4a 100644
--- a/tests/PathOpsOpRectThreadedTest.cpp
+++ b/tests/PathOpsOpRectThreadedTest.cpp
@@ -29,8 +29,13 @@
                 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)   {
+        static int testNum = 6;
         if (progress) {
             char* str = pathStr;
+            str += sprintf(str,
+                    "static void rects%d(skiatest::Reporter* reporter, const char* filename) {\n",
+                    testNum);
+            str += sprintf(str, "    SkPath path, pathB;");
             str += sprintf(str, "    path.setFillType(SkPath::k%s_FillType);\n",
                     e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
                     ? "EvenOdd" : "?UNDEFINED");
@@ -45,6 +50,9 @@
                     " SkPath::kCW_Direction);\n", a, a, b, b);
             str += sprintf(str, "    pathB.addRect(%d, %d, %d, %d,"
                     " SkPath::kCW_Direction);\n", c, c, d, d);
+            str += sprintf(str,
+                    "    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);\n");
+            str += sprintf(str, "}\n\n");
         }
         SkPath pathA, pathB;
         pathA.setFillType((SkPath::FillType) e);
@@ -59,11 +67,11 @@
         pathB.addRect(SkIntToScalar(c), SkIntToScalar(c), SkIntToScalar(d),
                 SkIntToScalar(d), SkPath::kCW_Direction);
         pathB.close();
-        for (int op = 0 ; op <= kXOR_PathOp; ++op)    {
+        for (int op = 0 ; op <= kXOR_SkPathOp; ++op)    {
             if (progress) {
                 outputProgress(state.fPathStr, pathStr, (SkPathOp) op);
             }
-            testThreadedPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "rects");
+            testPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "rects");
         }
     }
                     }
diff --git a/tests/PathOpsOpTest.cpp b/tests/PathOpsOpTest.cpp
index fbfa0b5..637bd04 100644
--- a/tests/PathOpsOpTest.cpp
+++ b/tests/PathOpsOpTest.cpp
@@ -7,7 +7,27 @@
 #include "PathOpsExtendedTest.h"
 #include "PathOpsTestCommon.h"
 
-#define TEST(name) { name, #name }
+class PathTest_Private {
+public:
+    PathTest_Private(SkPath* path)
+        : fPath(path) {}
+
+    void setPt(int index, SkScalar x, SkScalar y) {
+        fPath->setPt(index, x, y);
+    }
+
+    SkPath* fPath;
+};
+
+static void path_edit(const SkPoint& from, const SkPoint& to, SkPath* path) {
+    PathTest_Private testPath(path);
+    for (int index = 0; index < path->countPoints(); ++index) {
+        if (SkDPoint::ApproximatelyEqual(path->getPoint(index), from)) {
+            testPath.setPt(index, to.fX, to.fY);
+            return;
+        }
+    }
+}
 
 static void cubicOp1d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
@@ -19,7 +39,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp2d(skiatest::Reporter* reporter, const char* filename) {
@@ -32,7 +52,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 2,0, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp3d(skiatest::Reporter* reporter, const char* filename) {
@@ -45,7 +65,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 1,0, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp5d(skiatest::Reporter* reporter, const char* filename) {
@@ -58,7 +78,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,2, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp6d(skiatest::Reporter* reporter, const char* filename) {
@@ -71,7 +91,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,3, 1,0, 6,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp7d(skiatest::Reporter* reporter, const char* filename) {
@@ -84,7 +104,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,3, 1,0, 4,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp8d(skiatest::Reporter* reporter, const char* filename) {
@@ -97,7 +117,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,4, 1,0, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp9d(skiatest::Reporter* reporter, const char* filename) {
@@ -110,7 +130,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(1,2, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void quadOp9d(skiatest::Reporter* reporter, const char* filename) {
@@ -125,7 +145,7 @@
     pathB.quadTo(1,2, 1.4f,1);
     pathB.quadTo(3,0.4f, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void lineOp9d(skiatest::Reporter* reporter, const char* filename) {
@@ -144,7 +164,7 @@
     pathB.lineTo(3,0.4f);
     pathB.lineTo(6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp1i(skiatest::Reporter* reporter, const char* filename) {
@@ -157,7 +177,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(1,2, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp10d(skiatest::Reporter* reporter, const char* filename) {
@@ -170,7 +190,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(1,4, 1,0, 3,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp11d(skiatest::Reporter* reporter, const char* filename) {
@@ -183,7 +203,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(1,5, 1,0, 4,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp12d(skiatest::Reporter* reporter, const char* filename) {
@@ -196,7 +216,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp13d(skiatest::Reporter* reporter, const char* filename) {
@@ -209,7 +229,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(3,5, 1,0, 5,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp14d(skiatest::Reporter* reporter, const char* filename) {
@@ -222,7 +242,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp15d(skiatest::Reporter* reporter, const char* filename) {
@@ -235,7 +255,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 6,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp16d(skiatest::Reporter* reporter, const char* filename) {
@@ -248,7 +268,7 @@
     pathB.moveTo(0,3);
     pathB.cubicTo(0,1, 2,0, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp17d(skiatest::Reporter* reporter, const char* filename) {
@@ -261,7 +281,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(1,2, 2,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp18d(skiatest::Reporter* reporter, const char* filename) {
@@ -274,7 +294,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 5,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp19i(skiatest::Reporter* reporter, const char* filename) {
@@ -287,7 +307,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(2,6, 2,0, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp20d(skiatest::Reporter* reporter, const char* filename) {
@@ -300,7 +320,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(1,2, 1,0, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp21d(skiatest::Reporter* reporter, const char* filename) {
@@ -313,7 +333,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(5,6, 1,0, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp22d(skiatest::Reporter* reporter, const char* filename) {
@@ -326,7 +346,7 @@
     pathB.moveTo(0,3);
     pathB.cubicTo(1,2, 1,0, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp23d(skiatest::Reporter* reporter, const char* filename) {
@@ -339,7 +359,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(1,2, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp24d(skiatest::Reporter* reporter, const char* filename) {
@@ -352,63 +372,63 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(2,3, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testIntersect1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kIntersect_PathOp, filename);
+    testPathOp(reporter, one, two, kIntersect_SkPathOp, filename);
 }
 
 static void testUnion1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kUnion_PathOp, filename);
+    testPathOp(reporter, one, two, kUnion_SkPathOp, filename);
 }
 
 static void testDiff1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kDifference_PathOp, filename);
+    testPathOp(reporter, one, two, kDifference_SkPathOp, filename);
 }
 
 static void testXor1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kXOR_PathOp, filename);
+    testPathOp(reporter, one, two, kXOR_SkPathOp, filename);
 }
 
 static void testIntersect2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kIntersect_PathOp, filename);
+    testPathOp(reporter, one, two, kIntersect_SkPathOp, filename);
 }
 
 static void testUnion2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kUnion_PathOp, filename);
+    testPathOp(reporter, one, two, kUnion_SkPathOp, filename);
 }
 
 static void testDiff2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kDifference_PathOp, filename);
+    testPathOp(reporter, one, two, kDifference_SkPathOp, filename);
 }
 
 static void testXor2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
     one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
     two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
-    testPathOp(reporter, one, two, kXOR_PathOp, filename);
+    testPathOp(reporter, one, two, kXOR_SkPathOp, filename);
 }
 
 static void testOp1d(skiatest::Reporter* reporter, const char* filename) {
@@ -419,7 +439,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp2d(skiatest::Reporter* reporter, const char* filename) {
@@ -430,7 +450,7 @@
     pathB.setFillType(SkPath::kEvenOdd_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp3d(skiatest::Reporter* reporter, const char* filename) {
@@ -441,7 +461,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp1u(skiatest::Reporter* reporter, const char* filename) {
@@ -452,7 +472,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void testOp4d(skiatest::Reporter* reporter, const char* filename) {
@@ -463,7 +483,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp5d(skiatest::Reporter* reporter, const char* filename) {
@@ -474,7 +494,7 @@
     pathB.setFillType(SkPath::kEvenOdd_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp6d(skiatest::Reporter* reporter, const char* filename) {
@@ -485,7 +505,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp7d(skiatest::Reporter* reporter, const char* filename) {
@@ -496,7 +516,7 @@
     pathB.setFillType(SkPath::kEvenOdd_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp2u(skiatest::Reporter* reporter, const char* filename) {
@@ -507,7 +527,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 3, 3, SkPath::kCW_Direction);
     pathB.addRect(1, 1, 2, 2, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void testOp8d(skiatest::Reporter* reporter, const char* filename) {
@@ -516,7 +536,7 @@
     pathB.moveTo(577330, 1971.72f);
     pathB.cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 static void cubicOp25i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
@@ -528,7 +548,7 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(2,3, 1,0, 4,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp26d(skiatest::Reporter* reporter, const char* filename) {
@@ -541,7 +561,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(2,3, 1,0, 4,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp27d(skiatest::Reporter* reporter, const char* filename) {
@@ -554,7 +574,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(2,5, 1,0, 6,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp28u(skiatest::Reporter* reporter, const char* filename) {
@@ -567,7 +587,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(2,3, 1,0, 4,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp29d(skiatest::Reporter* reporter, const char* filename) {
@@ -580,7 +600,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(2,4, 1,0, 5,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp30d(skiatest::Reporter* reporter, const char* filename) {
@@ -593,7 +613,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(3,5, 1,0, 5,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp31d(skiatest::Reporter* reporter, const char* filename) {
@@ -606,7 +626,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 2,0, 3,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp31u(skiatest::Reporter* reporter, const char* filename) {
@@ -619,7 +639,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 2,0, 3,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp31x(skiatest::Reporter* reporter, const char* filename) {
@@ -632,7 +652,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 2,0, 3,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kXOR_PathOp, filename);
+    testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename);
 }
 
 static void cubicOp32d(skiatest::Reporter* reporter, const char* filename) {
@@ -645,7 +665,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(1,3, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp33i(skiatest::Reporter* reporter, const char* filename) {
@@ -658,7 +678,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(1,3, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp34d(skiatest::Reporter* reporter, const char* filename) {
@@ -671,7 +691,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(1,3, 1,0, 5,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp35d(skiatest::Reporter* reporter, const char* filename) {
@@ -684,7 +704,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 1,0, 5,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp36u(skiatest::Reporter* reporter, const char* filename) {
@@ -697,7 +717,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(1,5, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp37d(skiatest::Reporter* reporter, const char* filename) {
@@ -710,14 +730,9 @@
     pathB.moveTo(1,6);
     pathB.cubicTo(3,4, 1,0, 6,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// this fails to detect a cubic/cubic intersection
-// the slight overlap is missed when the cubics are approximated by quadratics
-// and the subsequent line/cubic intersection also (correctly) misses the intersection
-// if the line/cubic was a matching line/approx.quadratic then the missing intersection
-// could have been detected
 static void cubicOp38d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
     path.setFillType(SkPath::kWinding_FillType);
@@ -728,7 +743,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(1,4, 1,0, 6,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp39d(skiatest::Reporter* reporter, const char* filename) {
@@ -741,7 +756,7 @@
     pathB.moveTo(1,5);
     pathB.cubicTo(3,4, 1,0, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp40d(skiatest::Reporter* reporter, const char* filename) {
@@ -754,7 +769,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(2,4, 1,0, 5,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp41i(skiatest::Reporter* reporter, const char* filename) {
@@ -767,7 +782,7 @@
     pathB.moveTo(3,4);
     pathB.cubicTo(4,6, 1,0, 6,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp42d(skiatest::Reporter* reporter, const char* filename) {
@@ -780,7 +795,7 @@
     pathB.moveTo(5,6);
     pathB.cubicTo(4,5, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp43d(skiatest::Reporter* reporter, const char* filename) {
@@ -793,7 +808,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(1,3, 2,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp44d(skiatest::Reporter* reporter, const char* filename) {
@@ -806,7 +821,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(2,3, 2,0, 6,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp45d(skiatest::Reporter* reporter, const char* filename) {
@@ -819,7 +834,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(2,3, 2,0, 4,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp46d(skiatest::Reporter* reporter, const char* filename) {
@@ -832,7 +847,7 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(2,4, 2,0, 5,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp47d(skiatest::Reporter* reporter, const char* filename) {
@@ -845,7 +860,7 @@
     pathB.moveTo(2,6);
     pathB.cubicTo(4,5, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp48d(skiatest::Reporter* reporter, const char* filename) {
@@ -858,7 +873,7 @@
     pathB.moveTo(1,5);
     pathB.cubicTo(2,3, 2,0, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp49d(skiatest::Reporter* reporter, const char* filename) {
@@ -871,7 +886,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(1,4, 2,0, 5,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp50d(skiatest::Reporter* reporter, const char* filename) {
@@ -884,7 +899,7 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(1,5, 3,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp51d(skiatest::Reporter* reporter, const char* filename) {
@@ -897,7 +912,7 @@
     pathB.moveTo(1,4);
     pathB.cubicTo(0,6, 3,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp52d(skiatest::Reporter* reporter, const char* filename) {
@@ -910,7 +925,7 @@
     pathB.moveTo(4,5);
     pathB.cubicTo(3,4, 2,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp53d(skiatest::Reporter* reporter, const char* filename) {
@@ -923,7 +938,7 @@
     pathB.moveTo(3,5);
     pathB.cubicTo(1,2, 3,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp54d(skiatest::Reporter* reporter, const char* filename) {
@@ -936,7 +951,7 @@
     pathB.moveTo(4,5);
     pathB.cubicTo(2,4, 4,0, 3,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp55d(skiatest::Reporter* reporter, const char* filename) {
@@ -949,7 +964,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(0,5, 5,0, 3,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp56d(skiatest::Reporter* reporter, const char* filename) {
@@ -962,7 +977,7 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(1,2, 1,0, 6,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp57d(skiatest::Reporter* reporter, const char* filename) {
@@ -975,7 +990,7 @@
     pathB.moveTo(4,5);
     pathB.cubicTo(4,6, 5,0, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp58d(skiatest::Reporter* reporter, const char* filename) {
@@ -988,7 +1003,7 @@
     pathB.moveTo(5,6);
     pathB.cubicTo(3,5, 5,0, 4,3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp59d(skiatest::Reporter* reporter, const char* filename) {
@@ -1001,7 +1016,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(1,4, 1,0, 6,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp60d(skiatest::Reporter* reporter, const char* filename) {
@@ -1014,7 +1029,7 @@
     pathB.moveTo(0,6);
     pathB.cubicTo(2,5, 2,0, 6,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp61d(skiatest::Reporter* reporter, const char* filename) {
@@ -1027,7 +1042,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(1,6, 2,1, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp62d(skiatest::Reporter* reporter, const char* filename) {
@@ -1040,7 +1055,7 @@
     pathB.moveTo(3,5);
     pathB.cubicTo(4,5, 3,1, 6,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp63d(skiatest::Reporter* reporter, const char* filename) {
@@ -1053,7 +1068,7 @@
     pathB.moveTo(2,3);
     pathB.cubicTo(3,5, 3,2, 4,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp64d(skiatest::Reporter* reporter, const char* filename) {
@@ -1066,7 +1081,7 @@
     pathB.cubicTo(0,3, 1,0, 1,0);
     pathB.lineTo(0,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp65d(skiatest::Reporter* reporter, const char* filename) {
@@ -1079,7 +1094,7 @@
     pathB.cubicTo(0,1, 1,0, 5,1);
     pathB.lineTo(0,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void rectOp1d(skiatest::Reporter* reporter, const char* filename) {
@@ -1092,7 +1107,7 @@
     pathB.cubicTo(0,3, 1,0, 1,0);
     pathB.lineTo(0,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp66u(skiatest::Reporter* reporter, const char* filename) {
@@ -1105,7 +1120,7 @@
     pathB.moveTo(2,4);
     pathB.cubicTo(3,5, 1,0, 6,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp67u(skiatest::Reporter* reporter, const char* filename) {
@@ -1118,7 +1133,7 @@
     pathB.cubicTo(1,3, 5,3, 6,1);
     pathB.lineTo(0,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp68u(skiatest::Reporter* reporter, const char* filename) {
@@ -1129,7 +1144,7 @@
     pathB.moveTo(1,4);
     pathB.cubicTo(0,5, 5,0, 5,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp69d(skiatest::Reporter* reporter, const char* filename) {
@@ -1140,15 +1155,15 @@
     pathB.moveTo(1,3);
     pathB.cubicTo(0,2, 3,1, 1,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 SkPathOp ops[] = {
-    kUnion_PathOp,
-    kXOR_PathOp,
-    kReverseDifference_PathOp,
-    kXOR_PathOp,
-    kReverseDifference_PathOp,
+    kUnion_SkPathOp,
+    kXOR_SkPathOp,
+    kReverseDifference_SkPathOp,
+    kXOR_SkPathOp,
+    kReverseDifference_SkPathOp,
 };
 
 static void rRect1(skiatest::Reporter* reporter, const char* filename) {
@@ -1216,7 +1231,7 @@
     pathB.lineTo(246,4);
     pathB.lineTo(189,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skp2(skiatest::Reporter* reporter, const char* filename) {
@@ -1236,7 +1251,7 @@
     pathB.lineTo(823.000000f, 1028.00000f);
     pathB.lineTo(258.000000f, 1028.00000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skp3(skiatest::Reporter* reporter, const char* filename) {
@@ -1268,7 +1283,7 @@
     pathB.lineTo(973.000000f, 510.000000f);
     pathB.lineTo(717.000000f, 510.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skp4(skiatest::Reporter* reporter, const char* filename) {
@@ -1300,7 +1315,7 @@
     pathB.lineTo(306.000000f, 617.000000f);
     pathB.lineTo(306.000000f, 590.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skp5(skiatest::Reporter* reporter, const char* filename) {
@@ -1326,7 +1341,7 @@
     pathB.lineTo(10.0000000f, 234.000000f);
     pathB.cubicTo(10.0000000f, 229.581726f, 13.5817204f, 226.000000f, 18.0000000f, 226.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp70d(skiatest::Reporter* reporter, const char* filename) {
@@ -1339,7 +1354,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(0,5, 1,0, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp71d(skiatest::Reporter* reporter, const char* filename) {
@@ -1352,7 +1367,7 @@
     pathB.moveTo(1,4);
     pathB.cubicTo(4,6, 1,0, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp72i(skiatest::Reporter* reporter, const char* filename) {
@@ -1365,7 +1380,7 @@
     pathB.moveTo(2,5);
     pathB.cubicTo(4,5, 1,0, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp73d(skiatest::Reporter* reporter, const char* filename) {
@@ -1380,7 +1395,7 @@
     pathB.cubicTo(4,6, 1,0, 4,3);
     pathB.lineTo(0,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp74d(skiatest::Reporter* reporter, const char* filename) {
@@ -1395,7 +1410,7 @@
     pathB.cubicTo(1,5, 1,0, 5,1);
     pathB.lineTo(1,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp75d(skiatest::Reporter* reporter, const char* filename) {
@@ -1410,7 +1425,7 @@
     pathB.cubicTo(4,6, 1,0, 4,0);
     pathB.lineTo(1,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp76u(skiatest::Reporter* reporter, const char* filename) {
@@ -1423,7 +1438,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(3,5, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp77i(skiatest::Reporter* reporter, const char* filename) {
@@ -1438,7 +1453,7 @@
     pathB.cubicTo(2,3, 1,0, 3,1);
     pathB.lineTo(0,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp78u(skiatest::Reporter* reporter, const char* filename) {
@@ -1453,7 +1468,7 @@
     pathB.cubicTo(1,6, 6,1, 6,1);
     pathB.lineTo(0,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp79u(skiatest::Reporter* reporter, const char* filename) {
@@ -1466,7 +1481,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(4,6, 1,0, 3,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp80i(skiatest::Reporter* reporter, const char* filename) {
@@ -1481,7 +1496,7 @@
     pathB.cubicTo(3,4, 1,0, 3,2);
     pathB.lineTo(1,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp81d(skiatest::Reporter* reporter, const char* filename) {
@@ -1494,7 +1509,7 @@
     pathB.moveTo(3,4);
     pathB.cubicTo(4,5, 1,0, 6,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp82i(skiatest::Reporter* reporter, const char* filename) {
@@ -1509,7 +1524,7 @@
     pathB.cubicTo(0,3, 1,0, 3,2);
     pathB.lineTo(2,5);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp83i(skiatest::Reporter* reporter, const char* filename) {
@@ -1524,7 +1539,7 @@
     pathB.cubicTo(1,4, 1,0, 3,0);
     pathB.lineTo(1,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp84d(skiatest::Reporter* reporter, const char* filename) {
@@ -1537,7 +1552,7 @@
     pathB.moveTo(3,6);
     pathB.cubicTo(2,3, 4,0, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void skpClip1(skiatest::Reporter* reporter, const char* filename) {
@@ -1567,7 +1582,7 @@
     pathB.lineTo(1247.00000f, 907.000000f);
     pathB.lineTo(1247.00000f, 876.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpClip2(skiatest::Reporter* reporter, const char* filename) {
@@ -1593,7 +1608,7 @@
     pathB.lineTo(130.000000f, 11416.0000f);
     pathB.cubicTo(130.000000f, 11415.4473f, 130.895432f, 11415.0000f, 132.000000f, 11415.0000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skp96prezzi1(skiatest::Reporter* reporter, const char* filename) {
@@ -1623,7 +1638,7 @@
     pathB.lineTo(253.000000f, 669.000000f);
     pathB.lineTo(156.000000f, 669.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpancestry_com1(skiatest::Reporter* reporter, const char* filename) {
@@ -1649,7 +1664,7 @@
     pathB.lineTo(157.000000f, 930.000000f);
     pathB.cubicTo(157.000000f, 927.790833f, 158.790863f, 926.000000f, 161.000000f, 926.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpeldorado_com_ua1(skiatest::Reporter* reporter, const char* filename) {
@@ -1673,7 +1688,7 @@
     pathB.cubicTo(283.840179f, 304.431458f, 300.126587f, 291.000000f, 316.695129f, 291.000000f);
     pathB.lineTo(1006.69513f, 291.000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbyte_com1(skiatest::Reporter* reporter, const char* filename) {
@@ -1701,7 +1716,7 @@
     pathB.lineTo(963.000000f, 19.0000000f);
     pathB.cubicTo(963.000000f, 16.2385750f, 965.238586f, 14.0000000f, 968.000000f, 14.0000000f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skphealth_com76(skiatest::Reporter* reporter, const char* filename) {
@@ -1722,7 +1737,7 @@
     pathB.lineTo(704.000000f, 33.0000000f);
     pathB.lineTo(705.000000f, 33.0000000f);
     pathB.lineTo(719.500000f, 3.00000000f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpahrefs_com88(skiatest::Reporter* reporter, const char* filename) {
@@ -1748,7 +1763,7 @@
     pathB.lineTo(1088.00000f, 6.00000000f);
     pathB.lineTo(1088.00000f, 19.0000000f);
     pathB.lineTo(1101.00000f, 32.0000000f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpahrefs_com29(skiatest::Reporter* reporter, const char* filename) {
@@ -1778,7 +1793,7 @@
     pathB.lineTo(1049.00000f, 19.0000000f);
     pathB.lineTo(1073.00000f, 31.0000000f);
     pathB.lineTo(1074.00000f, 32.0000000f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp85d(skiatest::Reporter* reporter, const char* filename) {
@@ -1792,12 +1807,9 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(2,6, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// this fails because the pair of nearly coincident cubics intersect at the ends
-// but the line connected to one of the cubics at the same point does not intersect
-// the other
 static void skpkkiste_to98(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1823,7 +1835,7 @@
     pathB.cubicTo(91, 124.238579f, 93.2385788f, 122, 96, 122);
     pathB.lineTo(258, 122);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void issue1417(skiatest::Reporter* reporter, const char* filename) {
@@ -1934,8 +1946,8 @@
     path2.lineTo(113.232177734375f, 173.5789947509765625f);
     path2.lineTo(113.232177734375f, 173.5789947509765625f);
     path2.close();
-
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    // FIXME : difficult data, circle back later
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void issue1418(skiatest::Reporter* reporter, const char* filename) {
@@ -1961,7 +1973,7 @@
     path2.quadTo(0.79289329051971435547f, 0.50000005960464477539f, 0.64644664525985717773f, 0.35355341434478759766f);
     path2.quadTo(0.50000005960464477539f, 0.20710679888725280762f, 0.50000005960464477539f, 0);
     path2.quadTo(0.50000005960464477539f, -0.20710679888725280762f, 0.64644664525985717773f, -0.35355341434478759766f);
-    testPathOp(reporter, path1, path2, kIntersect_PathOp, filename);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp85i(skiatest::Reporter* reporter, const char* filename) {
@@ -1974,7 +1986,7 @@
     pathB.moveTo(3, 4);
     pathB.cubicTo(4, 6, 4, 3, 5, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void issue1418b(skiatest::Reporter* reporter, const char* filename) {
@@ -2004,7 +2016,7 @@
     path2.quadTo(0.792893291f, 0.50000006f, 1.00000012f, 0.50000006f);
     path2.close();
     path2.setFillType(SkPath::kEvenOdd_FillType);
-    testPathOp(reporter, path1, path2, kIntersect_PathOp, filename);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
 }
 
 static void rectOp1i(skiatest::Reporter* reporter, const char* filename) {
@@ -2015,7 +2027,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void rectOp2i(skiatest::Reporter* reporter, const char* filename) {
@@ -2026,7 +2038,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void rectOp3x(skiatest::Reporter* reporter, const char* filename) {
@@ -2053,12 +2065,9 @@
     pathB.lineTo(3, 3);
     pathB.lineTo(2, 3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kXOR_PathOp, filename);
+    testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename);
 }
 
-// this fails to generate two interior line segments 
-// an earlier pathops succeeded, but still failed to generate one interior line segment
-// (but was saved by assemble, which works around a single line missing segment)
 static void issue1435(skiatest::Reporter* reporter, const char* filename) {
     SkPath path1;
     path1.moveTo(160, 60);
@@ -2107,7 +2116,7 @@
     path2.lineTo(195.830978f, 161.521133f);
     path2.close();
     path2.setFillType(SkPath::kEvenOdd_FillType);
-    testPathOp(reporter, path1, path2, kIntersect_PathOp, filename);
+    testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
 }
 
 static void skpkkiste_to716(skiatest::Reporter* reporter, const char* filename) {
@@ -2131,7 +2140,7 @@
     pathB.cubicTo(1173, 124.238579f, 1175.23853f, 122, 1178, 122);
     pathB.lineTo(1340, 122);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void loopEdge1(skiatest::Reporter* reporter, const char* filename) {
@@ -2153,7 +2162,7 @@
     pathB.lineTo(2,4);
     pathB.lineTo(1,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void loopEdge2(skiatest::Reporter* reporter, const char* filename) {
@@ -2175,7 +2184,7 @@
     pathB.lineTo(2 - 1e-6f,4);
     pathB.lineTo(1 - 1e-6f,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp86i(skiatest::Reporter* reporter, const char* filename) {
@@ -2188,7 +2197,7 @@
     pathB.moveTo(2, 6);
     pathB.cubicTo(2, 5, 4, 0, 4, 3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp87u(skiatest::Reporter* reporter, const char* filename) {
@@ -2201,7 +2210,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(4,6, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp88u(skiatest::Reporter* reporter, const char* filename) {
@@ -2214,7 +2223,7 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(4,6, 1,0, 5,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp89u(skiatest::Reporter* reporter, const char* filename) {
@@ -2227,7 +2236,7 @@
     pathB.moveTo(0, 5);
     pathB.cubicTo(3, 6, 3, 0, 6, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp90u(skiatest::Reporter* reporter, const char* filename) {
@@ -2240,7 +2249,7 @@
     pathB.moveTo(2, 5);
     pathB.cubicTo(1, 4, 5, 0, 2, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp91u(skiatest::Reporter* reporter, const char* filename) {
@@ -2253,10 +2262,10 @@
     pathB.moveTo(3, 6);
     pathB.cubicTo(0, 5, 6, 1, 3, 0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
-static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filename) {  //  add t cancel
+static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
    path.moveTo(-1.24344979e-014f, 348);
@@ -2274,10 +2283,10 @@
     pathB.lineTo(258, 348);
     pathB.lineTo(0, 348);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename) {  //  add t cancel
+static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(1.99840144e-015f, 494);
@@ -2297,10 +2306,10 @@
     pathB.lineTo(105, 494);
     pathB.lineTo(0, 494);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filename) {  // partial coincidence
+static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(670.537415f, 285);
@@ -2323,10 +2332,10 @@
     pathB.quadTo(691.660889f, 337.416199f, 685.173523f, 352.661896f);
     pathB.quadTo(678.686157f, 367.907562f, 663.318542f, 374.100616f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpact_com43(skiatest::Reporter* reporter, const char* filename) {  // bridge op
+static void skpact_com43(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(1.45716772e-016f, 924.336121f);
@@ -2348,10 +2357,10 @@
     pathB.lineTo(0, 920);
     pathB.lineTo(3, 927);
     pathB.lineTo(-1, 927);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) {  // zero span
+static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(320.097229f, 628.573669f);
@@ -2372,10 +2381,10 @@
     pathB.lineTo(334.366943f, 625.145508f);
     pathB.cubicTo(333.773315f, 624.828247f, 333.549286f, 624.089783f, 333.866608f, 623.496155f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) {  // find chase op
+static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(0, 926);
@@ -2391,10 +2400,10 @@
     pathB.lineTo(49, 178);
     pathB.lineTo(49, 312);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const char* filename) { // calc common
+static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(205.605804f, 142.334625f);
@@ -2415,10 +2424,10 @@
     pathB.cubicTo(243.829437f, 98.1356659f, 267.195221f, 96.4417267f, 282.651581f, 109.808517f);
     pathB.lineTo(283.407959f, 110.462646f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename) {  // mark and chase winding
+static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(-4.4408921e-016f, 682.5f);
@@ -2436,10 +2445,10 @@
     pathB.lineTo(35, 683);
     pathB.lineTo(0, 683);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filename) {  // cubic/cubic intersect
+static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(324.071075f, 845.071045f);
@@ -2466,7 +2475,7 @@
     pathB.cubicTo(145, 715.477173f, 149.477158f, 711, 155, 711);
     pathB.lineTo(317, 711);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp92i(skiatest::Reporter* reporter, const char* filename) {
@@ -2479,7 +2488,7 @@
     pathB.moveTo(1, 4);
     pathB.cubicTo(4, 5, 1, 0, 6, 2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp93d(skiatest::Reporter* reporter, const char* filename) {
@@ -2492,7 +2501,7 @@
     pathB.moveTo(1, 4);
     pathB.cubicTo(3, 4, 1, 0, 6, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp94u(skiatest::Reporter* reporter, const char* filename) {
@@ -2505,7 +2514,7 @@
     pathB.moveTo(0, 5);
     pathB.cubicTo(3, 5, 3, 0, 3, 2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void skpadbox_lt15(skiatest::Reporter* reporter, const char* filename) {
@@ -2529,7 +2538,7 @@
     pathB.cubicTo(333.773315f, 624.828247f, 333.549286f, 624.089783f, 333.866608f, 623.496155f);
     pathB.lineTo(613.368042f, 100.585754f);
      pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpadoption_org196(skiatest::Reporter* reporter, const char* filename) {
@@ -2554,7 +2563,7 @@
     pathB.cubicTo(805.238586f, 375, 803, 372.761414f, 803, 370);
     pathB.lineTo(803, 326);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpadspert_net23(skiatest::Reporter* reporter, const char* filename) {
@@ -2581,7 +2590,7 @@
     pathB.lineTo(35, 683);
     pathB.lineTo(0, 683);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpadventistmission_org572(skiatest::Reporter* reporter, const char* filename) {
@@ -2600,7 +2609,7 @@
     pathB.lineTo(1182, 926);
     pathB.lineTo(934, 926);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpagentxsites_com55(skiatest::Reporter* reporter, const char* filename) {
@@ -2628,7 +2637,7 @@
     pathB.cubicTo(924, 27.8954315f, 924.895447f, 27, 926, 27);
     pathB.lineTo(1103, 27);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbakosoft_com10(skiatest::Reporter* reporter, const char* filename) {
@@ -2654,7 +2663,7 @@
     pathB.quadTo(198.284271f, 170, 204.142136f, 175.857864f);
     pathB.quadTo(210, 181.715729f, 210, 190);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbambootheme_com12(skiatest::Reporter* reporter, const char* filename) {
@@ -2678,7 +2687,7 @@
     pathB.lineTo(-51, 47);
     pathB.cubicTo(-51, 19.3857498f, -28.6142502f, -3, -1, -3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpakmmos_ru100(skiatest::Reporter* reporter, const char* filename) {
@@ -2697,7 +2706,7 @@
     pathB.lineTo(693, 926);
     pathB.lineTo(575, 926);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcarpetplanet_ru22(skiatest::Reporter* reporter, const char* filename) {
@@ -2721,10 +2730,9 @@
     pathB.cubicTo(67, 842.307556f, 123.85984f, 785, 194, 785);
     pathB.lineTo(195, 785);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// this fails because cubic/quad misses an intersection (failure is isolated in c/q int test)
 static void skpcarrot_is24(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2748,7 +2756,7 @@
     pathB.cubicTo(1019.77502f, 679.955017f, 1020.08099f, 676.094971f, 1020.08099f, 672.161987f);
     pathB.cubicTo(1020.08002f, 630.73999f, 986.502014f, 597.161987f, 945.080994f, 597.161987f);
     pathB.close();
-    testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbangalorenest_com4(skiatest::Reporter* reporter, const char* filename) {
@@ -2767,7 +2775,7 @@
     pathB.lineTo(30, 146);
     pathB.lineTo(30, 290);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbenzoteh_ru152(skiatest::Reporter* reporter, const char* filename) {
@@ -2794,7 +2802,7 @@
     pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);
     pathB.quadTo(883, 25.0710678f, 883, 23);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbestred_ru37(skiatest::Reporter* reporter, const char* filename) {
@@ -2821,7 +2829,7 @@
     pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);
     pathB.quadTo(883, 25.0710678f, 883, 23);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbingoentertainment_net189(skiatest::Reporter* reporter, const char* filename) {
@@ -2845,7 +2853,7 @@
     pathB.cubicTo(922.567993f, 755.399414f, 920.880615f, 748.474304f, 918.799133f, 748.216003f);
     pathB.lineTo(899.200928f, 745.783997f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcarrefour_ro62(skiatest::Reporter* reporter, const char* filename) {
@@ -2868,7 +2876,7 @@
     pathB.cubicTo(402.686279f, 666, 400, 663.313721f, 400, 660);
     pathB.lineTo(400, 453);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcaffelavazzait_com_ua21(skiatest::Reporter* reporter, const char* filename) {
@@ -2895,7 +2903,7 @@
     pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);
     pathB.quadTo(883, 25.0710678f, 883, 23);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcamcorder_kz21(skiatest::Reporter* reporter, const char* filename) {
@@ -2922,7 +2930,7 @@
     pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);
     pathB.quadTo(883, 25.0710678f, 883, 23);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcavablar_net563(skiatest::Reporter* reporter, const char* filename) {
@@ -2941,7 +2949,7 @@
     pathB.lineTo(160, 918);
     pathB.lineTo(91, 918);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpinsomnia_gr72(skiatest::Reporter* reporter, const char* filename) {
@@ -2960,7 +2968,7 @@
     pathB.lineTo(1138, 231);
     pathB.lineTo(633, 6101);
     pathB.lineTo(1139, 6607);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp95u(skiatest::Reporter* reporter, const char* filename) {
@@ -2973,7 +2981,7 @@
     pathB.moveTo(1, 5);
     pathB.cubicTo(2, 3, 2, 0, 3, 2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp96d(skiatest::Reporter* reporter, const char* filename) {
@@ -2986,7 +2994,7 @@
     pathB.moveTo(3, 6);
     pathB.cubicTo(0, 5, 6, 1, 3, 0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp97x(skiatest::Reporter* reporter, const char* filename) {
@@ -2999,7 +3007,7 @@
     pathB.moveTo(1, 2);
     pathB.cubicTo(1, 2, 2, 0, 6, 0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kXOR_PathOp, filename);
+    testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename);
 }
 
 static void cubicOp98x(skiatest::Reporter* reporter, const char* filename) {
@@ -3012,7 +3020,7 @@
     pathB.moveTo(1, 4);
     pathB.cubicTo(3, 6, 3, 0, 6, 3);
     pathB.close();
-    testPathOp(reporter, path, pathB, kXOR_PathOp, filename);
+    testPathOp(reporter, path, pathB, kXOR_SkPathOp, filename);
 }
 
 static void cubicOp99(skiatest::Reporter* reporter, const char* filename) {
@@ -3025,7 +3033,7 @@
     pathB.moveTo(5,6);
     pathB.cubicTo(4,5, 6,3, 3,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp100(skiatest::Reporter* reporter, const char* filename) {
@@ -3038,7 +3046,7 @@
     pathB.moveTo(1,2);
     pathB.cubicTo(2,4, 1,0, 2,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp101(skiatest::Reporter* reporter, const char* filename) {
@@ -3051,7 +3059,7 @@
     pathB.moveTo(1, 2);
     pathB.cubicTo(3, 5, 1, 0, 3, 2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp102(skiatest::Reporter* reporter, const char* filename) {
@@ -3064,7 +3072,7 @@
     pathB.moveTo(0,1);
     pathB.cubicTo(0,3, 1,0, 2,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp103(skiatest::Reporter* reporter, const char* filename) {
@@ -3077,7 +3085,7 @@
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 5,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp104(skiatest::Reporter* reporter, const char* filename) {
@@ -3090,7 +3098,7 @@
     pathB.moveTo(0,4);
     pathB.cubicTo(1,6, 1,0, 6,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp105(skiatest::Reporter* reporter, const char* filename) {
@@ -3103,7 +3111,7 @@
     pathB.moveTo(5,6);
     pathB.cubicTo(0,2, 1,0, 4,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp106(skiatest::Reporter* reporter, const char* filename) {
@@ -3116,7 +3124,7 @@
     pathB.moveTo(1, 2);
     pathB.cubicTo(0, 2, 1, 0, 6, 4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp107(skiatest::Reporter* reporter, const char* filename) {
@@ -3129,7 +3137,7 @@
     pathB.moveTo(1, 2);
     pathB.cubicTo(0, 2, 1, 0, 6, 4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp108(skiatest::Reporter* reporter, const char* filename) {
@@ -3142,7 +3150,7 @@
     pathB.moveTo(1, 2);
     pathB.cubicTo(0, 2, 1, 0, 6, 4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp109(skiatest::Reporter* reporter, const char* filename) {
@@ -3155,7 +3163,7 @@
     pathB.moveTo(3,6);
     pathB.cubicTo(4,5, 1,0, 5,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp110(skiatest::Reporter* reporter, const char* filename) {
@@ -3166,7 +3174,7 @@
     pathB.setFillType(SkPath::kEvenOdd_FillType);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp111(skiatest::Reporter* reporter, const char* filename) {
@@ -3179,7 +3187,7 @@
     pathB.moveTo(1,4);
     pathB.cubicTo(1,3, 4,1, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void xOp1u(skiatest::Reporter* reporter, const char* filename) {
@@ -3192,7 +3200,7 @@
     pathB.moveTo(2, 3);
     pathB.cubicTo(3, 6, 4, 1, 5, 4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void xOp1i(skiatest::Reporter* reporter, const char* filename) {
@@ -3205,7 +3213,7 @@
     pathB.moveTo(0, 6);
     pathB.cubicTo(1, 5, 4, 1, 5, 1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void xOp2i(skiatest::Reporter* reporter, const char* filename) {
@@ -3218,7 +3226,7 @@
     pathB.moveTo(2, 3);
     pathB.cubicTo(1, 6, 5, 1, 4, 0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void xOp3i(skiatest::Reporter* reporter, const char* filename) {
@@ -3231,7 +3239,7 @@
     pathB.moveTo(1,4);
     pathB.cubicTo(1,3, 4,1, 5,0);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void findFirst1(skiatest::Reporter* reporter, const char* filename) {
@@ -3244,10 +3252,9 @@
     pathB.moveTo(0,5);
     pathB.cubicTo(1,2, 1,0, 6,1);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// triggers addSimpleAngle with non-zero argument
 static void cubicOp112(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
     path.setFillType(SkPath::kWinding_FillType);
@@ -3258,7 +3265,7 @@
     pathB.moveTo(4,6);
     pathB.cubicTo(0,1, 4,2, 3,2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp113(skiatest::Reporter* reporter, const char* filename) {
@@ -3269,7 +3276,7 @@
     pathB.moveTo(3,5);
     pathB.cubicTo(2.33333325f,4.33333349f, 3.83333325f,3.83333349f, 2,4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp114(skiatest::Reporter* reporter, const char* filename) {
@@ -3282,7 +3289,7 @@
     pathB.moveTo(1, 3);
     pathB.cubicTo(-1, 2, 3.5f, 1.33333337f, 0, 1);
     pathB.close();
-    testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp114asQuad(skiatest::Reporter* reporter, const char* filename) {
@@ -3298,7 +3305,7 @@
     SkPath qPath, qPathB;
     CubicPathToQuads(path, &qPath);
     CubicPathToQuads(pathB, &qPathB);
-    testPathOp(reporter, qPath, qPathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, qPath, qPathB, kIntersect_SkPathOp, filename);
 }
 
 static void quadOp10i(skiatest::Reporter* reporter, const char* filename) {
@@ -3310,7 +3317,7 @@
     pathB.moveTo(0, 0);
     pathB.quadTo(8, 1, 4, 8);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void kari1(skiatest::Reporter* reporter, const char* filename) {
@@ -3329,7 +3336,7 @@
     path2.cubicTo(38.0664443969727f, -0.154893040657043f, 37.1809883117676f, -1.18359375f, 37.52734375, -1.44140625f);
     path2.close();
 
-    testPathOp(reporter, path1, path2, kDifference_PathOp, filename);
+    testPathOp(reporter, path1, path2, kDifference_SkPathOp, filename);
 }
 
 static void issue2504(skiatest::Reporter* reporter, const char* filename) {
@@ -3348,7 +3355,7 @@
                   34.53484344482421875, -5.6777553558349609375,
                   34.53484344482421875, -5.6777553558349609375);
     path2.close();
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void issue2540(skiatest::Reporter* reporter, const char* filename) {
@@ -3368,7 +3375,7 @@
     path2.cubicTo(-16.06999969482421875, 124.66899871826171875, 1.2680000066757202148437500, 91.23999786376953125, 37.264003753662109375, 95.35400390625);
     path2.cubicTo(37.264003753662109375, 95.35400390625, 11.3710002899169921875, 83.7339935302734375, -25.077999114990234375, 124.9120025634765625);
     path2.close();
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void rects1(skiatest::Reporter* reporter, const char* filename) {
@@ -3395,7 +3402,7 @@
     pathB.lineTo(2, 2);
     pathB.lineTo(0, 2);
     pathB.close();
-    testPathOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void rects2(skiatest::Reporter* reporter, const char* filename) {
@@ -3422,7 +3429,7 @@
     pathB.lineTo(4, 4);
     pathB.lineTo(3, 4);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void rects3(skiatest::Reporter* reporter, const char* filename) {
@@ -3433,7 +3440,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void rects4(skiatest::Reporter* reporter, const char* filename) {
@@ -3444,7 +3451,7 @@
     pathB.setFillType(SkPath::kWinding_FillType);
     pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
     pathB.addRect(0, 0, 3, 3, SkPath::kCW_Direction);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void issue2753(skiatest::Reporter* reporter, const char* filename) {
@@ -3464,7 +3471,7 @@
     path2.cubicTo(188.201f, 117.601f, 174.801f, 93, 39, 124.001f);
     path2.close();
 
-    testPathOpCheck(reporter, path1, path2, kUnion_PathOp, filename, FLAGS_runFail);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void issue2808(skiatest::Reporter* reporter, const char* filename) {
@@ -3492,7 +3499,7 @@
 	path2.quadTo(449.033996582f, 280.717712402f, 449.033996582f, 290.87298584f);
 	path2.close();
 
-    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOp(reporter, path1, path2, kUnion_SkPathOp, filename);
 }
 
 static void cubicOp115(skiatest::Reporter* reporter, const char* filename) {
@@ -3506,16 +3513,1779 @@
     pathB.cubicTo(3,5, 1,0, 4,3);
     pathB.close();
     SkPath path2(path);
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void testRect1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, path2;
+    path.addRect(0, 0, 60, 60, SkPath::kCCW_Direction);
+    path.addRect(30, 20, 50, 50, SkPath::kCCW_Direction);
+    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
+//    path.addRect(32, 24, 36, 41, SkPath::kCCW_Direction);
+    testPathOp(reporter, path, path2, kUnion_SkPathOp, filename);
+}
+
+static void testRect2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
+    path.addRect(4, 4, 5, 5, SkPath::kCW_Direction);
+    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
+    pathB.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp116(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(4,6, 2,0, 2,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,2);
+    pathB.cubicTo(0,2, 1,0, 6,4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp117(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(4,5, 6,0, 1,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,6);
+    pathB.cubicTo(0,1, 1,0, 5,4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp118(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(4,6, 5,1, 6,2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1,5);
+    pathB.cubicTo(2,6, 1,0, 6,4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void loop1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(0,1);
+    path.cubicTo(1,5, -5.66666651f,3.33333349f, 8.83333302f,2.33333349f);
+    path.close();
+    pathB.moveTo(1,5);
+    pathB.cubicTo(-5.66666651f,3.33333349f, 8.83333302f,2.33333349f, 0,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+#include "SkPathOpsCubic.h"
+
+static void loop1asQuad(skiatest::Reporter* reporter, const char* filename) {
+    SkDCubic c1 = {{{0,1}, {1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}}};
+    SkDCubic c2 = {{{1,5}, {-5.66666651f,3.33333349f}, {8.83333302f,2.33333349f}, {0,1}}};
+    double c1InflectionTs[2], c2InflectionTs[2];
+    SkDEBUGCODE(int c1InfTCount =) c1.findInflections(c1InflectionTs);
+    SkASSERT(c1InfTCount == 2);
+    SkDEBUGCODE(int c2InfTCount =) c2.findInflections(c2InflectionTs);
+    SkASSERT(c2InfTCount == 1);
+    SkASSERT(c1InflectionTs[0] > c1InflectionTs[1]);
+    SkDCubicPair c1pair = c1.chopAt(c1InflectionTs[0]);
+    SkDCubicPair c1apair = c1pair.first().chopAt(c1InflectionTs[1]);
+    SkDCubicPair c2pair = c2.chopAt(c2InflectionTs[0]);
+    SkDQuad q1[2] = { c1pair.first().toQuad(), c1pair.second().toQuad() };
+    SkDQuad q1a[2] = { c1apair.first().toQuad(), c1apair.second().toQuad() };
+    SkDQuad q2[2] = { c2pair.first().toQuad(), c2pair.second().toQuad() };
+    SkPath path, pathB;
+    path.moveTo(q1a[0].fPts[0].asSkPoint());
+    path.quadTo(q1a[0].fPts[1].asSkPoint(), q1a[0].fPts[2].asSkPoint());
+    path.quadTo(q1a[1].fPts[1].asSkPoint(), q1a[1].fPts[2].asSkPoint());
+    path.quadTo(q1[1].fPts[1].asSkPoint(), q1[1].fPts[2].asSkPoint());
+    path.close();
+    pathB.moveTo(q2[0].fPts[0].asSkPoint());
+    pathB.quadTo(q2[0].fPts[1].asSkPoint(), q2[0].fPts[2].asSkPoint());
+    pathB.quadTo(q2[1].fPts[1].asSkPoint(), q2[1].fPts[2].asSkPoint());
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loop2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(0,1);
+    path.cubicTo(3,4, 3.f,4.f, 4.5f,1.5f);
+    path.close();
+    pathB.moveTo(3,4);
+    pathB.cubicTo(3.f,4.f, 4.5f,1.5f, 0,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loop3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(0,1);
+    path.cubicTo(3,5, -3.66666651f,0, 10.5f,-1.66666651f);
+    path.close();
+    pathB.moveTo(3,5);
+    pathB.cubicTo(-3.66666651f,0, 10.5f,-1.66666651f, 0,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loop4(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(0,5);
+    path.cubicTo(1,5, 1,4, 0.833333313f,3);
+    path.close();
+    pathB.moveTo(1,5);
+    pathB.cubicTo(1,4, 0.833333313f,3, 0,5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+#include "SkParsePath.h"
+
+static void issue3517(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+
+    const char str[] = "M31.35 57.75L31.35 57.75C31.9 57.7486 32.45 57.7948 33 57.7413C33.55 57.6878 34.1 57.5014 34.65 57.4291C35.2 57.3569 35.75 57.3223 36.3 57.3079C36.85 57.2935 37.4 57.3143 37.95 57.3428C38.5 57.3712 39.05 57.4112 39.6 57.4786C40.15 57.546 40.7 57.7029 41.25 57.7472C41.8 57.7916 42.35 57.7962 42.9 57.7445C43.45 57.6928 44 57.5345 44.55 57.4373C45.1 57.34 45.65 57.2115 46.2 57.1611C46.75 57.1107 47.3 57.1371 47.85 57.1349C48.4 57.1327 48.95 57.144 49.5 57.1478C50.05 57.1516 50.6 57.1553 51.15 57.1579C51.7 57.1605 52.25 57.1601 52.8 57.1634C53.35 57.1667 53.9 57.1731 54.45 57.1776C55 57.182 55.55 57.1916 56.1 57.19C56.65 57.1884 57.2 57.178 57.75 57.168C58.3 57.158 58.85 57.1355 59.4 57.1299C59.95 57.1243 60.5 57.1338 61.05 57.1345C61.6 57.1352 62.15 57.124 62.7 57.134C63.25 57.1441 63.8 57.1731 64.35 57.195C64.9 57.2169 65.45 57.2532 66 57.2655C66.55 57.2778 67.1 57.2647 67.65 57.2687C68.2 57.2728 68.75 57.267 69.3 57.2896C69.85 57.3122 70.4 57.371 70.95 57.4044C71.5 57.4377 72.05 57.4668 72.6 57.4896C73.15 57.5123 73.7 57.545 74.25 57.5408C74.8 57.5365 75.35 57.5068 75.9 57.4641C76.45 57.4213 77 57.3244 77.55 57.2842C78.1 57.244 78.65 57.2163 79.2 57.2228C79.75 57.2293 80.3 57.29 80.85 57.3232C81.4 57.3563 81.95 57.396 82.5 57.4219C83.05 57.4478 83.6 57.4637 84.15 57.4787C84.7 57.4937 85.25 57.5011 85.8 57.5121C86.35 57.523 86.9 57.5411 87.45 57.5444C88 57.5477 88.55 57.5663 89.1 57.5318C89.65 57.4972 90.2 57.3126 90.75 57.337C91.3 57.3613 91.85 57.6088 92.4 57.6776C92.95 57.7465 93.5 57.7379 94.05 57.75C94.6 57.7621 95.15 57.75 95.7 57.75L95.7 57.75L31.35 57.75Z";
+    SkParsePath::FromSVGString(str, &path);
+
+    const char strB[] = "M31.35 57.75L31.35 57.75C31.9 57.7514 32.45 57.7052 33 57.7587C33.55 57.8122 34.1 57.9986 34.65 58.0709C35.2 58.1431 35.75 58.1777 36.3 58.1921C36.85 58.2065 37.4 58.1857 37.95 58.1572C38.5 58.1288 39.05 58.0888 39.6 58.0214C40.15 57.954 40.7 57.7971 41.25 57.7528C41.8 57.7084 42.35 57.7038 42.9 57.7555C43.45 57.8072 44 57.9655 44.55 58.0627C45.1 58.16 45.65 58.2885 46.2 58.3389C46.75 58.3893 47.3 58.3629 47.85 58.3651C48.4 58.3673 48.95 58.356 49.5 58.3522C50.05 58.3484 50.6 58.3447 51.15 58.3421C51.7 58.3395 52.25 58.3399 52.8 58.3366C53.35 58.3333 53.9 58.3269 54.45 58.3224C55 58.318 55.55 58.3084 56.1 58.31C56.65 58.3116 57.2 58.322 57.75 58.332C58.3 58.342 58.85 58.3645 59.4 58.3701C59.95 58.3757 60.5 58.3662 61.05 58.3655C61.6 58.3648 62.15 58.376 62.7 58.366C63.25 58.3559 63.8 58.3269 64.35 58.305C64.9 58.2831 65.45 58.2468 66 58.2345C66.55 58.2222 67.1 58.2353 67.65 58.2313C68.2 58.2272 68.75 58.233 69.3 58.2104C69.85 58.1878 70.4 58.129 70.95 58.0956C71.5 58.0623 72.05 58.0332 72.6 58.0104C73.15 57.9877 73.7 57.955 74.25 57.9592C74.8 57.9635 75.35 57.9932 75.9 58.0359C76.45 58.0787 77 58.1756 77.55 58.2158C78.1 58.256 78.65 58.2837 79.2 58.2772C79.75 58.2707 80.3 58.21 80.85 58.1768C81.4 58.1437 81.95 58.104 82.5 58.0781C83.05 58.0522 83.6 58.0363 84.15 58.0213C84.7 58.0063 85.25 57.9989 85.8 57.9879C86.35 57.977 86.9 57.9589 87.45 57.9556C88 57.9523 88.55 57.9337 89.1 57.9682C89.65 58.0028 90.2 58.1874 90.75 58.163C91.3 58.1387 91.85 57.8912 92.4 57.8224C92.95 57.7535 93.5 57.7621 94.05 57.75C94.6 57.7379 95.15 57.75 95.7 57.75L95.7 57.75L31.35 57.75Z";
+    SkParsePath::FromSVGString(strB, &pathB);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
+}
+
+static void cubicOp119(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(3,5, 2,1, 3,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1,2);
+    pathB.cubicTo(1,3, 1,0, 5,3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubicOp120(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(2,4, 2,1, 4,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1,2);
+    pathB.cubicTo(0,4, 1,0, 4,2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp121(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(3,4, 3,2, 4,3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2,3);
+    pathB.cubicTo(3,4, 1,0, 4,3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+// FIXME : haven't debugged this failure yet
+static void cubicOp122(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(3,5, 4,1, 4,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1,4);
+    pathB.cubicTo(0,4, 1,0, 5,3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp123(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(1,5, 2,0, 6,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,2);
+    pathB.cubicTo(0,6, 1,0, 5,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void loop5(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(0,2);
+    path.cubicTo(1,2, 1,1.66666663f, 0.833333313f,1.33333325f);
+    path.close();
+    pathB.moveTo(1,2);
+    pathB.cubicTo(1,1.66666663f, 0.833333313f,1.33333325f, 0,2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loop6(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(0,1);
+    path.cubicTo(1,3, -1.66666675f,1.66666663f, 4.16666651f,1.00000012f);
+    path.close();
+    pathB.moveTo(1,3);
+    pathB.cubicTo(-1.66666675f,1.66666663f, 4.16666651f,1.00000012f, 0,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubicOp124(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(1,5, 6,0, 3,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,6);
+    pathB.cubicTo(0,3, 1,0, 5,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp125(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(3,6, 3,1, 6,2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1,3);
+    pathB.cubicTo(2,6, 1,0, 6,3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp126(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(0,3, 6,0, 2,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,6);
+    pathB.cubicTo(1,2, 1,0, 3,0);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp127(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(1,5, 6,0, 3,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,6);
+    pathB.cubicTo(0,3, 1,0, 5,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubicOp128(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(0,3, 3,2, 5,2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2,3);
+    pathB.cubicTo(2,5, 1,0, 3,0);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp129(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(5,6);
+    path.cubicTo(3,4, 2,0, 2,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,2);
+    pathB.cubicTo(1,2, 6,5, 4,3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp130(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(5,6);
+    path.cubicTo(4,6, 3,0, 2,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,3);
+    pathB.cubicTo(1,2, 6,5, 6,4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+#include "SkGeometry.h"
+
+static void complex_to_quads(const SkPoint pts[], SkPath* path) {
+    SkScalar loopT;
+    SkDCubic::CubicType dType;
+    if (SkDCubic::ComplexBreak(pts, &loopT, &dType)) {
+        SkPoint cubicPair[7]; 
+        SkChopCubicAt(pts, cubicPair, loopT);
+        SkDCubic c1, c2;
+        c1.set(cubicPair);
+        c2.set(&cubicPair[3]);
+        SkDQuad q1 = c1.toQuad();
+        SkDQuad q2 = c2.toQuad();
+        path->quadTo(q1[1].asSkPoint(), q1[2].asSkPoint());
+        path->quadTo(q2[1].asSkPoint(), q2[2].asSkPoint());
+    } else {
+        path->cubicTo(pts[1], pts[2], pts[3]);
+    }
+}
+
+static void cubicOp130a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(5,6);
+    SkPoint pts[] = { {5,6}, {4,6}, {3,0}, {2,1} };
+    complex_to_quads(pts, &path);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,3);
+    SkPoint pts2[] = { {0,3}, {1,2}, {6,5}, {6,4} };
+    complex_to_quads(pts2, &path);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp131(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(3,4, 3,0, 6,2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,3);
+    pathB.cubicTo(2,6, 1,0, 4,3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void circlesOp1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addCircle(0, 1, 2, SkPath::kCCW_Direction);
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.addCircle(0, 1, 1, SkPath::kCW_Direction);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void circlesOp2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addCircle(0, 1, 4, SkPath::kCCW_Direction);
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.addCircle(0, 4, 3, SkPath::kCW_Direction);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void rRect1x(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(20.65f, 5.65f);
+    path.conicTo(20.65f, 1.13612f, 25.1404f, 0.65f, 0.888488f);
+    path.lineTo(25.65f, 0.65f);
+    path.lineTo(26.1596f, 0.67604f);
+    path.conicTo(30.65f, 1.13612f, 30.65f, 5.65f, 0.888488f);
+    path.lineTo(30.65f, 25.65f);
+    path.conicTo(30.65f, 20.65f, 25.65f, 20.65f, 0.707107f);
+    path.lineTo(20.65f, 20.65f);
+    path.lineTo(20.65f, 5.65f);
+    path.close();
+    path.moveTo(20.65f, 20.65f);
+    path.lineTo(5.65f, 20.65f);
+    path.conicTo(0.65f, 20.65f, 0.65f, 25.65f, 0.707107f);
+    path.lineTo(0.65f, 45.65f);
+    path.conicTo(0.65f, 50.65f, 5.65f, 50.65f, 0.707107f);
+    path.lineTo(25.65f, 50.65f);
+    path.conicTo(30.65f, 50.65f, 30.65f, 45.65f, 0.707107f);
+    path.lineTo(30.65f, 25.65f);
+    path.conicTo(30.65f, 30.65f, 25.65f, 30.65f, 0.707107f);
+    path.conicTo(20.65f, 30.65f, 20.65f, 25.65f, 0.707107f);
+    path.lineTo(20.65f, 20.65f);
+    path.close();
+    SkPath path1(path);
+
+    path.reset();
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(20.65f, 45.65f);
+    path.lineTo(20.65f, 25.65f);
+    path.conicTo(20.65f, 20.65f, 25.65f, 20.65f, 0.707107f);
+    path.lineTo(45.65f, 20.65f);
+    path.conicTo(50.65f, 20.65f, 50.65f, 25.65f, 0.707107f);
+    path.lineTo(50.65f, 45.65f);
+    path.conicTo(50.65f, 50.65f, 45.65f, 50.65f, 0.707107f);
+    path.lineTo(25.65f, 50.65f);
+    path.conicTo(20.65f, 50.65f, 20.65f, 45.65f, 0.707107f);
+    path.close();
+    SkPath path2(path);
+
+    testPathOp(reporter, path1, path2, kDifference_SkPathOp, filename);
+}
+
+static void loop7(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(0,1);
+    path.cubicTo(3,4, -1,0, 8.5f,-2.5f);
+    path.close();
+    pathB.moveTo(3,4);
+    pathB.cubicTo(-1,0, 8.5f,-2.5f, 0,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void rects5(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addRect(5, 5, 6, 6, SkPath::kCW_Direction);
+    path.addRect(5, 5, 6, 6, SkPath::kCW_Direction);
+    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
+    pathB.addRect(5, 5, 6, 6, SkPath::kCW_Direction);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void loop8(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(0,1);
+    path.cubicTo(1,4, -3.83333325f,0.166666627f, 6,-1);
+    path.close();
+    pathB.moveTo(1,4);
+    pathB.cubicTo(-3.83333325f,0.166666627f, 6,-1, 0,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loop9(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(0,1);
+    path.cubicTo(1,3, -2.5f,0, 3.33333325f,-0.666666627f);
+    path.close();
+    pathB.moveTo(1,3);
+    pathB.cubicTo(-2.5f,0, 3.33333325f,-0.666666627f, 0,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void circlesOp3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addCircle(0, 1, 2, SkPath::kCCW_Direction);
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.addCircle(3, 5, 3, SkPath::kCW_Direction);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void loop10(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(5,6);
+    path.cubicTo(1,2, 1,2, -3.66666651f,13.333334f);
+    path.close();
+    pathB.moveTo(1,2);
+    pathB.cubicTo(1,2, -3.66666651f,13.333334f, 5,6);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loop11(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(0,1);
+    path.cubicTo(1,3, -1.83333349f,1.33333337f, 4,-1);
+    path.close();
+    pathB.moveTo(1,3);
+    pathB.cubicTo(-1.83333349f,1.33333337f, 4,-1, 0,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubicOp132(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(5,6);
+    path.cubicTo(3,4, 3,0, 3,2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,3);
+    pathB.cubicTo(2,3, 6,5, 4,3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void loop12(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(1,2);
+    path.cubicTo(0,6, -3.16666675f,3.66666675f, 6.33333349f,3.33333349f);
+    path.close();
+    pathB.moveTo(0,6);
+    pathB.cubicTo(-3.16666675f,3.66666675f, 6.33333349f,3.33333349f, 1,2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubicOp133(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(5,6);
+    path.cubicTo(5,6, 5,0, 4,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,5);
+    pathB.cubicTo(1,4, 6,5, 6,5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp134(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(5,6);
+    path.cubicTo(5,6, 6,0, 3,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,6);
+    pathB.cubicTo(1,3, 6,5, 6,5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp135(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(5,6);
+    path.cubicTo(5,6, 6,0, 4,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,6);
+    pathB.cubicTo(1,4, 6,5, 6,5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp136(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(5,6);
+    path.cubicTo(5,6, 5,0, 3,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,5);
+    pathB.cubicTo(1,3, 6,5, 6,5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp136a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(5,6);
+    path.quadTo(5,0, 3,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,5);
+    pathB.cubicTo(1,3, 6,5, 6,5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics137(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 5);
+    path.cubicTo(3, 6, 1, 0, 3, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 1);
+    pathB.cubicTo(2, 3, 5, 0, 6, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics138(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 5);
+    path.cubicTo(3, 6, 1, 0, 4, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 1);
+    pathB.cubicTo(2, 4, 5, 0, 6, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+// three curves intersect successfully nearby -- the angle only gets 2 of the 3 pts
+static void cubicOp139(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,2);
+    path.cubicTo(0,4, 3,1, 5,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1,3);
+    pathB.cubicTo(1,5, 2,0, 4,0);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp140(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,2);
+    path.cubicTo(1,2, 5,4, 3,2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(4,5);
+    pathB.cubicTo(2,3, 2,0, 2,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp141(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,2);
+    path.cubicTo(1,2, 6,4, 3,2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(4,6);
+    pathB.cubicTo(2,3, 2,0, 2,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void quadRect1(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(6,15);
+    path.quadTo(16,0, 8,4);
+    path.quadTo(2,7, 12,12);
+    path.close();
+    pathB.addRect(4,11, 13,16);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void quadRect2(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(5,12);
+    path.quadTo(15,7, 9,4);
+    path.quadTo(1,0, 11,15);
+    path.close();
+    pathB.addRect(4,11, 13,16);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void quadRect3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(12,12);
+    path.quadTo(2,7, 8,4);
+    path.quadTo(16,0, 6,15);
+    path.close();
+    pathB.addRect(4,11, 13,16);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void quadRect4(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(11,15);
+    path.quadTo(1,0, 9,4);
+    path.quadTo(15,7, 5,12);
+    path.close();
+    pathB.addRect(4,11, 13,16);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void quadRect5(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(11,13);
+    path.quadTo(4,4, 8,4);
+    path.quadTo(12,4, 5,13);
+    path.close();
+    pathB.addRect(4,11, 13,16);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void quadRect6(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.moveTo(5,13);
+    path.quadTo(12,4, 8,4);
+    path.quadTo(4,4, 11,13);
+    path.close();
+    pathB.addRect(4,11, 13,16);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops4i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 3);
+    path.cubicTo(0, 2, 0, 2, -1.66666663f, 2.16666675f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 2);
+    pathB.cubicTo(0, 2, -1.66666663f, 2.16666675f, 0, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops5i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 2);
+    path.cubicTo(0, 2, 0, 2, 0.166666672f, 2.66666675f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 2);
+    pathB.cubicTo(0, 2, 0.166666672f, 2.66666675f, 1, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubicOp142(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(5,6);
+    path.cubicTo(2,5, 2,1, 1,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1,2);
+    pathB.cubicTo(0,1, 6,5, 5,2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics6d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 5);
+    path.cubicTo(1, 5, 4, 2, 4, 0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2, 4);
+    pathB.cubicTo(0, 4, 5, 3, 5, 1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics7d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 6);
+    path.cubicTo(2, 4, 5, 1, 3, 1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 5);
+    pathB.cubicTo(1, 3, 6, 2, 4, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics8d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 5);
+    path.cubicTo(2, 4, 5, 1, 3, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 5);
+    pathB.cubicTo(2, 3, 5, 2, 4, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics9d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 4);
+    path.cubicTo(2, 6, 3, 1, 5, 1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 3);
+    pathB.cubicTo(1, 5, 4, 2, 6, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics10u(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 4);
+    path.cubicTo(1, 6, 4, 1, 5, 1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 4);
+    pathB.cubicTo(1, 5, 4, 2, 6, 1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
+}
+
+static void cubics11i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 4);
+    path.cubicTo(2, 5, 3, 2, 5, 1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2, 3);
+    pathB.cubicTo(1, 5, 4, 2, 5, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubics12d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 4);
+    path.cubicTo(0, 4, 5, 3, 5, 1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(3, 5);
+    pathB.cubicTo(1, 5, 4, 2, 4, 0);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics13d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 3);
+    path.cubicTo(1, 5, 4, 2, 5, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2, 4);
+    pathB.cubicTo(2, 5, 3, 2, 5, 1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics14d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 3);
+    path.cubicTo(0, 4, 3, 1, 3, 0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 3);
+    pathB.cubicTo(0, 3, 3, 2, 4, 0);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics15d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 5);
+    path.cubicTo(3, 5, 4, 0, 4, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 4);
+    pathB.cubicTo(2, 4, 5, 1, 5, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics16i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 5);
+    path.cubicTo(2, 5, 5, 0, 4, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 5);
+    pathB.cubicTo(2, 4, 5, 1, 5, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubics17d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 5);
+    path.cubicTo(3, 4, 4, 1, 4, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 4);
+    pathB.cubicTo(2, 4, 5, 1, 4, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics18d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 5);
+    path.cubicTo(1, 3, 4, 0, 2, 0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 4);
+    pathB.cubicTo(0, 2, 5, 1, 3, 1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics19d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 5);
+    path.cubicTo(2, 3, 5, 2, 4, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2, 5);
+    pathB.cubicTo(2, 4, 5, 1, 3, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubicOp157(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1,5);
+    path.cubicTo(1,3, 6,2, 4,2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2,6);
+    pathB.cubicTo(2,4, 5,1, 3,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics20d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 2);
+    path.cubicTo(0, 3, 6, 0, 3, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 6);
+    pathB.cubicTo(2, 3, 2, 1, 3, 0);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void loops20i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 2);
+    path.cubicTo(0, 2, 0.833333313f, 2, 1, 3.66666651f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 2);
+    pathB.cubicTo(0.833333313f, 2, 1, 3.66666651f, 1, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops21i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 2);
+    path.cubicTo(0, 2, 0.833333313f, 2, 1, 4);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 2);
+    pathB.cubicTo(0.833333313f, 2, 1, 4, 1, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops22i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 3);
+    path.cubicTo(0, 3, 0.833333313f, 3, 1, 4.66666651f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 3);
+    pathB.cubicTo(0.833333313f, 3, 1, 4.66666651f, 1, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops23i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 5);
+    path.cubicTo(0, 1, 6.16666698f, 5.66666698f, -5.66666651f, 6.66666651f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 1);
+    pathB.cubicTo(6.16666698f, 5.66666698f, -5.66666651f, 6.66666651f, 1, 5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops24i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 2);
+    path.cubicTo(0, 2, 0.833333313f, 2, 1, 3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 2);
+    pathB.cubicTo(0.833333313f, 2, 1, 3, 1, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops25i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 5);
+    path.cubicTo(0, 5, 0.833333313f, 5, 1, 7);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 5);
+    pathB.cubicTo(0.833333313f, 5, 1, 7, 1, 5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops26i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 6);
+    path.cubicTo(0, 2, 6.16666698f, 6.66666698f, -5.66666651f, 7.66666651f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 2);
+    pathB.cubicTo(6.16666698f, 6.66666698f, -5.66666651f, 7.66666651f, 1, 6);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops27i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 3);
+    path.cubicTo(0, 3, 0.833333313f, 3, 1, 4.33333349f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 3);
+    pathB.cubicTo(0.833333313f, 3, 1, 4.33333349f, 1, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops28i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 3);
+    path.cubicTo(1, 3, 1.83333337f, 3, 2, 4.66666651f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 3);
+    pathB.cubicTo(1.83333337f, 3, 2, 4.66666651f, 2, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops29i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 4);
+    path.cubicTo(0, 4, 1.66666663f, 4, 2, 7.33333302f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 4);
+    pathB.cubicTo(1.66666663f, 4, 2, 7.33333302f, 2, 4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops30i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 4);
+    path.cubicTo(0, 4, 1.66666663f, 4, 2, 8);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 4);
+    pathB.cubicTo(1.66666663f, 4, 2, 8, 2, 4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops31i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 5);
+    path.cubicTo(1, 5, 1.83333337f, 5, 2, 6.66666651f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 5);
+    pathB.cubicTo(1.83333337f, 5, 2, 6.66666651f, 2, 5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops32i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 6);
+    path.cubicTo(1, 6, 1.83333337f, 6, 2, 8);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 6);
+    pathB.cubicTo(1.83333337f, 6, 2, 8, 2, 6);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops33i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 6);
+    path.cubicTo(1, 2, 7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 2);
+    pathB.cubicTo(7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f, 2, 6);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops33iMod(skiatest::Reporter* reporter, const char* filename) {
+    SkPoint pts[] = {{2, 6}, {1, 2}, {7.16666698f, 6.66666698f}, {-4.66666651f, 7.66666651f},
+                     {1, 2}, {7.16666698f, 6.66666698f}, {-4.66666651f, 7.66666651f}, {2, 6}};
+    bool up = false;
+    float offset = 0.0380172729f;
+    float step = 7.62939453e-006f;
+    bool lastResult = true;
+ //   for (int i = 0; i < 30; ++i) {
+        SkString name(filename);
+ //       name.appendS32(i);
+ //       if (i > 0) {
+ //           SkDebugf("\n\n<div id=\"%s\">\n", name.c_str());
+ //       }
+        pts[5].fY = 6.66666698f + offset;
+        SkPath path, pathB;
+        path.setFillType(SkPath::kWinding_FillType);
+        path.moveTo(pts[0]);
+        path.cubicTo(pts[1], pts[2], pts[3]);
+        path.close();
+        pathB.setFillType(SkPath::kWinding_FillType);
+        pathB.moveTo(pts[4]);
+        pathB.cubicTo(pts[5], pts[6], pts[7]);
+        pathB.close();
+        bool result = testPathOp(reporter, path, pathB, kIntersect_SkPathOp, name.c_str());
+        if (lastResult != result) {
+            up = !up;
+        }
+        step /= 2;
+        offset += up ? step : -step;
+        lastResult = result;
+ //   }
+}
+
+
+static void loops33iAsQuads(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 6);
+    path.cubicTo(1, 2, 7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 2);
+    pathB.cubicTo(7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f, 2, 6);
+    pathB.close();
+    SkPath qPath, qPathB;
+    CubicPathToQuads(path, &qPath);
+    CubicPathToQuads(pathB, &qPathB);
+    testPathOp(reporter, qPath, qPathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops34i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 4);
+    path.cubicTo(0, 4, 2.5f, 4, 3, 9);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 4);
+    pathB.cubicTo(2.5f, 4, 3, 9, 3, 4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops35i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 4);
+    path.cubicTo(0, 4, 2.5f, 4, 3, 10);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 4);
+    pathB.cubicTo(2.5f, 4, 3, 10, 3, 4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops36i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 4);
+    path.cubicTo(1, 4, 2.66666675f, 4, 3, 8);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 4);
+    pathB.cubicTo(2.66666675f, 4, 3, 8, 3, 4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops37i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 4);
+    path.cubicTo(1, 4, 1.83333337f, 4, 2, 5.33333349f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 4);
+    pathB.cubicTo(1.83333337f, 4, 2, 5.33333349f, 2, 4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops38i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 4);
+    path.cubicTo(2, 4, 2.83333325f, 4, 3, 6);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2, 4);
+    pathB.cubicTo(2.83333325f, 4, 3, 6, 3, 4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops39i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 5);
+    path.cubicTo(0, 5, 2.5f, 5, 3, 10);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 5);
+    pathB.cubicTo(2.5f, 5, 3, 10, 3, 5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops40i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 5);
+    path.cubicTo(0, 5, 2.5f, 5, 3, 11);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 5);
+    pathB.cubicTo(2.5f, 5, 3, 11, 3, 5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops40iAsQuads(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 5);
+    path.cubicTo(0, 5, 2.5f, 5, 3, 11);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 5);
+    pathB.cubicTo(2.5f, 5, 3, 11, 3, 5);
+    pathB.close();
+    SkPath qPath, qPathB;
+    CubicPathToQuads(path, &qPath);
+    CubicPathToQuads(pathB, &qPathB);
+    testPathOp(reporter, qPath, qPathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops44i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 5);
+    path.cubicTo(0, 1, 7.33333302f, 5.33333349f, -7, 7);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 1);
+    pathB.cubicTo(7.33333302f, 5.33333349f, -7, 7, 1, 5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops45i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 6);
+    path.cubicTo(0, 2, 7.33333302f, 6.33333302f, -7, 8);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 2);
+    pathB.cubicTo(7.33333302f, 6.33333302f, -7, 8, 1, 6);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops46i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 6);
+    path.cubicTo(1, 2, 8.33333302f, 6.33333302f, -6, 8);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 2);
+    pathB.cubicTo(8.33333302f, 6.33333302f, -6, 8, 2, 6);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+/*
+FAILED: d:\cygwin\puregit\tests\pathopsextendedtest.cpp:346	0 */
+static void loops47i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 4);
+    path.cubicTo(0, 1, 6, 5.83333302f, -4, 8);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 1);
+    pathB.cubicTo(6, 5.83333302f, -4, 8, 2, 4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops48i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 6);
+    path.cubicTo(0, 1, 9.33333302f, 6.83333302f, -8.33333302f, 9.16666603f);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 1);
+    pathB.cubicTo(9.33333302f, 6.83333302f, -8.33333302f, 9.16666603f, 2, 6);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops49i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 2);
+    path.cubicTo(1, 4, -0.166666687f, 2.66666675f, 1.66666675f, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 4);
+    pathB.cubicTo(-0.166666687f, 2.66666675f, 1.66666675f, 2, 0, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops50i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 3);
+    path.cubicTo(1, 5, -0.166666687f, 3.66666675f, 1.66666675f, 3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 5);
+    pathB.cubicTo(-0.166666687f, 3.66666675f, 1.66666675f, 3, 0, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops51i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 2);
+    path.cubicTo(2, 4, 0.833333313f, 2.66666675f, 2.66666675f, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2, 4);
+    pathB.cubicTo(0.833333313f, 2.66666675f, 2.66666675f, 2, 1, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops52i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 3);
+    path.cubicTo(2, 5, 0.833333313f, 3.66666675f, 2.66666675f, 3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2, 5);
+    pathB.cubicTo(0.833333313f, 3.66666675f, 2.66666675f, 3, 1, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops53i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 3);
+    path.cubicTo(3, 5, 1.83333325f, 3.66666675f, 3.66666651f, 3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(3, 5);
+    pathB.cubicTo(1.83333325f, 3.66666675f, 3.66666651f, 3, 2, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops54i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 2);
+    path.cubicTo(1, 4, 0, 3, 1.66666675f, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 4);
+    pathB.cubicTo(0, 3, 1.66666675f, 2, 0, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops55i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 3);
+    path.cubicTo(1, 5, 0, 4, 1.66666675f, 3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 5);
+    pathB.cubicTo(0, 4, 1.66666675f, 3, 0, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops56i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 2);
+    path.cubicTo(2, 4, 0.99999994f, 3, 2.66666675f, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2, 4);
+    pathB.cubicTo(0.99999994f, 3, 2.66666675f, 2, 1, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops57i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 3);
+    path.cubicTo(2, 5, 0.99999994f, 4, 2.66666675f, 3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2, 5);
+    pathB.cubicTo(0.99999994f, 4, 2.66666675f, 3, 1, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops58i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 3);
+    path.cubicTo(3, 5, 2, 4, 3.66666651f, 3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(3, 5);
+    pathB.cubicTo(2, 4, 3.66666651f, 3, 2, 3);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops58iAsQuads(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(2, 3);
+    path.cubicTo(3, 5, 2, 4, 3.66666651f, 3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(3, 5);
+    pathB.cubicTo(2, 4, 3.66666651f, 3, 2, 3);
+    pathB.close();
+    SkPath qPath, qPathB;
+    CubicPathToQuads(path, &qPath);
+    CubicPathToQuads(pathB, &qPathB);
+//    SkPoint from = {2.61714339f,1.90228665f};
+//    SkPoint to = {2.617045833359139f,1.9013528935803314f};
+//    path_edit(from, to, &qPathB);
+    testPathOp(reporter, qPath, qPathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops59i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 6);
+    path.cubicTo(1, 2, 7.33333302f, 1.66666663f, -7.5f, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 2);
+    pathB.cubicTo(7.33333302f, 1.66666663f, -7.5f, 2, 0, 6);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops59iasQuads(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 6);
+    path.cubicTo(1, 2, 7.33333302f, 1.66666663f, -7.5f, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 2);
+    pathB.cubicTo(7.33333302f, 1.66666663f, -7.5f, 2, 0, 6);
+    pathB.close();
+    SkPath qPath, qPathB;
+    CubicPathToQuads(path, &qPath);
+    CubicPathToQuads(pathB, &qPathB);
+    SkPoint from = {2.61714339f,1.90228665f};
+    SkPoint to = {2.617045833359139f,1.9013528935803314f};
+    path_edit(from, to, &qPathB);
+    testPathOp(reporter, qPath, qPathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubics41d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 1);
+    path.cubicTo(1, 4, 3, 0, 3, 1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0, 3);
+    pathB.cubicTo(1, 3, 1, 0, 4, 1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+void loops61i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 1);
+    path.cubicTo(1, 5, -6.33333302f, 0.666666627f, 8, -1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 5);
+    pathB.cubicTo(-6.33333302f, 0.666666627f, 8, -1, 0, 1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops62i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 2);
+    path.cubicTo(1, 6, -6.33333302f, 1.66666663f, 8, 0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 6);
+    pathB.cubicTo(-6.33333302f, 1.66666663f, 8, 0, 0, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void loops63i(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0, 1);
+    path.cubicTo(2, 4, -4, -0.833333254f, 6, -3);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2, 4);
+    pathB.cubicTo(-4, -0.833333254f, 6, -3, 0, 1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+}
+
+static void cubics44d(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(3, 4);
+    path.cubicTo(2, 5, 3, 1, 6, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1, 3);
+    pathB.cubicTo(2, 6, 4, 3, 5, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
+}
+
+static void cubics45u(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(1, 3);
+    path.cubicTo(2, 6, 4, 3, 5, 2);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(3, 4);
+    pathB.cubicTo(2, 5, 3, 1, 6, 2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
+}
+
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = loops63i;
 static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
+#define TEST(name) { name, #name }
+
 static struct TestDesc tests[] = {
+    TEST(cubics44d),
+    TEST(cubics45u),
+    TEST(loops61i),
+    TEST(loops62i),
+    TEST(loops63i),
+    TEST(loops58iAsQuads),
+    TEST(cubics41d),
+    TEST(loops59iasQuads),
+    TEST(loops59i),
+    TEST(loops44i),
+    TEST(loops45i),
+    TEST(loops46i),
+    TEST(loops47i),
+    TEST(loops48i),
+    TEST(loops49i),
+    TEST(loops50i),
+    TEST(loops51i),
+    TEST(loops52i),
+    TEST(loops53i),
+    TEST(loops54i),
+    TEST(loops55i),
+    TEST(loops56i),
+    TEST(loops57i),
+    TEST(loops58i),
+    TEST(loops33iMod),
+    TEST(loops33iAsQuads),
+    TEST(loops33i),
+    TEST(loops40i),
+    TEST(loops40iAsQuads),
+    TEST(loops39i),
+    TEST(loops38i),
+    TEST(loops37i),
+    TEST(loops36i),
+    TEST(loops35i),
+    TEST(loops34i),
+    TEST(loops32i),
+    TEST(loops31i),
+    TEST(loops30i),
+    TEST(loops29i),
+    TEST(loops28i),
+    TEST(loops27i),
+    TEST(loops26i),
+    TEST(loops25i),
+    TEST(loops24i),
+    TEST(loops23i),
+    TEST(loops22i),
+    TEST(loops21i),
+    TEST(loops20i),
+    TEST(cubics20d),
+    TEST(cubics6d),
+    TEST(cubics7d),
+    TEST(cubics8d),
+    TEST(cubics9d),
+    TEST(cubics10u),
+    TEST(cubics11i),
+    TEST(cubics12d),
+    TEST(cubics13d),
+    TEST(cubics14d),
+    TEST(cubics15d),
+    TEST(cubics16i),
+    TEST(cubics17d),
+    TEST(cubics18d),
+    TEST(cubics19d),
+    TEST(cubicOp157),
+    TEST(cubicOp142),
+    TEST(loops4i),
+    TEST(quadRect1),
+    TEST(quadRect2),
+    TEST(quadRect3),
+    TEST(quadRect4),
+    TEST(quadRect5),
+    TEST(quadRect6),
+    TEST(cubicOp141),
+    TEST(cubicOp58d),
+    TEST(loops5i),
+    TEST(cubicOp140),
+    TEST(cubicOp139),
+    TEST(cubics138),
+    TEST(cubics137),
+    TEST(cubicOp136a),
+    TEST(cubicOp136),
+    TEST(cubicOp135),
+    TEST(cubicOp134),
+    TEST(cubicOp133),
+    TEST(loop12),
+    TEST(cubicOp132),
+    TEST(loop11),
+    TEST(loop10),
+    TEST(circlesOp3),
+    TEST(loop9),
+    TEST(loop8),
+    TEST(rects5),
+    TEST(loop7),
+    TEST(cubicOp130a),
+    TEST(rRect1x),
+    TEST(circlesOp2),
+    TEST(circlesOp1),
+    TEST(cubicOp131),
+    TEST(cubicOp130),
+    TEST(cubicOp129),
+    TEST(cubicOp128),
+    TEST(cubicOp127),
+    TEST(cubicOp126),
+    TEST(cubicOp125),
+    TEST(cubicOp124),
+    TEST(loop6),
+    TEST(loop5),
+    TEST(cubicOp123),
+    TEST(cubicOp122),
+    TEST(cubicOp121),
+    TEST(cubicOp120),
+    TEST(cubicOp119),
+    TEST(loop4),
+    TEST(loop3),
+    TEST(loop2),
+    TEST(loop1asQuad),
+    TEST(loop1),
+    TEST(issue3517),
+    TEST(cubicOp118),
+    TEST(cubicOp117),
+    TEST(cubicOp116),
+    TEST(testRect2),
+    TEST(testRect1),
     TEST(cubicOp115),
-    TEST(issue2753),  // FIXME: pair of cubics miss intersection
-    TEST(cubicOp114),  // FIXME: curve with inflection is ordered the wrong way
+    TEST(issue2753),
+    TEST(cubicOp114),
     TEST(issue2808),
     TEST(cubicOp114asQuad),
     TEST(rects4),
@@ -3527,8 +5297,6 @@
     TEST(kari1),
     TEST(quadOp10i),
     TEST(cubicOp113),
-    // fails because a cubic/quadratic intersection is missed
-    // the internal quad/quad is far enough away from the real cubic/quad that it is rejected
     TEST(skpcarrot_is24),
     TEST(issue1417),
     TEST(cubicOp112),
@@ -3555,7 +5323,7 @@
     TEST(issue1435),
     TEST(cubicOp98x),
     TEST(cubicOp97x),
-    TEST(skpcarpetplanet_ru22),  // cubic/cubic intersect detects unwanted coincidence
+    TEST(skpcarpetplanet_ru22),
     TEST(cubicOp96d),
     TEST(cubicOp95u),
     TEST(skpadbox_lt15),
@@ -3644,7 +5412,6 @@
     TEST(cubicOp61d),
     TEST(cubicOp60d),
     TEST(cubicOp59d),
-    TEST(cubicOp58d),
     TEST(cubicOp57d),
     TEST(cubicOp56d),
     TEST(cubicOp55d),
@@ -3730,8 +5497,10 @@
 static const size_t testCount = SK_ARRAY_COUNT(tests);
 
 static struct TestDesc subTests[] = {
-    TEST(cubicOp58d),
-    TEST(cubicOp53d),
+    TEST(loops47i),
+    TEST(loops61i),
+    TEST(loops62i),
+    TEST(issue3517),
 };
 
 static const size_t subTestCount = SK_ARRAY_COUNT(subTests);
@@ -3739,7 +5508,7 @@
 static void (*firstSubTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static bool runSubTests = false;
-static bool runSubTestsFirst = false;
+static bool runSubTestsFirst = true;
 static bool runReverse = false;
 
 DEF_TEST(PathOpsOp, reporter) {
@@ -3747,11 +5516,11 @@
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
     if (runSubTests && runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
     }
-    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
     if (runSubTests && !runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
     }
 }
 
@@ -3760,7 +5529,7 @@
     path.addRect(0,0, 300,170141183460469231731687303715884105728.f);
     SkPath pathB;
     pathB.addRect(0,0, 300,16);
-    testPathFailOp(reporter, path, pathB, kUnion_PathOp, filename);
+    testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 // m 100,0 60,170 -160,-110 200,0 -170,11000000000 z
@@ -3780,7 +5549,7 @@
     path2.lineTo(-170 + 20,11000000000.0f + 20);
     path2.close();
 
-    testPathFailOp(reporter, path1, path2, kIntersect_PathOp, filename);
+    testPathOpCheck(reporter, path1, path2, kIntersect_SkPathOp, filename, FLAGS_runFail);
 }
 
 static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
@@ -3803,7 +5572,7 @@
     path2.lineTo(190, 60);
     path2.close();
 
-    testPathFailOp(reporter, path1, path2, kUnion_PathOp, filename);
+    testPathOpCheck(reporter, path1, path2, kUnion_SkPathOp, filename, FLAGS_runFail);
 }
 
 static void fuzz487a(skiatest::Reporter* reporter, const char* filename) {
@@ -3849,7 +5618,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 static void fuzz487b(skiatest::Reporter* reporter, const char* filename) {
@@ -3895,7 +5664,7 @@
 path.close();
 
     SkPath path2(path);
-    testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 static void fuzz714(skiatest::Reporter* reporter, const char* filename) {
@@ -3904,8 +5673,8 @@
 path.moveTo(SkBits2Float(0x430c0000), SkBits2Float(0x42200000));
 path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0x43520000));
 path.lineTo(SkBits2Float(0x42200000), SkBits2Float(0x42c80000));
-path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x42c80000));
-path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x43520000));
+path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x42c80000));  // 2.22222e+022f
+path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x43520000));  // 2.22222e+022f
 path.lineTo(SkBits2Float(0x430c0000), SkBits2Float(0x42200000));
 path.close();
 
@@ -3915,13 +5684,13 @@
 path.moveTo(SkBits2Float(0x43200000), SkBits2Float(0x42700000));
 path.lineTo(SkBits2Float(0x435c0000), SkBits2Float(0x43660000));
 path.lineTo(SkBits2Float(0x42700000), SkBits2Float(0x42f00000));
-path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x42f00000));
-path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x43660000));
+path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x42f00000));  // 2.22222e+022f
+path.lineTo(SkBits2Float(0x64969569), SkBits2Float(0x43660000));  // 2.22222e+022f
 path.lineTo(SkBits2Float(0x43200000), SkBits2Float(0x42700000));
 path.close();
 
     SkPath path2(path);
-    testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
+    testPathOpCheck(reporter, path1, path2, (SkPathOp) 2, filename, FLAGS_runFail);
 }
 
 static void fuzz1(skiatest::Reporter* reporter, const char* filename) {
@@ -3947,12 +5716,12 @@
 }
 
 static struct TestDesc failTests[] = {
+    TEST(fuzz487a),
+    TEST(fuzz433),
     TEST(fuzz1),
     TEST(fuzz714),
-    TEST(fuzz487a),
     TEST(fuzz487b),
     TEST(fuzz433b),
-    TEST(fuzz433),
     TEST(bufferOverflow),
 };
 
@@ -3962,5 +5731,5 @@
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    RunTestSet(reporter, failTests, failTestCount, 0, 0, false);
+    RunTestSet(reporter, failTests, failTestCount, NULL, NULL, NULL, false);
 }
diff --git a/tests/PathOpsQuadIntersectionTest.cpp b/tests/PathOpsQuadIntersectionTest.cpp
index 565098a..f012622 100644
--- a/tests/PathOpsQuadIntersectionTest.cpp
+++ b/tests/PathOpsQuadIntersectionTest.cpp
@@ -53,6 +53,42 @@
 }
 
 static const SkDQuad testSet[] = {
+{{{1, 1}, {0, 2}, {3, 3}}},
+{{{3, 0}, {0, 1}, {1, 2}}},
+
+{{{0.33333333333333326, 0.81481481481481488}, {0.63395173631977997, 0.68744136726313931}, {1.205684411948591, 0.81344322326274499}}},
+{{{0.33333333333333326, 0.81481481481481488}, {0.63396444791444551, 0.68743368362444768}, {1.205732763658403, 0.81345617746834109}}},
+
+{{{4981.9990234375, 1590}, {4981.9990234375, 1617.7523193359375}, {4962.375, 1637.3760986328125}}},
+{{{4962.3759765625, 1637.3760986328125}, {4982, 1617.7523193359375}, {4982, 1590}}},
+
+{{{48.7416f, 7.74160004f}, {96.4831848f, -40}, {164, -40}}},
+{{{56.9671326f, 0}, {52.7835083f, 3.69968891f}, {48.7416f, 7.74160004f}}},
+
+{{{138, 80}, {147.15692138671875, 80}, {155.12803649902344, 82.86279296875}}},
+{{{155.12803649902344, 82.86279296875}, {153.14971923828125, 82.152290344238281}, {151.09841918945312, 81.618133544921875}}},
+
+{{{88, 130}, {88, 131.54483032226562}, {88.081489562988281, 133.0560302734375}}},
+{{{88.081489562988281, 133.0560302734375}, {88, 131.54483032226562}, {88, 130}}},
+
+{{{0.59987992,2.14448452}, {0.775417507,1.95606446}, {1.00564098,1.79310346}}},
+{{{1.00564098,1.79310346}, {1.25936198,1.615623}, {1.35901463,1.46834028}}},
+
+{{{3,0}, {0,1}, {3,2}}},
+{{{2,0}, {1,1}, {2,2}}},
+
+{{{38.656852722167969, 38.656852722167969}, {38.651023864746094, 38.662681579589844}, {38.644744873046875, 38.668937683105469}}},
+{{{38.656852722167969, 38.656852722167969}, {36.313709259033203, 41}, {33, 41}}},
+
+{{{4914.9990234375, 1523}, {4942.75146484375, 1523}, {4962.375, 1542.6239013671875}}},
+{{{4962.3759765625, 1542.6239013671875}, {4942.75244140625, 1523}, {4915, 1523}}},
+
+{{{4867.623046875, 1637.3760986328125}, {4847.9990234375, 1617.7523193359375}, {4847.9990234375, 1590}}},
+{{{4848, 1590}, {4848, 1617.7523193359375}, {4867.6240234375, 1637.3760986328125}}},
+
+{{{102.64466094970703, 165.3553466796875}, {110.79246520996094, 173.50314331054687}, {120.81797790527344, 177.11778259277344}}},
+{{{113.232177734375, 173.57899475097656}, {116.88026428222656, 175.69805908203125}, {120.81797790527344, 177.11778259277344}}},
+
 {{{-37.3484879,10.0192947}, {-36.4966316,13.2140198}, {-38.1506348,16.0788383}}},
 {{{-38.1462746,16.08918}, {-36.4904327,13.2193804}, {-37.3484879,10.0192947}}},
 
@@ -291,12 +327,12 @@
     SkASSERT(ValidQuad(quad1));
     const SkDQuad& quad2 = testSet[inner];
     SkASSERT(ValidQuad(quad2));
-    SkIntersections intersections2;
-    intersections2.intersect(quad1, quad2);
-    for (int pt = 0; pt < intersections2.used(); ++pt) {
-        double tt1 = intersections2[0][pt];
+    SkIntersections intersections;
+    intersections.intersect(quad1, quad2);
+    for (int pt = 0; pt < intersections.used(); ++pt) {
+        double tt1 = intersections[0][pt];
         SkDPoint xy1 = quad1.ptAtT(tt1);
-        double tt2 = intersections2[1][pt];
+        double tt2 = intersections[1][pt];
         SkDPoint xy2 = quad2.ptAtT(tt2);
         if (!xy1.approximatelyEqual(xy2)) {
             SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
@@ -320,6 +356,8 @@
 }
 
 static const SkDQuad coincidentTestSet[] = {
+    {{{4914.9990234375, 1523}, {4942.75146484375, 1523}, {4962.375, 1542.6239013671875}}},
+    {{{4962.3759765625, 1542.6239013671875}, {4942.75244140625, 1523}, {4915, 1523}}},
 #if 0
     {{{97.9337615966796875,100}, {88,112.94264984130859375}, {88,130}}},
     {{{88,130}, {88,124.80951690673828125}, {88.91983795166015625,120}}},
@@ -339,9 +377,9 @@
     SkASSERT(ValidQuad(quad2));
     SkIntersections intersections2;
     intersections2.intersect(quad1, quad2);
-    REPORTER_ASSERT(reporter, intersections2.coincidentUsed() == 2);
-    REPORTER_ASSERT(reporter, intersections2.used() == 2);
-    for (int pt = 0; pt < intersections2.coincidentUsed(); ++pt) {
+    REPORTER_ASSERT(reporter, intersections2.debugCoincidentUsed() >= 2);
+    REPORTER_ASSERT(reporter, intersections2.used() >= 2);
+    for (int pt = 0; pt < intersections2.debugCoincidentUsed(); pt += 2) {
         double tt1 = intersections2[0][pt];
         double tt2 = intersections2[1][pt];
         SkDPoint pt1 = quad1.ptAtT(tt1);
@@ -356,71 +394,6 @@
     }
 }
 
-static int floatSign(double x) {
-    return x < 0 ? -1 : x > 0 ? 1 : 0;
-}
-
-static const SkDQuad pointFinderTestSet[] = {
-                                                                                                                                //>=0.633974464         0.633974846 <=
-{{{1.2071879545809394, 0.82163474041730045}, {1.1534203513372994, 0.52790870069930229},
-        {1.0880000000000001, 0.29599999999999982}}},  //t=0.63155333662549329, 0.80000000000000004
-{{{1.2071879545809394, 0.82163474041730045}, {1.2065040319428038, 0.81766753259119995},
-        {1.2058123269101506, 0.81370135061854221}}},  //t=0.63155333662549329, 0.6339049773632347
-{{{1.2058123269101506, 0.81370135061854221}, {1.152376363978022, 0.5244097415381026},
-        {1.0880000000000001, 0.29599999999999982}}},  //t=0.6339049773632347,  0.80000000000000004
-                                                                                                                                //>=0.633974083         0.633975227 <=
-{{{0.33333333333333326, 0.81481481481481488}, {0.63395173631977997, 0.68744136726313931},
-        {1.205684411948591, 0.81344322326274499}}},   //t=0.33333333333333331, 0.63395173631977986
-{{{0.33333333333333326, 0.81481481481481488}, {0.63396444791444551, 0.68743368362444768},
-        {1.205732763658403, 0.81345617746834109}}},   //t=0.33333333333333331, 0.63396444791444551
-{{{1.205684411948591, 0.81344322326274499}, {1.2057085875611198, 0.81344969999329253},
-        {1.205732763658403, 0.81345617746834109}}},   //t=0.63395173631977986, 0.63396444791444551
-{{{1.205732763658403, 0.81345617746834109}, {1.267928895828891, 0.83008534558465619},
-        {1.3333333333333333, 0.85185185185185175}}},  //t=0.63396444791444551, 0.66666666666666663
-};
-
-static void pointFinder(const SkDQuad& q1, const SkDQuad& q2) {
-    for (int index = 0; index < 3; ++index) {
-        double t = q1.nearestT(q2[index]);
-        SkDPoint onQuad = q1.ptAtT(t);
-        SkDebugf("%s t=%1.9g (%1.9g,%1.9g) dist=%1.9g\n", __FUNCTION__, t, onQuad.fX, onQuad.fY,
-                onQuad.distance(q2[index]));
-        double left[3];
-        left[0] = ((const SkDLine&) q1[0]).isLeft(q2[index]);
-        left[1] = ((const SkDLine&) q1[1]).isLeft(q2[index]);
-        SkDLine diag = {{q1[0], q1[2]}};
-        left[2] = diag.isLeft(q2[index]);
-        SkDebugf("%s left=(%d, %d, %d) inHull=%s\n", __FUNCTION__, floatSign(left[0]),
-                floatSign(left[1]), floatSign(left[2]),
-                q1.pointInHull(q2[index]) ? "true" : "false");
-    }
-    SkDebugf("\n");
-}
-
-static void hullIntersect(const SkDQuad& q1, const SkDQuad& q2) {
-    SkDebugf("%s", __FUNCTION__);
-    SkIntersections ts;
-    for (int i1 = 0; i1 < 3; ++i1) {
-        SkDLine l1 = {{q1[i1], q1[(i1 + 1) % 3]}};
-        for (int i2 = 0; i2 < 3; ++i2) {
-            SkDLine l2 = {{q2[i2], q2[(i2 + 1) % 3]}};
-            if (ts.intersect(l1, l2)) {
-                SkDebugf(" [%d,%d]", i1, i2);
-            }
-        }
-    }
-    SkDebugf("\n");
-}
-
-static void QuadraticIntersection_PointFinder() {
-    pointFinder(pointFinderTestSet[0], pointFinderTestSet[4]);
-    pointFinder(pointFinderTestSet[4], pointFinderTestSet[0]);
-    pointFinder(pointFinderTestSet[0], pointFinderTestSet[6]);
-    pointFinder(pointFinderTestSet[6], pointFinderTestSet[0]);
-    hullIntersect(pointFinderTestSet[0], pointFinderTestSet[4]);
-    hullIntersect(pointFinderTestSet[0], pointFinderTestSet[6]);
-}
-
 static void intersectionFinder(int test1, int test2) {
     const SkDQuad& quad1 = testSet[test1];
     const SkDQuad& quad2 = testSet[test2];
@@ -519,18 +492,46 @@
     intersectionFinder(0, 1);
 }
 
-DEF_TEST(PathOpsQuadIntersection, reporter) {
-    oneOffTests(reporter);
-    coincidentTest(reporter);
-    standardTestCases(reporter);
-    if (false) QuadraticIntersection_IntersectionFinder();
-    if (false) QuadraticIntersection_PointFinder();
+DEF_TEST(PathOpsQuadIntersectionOneOff, reporter) {
+    oneOffTest1(reporter, 0, 1);
 }
 
 DEF_TEST(PathOpsQuadIntersectionCoincidenceOneOff, reporter) {
     coincidentTestOne(reporter, 0, 1);
 }
 
-DEF_TEST(PathOpsQuadIntersectionOneOff, reporter) {
-    oneOffTest1(reporter, 0, 1);
+DEF_TEST(PathOpsQuadIntersection, reporter) {
+    oneOffTests(reporter);
+    coincidentTest(reporter);
+    standardTestCases(reporter);
+    if (false) QuadraticIntersection_IntersectionFinder();
+}
+
+#include "SkCommonFlags.h"
+
+DEF_TEST(PathOpsQuadBinaryProfile, reporter) {
+    if (!FLAGS_veryVerbose) {
+            return;
+    }
+    SkIntersections intersections;
+    for (int x = 0; x < 100; ++x) {
+        int outer = 0;
+        int inner = outer + 1;
+        do {
+            const SkDQuad& quad1 = testSet[outer];
+            const SkDQuad& quad2 = testSet[inner];
+            (void) intersections.intersect(quad1, quad2);
+            REPORTER_ASSERT(reporter, intersections.used() >= 0);  // make sure code isn't tossed
+            inner += 2;
+            outer += 2;
+        } while (outer < (int) testSetCount);
+    }
+    for (int x = 0; x < 100; ++x) {
+        for (size_t test = 0; test < quadraticTests_count; ++test) {
+            const SkDQuad& quad1 = quadraticTests[test][0];
+            const SkDQuad& quad2 = quadraticTests[test][1];
+            (void) intersections.intersect(quad1, quad2);
+            REPORTER_ASSERT(reporter, intersections.used() >= 0);  // make sure code isn't tossed
+        }
+    }
 }
diff --git a/tests/PathOpsQuadIntersectionTestData.cpp b/tests/PathOpsQuadIntersectionTestData.cpp
index 0706efc..f51f951 100644
--- a/tests/PathOpsQuadIntersectionTestData.cpp
+++ b/tests/PathOpsQuadIntersectionTestData.cpp
@@ -44,10 +44,10 @@
 
 const size_t quadraticLines_count = SK_ARRAY_COUNT(quadraticLines);
 
-static const double F = FLT_EPSILON * 3;
-static const double H = FLT_EPSILON * 4;
-static const double J = FLT_EPSILON * 5;
-static const double K = FLT_EPSILON * 8;  // INVESTIGATE: why are larger multiples necessary?
+static const double F = FLT_EPSILON * 32;
+static const double H = FLT_EPSILON * 32;
+static const double J = FLT_EPSILON * 32;
+static const double K = FLT_EPSILON * 32;  // INVESTIGATE: why are larger multiples necessary?
 
 const SkDQuad quadraticModEpsilonLines[] = {
     {{{0, F}, {0, 0}, {1, 0}}},
@@ -64,7 +64,7 @@
     {{{1, 1+J}, {2, 2}, {3, 3}}},
     {{{1, 1}, {3, 3}, {3+F, 3}}},
     {{{1, 1}, {1+F, 1}, {2, 2}}},
-    {{{1, 1}, {2, 2}, {1, 1+F}}},
+    {{{1, 1}, {2, 2}, {1, 1+K}}},
     {{{1, 1}, {1, 1+F}, {3, 3}}},
     {{{1+H, 1}, {2, 2}, {4, 4}}},  // no coincident
     {{{1, 1+K}, {3, 3}, {4, 4}}},
diff --git a/tests/PathOpsQuadParameterizationTest.cpp b/tests/PathOpsQuadParameterizationTest.cpp
deleted file mode 100644
index c7a2e87..0000000
--- a/tests/PathOpsQuadParameterizationTest.cpp
+++ /dev/null
@@ -1,50 +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.
- */
-#include "SkDQuadImplicit.h"
-#include "SkPathOpsQuad.h"
-#include "Test.h"
-
-static bool point_on_parameterized_curve(const SkDQuad& quad, const SkDPoint& point) {
-    SkDQuadImplicit q(quad);
-    double  xx = q.x2() * point.fX * point.fX;
-    double  xy = q.xy() * point.fX * point.fY;
-    double  yy = q.y2() * point.fY * point.fY;
-    double   x = q.x() * point.fX;
-    double   y = q.y() * point.fY;
-    double   c = q.c();
-    double sum = xx + xy + yy + x + y + c;
-    return approximately_zero(sum);
-}
-
-static const SkDQuad quadratics[] = {
-    {{{0, 0}, {1, 0}, {1, 1}}},
-};
-
-static const int quadratics_count = (int) SK_ARRAY_COUNT(quadratics);
-
-DEF_TEST(PathOpsQuadImplicit, reporter) {
-    // split large quadratic
-    // compare original, parts, to see if the are coincident
-    for (int index = 0; index < quadratics_count; ++index) {
-        const SkDQuad& test = quadratics[index];
-        SkDQuadPair split = test.chopAt(0.5);
-        SkDQuad midThird = test.subDivide(1.0/3, 2.0/3);
-        const SkDQuad* quads[] = {
-            &test, &midThird, &split.first(), &split.second()
-        };
-        int quadsCount = (int) SK_ARRAY_COUNT(quads);
-        for (int one = 0; one < quadsCount; ++one) {
-            for (int two = 0; two < quadsCount; ++two) {
-                for (int inner = 0; inner < 3; inner += 2) {
-                     REPORTER_ASSERT(reporter, point_on_parameterized_curve(*quads[one],
-                            (*quads[two])[inner]));
-                }
-                REPORTER_ASSERT(reporter, SkDQuadImplicit::Match(*quads[one], *quads[two]));
-            }
-        }
-    }
-}
diff --git a/tests/PathOpsSimplifyFailTest.cpp b/tests/PathOpsSimplifyFailTest.cpp
index 2a4b0a0..01c6272 100644
--- a/tests/PathOpsSimplifyFailTest.cpp
+++ b/tests/PathOpsSimplifyFailTest.cpp
@@ -86,11 +86,10 @@
     SkPath result;
     result.setFillType(SkPath::kWinding_FillType);
     bool success = Simplify(path, &result);
-    // linux 32 debug fails test 13 because the quad is not treated as linear
-    // there's no error in the math that I can find -- it looks like a processor
-    // or compiler bug -- so for now, allow either to work
-    REPORTER_ASSERT(reporter, success || index == 13);
-    REPORTER_ASSERT(reporter, result.getFillType() != SkPath::kWinding_FillType);
+    if (index != 17 && index != 31 && index != 38) {  // cubic fails to chop in two without creating NaNs
+        REPORTER_ASSERT(reporter, success);
+        REPORTER_ASSERT(reporter, result.getFillType() != SkPath::kWinding_FillType);
+    }
     reporter->bumpTestCount();
 }
 
@@ -109,6 +108,6 @@
 }
 
 DEF_TEST(PathOpsSimplifyDontFailOne, reporter) {
-    int index = 13;
+    int index = 17;
     dontFailOne(reporter, index);
 }
diff --git a/tests/PathOpsSimplifyQuadThreadedTest.cpp b/tests/PathOpsSimplifyQuadThreadedTest.cpp
index 3c92cca..047aa6b 100644
--- a/tests/PathOpsSimplifyQuadThreadedTest.cpp
+++ b/tests/PathOpsSimplifyQuadThreadedTest.cpp
@@ -49,8 +49,11 @@
                             SkIntToScalar(hx), SkIntToScalar(hy));
                     path.close();
                     if (progress) {
-                        // gdb: set print elements 400
+                        static int quadTest = 65;
                         char* str = pathStr;
+                        str += sprintf(str, "static void testQuads%d(skiatest::Reporter* reporter,"
+                                "const char* filename) {\n", quadTest);
+                        str += sprintf(str, "    SkPath path;\n");
                         str += sprintf(str, "    path.moveTo(%d, %d);\n", ax, ay);
                         str += sprintf(str, "    path.quadTo(%d, %d, %d, %d);\n", bx, by, cx, cy);
                         str += sprintf(str, "    path.lineTo(%d, %d);\n", dx, dy);
@@ -59,6 +62,8 @@
                         str += sprintf(str, "    path.lineTo(%d, %d);\n", fx, fy);
                         str += sprintf(str, "    path.quadTo(%d, %d, %d, %d);\n", gx, gy, hx, hy);
                         str += sprintf(str, "    path.close();\n");
+                        str += sprintf(str, "    testSimplify(reporter, path, filename);\n");
+                        str += sprintf(str, "}\n");
                         outputProgress(state.fPathStr, pathStr, SkPath::kWinding_FillType);
                     }
                     testSimplify(path, false, out, state, pathStr);
@@ -90,5 +95,5 @@
     }
 finish:
     testRunner.render();
-    ShowTestArray();
+    ShowTestArray("testQuads");
 }
diff --git a/tests/PathOpsSimplifyTest.cpp b/tests/PathOpsSimplifyTest.cpp
index 88547a0..65a6441 100644
--- a/tests/PathOpsSimplifyTest.cpp
+++ b/tests/PathOpsSimplifyTest.cpp
@@ -3649,7 +3649,7 @@
     testSimplify(reporter, path, filename);
 }
 
-// A test this for this case:
+// A test for this case:
 // contourA has two segments that are coincident
 // contourB has two segments that are coincident in the same place
 // each ends up with +2/0 pairs for winding count
@@ -4506,8 +4506,6 @@
     testSimplify(reporter, path, filename);
 }
 
-// this fails because there is a short unorderable segment and the unordered state isn't handled
-// correctly later on.
 static void testQuads46x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -4679,9 +4677,124 @@
     testSimplify(reporter, path, filename);
 }
 
+static void testRect4(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 30, 60, SkPath::kCCW_Direction);
+    path.addRect(10, 0, 40, 30, SkPath::kCCW_Direction);
+    path.addRect(20, 0, 30, 40, SkPath::kCCW_Direction);
+    path.addRect(32, 0, 36, 41, SkPath::kCCW_Direction);
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads62(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.moveTo(3, 2);
+    path.quadTo(1, 3, 3, 3);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 0);
+    path.quadTo(1, 3, 3, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads63(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(3, 0);
+    path.quadTo(0, 1, 1, 2);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.quadTo(0, 2, 3, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testQuads64(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(3, 0);
+    path.quadTo(0, 1, 1, 2);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.quadTo(0, 2, 3, 3);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testTriangle1(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 2);
+    path.lineTo(1, 0);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testTriangle2(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(0, 2);
+    path.lineTo(2, 2);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void testArc(skiatest::Reporter* reporter,const char* filename) {
+    SkRect r = SkRect::MakeWH(150, 100);
+    SkPath path;
+    path.arcTo(r, 0, 0.0025f, false);
+    testSimplify(reporter, path, filename);
+}
+
+static void testIssue3838(skiatest::Reporter* reporter,const char* filename) {
+    SkPath path;
+    path.moveTo(220, 170);
+    path.lineTo(200, 170);
+    path.lineTo(200, 190);
+    path.lineTo(180, 190);
+    path.lineTo(180, 210);
+    path.lineTo(200, 210);
+    path.lineTo(200, 250);
+    path.lineTo(260, 250);
+    path.lineTo(260, 190);
+    path.lineTo(220, 190);
+    path.lineTo(220, 170);
+    path.close();
+    path.moveTo(220, 210);
+    path.lineTo(220, 230);
+    path.lineTo(240, 230);
+    path.lineTo(240, 210);
+    path.lineTo(220, 210);
+    path.close();
+    testSimplify(reporter, path, filename);
+}
+
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static TestDesc tests[] = {
+    TEST(testIssue3838),
+    TEST(testArc),
+    TEST(testTriangle2),
+    TEST(testTriangle1),
+    TEST(testQuads64),
+    TEST(testQuads63),
+    TEST(testQuads62),
+    TEST(testRect4),
     TEST(testRect3),
     TEST(testQuadralateral10),
     TEST(testQuads61),
@@ -4738,7 +4851,7 @@
     TEST(testQuadralateral3),
     TEST(testDegenerate5),
     TEST(testQuad12),
-    TEST(testQuadratic51),  // has unorderable angles
+    TEST(testQuadratic51),
     TEST(testQuad8),
     TEST(testQuad11),
     TEST(testQuad10),
@@ -5111,14 +5224,13 @@
 static bool runSubTests = false;
 static bool runSubTestsFirst = false;
 static bool runReverse = false;
-static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 DEF_TEST(PathOpsSimplify, reporter) {
     if (runSubTests && runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
     }
-    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
     if (runSubTests && !runSubTestsFirst) {
-        RunTestSet(reporter, subTests, subTestCount, firstSubTest, stopTest, runReverse);
+        RunTestSet(reporter, subTests, subTestCount, firstSubTest, NULL, stopTest, runReverse);
     }
 }
diff --git a/tests/PathOpsSkpClipTest.cpp b/tests/PathOpsSkpClipTest.cpp
index 3cc788f..1f69ff0 100755
--- a/tests/PathOpsSkpClipTest.cpp
+++ b/tests/PathOpsSkpClipTest.cpp
@@ -1,3 +1,10 @@
+/*
+ * 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 "CrashHandler.h"
 // #include "OverwriteLine.h"
 #include "Resources.h"
@@ -253,7 +260,7 @@
 
 class TestRunnable : public SkRunnable {
 public:
-    void run() SK_OVERRIDE {
+    void run() override {
         SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
         (*fTestFun)(&fState);
     }
@@ -819,10 +826,10 @@
     public:                                                             \
         static Test* Factory(void*) { return SkNEW(name##Class); }      \
     protected:                                                          \
-        void onGetName(SkString* name) SK_OVERRIDE {            \
+        void onGetName(SkString* name) override {            \
             name->set(#name);                                           \
         }                                                               \
-        void onRun() SK_OVERRIDE { test_##name(); } \
+        void onRun() override { test_##name(); } \
     };                                                                  \
     static TestRegistry gReg_##name##Class(name##Class::Factory);       \
     static void test_##name()
@@ -1099,7 +1106,7 @@
     return 0;
 }
 
-#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
+#if !defined(SK_BUILD_FOR_IOS)
 int main(int argc, char * const argv[]) {
     return tool_main(argc, (char**) argv);
 }
diff --git a/tests/PathOpsSkpTest.cpp b/tests/PathOpsSkpTest.cpp
index 6af790f..bbf3453 100755
--- a/tests/PathOpsSkpTest.cpp
+++ b/tests/PathOpsSkpTest.cpp
@@ -366,7 +366,7 @@
     pathB.lineTo(716.868225f, 365.046783f);
     pathB.cubicTo(716.868225f, 363.740021f, 716.960083f, 363.043213f, 717.597961f, 362);
     pathB.cubicTo(715.331848f, 363.104095f, 714.19873f, 363.657166f, 711.928711f, 364.782227f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpeverytechpro_blogspot_com100(skiatest::Reporter* reporter, const char* filename) {
@@ -400,7 +400,7 @@
     pathB.lineTo(1075, 628);
     pathB.lineTo(1116.5f, 644.5f);
     pathB.lineTo(1134, 627);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpflite_com41(skiatest::Reporter* reporter, const char* filename) {
@@ -423,7 +423,7 @@
     pathB.lineTo(304.510101f, 438.724121f);
     pathB.lineTo(295.849854f, 433.724121f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpilkoora_com37(skiatest::Reporter* reporter, const char* filename) {
@@ -455,7 +455,7 @@
     pathB.lineTo(1001.5f, 325.5f);
     pathB.lineTo(1001.5f, 782.5f);
     pathB.lineTo(1185, 966);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpmm4everfriends_com43(skiatest::Reporter* reporter, const char* filename) {
@@ -478,7 +478,7 @@
     pathB.lineTo(576.435852f, 247.626068f);
     pathB.lineTo(535.280823f, 235.165573f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpmtrk_uz27(skiatest::Reporter* reporter, const char* filename) {
@@ -505,7 +505,7 @@
     pathB.quadTo(41.7867432f, 802, 37.3919678f, 797.608032f);
     pathB.quadTo(33, 793.213196f, 33, 787);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpfrauen_magazin_com83(skiatest::Reporter* reporter, const char* filename) {
@@ -529,7 +529,7 @@
     pathB.lineTo(803, 891);
     pathB.cubicTo(803, 888.238586f, 805.238586f, 886, 808, 886);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpi_gino_com16(skiatest::Reporter* reporter, const char* filename) {
@@ -554,7 +554,7 @@
     pathB.cubicTo(61, 789.06897f, 116.068977f, 734, 184, 734);
     pathB.lineTo(185, 734);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skppchappy_com_au102(skiatest::Reporter* reporter, const char* filename) {
@@ -582,7 +582,7 @@
     pathB.lineTo(359, 496);
     pathB.cubicTo(359, 494.895416f, 360.34314f, 494, 362, 494);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpsciality_com161(skiatest::Reporter* reporter, const char* filename) {
@@ -610,7 +610,7 @@
     pathB.lineTo(652, 731);
     pathB.cubicTo(652, 729.895447f, 653.34314f, 729, 655, 729);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpsudoestenegocios_com186(skiatest::Reporter* reporter, const char* filename) {
@@ -640,7 +640,7 @@
     pathB.lineTo(24, 471);
     pathB.lineTo(24, 317);
     pathB.lineTo(48, 293);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpthesuburbanite_com213(skiatest::Reporter* reporter, const char* filename) {
@@ -661,7 +661,7 @@
     pathB.lineTo(866.016724f, 701.620361f);
     pathB.lineTo(785.84491f, 723.102356f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skphostloco_com11(skiatest::Reporter* reporter, const char* filename) {
@@ -684,7 +684,7 @@
     pathB.lineTo(30, 648);
     pathB.lineTo(0, 648);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpsergeychunkevich_com8(skiatest::Reporter* reporter, const char* filename) {
@@ -706,7 +706,7 @@
     pathB.lineTo(34, 371);
     pathB.cubicTo(35.6568565f, 371, 37, 372.34314f, 37, 374);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skptracksflow_com9(skiatest::Reporter* reporter, const char* filename) {
@@ -740,7 +740,7 @@
     pathB.cubicTo(26.0091248f, 64.2129364f, 24.2174377f, 66.0046234f, 22.0072803f, 66.0046234f);
     pathB.cubicTo(19.7970943f, 66.0045929f, 18.0054054f, 64.2129059f, 18.0054054f, 62.0027809f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpautobutler_dk29(skiatest::Reporter* reporter, const char* filename) {
@@ -759,7 +759,7 @@
     pathB.lineTo(8.57224448e-15f, 301);
     pathB.lineTo(6.12303177e-17f, 162);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skponlinecollege_org144(skiatest::Reporter* reporter, const char* filename) {
@@ -787,7 +787,7 @@
     pathB.lineTo(177, 410);
     pathB.cubicTo(177, 408.895416f, 177.895432f, 408, 179, 408);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpnational_com_au81(skiatest::Reporter* reporter, const char* filename) {
@@ -813,7 +813,7 @@
     pathB.lineTo(806, 818);
     pathB.cubicTo(806, 817.447693f, 806.447693f, 817, 807, 817);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skprentacheat_com30(skiatest::Reporter* reporter, const char* filename) {
@@ -839,7 +839,7 @@
     pathB.lineTo(966, 264);
     pathB.cubicTo(966, 263.447723f, 966.447693f, 263, 967, 263);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpbreakmystyle_com10(skiatest::Reporter* reporter, const char* filename) {
@@ -868,7 +868,7 @@
     pathB.quadTo(231.516815f, -40, 279.258392f, 7.74160004f);
     pathB.quadTo(327, 55.4831848f, 327, 123);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpsd_graphic_net104(skiatest::Reporter* reporter, const char* filename) {
@@ -893,22 +893,9 @@
     pathB.lineTo(390.578583f, 867.014099f);
     pathB.lineTo(433, 852.000061f);
     pathB.lineTo(490.435486f, 879.40741f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* this cubic/quad pair
- c = 430,280 430,278.895416 473.876068,278 528,278
- q = 430,280 430.009796,277.101196 458.703552,275.050262
- only intersect at the shared point (430,280)
- they sort backwards because the tangent from pt[0] to control pt[1]
- c' = (0.00000000000000000, -1.1045837402343750)
- q' = (0.0097961425781250000, -2.8988037109375000)
- suggests that the quad is counterclockwise of the cubic, when the reverse is true
- the angle code is fooled because the control pt[1] of both the quad and cubic
- is far away from cubic cntl [2] and quad pt [2].
- Maybe in angle setup, this instability can be detected to suppress sorting on the initial tangent
- Or the error term can be passed to NearRay that is magnified by the distance from the next ctrl?
- */
 static void skpnaoxrane_ru23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -938,14 +925,9 @@
     pathB.lineTo(430, 280);
     pathB.cubicTo(430, 278.895416f, 473.876068f, 278, 528, 278);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* didn't investigate thoroughly, but looks to be missorting quad and cubic
-    {{468.507751,560.724426}, {467.275146,552.856262}, {465.84668,547.288391}}
-    {{463.779907,542.671143}, {464.829529,542.672974}, {466.946289,550.755676}, {468.507751,560.724426}}
-    decision maker is case 14 leftLessThanRight
- */
 static void skptcmevents_org23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -971,7 +953,7 @@
     pathB.lineTo(325.968597f, 560.475708f);
     pathB.cubicTo(324.407104f, 550.506958f, 341.01001f, 542.456909f, 363.052246f, 542.495361f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpredbullskatearcade_es16(skiatest::Reporter* reporter, const char* filename) {
@@ -1002,7 +984,7 @@
     pathB.lineTo(652.258179f, 468.503662f);
     pathB.cubicTo(652.520996f, 463.401611f, 656.829834f, 459.128235f, 661.882263f, 458.958862f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpfinanzasdigital_com9(skiatest::Reporter* reporter, const char* filename) {
@@ -1028,7 +1010,7 @@
     pathB.lineTo(153, 130);
     pathB.cubicTo(153, 127.790863f, 154.34314f, 126, 156, 126);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skppartainasdemo250_org56(skiatest::Reporter* reporter, const char* filename) {
@@ -1051,7 +1033,7 @@
     pathB.lineTo(206.748749f, 634.748718f);
     pathB.lineTo(182.000015f, 610);
     pathB.lineTo(132.502533f, 610);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpmlk_com326(skiatest::Reporter* reporter, const char* filename) {
@@ -1079,7 +1061,7 @@
     pathB.lineTo(149, 675);
     pathB.cubicTo(149, 672.790833f, 151.238571f, 671, 154, 671);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpcyclist_friends_gr52(skiatest::Reporter* reporter, const char* filename) {
@@ -1105,10 +1087,12 @@
     pathB.cubicTo(52.238575f, 207, 50, 204.761429f, 50, 202);
     pathB.lineTo(50, 183);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    // FIXME: this generates quads and cubics that are (correctly) not coincident unlike the old code
+    // however, somewhere the angles are sorted incorrectly and the winding is computed to be -1/-2
+    // but I can't find the error
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* cubic ends just above opp line */
 static void skpwww_fj_p_com_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1125,10 +1109,9 @@
     pathB.lineTo(161, 199);
     pathB.lineTo(223, 199.000015f);
     pathB.lineTo(223, 202);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// pair of lines are not quite coincident, so sorting line/cubic fails (i think)
 static void skpwww_lavoixdunord_fr_11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1158,12 +1141,9 @@
     pathB.lineTo(808, 56);
     pathB.lineTo(935.02002f, 56.0200005f);
     pathB.lineTo(933, 54);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// pair of curves have nearly the same initial tangent but are sorting by
-// that alone sorts them incorrectly. Need to detect that tangents are nearly
-// identical and not reliable by themselves
 static void skppptv_com_62(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1187,10 +1167,9 @@
     pathB.lineTo(169, 5346);
     pathB.cubicTo(169, 5343.79102f, 170.790863f, 5342, 173, 5342);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// nearly identical to lavoixdunord -- to not-quite-coincident lines
 static void skpwww_booking_com_68(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1220,10 +1199,9 @@
     pathB.lineTo(92, 186);
     pathB.lineTo(593.02002f, 186.020004f);
     pathB.lineTo(591, 184);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// visually looks like lavoixdunord and www_booking_com
 static void skpwww_despegar_com_mx_272(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1253,7 +1231,7 @@
     pathB.lineTo(833, 1787);
     pathB.lineTo(832.97998f, 1817.02002f);
     pathB.lineTo(835, 1815);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_joomla_org_23(skiatest::Reporter* reporter, const char* filename) {
@@ -1281,7 +1259,7 @@
     pathB.lineTo(320, 378);
     pathB.lineTo(421, 378.000031f);
     pathB.lineTo(421, 383);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_macrumors_com_131(skiatest::Reporter* reporter, const char* filename) {
@@ -1307,7 +1285,7 @@
     pathB.cubicTo(137.790863f, 14093, 136, 14091.209f, 136, 14089);
     pathB.lineTo(136, 14057);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_leadpages_net_84(skiatest::Reporter* reporter, const char* filename) {
@@ -1328,7 +1306,7 @@
     pathB.lineTo(378.481873f, 5909);
     pathB.lineTo(379.999878f, 5976);
     pathB.lineTo(376, 5976);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_briian_com_34(skiatest::Reporter* reporter, const char* filename) {
@@ -1360,7 +1338,7 @@
     pathB.lineTo(843, 779);
     pathB.lineTo(1196, 779.000061f);
     pathB.lineTo(1196, 784);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_sciality_com_100(skiatest::Reporter* reporter, const char* filename) {
@@ -1388,7 +1366,7 @@
     pathB.cubicTo(158, 469.34314f, 159.34314f, 468, 161, 468);
     pathB.lineTo(275, 468);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_sciality_com_101(skiatest::Reporter* reporter, const char* filename) {
@@ -1416,7 +1394,7 @@
     pathB.lineTo(158, 471);
     pathB.cubicTo(158, 469.895416f, 159.34314f, 469, 161, 469);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_meb_gov_tr_5(skiatest::Reporter* reporter, const char* filename) {
@@ -1439,7 +1417,7 @@
     pathB.lineTo(250, 177);
     pathB.lineTo(135, 177);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_meb_gov_tr_6(skiatest::Reporter* reporter, const char* filename) {
@@ -1465,7 +1443,7 @@
     pathB.lineTo(135, 151);
     pathB.cubicTo(135, 146.581726f, 138.581726f, 143, 143, 143);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpgithub_io_25(skiatest::Reporter* reporter, const char* filename) {
@@ -1497,7 +1475,7 @@
     pathB.lineTo(1003, 18);
     pathB.cubicTo(1003, 16.8954296f, 1003.89545f, 16, 1005, 16);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpgithub_io_26(skiatest::Reporter* reporter, const char* filename) {
@@ -1533,7 +1511,7 @@
     pathB.lineTo(1106, 16);
     pathB.lineTo(1105.97998f, 46.0200005f);
     pathB.lineTo(1108, 44);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpskpicture14(skiatest::Reporter* reporter, const char* filename) {
@@ -1556,7 +1534,7 @@
     pathB.lineTo(323, 193);
     pathB.lineTo(-317, 193);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpskpicture15(skiatest::Reporter* reporter, const char* filename) {
@@ -1580,14 +1558,9 @@
     pathB.lineTo(-317, 168);
     pathB.cubicTo(-317, 166.34314f, -315.65686f, 165, -314, 165);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* Three edges are partially coincident. Only one of the three knows about the other two.
-   Subsequently, when the angle loop is created, it misses one of the edges.
-   After coincident edges are processed, probably need a check-and-correct that makes sure the
-   coincidences are all self-consistent.
- */
 static void skpelpais_com_18(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1603,16 +1576,9 @@
     pathB.lineTo(183, 8506.99023f);
     pathB.lineTo(552, 8507);
     pathB.lineTo(552, 8508);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* this generates a pair of lines that are essentially coincident; but the next line at a right
-   angle is not treated as if it intersects at the same point.
-   There are several of options:
-     move the intersection of the right angle line to the coincident point (should 'near' do this?
-     construct another coincident pair from the right angle line to the coincident point
-     treat the intersection as simple and not coincident
- */
 static void skpnamecheap_com_405(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1628,10 +1594,9 @@
     pathB.lineTo(141.008835f, 837.9646f);
     pathB.lineTo(141.235291f, 1109.05884f);
     pathB.lineTo(140, 1114);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// fails on angle insert -- haven't investigated yet
 static void skpwww_alrakoba_net_62(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1654,10 +1619,9 @@
     pathB.lineTo(135.962357f, 9800);
     pathB.lineTo(140, 9830);
     pathB.lineTo(132, 9830);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-/* asserts in alignSpanState looks like a coincident related bug */
 static void skpwww_cityads_ru_249(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1691,10 +1655,9 @@
     pathB.lineTo(1000, 13);
     pathB.lineTo(999.969971f, 37.0299988f);
     pathB.lineTo(1003, 34);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// fails on angle insert
 static void skpwww_dealnews_com_315(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1718,10 +1681,9 @@
     pathB.lineTo(967.716675f, 4260);
     pathB.lineTo(970, 4281);
     pathB.lineTo(965, 4281);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// fails in intersections insert
 static void skpwww_inmotionhosting_com_9(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1741,7 +1703,7 @@
     pathB.lineTo(1018.73242f, 1894.26501f);
     pathB.lineTo(963.734985f, 1893.73242f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_alucinados_net_101(skiatest::Reporter* reporter, const char* filename) {
@@ -1763,10 +1725,9 @@
     pathB.lineTo(-43515.8555f, -177415.594f);
     pathB.lineTo(1129.76465f, 1173.05884f);
     pathB.lineTo(1131, 1178);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// /SkOpContour.cpp:278: failed assertion "!approximately_negative(oEndT - oStartT)
 static void skpwww_hairjobsearch_com_31(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1784,10 +1745,9 @@
     pathB.lineTo(144, 0);
     pathB.lineTo(1122, 0);
     pathB.lineTo(1123, 1);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpSegment::checkSmallCoincidence; line 1958 SkASSERT(span.fWindValue);
 static void skpwww_heartiste_wordpress_com_86(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1803,7 +1763,7 @@
     pathB.lineTo(741, 9431.99023f);
     pathB.lineTo(761, 9432);
     pathB.lineTo(761, 9433);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_argus_presse_fr_41(skiatest::Reporter* reporter, const char* filename) {
@@ -1822,10 +1782,9 @@
     pathB.lineTo(1000, 364.869904f);
     pathB.lineTo(165, 364.869904f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpSegment::checkSmallCoincidence; line 1958 SkASSERT(span.fWindValue);
 static void skpwww_320kbps_net_2231(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1841,10 +1800,9 @@
     pathB.lineTo(838, 9124.99023f);
     pathB.lineTo(862, 9125);
     pathB.lineTo(862, 9126);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// debugValidateLoop loop sum fails
 static void skpwww_exystence_net_61(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1860,10 +1818,9 @@
     pathB.lineTo(143, 9073.99023f);
     pathB.lineTo(316, 9074);
     pathB.lineTo(316, 9075);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// debugValidateLoop loop sum fails
 static void skpwww_trashness_com_36(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1879,10 +1836,9 @@
     pathB.lineTo(91.5f, 4835.99512f);
     pathB.lineTo(541.5f, 4836);
     pathB.lineTo(541.5f, 4836.5f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkIntersections::lineVertical fUsed >= fMax
 static void skpwww_getgold_jp_731(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1901,10 +1857,9 @@
     pathB.lineTo(286.05957f, 10129.8809f);
     pathB.lineTo(285.399994f, 10216.2002f);
     pathB.lineTo(284, 10219);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpContour::calcPartialCoincidentWinding SkASSERT(!approximately_negative(endT - startT));
 static void skpwww_maturesupertube_com_21(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1942,10 +1897,9 @@
     pathB.lineTo(4.5f, 11832.5f);
     pathB.lineTo(1260.5f, 11832.5f);
     pathB.lineTo(1263, 11830);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// can't find winding of remaining vertical edges
 static void skpwww_hubbyscook_com_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -1971,7 +1925,7 @@
     pathB.quadTo(1005.02942f, 920, 1001.51471f, 917.071045f);
     pathB.quadTo(998, 914.142151f, 998, 910);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void skpwww_gruposejaumdivulgador_com_br_4(skiatest::Reporter* reporter, const char* filename) {
@@ -1990,10 +1944,9 @@
     pathB.lineTo(1084, 469);
     pathB.lineTo(611, 469);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// asserts in bridgeOp simple->isClosed()
 static void skpwww_phototransferapp_com_24(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2016,7 +1969,7 @@
     pathB.lineTo(1219.10657f, 13);
     pathB.lineTo(80.1065979f, 13);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_phototransferapp_com_24x(skiatest::Reporter* reporter, const char* filename) {
@@ -2041,7 +1994,7 @@
     pathB.lineTo(119.10657f, 13);
     pathB.lineTo(80.1065979f, 13);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_helha_be_109(skiatest::Reporter* reporter, const char* filename) {
@@ -2063,7 +2016,7 @@
     pathB.lineTo(117.686981f, 3339.08423f);
     pathB.lineTo(98.4669647f, 3351.56104f);
     pathB.lineTo(104.291214f, 3359.87891f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_cooksnaps_com_32(skiatest::Reporter* reporter, const char* filename) {
@@ -2115,7 +2068,7 @@
     pathB.quadTo(509.696686f, 241.450104f, 497.29361f, 238.126709f);
     pathB.quadTo(484.890533f, 234.803314f, 478.470215f, 223.683014f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_cooksnaps_com_32a(skiatest::Reporter* reporter, const char* filename) {
@@ -2131,10 +2084,9 @@
     pathB.quadTo(478.196686f, 186.890503f, 489.316986f, 180.4702f);
     pathB.lineTo(490.183014f, 179.9702f);
     pathB.quadTo(501.303345f, 173.549896f, 513.706421f, 176.873276f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// !simple->isClosed()
 static void skpwww_contextualnewsfeeds_com_346(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2155,10 +2107,9 @@
     pathB.lineTo(458.828979f, 1203.67822f);
     pathB.lineTo(465.914215f, 1196.62122f);
     pathB.lineTo(467.32843f, 1198.03552f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// line quad intersection SkIntersections::assert
 static void skpwww_pindosiya_com_99(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2177,10 +2128,9 @@
     pathB.lineTo(901.086914f, 547);
     pathB.lineTo(899, 556);
     pathB.lineTo(898, 556);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpAngle::setSector SkASSERT(fSectorStart >= 0);
 static void skpwww_karnivool_com_au_11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2197,7 +2147,7 @@
     pathB.lineTo(427, 1081);
     pathB.lineTo(-3.81469727e-06f, 1081);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_tunero_de_24(skiatest::Reporter* reporter, const char* filename) {
@@ -2236,7 +2186,7 @@
     pathB.quadTo(1013.13599f, 2273, 1012.06104f, 2271.53564f);
     pathB.quadTo(1010.98615f, 2270.07104f, 1011.53705f, 2268);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_docgelo_com_66(skiatest::Reporter* reporter, const char* filename) {
@@ -2254,7 +2204,7 @@
     pathB.lineTo(22.5f, 24174.498f);
     pathB.lineTo(185.5f, 24174.5f);
     pathB.lineTo(185.5f, 24174.75f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_kpopexplorer_net_22(skiatest::Reporter* reporter, const char* filename) {
@@ -2282,7 +2232,7 @@
     pathB.quadTo(1005.02942f, 884, 1001.51471f, 881.071045f);
     pathB.quadTo(998, 878.142151f, 998, 874);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void skpwww_artblart_com_8(skiatest::Reporter* reporter, const char* filename) {
@@ -2300,10 +2250,9 @@
     pathB.lineTo(22.5f, 24527.248f);
     pathB.lineTo(45, 24527.25f);
     pathB.lineTo(45, 24527.5f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// joinCoincidence / findT / assert
 static void skpwww_jessicaslens_wordpress_com_222(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2329,10 +2278,9 @@
     pathB.quadTo(1005.02942f, 862, 1001.51471f, 859.071045f);
     pathB.quadTo(998, 856.142151f, 998, 852);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// joinCoincidence / findT / assert
 static void skpwww_simplysaru_com_40(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2358,10 +2306,9 @@
     pathB.quadTo(1005.02942f, 884, 1001.51471f, 881.071045f);
     pathB.quadTo(998, 878.142151f, 998, 874);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// cubic-cubic intersection reduce checkLinear assert
 static void skpwww_partsdata_de_53(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2657,10 +2604,9 @@
     pathB.cubicTo(631.989807f, 46.6754761f, 632.04364f, 47.0436478f, 632.04364f, 47.4595947f);
     pathB.cubicTo(632.042847f, 47.949852f, 631.916565f, 48.3282623f, 631.656494f, 48.6171875f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpAngle::setSector SkASSERT(fSectorStart >= 0);
 static void skpwww_seopack_blogspot_com_2153(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2681,10 +2627,9 @@
     pathB.lineTo(924, 245.472672f);
     pathB.lineTo(1143, 247);
     pathB.lineTo(1143, 248);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// joinCoincidence / findT / assert
 static void skpwww_lokado_de_173(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2718,10 +2663,9 @@
     pathB.quadTo(951.857849f, 916, 948.928955f, 913.071045f);
     pathB.quadTo(946, 910.142151f, 946, 906);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// !simple->isClosed()
 static void skpwww_wartepop_blogspot_com_br_6(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2751,7 +2695,7 @@
     pathB.lineTo(90, 163.666672f);
     pathB.lineTo(90, 163.666672f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void skpwww_wartepop_blogspot_com_br_6a(skiatest::Reporter* reporter, const char* filename) {
@@ -2783,10 +2727,9 @@
     pathB.lineTo(90, 163.666672f);
     pathB.lineTo(90, 163.666672f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// !simple->isClosed()
 static void skpwww_odia_com_br_26(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2831,7 +2774,7 @@
     pathB.lineTo(364.345337f, 754.288269f);
     pathB.lineTo(352.711792f, 751.345337f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_evolvehq_com_210(skiatest::Reporter* reporter, const char* filename) {
@@ -2867,10 +2810,9 @@
     pathB.quadTo(171.17157f, 1174, 170.585785f, 1173.12134f);
     pathB.quadTo(170, 1172.24268f, 170, 1171);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
-// hangs
 static void skpwww_catingueiraonline_com_352(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2886,10 +2828,9 @@
     pathB.lineTo(444.01001f, 8140);
     pathB.lineTo(444, 8292);
     pathB.lineTo(443, 8292);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// hangs
 static void skpwww_galaxystwo_com_4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2905,7 +2846,7 @@
     pathB.lineTo(10105, 2509.98999f);
     pathB.lineTo(10123, 2510);
     pathB.lineTo(10123, 2511);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_thaienews_blogspot_com_36(skiatest::Reporter* reporter, const char* filename) {
@@ -2923,7 +2864,7 @@
     pathB.lineTo(429.994995f, 2187);
     pathB.lineTo(430, 6268);
     pathB.lineTo(430.5f, 6268);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_fashionscandal_com_94(skiatest::Reporter* reporter, const char* filename) {
@@ -2959,7 +2900,7 @@
     pathB.quadTo(26.2238579f, 418, 26.0285969f, 417.75592f);
     pathB.quadTo(25.833334f, 417.511841f, 25.833334f, 417.166656f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
+    testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void skpwww_kenlevine_blogspot_com_28(skiatest::Reporter* reporter, const char* filename) {
@@ -2977,7 +2918,7 @@
     pathB.lineTo(277.01001f, 7531);
     pathB.lineTo(277, 9506);
     pathB.lineTo(276, 9506);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_defense_studies_blogspot_com_64(skiatest::Reporter* reporter, const char* filename) {
@@ -2995,7 +2936,7 @@
     pathB.lineTo(277.01001f, 7703);
     pathB.lineTo(277, 9600);
     pathB.lineTo(276, 9600);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_uniquefx_net_442(skiatest::Reporter* reporter, const char* filename) {
@@ -3013,7 +2954,7 @@
     pathB.lineTo(958.997253f, 306.002747f);
     pathB.lineTo(1017, 307);
     pathB.lineTo(1019, 305);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_kitcheninspirations_wordpress_com_32(skiatest::Reporter* reporter, const char* filename) {
@@ -3031,7 +2972,7 @@
     pathB.lineTo(47.1666679f, 19651.332f);
     pathB.lineTo(65.8333359f, 19651.334f);
     pathB.lineTo(65.8333359f, 19651.5f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_educationalcraft_com_4(skiatest::Reporter* reporter, const char* filename) {
@@ -3209,7 +3150,7 @@
     pathB.cubicTo(980.968994f, 1478.18005f, 979.718018f, 1475.66199f, 983.632019f, 1473.87805f);
     pathB.cubicTo(983.632019f, 1473.87805f, 984.229004f, 1477.80103f, 980.968994f, 1478.18005f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_narayana_publishers_com_194(skiatest::Reporter* reporter, const char* filename) {
@@ -3237,7 +3178,7 @@
     pathB.lineTo(1082, 440);
     pathB.lineTo(1090.01001f, 448);
     pathB.lineTo(1081, 448);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_cooksnaps_com_17(skiatest::Reporter* reporter, const char* filename) {
@@ -3286,7 +3227,7 @@
     pathB.quadTo(170.696686f, 241.450104f, 158.293594f, 238.126709f);
     pathB.quadTo(145.890503f, 234.803314f, 139.4702f, 223.683014f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_swapspacesystems_com_5(skiatest::Reporter* reporter, const char* filename) {
@@ -3313,7 +3254,7 @@
     pathB.lineTo(1186.5199f, 5809.85059f);
     pathB.lineTo(811.648376f, 5800.03418f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_kitcheninspirations_wordpress_com_66(skiatest::Reporter* reporter, const char* filename) {
@@ -3331,7 +3272,7 @@
     pathB.lineTo(47.1666679f, 27820.498f);
     pathB.lineTo(60.8333359f, 27820.5f);
     pathB.lineTo(60.8333359f, 27820.668f);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_etiqadd_com_2464(skiatest::Reporter* reporter, const char* filename) {
@@ -3358,7 +3299,7 @@
     pathB.lineTo(632.5f, 1309.69238f);
     pathB.lineTo(623.307617f, 1300.5f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_narayana_verlag_de_194(skiatest::Reporter* reporter, const char* filename) {
@@ -3386,7 +3327,7 @@
     pathB.lineTo(1082, 508);
     pathB.lineTo(1090.01001f, 516);
     pathB.lineTo(1081, 516);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_americascup_com_108(skiatest::Reporter* reporter, const char* filename) {
@@ -3409,7 +3350,7 @@
     pathB.lineTo(1002.17114f, 713);
     pathB.lineTo(987.171143f, 713);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_vantageproduction_com_109(skiatest::Reporter* reporter, const char* filename) {
@@ -3437,7 +3378,7 @@
     pathB.lineTo(792, 751);
     pathB.lineTo(804.01001f, 763);
     pathB.lineTo(791, 763);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_aceinfographics_com_106(skiatest::Reporter* reporter, const char* filename) {
@@ -3461,7 +3402,7 @@
     pathB.lineTo(168.020004f, 7635.97998f);
     pathB.lineTo(168, 11578);
     pathB.lineTo(166, 11580);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_tcmevents_org_13(skiatest::Reporter* reporter, const char* filename) {
@@ -3488,7 +3429,7 @@
     pathB.lineTo(468.507751f, 560.724426f);
     pathB.lineTo(325.968597f, 560.475708f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_paseoitaigara_com_br_56(skiatest::Reporter* reporter, const char* filename) {
@@ -3516,7 +3457,7 @@
     pathB.quadTo(634.389832f, 1248.24268f, 634.389832f, 1247);
     pathB.quadTo(634.389832f, 1245.75732f, 635.268494f, 1244.87866f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_mortgagemarketguide_com_109(skiatest::Reporter* reporter, const char* filename) {
@@ -3544,7 +3485,7 @@
     pathB.lineTo(814, 773);
     pathB.lineTo(826.01001f, 785);
     pathB.lineTo(813, 785);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_9to5mac_com_64(skiatest::Reporter* reporter, const char* filename) {
@@ -3566,7 +3507,7 @@
     pathB.lineTo(365.848175f, 5081.15186f);
     pathB.lineTo(368, 5103);
     pathB.lineTo(365, 5106);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_googleventures_com_32(skiatest::Reporter* reporter, const char* filename) {
@@ -3585,7 +3526,7 @@
     pathB.lineTo(738.767395f, 914.088379f);
     pathB.lineTo(713.055908f, 883.446594f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_devbridge_com_22(skiatest::Reporter* reporter, const char* filename) {
@@ -3613,12 +3554,7 @@
     pathB.quadTo(4942.75146f, 1523, 4962.375f, 1542.6239f);
     pathB.quadTo(4981.99902f, 1562.24768f, 4981.99902f, 1590);
     pathB.close();
-    if (FLAGS_runFail) {
-        testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
-    } else {
-        // INVESTIGATE : why this normal test takes fail case (test has never worked)
-        testPathFailOp(reporter, path, pathB, kIntersect_PathOp, filename);
-    }
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_alamdi_com_3(skiatest::Reporter* reporter, const char* filename) {
@@ -3650,7 +3586,7 @@
     pathB.lineTo(10210, 5318);
     pathB.cubicTo(10210, 5316.34326f, 10211.3428f, 5315, 10213, 5315);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_familysurvivalprotocol_wordpress_com_61(skiatest::Reporter* reporter, const char* filename) {
@@ -3668,7 +3604,7 @@
     pathB.lineTo(143, 14555.9902f);
     pathB.lineTo(165, 14556);
     pathB.lineTo(165, 14557);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_firstunitedbank_com_19(skiatest::Reporter* reporter, const char* filename) {
@@ -3704,10 +3640,9 @@
     pathB.lineTo(809.5f, 11701.5f);
     pathB.lineTo(1062.91907f, 11687.0811f);
     pathB.lineTo(1047, 11703);
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// addSimpleAngle: failed assertion "index == count() - 2"
 static void skpwww_shinydemos_com_5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3724,10 +3659,9 @@
     pathB.lineTo(545.296204f, 987.615051f);
     pathB.lineTo(205.884949f, 648.203796f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// addTCoincident oPeek = &other->fTs[++oPeekIndex];
 static void skpwww_lptemp_com_3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3757,7 +3691,7 @@
     pathB.lineTo(77.6666718f, 1396.66675f);
     pathB.cubicTo(77.6666718f, 1394.82568f, 79.15905f, 1393.33337f, 81, 1393.33337f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void skpwww_shinydemos_com_15(skiatest::Reporter* reporter, const char* filename) {
@@ -3778,14 +3712,10 @@
     pathB.lineTo(545.296204f, 987.615051f);
     pathB.lineTo(205.884949f, 648.203796f);
     pathB.close();
-    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-// SkOpSegment.cpp:4398: failed assertion "!span->fDone"
 static void skpwww_lptemp_com_5(skiatest::Reporter* reporter, const char* filename) {
-    if (/* 0 && */ !FLAGS_runFail) {  // has never worked MUST BE FIXED BEFORE NEXT CHECKIN
-        return;
-    }
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.moveTo(78.6429825f, 3150.97632f);
@@ -3814,12 +3744,36 @@
     pathB.lineTo(77.6666718f, 3153.3335f);
     pathB.cubicTo(77.6666718f, 3151.49268f, 79.15905f, 3150, 81, 3150);
     pathB.close();
-    testPathOpCheck(reporter, path, pathB, kIntersect_PathOp, filename, FLAGS_runFail);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
-static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
+static void skpwww_educationalcraft_com_4a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(941, 1494);
+    path.lineTo(941, 1464);
+    path.lineTo(985, 1464);
+    path.lineTo(985, 1494);
+    path.lineTo(941, 1494);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+
+pathB.moveTo(984.546021f, 1478.31494f);
+pathB.cubicTo(984.546021f, 1478.31494f, 984.543213f, 1478.32239f, 984.537598f, 1478.33655f);
+pathB.cubicTo(984.419006f, 1478.63477f, 983.044373f, 1481.90405f, 980.026001f, 1481.276f);
+pathB.cubicTo(980.026001f, 1481.276f, 980.02594f, 1481.27576f, 980.025879f, 1481.27527f);
+pathB.cubicTo(980.018494f, 1481.22131f, 979.602478f, 1478.38831f, 984.546021f, 1478.31494f);
+    testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
+
+}
+
+static void (*skipTest)(skiatest::Reporter* , const char* filename) = 0;
+static void (*firstTest)(skiatest::Reporter* , const char* filename) = skpwww_cooksnaps_com_32;
+static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static struct TestDesc tests[] = {
+    TEST(skpwww_educationalcraft_com_4a),
     TEST(skpwww_lptemp_com_3),
     TEST(skpwww_shinydemos_com_5),
     TEST(skpwww_lptemp_com_5),
@@ -3939,11 +3893,10 @@
 static const size_t testCount = SK_ARRAY_COUNT(tests);
 
 static bool runReverse = false;
-static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 DEF_TEST(PathOpsSkp, reporter) {
 #if DEBUG_SHOW_TEST_NAME
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
+    RunTestSet(reporter, tests, testCount, firstTest, skipTest, stopTest, runReverse);
 }
diff --git a/tests/PathOpsTSectDebug.h b/tests/PathOpsTSectDebug.h
index 5f8915f..5780610 100644
--- a/tests/PathOpsTSectDebug.h
+++ b/tests/PathOpsTSectDebug.h
@@ -7,78 +7,185 @@
 
 #include "SkPathOpsTSect.h"
 
-template<typename TCurve>
-void SkTSect<TCurve>::dump() const {
+template<typename TCurve, typename OppCurve>
+void SkTCoincident<TCurve, OppCurve>::dump() const {
+    SkDebugf("t=%1.9g pt=(%1.9g,%1.9g)%s\n", fPerpT, fPerpPt.fX, fPerpPt.fY,
+            fCoincident ? " coincident" : "");
+}
+
+template<typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::debugSpan(int id) const {
+    const SkTSpan<TCurve, OppCurve>* test = fHead;
+    do {
+        if (test->debugID() == id) {
+            return test;
+        }
+    } while ((test = test->next()));
+    return NULL;
+}
+
+template<typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* SkTSect<TCurve, OppCurve>::debugT(double t) const {
+    const SkTSpan<TCurve, OppCurve>* test = fHead;
+    const SkTSpan<TCurve, OppCurve>* closest = NULL;
+    double bestDist = DBL_MAX;
+    do {
+        if (between(test->fStartT, t, test->fEndT)) {
+            return test;
+        }
+        double testDist = SkTMin(fabs(test->fStartT - t), fabs(test->fEndT - t));
+        if (bestDist > testDist) {
+            bestDist = testDist;
+            closest = test;
+        }
+    } while ((test = test->next()));
+    SkASSERT(closest);
+    return closest;
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dump() const {
+    dumpCommon(fHead);
+}
+
+extern int gDumpTSectNum;
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpBoth(SkTSect<OppCurve, TCurve>* opp) const {
+#if DEBUG_T_SECT_DUMP <= 2
+#if DEBUG_T_SECT_DUMP == 2
+    SkDebugf("%d ", ++gDumpTSectNum);
+#endif
+    this->dump();
+    SkDebugf(" ");
+    opp->dump();
+    SkDebugf("\n");
+#elif DEBUG_T_SECT_DUMP == 3
+    SkDebugf("<div id=\"sect%d\">\n", ++gDumpTSectNum);
+    if (this->fHead) {
+        this->dumpCurves();
+    }
+    if (opp->fHead) {
+        opp->dumpCurves();
+    }
+    SkDebugf("</div>\n\n");
+#endif
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpBounded(int id) const {
+    const SkTSpan<TCurve, OppCurve>* bounded = debugSpan(id);
+    if (!bounded) {
+        SkDebugf("no span matches %d\n", id);
+        return;
+    }
+    const SkTSpan<OppCurve, TCurve>* test = bounded->debugOpp()->fHead;
+    do {
+        if (test->findOppSpan(bounded)) {
+            test->dump();
+        }
+    } while ((test = test->next()));
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpBounds() const {
+    const SkTSpan<TCurve, OppCurve>* test = fHead;
+    do {
+        test->dumpBounds();
+    } while ((test = test->next()));
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpCoin() const {
+    dumpCommon(fCoincident);
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpCoinCurves() const {
+    dumpCommonCurves(fCoincident);
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpCommon(const SkTSpan<TCurve, OppCurve>* test) const {
     SkDebugf("id=%d", debugID());
-    const SkTSpan<TCurve>* test = fHead;
     if (!test) {
         SkDebugf(" (empty)");
         return;
     }
     do {
         SkDebugf(" ");
-        test->dump(this);
+        test->dump();
     } while ((test = test->next()));
 }
 
-template<typename TCurve>
-void SkTSect<TCurve>::dumpBoth(const SkTSect& opp) const {
-    dump();
-    SkDebugf(" ");
-    opp.dump();
-    SkDebugf("\n");
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::dumpBoth(const SkTSect* opp) const {
-    dumpBoth(*opp);
-}
-
-template<typename TCurve>
-void SkTSect<TCurve>::dumpCurves() const {
-    const SkTSpan<TCurve>* test = fHead;
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpCommonCurves(const SkTSpan<TCurve, OppCurve>* test) const {
     do {
-        test->fPart.dump();
+        test->fPart.dumpID(test->debugID());
     } while ((test = test->next()));
 }
 
-#if !DEBUG_T_SECT
-template<typename TCurve>
-int SkTSpan<TCurve>::debugID(const SkTSect<TCurve>* sect) const {
-    if (!sect) {
-        return -1;
-    }
-    int id = 1;
-    const SkTSpan* test = sect->fHead;
-    while (test && test != this) {
-        ++id;
-        test = test->fNext;
-    }
-    return id;
+template<typename TCurve, typename OppCurve>
+void SkTSect<TCurve, OppCurve>::dumpCurves() const {
+    dumpCommonCurves(fHead);
 }
-#endif
 
-template<typename TCurve>
-void SkTSpan<TCurve>::dumpID(const SkTSect<TCurve>* sect) const {
+template<typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* SkTSpan<TCurve, OppCurve>::debugSpan(int id) const {
+    return SkDEBUGRELEASE(fDebugSect->debugSpan(id), NULL);
+}
+
+template<typename TCurve, typename OppCurve>
+const SkTSpan<TCurve, OppCurve>* SkTSpan<TCurve, OppCurve>::debugT(double t) const {
+    return SkDEBUGRELEASE(fDebugSect->debugT(t), NULL);
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::dump() const {
+    dumpID();
+    SkDebugf("=(%g,%g) [", fStartT, fEndT);
+    const SkTSpanBounded<OppCurve, TCurve>* testBounded = fBounded;
+    while (testBounded) {
+        const SkTSpan<OppCurve, TCurve>* span = testBounded->fBounded;
+        const SkTSpanBounded<OppCurve, TCurve>* next = testBounded->fNext;
+        span->dumpID();
+        if (next) {
+            SkDebugf(",");
+        }
+        testBounded = next;
+    }
+    SkDebugf("]");
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::dumpBounded(int id) const {
+    SkDEBUGCODE(fDebugSect->dumpBounded(id));
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::dumpBounds() const {
+    dumpID();
+    SkDebugf(" bounds=(%1.9g,%1.9g, %1.9g,%1.9g) boundsMax=%1.9g%s\n",
+            fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom, fBoundsMax,
+            fCollapsed ? " collapsed" : ""); 
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::dumpCoin() const {
+    dumpID();
+    SkDebugf(" coinStart ");
+    fCoinStart.dump();
+    SkDebugf(" coinEnd ");
+    fCoinEnd.dump();
+}
+
+template<typename TCurve, typename OppCurve>
+void SkTSpan<TCurve, OppCurve>::dumpID() const {
     if (fCoinStart.isCoincident()) {
         SkDebugf("%c", '*');
     }
-    SkDebugf("%d", debugID(sect));
+    SkDebugf("%d", debugID());
     if (fCoinEnd.isCoincident()) {
         SkDebugf("%c", '*');
     }
 }
-
-template<typename TCurve>
-void SkTSpan<TCurve>::dump(const SkTSect<TCurve>* sect) const {
-    dumpID(sect);
-    SkDebugf("=(%g,%g) [", fStartT, fEndT);
-    for (int index = 0; index < fBounded.count(); ++index) {
-        SkTSpan* span = fBounded[index];
-        span->dumpID(sect);
-        if (index < fBounded.count() - 1) {
-            SkDebugf(",");
-        }
-    }
-    SkDebugf("]");
-}
diff --git a/tests/PathOpsTestCommon.cpp b/tests/PathOpsTestCommon.cpp
index 60a12ee..f685225 100644
--- a/tests/PathOpsTestCommon.cpp
+++ b/tests/PathOpsTestCommon.cpp
@@ -6,14 +6,133 @@
  */
 #include "PathOpsTestCommon.h"
 #include "SkPathOpsBounds.h"
+#include "SkPathOpsConic.h"
 #include "SkPathOpsCubic.h"
 #include "SkPathOpsLine.h"
 #include "SkPathOpsQuad.h"
-#include "SkPathOpsTriangle.h"
+#include "SkReduceOrder.h"
+#include "SkTSort.h"
+
+static double calc_t_div(const SkDCubic& cubic, double precision, double start) {
+    const double adjust = sqrt(3.) / 36;
+    SkDCubic sub;
+    const SkDCubic* cPtr;
+    if (start == 0) {
+        cPtr = &cubic;
+    } else {
+        // OPTIMIZE: special-case half-split ?
+        sub = cubic.subDivide(start, 1);
+        cPtr = &sub;
+    }
+    const SkDCubic& c = *cPtr;
+    double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX;
+    double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY;
+    double dist = sqrt(dx * dx + dy * dy);
+    double tDiv3 = precision / (adjust * dist);
+    double t = SkDCubeRoot(tDiv3);
+    if (start > 0) {
+        t = start + (1 - start) * t;
+    }
+    return t;
+}
+
+static bool add_simple_ts(const SkDCubic& cubic, double precision, SkTArray<double, true>* ts) {
+    double tDiv = calc_t_div(cubic, precision, 0);
+    if (tDiv >= 1) {
+        return true;
+    }
+    if (tDiv >= 0.5) {
+        ts->push_back(0.5);
+        return true;
+    }
+    return false;
+}
+
+static void addTs(const SkDCubic& cubic, double precision, double start, double end,
+        SkTArray<double, true>* ts) {
+    double tDiv = calc_t_div(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->push_back(newT);
+        }
+    }
+}
+
+static void toQuadraticTs(const SkDCubic* cubic, double precision, SkTArray<double, true>* ts) {
+    SkReduceOrder reducer;
+    int order = reducer.reduce(*cubic, SkReduceOrder::kAllow_Quadratics);
+    if (order < 3) {
+        return;
+    }
+    double inflectT[5];
+    int inflections = cubic->findInflections(inflectT);
+    SkASSERT(inflections <= 2);
+    if (!cubic->endsAreExtremaInXOrY()) {
+        inflections += cubic->findMaxCurvature(&inflectT[inflections]);
+        SkASSERT(inflections <= 5);
+    }
+    SkTQSort<double>(inflectT, &inflectT[inflections - 1]);
+    // OPTIMIZATION: is this filtering common enough that it needs to be pulled out into its
+    // own subroutine?
+    while (inflections && approximately_less_than_zero(inflectT[0])) {
+        memmove(inflectT, &inflectT[1], sizeof(inflectT[0]) * --inflections);
+    }
+    int start = 0;
+    int next = 1;
+    while (next < inflections) {
+        if (!approximately_equal(inflectT[start], inflectT[next])) {
+            ++start;
+        ++next;
+            continue;
+        }
+        memmove(&inflectT[start], &inflectT[next], sizeof(inflectT[0]) * (--inflections - start));
+    }
+
+    while (inflections && approximately_greater_than_one(inflectT[inflections - 1])) {
+        --inflections;
+    }
+    SkDCubicPair pair;
+    if (inflections == 1) {
+        pair = cubic->chopAt(inflectT[0]);
+        int orderP1 = reducer.reduce(pair.first(), SkReduceOrder::kNo_Quadratics);
+        if (orderP1 < 2) {
+            --inflections;
+        } else {
+            int orderP2 = reducer.reduce(pair.second(), SkReduceOrder::kNo_Quadratics);
+            if (orderP2 < 2) {
+                --inflections;
+            }
+        }
+    }
+    if (inflections == 0 && add_simple_ts(*cubic, precision, ts)) {
+        return;
+    }
+    if (inflections == 1) {
+        pair = cubic->chopAt(inflectT[0]);
+        addTs(pair.first(), precision, 0, inflectT[0], ts);
+        addTs(pair.second(), precision, inflectT[0], 1, ts);
+        return;
+    }
+    if (inflections > 1) {
+        SkDCubic part = cubic->subDivide(0, inflectT[0]);
+        addTs(part, precision, 0, inflectT[0], ts);
+        int last = inflections - 1;
+        for (int idx = 0; idx < last; ++idx) {
+            part = cubic->subDivide(inflectT[idx], inflectT[idx + 1]);
+            addTs(part, precision, inflectT[idx], inflectT[idx + 1], ts);
+        }
+        part = cubic->subDivide(inflectT[last], 1);
+        addTs(part, precision, inflectT[last], 1, ts);
+        return;
+    }
+    addTs(*cubic, precision, 0, 1, ts);
+}
 
 void CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads) {
     SkTArray<double, true> ts;
-    cubic.toQuadraticTs(precision, &ts);
+    toQuadraticTs(&cubic, precision, &ts);
     if (ts.count() <= 0) {
         SkDQuad quad = cubic.toQuad();
         quads.push_back(quad);
@@ -22,8 +141,20 @@
     double tStart = 0;
     for (int i1 = 0; i1 <= ts.count(); ++i1) {
         const double tEnd = i1 < ts.count() ? ts[i1] : 1;
+        SkDRect bounds;
+        bounds.setBounds(cubic);
         SkDCubic part = cubic.subDivide(tStart, tEnd);
         SkDQuad quad = part.toQuad();
+        if (quad[1].fX < bounds.fLeft) {
+            quad[1].fX = bounds.fLeft;
+        } else if (quad[1].fX > bounds.fRight) {
+            quad[1].fX = bounds.fRight;
+        }
+        if (quad[1].fY < bounds.fTop) {
+            quad[1].fY = bounds.fTop;
+        } else if (quad[1].fY > bounds.fBottom) {
+            quad[1].fY = bounds.fBottom;
+        }
         quads.push_back(quad);
         tStart = tEnd;
     }
@@ -134,6 +265,18 @@
     return !SkScalarIsNaN(bounds.fBottom);
 }
 
+bool ValidConic(const SkDConic& conic) {
+    for (int index = 0; index < SkDConic::kPointCount; ++index) {
+        if (!ValidPoint(conic[index])) {
+            return false;
+        }
+    }
+    if (SkDoubleIsNaN(conic.fWeight)) {
+        return false;
+    }
+    return true;
+}
+
 bool ValidCubic(const SkDCubic& cubic) {
     for (int index = 0; index < 4; ++index) {
         if (!ValidPoint(cubic[index])) {
@@ -180,15 +323,6 @@
     return true;
 }
 
-bool ValidTriangle(const SkDTriangle& triangle) {
-    for (int index = 0; index < 3; ++index) {
-        if (!ValidPoint(triangle.fPts[index])) {
-            return false;
-        }
-    }
-    return true;
-}
-
 bool ValidVector(const SkDVector& v) {
     if (SkDoubleIsNaN(v.fX)) {
         return false;
diff --git a/tests/PathOpsTestCommon.h b/tests/PathOpsTestCommon.h
index 0c42bfb..3fd79e1 100644
--- a/tests/PathOpsTestCommon.h
+++ b/tests/PathOpsTestCommon.h
@@ -7,7 +7,7 @@
 #ifndef PathOpsTestCommon_DEFINED
 #define PathOpsTestCommon_DEFINED
 
-#include "SkPathOpsQuad.h"
+#include "SkPathOpsConic.h"
 #include "SkTArray.h"
 
 struct SkPathOpsBounds;
@@ -15,13 +15,13 @@
 void CubicPathToQuads(const SkPath& cubicPath, SkPath* quadPath);
 void CubicPathToSimple(const SkPath& cubicPath, SkPath* simplePath);
 void CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads);
-bool ValidBounds(const SkPathOpsBounds&);
+bool ValidBounds(const SkPathOpsBounds& );
+bool ValidConic(const SkDConic& cubic);
 bool ValidCubic(const SkDCubic& cubic);
 bool ValidLine(const SkDLine& line);
 bool ValidPoint(const SkDPoint& pt);
 bool ValidPoints(const SkPoint* pts, int count);
 bool ValidQuad(const SkDQuad& quad);
-bool ValidTriangle(const SkDTriangle& triangle);
 bool ValidVector(const SkDVector& v);
 
 #endif
diff --git a/tests/PathOpsThreadedCommon.h b/tests/PathOpsThreadedCommon.h
index e43afe1..5bf5da2 100644
--- a/tests/PathOpsThreadedCommon.h
+++ b/tests/PathOpsThreadedCommon.h
@@ -73,7 +73,7 @@
         fTestFun = testFun;
     }
 
-    void run() SK_OVERRIDE {
+    void run() override {
         SkBitmap bitmap;
         fState.fBitmap = &bitmap;
         char pathStr[PATH_STR_SIZE];
diff --git a/tests/PathOpsThreeWayTest.cpp b/tests/PathOpsThreeWayTest.cpp
index 15d6e54..bf634f9 100644
--- a/tests/PathOpsThreeWayTest.cpp
+++ b/tests/PathOpsThreeWayTest.cpp
@@ -49,14 +49,16 @@
             const Curve& iTest = testSet.tests[inner];
             SkIntersections* i = combos.append();
             sk_bzero(i, sizeof(SkIntersections));
+            SkDLine oLine = {{ oTest.curve[0], oTest.curve[1] }};
+            SkDLine iLine = {{ iTest.curve[0], iTest.curve[1] }};
             if (oTest.ptCount == 1 && iTest.ptCount == 1) {
-                i->intersect(*(const SkDLine*) &oTest.curve, *(const SkDLine*) &iTest.curve);
+                i->intersect(oLine, iLine);
             } else if (oTest.ptCount == 1 && iTest.ptCount == 4) {
-                i->intersect(iTest.curve, *(const SkDLine*) &oTest.curve);
+                i->intersect(iTest.curve, oLine);
             } else if (oTest.ptCount == 4 && iTest.ptCount == 1) {
-                i->intersect(oTest.curve, *(const SkDLine*) &oTest.curve);
+                i->intersect(oTest.curve, iLine);
             } else if (oTest.ptCount == 4 && iTest.ptCount == 4) {
-                i->intersectB(oTest.curve, iTest.curve);
+                i->intersect(oTest.curve, iTest.curve);
             } else {
                 SkASSERT(0);
             }
diff --git a/tests/PathOpsTightBoundsTest.cpp b/tests/PathOpsTightBoundsTest.cpp
index cea3752..d50c26a 100644
--- a/tests/PathOpsTightBoundsTest.cpp
+++ b/tests/PathOpsTightBoundsTest.cpp
@@ -8,7 +8,6 @@
 #include "PathOpsThreadedCommon.h"
 #include "SkCanvas.h"
 #include "SkRandom.h"
-#include "SkTArray.h"
 #include "SkTSort.h"
 #include "Test.h"
 
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index 964b7e1..b78b343 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -1857,7 +1857,7 @@
     REPORTER_ASSERT(reporter, !path1.isRect(NULL));
 }
 
-static void test_isNestedRects(skiatest::Reporter* reporter) {
+static void test_isNestedFillRects(skiatest::Reporter* reporter) {
     // passing tests (all moveTo / lineTo...
     SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
     SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
@@ -1884,7 +1884,7 @@
     SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
     SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
 
-    // failing, no close
+    // success, no close is OK
     SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
     SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
 
@@ -1919,8 +1919,8 @@
         { f7, SK_ARRAY_COUNT(f7), SkPath::kUnknown_Direction, true, false },
         { f8, SK_ARRAY_COUNT(f8), SkPath::kUnknown_Direction, true, false },
 
-        { c1, SK_ARRAY_COUNT(c1), SkPath::kUnknown_Direction, false, false },
-        { c2, SK_ARRAY_COUNT(c2), SkPath::kUnknown_Direction, false, false },
+        { c1, SK_ARRAY_COUNT(c1), SkPath::kCW_Direction, false, true },
+        { c2, SK_ARRAY_COUNT(c2), SkPath::kCW_Direction, false, true },
     };
 
     const size_t testCount = SK_ARRAY_COUNT(tests);
@@ -1941,7 +1941,8 @@
             if (!rectFirst) {
                 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
             }
-            REPORTER_ASSERT(reporter, tests[testIndex].fIsNestedRect == path.isNestedRects(NULL));
+            REPORTER_ASSERT(reporter,
+                    tests[testIndex].fIsNestedRect == path.isNestedFillRects(NULL));
             if (tests[testIndex].fIsNestedRect) {
                 SkRect expected[2], computed[2];
                 SkPath::Direction expectedDirs[2], computedDirs[2];
@@ -1955,7 +1956,7 @@
                     expectedDirs[0] = SkPath::kCCW_Direction;
                 }
                 expectedDirs[1] = tests[testIndex].fDirection;
-                REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs));
+                REPORTER_ASSERT(reporter, path.isNestedFillRects(computed, computedDirs));
                 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
                 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
                 REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]);
@@ -1977,7 +1978,7 @@
         if (!rectFirst) {
             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
         }
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(NULL));
 
         // fail, move in the middle
         path1.reset();
@@ -1995,7 +1996,7 @@
         if (!rectFirst) {
             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
         }
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(NULL));
 
         // fail, move on the edge
         path1.reset();
@@ -2010,7 +2011,7 @@
         if (!rectFirst) {
             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
         }
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(NULL));
 
         // fail, quad
         path1.reset();
@@ -2028,7 +2029,7 @@
         if (!rectFirst) {
             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
         }
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(NULL));
 
         // fail, cubic
         path1.reset();
@@ -2046,15 +2047,29 @@
         if (!rectFirst) {
             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
         }
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(NULL));
 
         // fail,  not nested
         path1.reset();
         path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
         path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
-        REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
+        REPORTER_ASSERT(reporter, !path1.isNestedFillRects(NULL));
     }
 
+    //  pass, constructed explicitly from manually closed rects specified as moves/lines.
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(10, 0);
+    path.lineTo(10, 10);
+    path.lineTo(0, 10);
+    path.lineTo(0, 0);
+    path.moveTo(1, 1);
+    path.lineTo(9, 1);
+    path.lineTo(9, 9);
+    path.lineTo(1, 9);
+    path.lineTo(1, 1);
+    REPORTER_ASSERT(reporter, path.isNestedFillRects(NULL));
+
     // pass, stroke rect
     SkPath src, dst;
     src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
@@ -2062,7 +2077,7 @@
     strokePaint.setStyle(SkPaint::kStroke_Style);
     strokePaint.setStrokeWidth(2);
     strokePaint.getFillPath(src, &dst);
-    REPORTER_ASSERT(reporter, dst.isNestedRects(NULL));
+    REPORTER_ASSERT(reporter, dst.isNestedFillRects(NULL));
 }
 
 static void write_and_read_back(skiatest::Reporter* reporter,
@@ -3719,7 +3734,7 @@
     test_operatorEqual(reporter);
     test_isLine(reporter);
     test_isRect(reporter);
-    test_isNestedRects(reporter);
+    test_isNestedFillRects(reporter);
     test_zero_length_paths(reporter);
     test_direction(reporter);
     test_convexity(reporter);
diff --git a/tests/PathUtilsTest.cpp b/tests/PathUtilsTest.cpp
deleted file mode 100644
index 4dfeda4..0000000
--- a/tests/PathUtilsTest.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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 "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkPathUtils.h"
-#include "SkRandom.h"
-#include "SkTime.h"
-#include "Test.h"
-
-const int kNumIt = 100;
-
-static void fill_random_bits(int chars, char* bits){
-    SkRandom rand(SkTime::GetMSecs());
-
-    for (int i = 0; i < chars; ++i){
-        bits[i] = rand.nextU();
-    }
-}
-
-static int get_bit(const char* buffer, int x) {
-    int byte = x >> 3;
-    int bit = x & 7;
-
-    return buffer[byte] & (128 >> bit);
-}
-
-/* // useful for debugging errors
-   #include <iostream>
-static void print_bits( const char* bits, int w, int h) {
-
-    for (int y = 0; y < h; ++y) {
-        for (int x = 0; x < w; ++x){
-            bool bit = get_bit(&bits[y], x)!=0;
-            std::cout << bit;
-            }
-        std::cout << std::endl;
-    }
-}
-
-static void print_bmp( SkBitmap* bmp, int w, int h){
-
-    for (int y = 0; y < h; ++y) {
-        for (int x = 0; x < w; ++x) {
-            int d = *bmp->getAddr32(x,y);
-            if (d == -1)
-                std::cout << 0;
-            else
-                std::cout << 1;
-        }
-        std::cout << std::endl;
-    }
-}
-*/
-
-static void binary_to_skbitmap(const char* bin_bmp, SkBitmap* sk_bmp,
-                         int w, int h, int rowBytes){
-    //init the SkBitmap
-    sk_bmp->allocN32Pixels(w, h);
-
-    for (int y = 0; y < h; ++y) { // for every row
-
-        const char* curLine = &bin_bmp[y * rowBytes];
-        for (int x = 0; x < w; ++x) {// for every pixel
-            if (get_bit(curLine, x)) {
-                *sk_bmp->getAddr32(x,y) = SK_ColorBLACK;
-            }
-            else {
-                *sk_bmp->getAddr32(x,y) = SK_ColorWHITE;
-            }
-        }
-    }
-}
-
-static bool test_bmp(skiatest::Reporter* reporter,
-                      const SkBitmap* bmp1, const SkBitmap* bmp2,
-                      int w, int h) {
-    for (int y = 0; y < h; ++y) { // loop through all pixels
-        for (int x = 0; x < w; ++x) {
-            REPORTER_ASSERT( reporter, *bmp1->getAddr32(x,y) == *bmp2->getAddr32(x,y) );
-        }
-    }
-    return true;
-}
-
-static void test_path_eq(skiatest::Reporter* reporter, const SkPath* path,
-                      const SkBitmap* truth, int w, int h){
-    // make paint
-    SkPaint bmpPaint;
-    bmpPaint.setAntiAlias(true);  // Black paint for bitmap
-    bmpPaint.setStyle(SkPaint::kFill_Style);
-    bmpPaint.setColor(SK_ColorBLACK);
-
-    // make bmp
-    SkBitmap bmp;
-    bmp.allocN32Pixels(w, h);
-    SkCanvas canvas(bmp);
-    canvas.clear(SK_ColorWHITE);
-    canvas.drawPath(*path, bmpPaint);
-
-    // test bmp
-    test_bmp(reporter, truth, &bmp, w, h);
-}
-
-static void test_path(skiatest::Reporter* reporter, const SkBitmap* truth,
-                            const char* bin_bmp, int w, int h, int rowBytes){
-    // make path
-    SkPath path;
-    SkPathUtils::BitsToPath_Path(&path, bin_bmp, w, h, rowBytes);
-
-    //test for correctness
-    test_path_eq(reporter, &path, truth, w, h);
-}
-
-static void test_region(skiatest::Reporter* reporter, const SkBitmap* truth,
-                            const char* bin_bmp, int w, int h, int rowBytes){
-    //generate bitmap
-    SkPath path;
-    SkPathUtils::BitsToPath_Region(&path, bin_bmp, w, h, rowBytes);
-
-    //test for correctness
-    test_path_eq(reporter, &path, truth, w, h);
-}
-
-DEF_TEST(PathUtils, reporter) {
-    const int w[] = {4, 8, 12, 16};
-    const int h = 8, rowBytes = 4;
-
-    char bits[ h * rowBytes ];
-    static char* binBmp = &bits[0];
-
-    //loop to run randomized test lots of times
-    for (int it = 0; it < kNumIt; ++it)
-    {
-        // generate a random binary bitmap
-        fill_random_bits( h * rowBytes, binBmp); // generate random bitmap
-
-        // for each bitmap width, use subset of binary bitmap
-        for (unsigned int i = 0; i < SK_ARRAY_COUNT(w); ++i) {
-            // generate truth bitmap
-            SkBitmap bmpTruth;
-            binary_to_skbitmap(binBmp, &bmpTruth, w[i], h, rowBytes);
-
-            test_path(reporter, &bmpTruth, binBmp, w[i], h, rowBytes);
-            test_region(reporter, &bmpTruth, binBmp, w[i], h, rowBytes);
-        }
-    }
-}
diff --git a/tests/PictureBBHTest.cpp b/tests/PictureBBHTest.cpp
index 771aa12..d09403f 100644
--- a/tests/PictureBBHTest.cpp
+++ b/tests/PictureBBHTest.cpp
@@ -62,7 +62,7 @@
         : PictureBBHTestBase(2, 2, 1, 1) { }
     virtual ~DrawEmptyPictureBBHTest() { }
 
-    void doTest(SkCanvas&, SkCanvas&) SK_OVERRIDE { }
+    void doTest(SkCanvas&, SkCanvas&) override { }
 };
 
 // Test to verify the playback of a picture into a canvas that has
@@ -73,7 +73,7 @@
     EmptyClipPictureBBHTest()
         : PictureBBHTestBase(2, 2, 3, 3) { }
 
-    void doTest(SkCanvas& playbackCanvas, SkCanvas& recordingCanvas) SK_OVERRIDE {
+    void doTest(SkCanvas& playbackCanvas, SkCanvas& recordingCanvas) override {
         // intersect with out of bounds rect -> empty clip.
         playbackCanvas.clipRect(SkRect::MakeXYWH(SkIntToScalar(10), SkIntToScalar(10),
             SkIntToScalar(1), SkIntToScalar(1)), SkRegion::kIntersect_Op);
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index ac750e9..16d98b3 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -28,6 +28,7 @@
 #include "SkRecord.h"
 #include "SkShader.h"
 #include "SkStream.h"
+#include "sk_tool_utils.h"
 
 #if SK_SUPPORT_GPU
 #include "SkSurface.h"
@@ -547,17 +548,17 @@
     }
 
     virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
-                                            SaveFlags flags) SK_OVERRIDE {
+                                            SaveFlags flags) override {
         ++fSaveLayerCount;
         return this->INHERITED::willSaveLayer(bounds, paint, flags);
     }
 
-    void willSave() SK_OVERRIDE {
+    void willSave() override {
         ++fSaveCount;
         this->INHERITED::willSave();
     }
 
-    void willRestore() SK_OVERRIDE {
+    void willRestore() override {
         ++fRestoreCount;
         this->INHERITED::willRestore();
     }
@@ -830,18 +831,6 @@
 }
 #endif
 
-// Encodes to PNG, unless there is already encoded data, in which case that gets
-// used.
-// FIXME: Share with PictureRenderer.cpp?
-class PngPixelSerializer : public SkPixelSerializer {
-public:
-    bool onUseEncodedData(const void*, size_t) SK_OVERRIDE { return true; }
-    SkData* onEncodePixels(const SkImageInfo& info, const void* pixels,
-                           size_t rowBytes) SK_OVERRIDE {
-        return SkImageEncoder::EncodeData(info, pixels, rowBytes, SkImageEncoder::kPNG_Type, 100);
-    }
-};
-
 static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
     SkPictureRecorder recorder;
     SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(bitmap.width()),
@@ -850,7 +839,7 @@
     SkAutoTUnref<SkPicture> picture(recorder.endRecording());
 
     SkDynamicMemoryWStream wStream;
-    PngPixelSerializer serializer;
+    sk_tool_utils::PngPixelSerializer serializer;
     picture->serialize(&wStream, &serializer);
     return wStream.copyToData();
 }
@@ -1005,26 +994,26 @@
 
     virtual void onClipRect(const SkRect& r,
                             SkRegion::Op op,
-                            ClipEdgeStyle edgeStyle) SK_OVERRIDE {
+                            ClipEdgeStyle edgeStyle) override {
         fClipCount += 1;
         this->INHERITED::onClipRect(r, op, edgeStyle);
     }
 
     virtual void onClipRRect(const SkRRect& rrect,
                              SkRegion::Op op,
-                             ClipEdgeStyle edgeStyle)SK_OVERRIDE {
+                             ClipEdgeStyle edgeStyle)override {
         fClipCount += 1;
         this->INHERITED::onClipRRect(rrect, op, edgeStyle);
     }
 
     virtual void onClipPath(const SkPath& path,
                             SkRegion::Op op,
-                            ClipEdgeStyle edgeStyle) SK_OVERRIDE {
+                            ClipEdgeStyle edgeStyle) override {
         fClipCount += 1;
         this->INHERITED::onClipPath(path, op, edgeStyle);
     }
 
-    void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE {
+    void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) override {
         fClipCount += 1;
         this->INHERITED::onClipRegion(deviceRgn, op);
     }
@@ -1130,7 +1119,7 @@
 
     // Protect against any unintentional bloat.
     size_t approxUsed = SkPictureUtils::ApproximateBytesUsed(empty.get());
-    REPORTER_ASSERT(reporter, approxUsed <= 136);
+    REPORTER_ASSERT(reporter, approxUsed <= 432);
 
     // Sanity check of nested SkPictures.
     SkPictureRecorder r2;
@@ -1138,7 +1127,7 @@
     r2.getRecordingCanvas()->drawPicture(empty.get());
     SkAutoTUnref<SkPicture> nested(r2.endRecording());
 
-    REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(nested.get()) >
+    REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(nested.get()) >=
                               SkPictureUtils::ApproximateBytesUsed(empty.get()));
 }
 
@@ -1252,19 +1241,19 @@
 
     CountingBBH(const SkRect& bound) : searchCalls(0), rootBound(bound) {}
 
-    void search(const SkRect& query, SkTDArray<unsigned>* results) const SK_OVERRIDE {
+    void search(const SkRect& query, SkTDArray<unsigned>* results) const override {
         this->searchCalls++;
     }
 
-    void insert(const SkRect[], int) SK_OVERRIDE {}
-    virtual size_t bytesUsed() const SK_OVERRIDE { return 0; }
-    SkRect getRootBound() const SK_OVERRIDE { return rootBound; }
+    void insert(const SkRect[], int) override {}
+    virtual size_t bytesUsed() const override { return 0; }
+    SkRect getRootBound() const override { return rootBound; }
 };
 
 class SpoonFedBBHFactory : public SkBBHFactory {
 public:
     explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
-    SkBBoxHierarchy* operator()(const SkRect&) const SK_OVERRIDE {
+    SkBBoxHierarchy* operator()(const SkRect&) const override {
         return SkRef(fBBH);
     }
 private:
@@ -1322,3 +1311,15 @@
     REPORTER_ASSERT(r, mut.pixelRef()->unique());
     REPORTER_ASSERT(r, immut.pixelRef()->unique());
 }
+
+// getRecordingCanvas() should return a SkCanvas when recording, null when not recording.
+DEF_TEST(Picture_getRecordingCanvas, r) {
+    SkPictureRecorder rec;
+    REPORTER_ASSERT(r, !rec.getRecordingCanvas());
+    for (int i = 0; i < 3; i++) {
+        rec.beginRecording(100, 100);
+        REPORTER_ASSERT(r, rec.getRecordingCanvas());
+        rec.endRecording()->unref();
+        REPORTER_ASSERT(r, !rec.getRecordingCanvas());
+    }
+}
diff --git a/tests/PixelRefTest.cpp b/tests/PixelRefTest.cpp
index b9a2824..e13d0e0 100644
--- a/tests/PixelRefTest.cpp
+++ b/tests/PixelRefTest.cpp
@@ -6,7 +6,7 @@
 class TestListener : public SkPixelRef::GenIDChangeListener {
 public:
     explicit TestListener(int* ptr) : fPtr(ptr) {}
-    void onChange() SK_OVERRIDE { (*fPtr)++; }
+    void onChange() override { (*fPtr)++; }
 private:
     int* fPtr;
 };
diff --git a/tests/QuickRejectTest.cpp b/tests/QuickRejectTest.cpp
index 447d8161..e5dea24 100644
--- a/tests/QuickRejectTest.cpp
+++ b/tests/QuickRejectTest.cpp
@@ -16,14 +16,14 @@
 class TestLooper : public SkDrawLooper {
 public:
 
-    SkDrawLooper::Context* createContext(SkCanvas*, void* storage) const SK_OVERRIDE {
+    SkDrawLooper::Context* createContext(SkCanvas*, void* storage) const override {
         return SkNEW_PLACEMENT(storage, TestDrawLooperContext);
     }
 
-    size_t contextSize() const SK_OVERRIDE { return sizeof(TestDrawLooperContext); }
+    size_t contextSize() const override { return sizeof(TestDrawLooperContext); }
 
 #ifndef SK_IGNORE_TO_STRING
-    void toString(SkString* str) const SK_OVERRIDE {
+    void toString(SkString* str) const override {
         str->append("TestLooper:");
     }
 #endif
@@ -36,7 +36,7 @@
         TestDrawLooperContext() : fOnce(true) {}
         virtual ~TestDrawLooperContext() {}
 
-        bool next(SkCanvas* canvas, SkPaint*) SK_OVERRIDE {
+        bool next(SkCanvas* canvas, SkPaint*) override {
             if (fOnce) {
                 fOnce = false;
                 canvas->translate(SkIntToScalar(10), 0);
diff --git a/tests/ReadPixelsTest.cpp b/tests/ReadPixelsTest.cpp
index cead36f..a45e9ee 100644
--- a/tests/ReadPixelsTest.cpp
+++ b/tests/ReadPixelsTest.cpp
@@ -318,7 +318,8 @@
                 desc.fHeight = DEV_H;
                 desc.fConfig = kSkia8888_GrPixelConfig;
                 desc.fOrigin = 1 == dtype ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
-                SkAutoTUnref<GrTexture> texture(context->createTexture(desc, false));
+                SkAutoTUnref<GrTexture> texture(
+                    context->textureProvider()->createTexture(desc, false));
                 surface.reset(SkSurface::NewRenderTargetDirect(texture->asRenderTarget()));
 #else
                 continue;
diff --git a/tests/ReadWriteAlphaTest.cpp b/tests/ReadWriteAlphaTest.cpp
index 3f9ff1b..6ae77d0 100644
--- a/tests/ReadWriteAlphaTest.cpp
+++ b/tests/ReadWriteAlphaTest.cpp
@@ -41,7 +41,7 @@
         desc.fHeight    = Y_SIZE;
 
         // We are initializing the texture with zeros here
-        GrTexture* texture = context->createTexture(desc, false, textureData, 0);
+        GrTexture* texture = context->textureProvider()->createTexture(desc, false, textureData, 0);
         if (!texture) {
             return;
         }
diff --git a/tests/RecordDrawTest.cpp b/tests/RecordDrawTest.cpp
index baee712..1e365bc 100644
--- a/tests/RecordDrawTest.cpp
+++ b/tests/RecordDrawTest.cpp
@@ -24,7 +24,7 @@
 public:
     JustOneDraw() : fCalls(0) {}
 
-    bool abort() SK_OVERRIDE { return fCalls++ > 0; }
+    bool abort() override { return fCalls++ > 0; }
 private:
     int fCalls;
 };
@@ -123,7 +123,7 @@
 }
 
 struct TestBBH : public SkBBoxHierarchy {
-    void insert(const SkRect boundsArray[], int N) SK_OVERRIDE {
+    void insert(const SkRect boundsArray[], int N) override {
         fEntries.setCount(N);
         for (int i = 0; i < N; i++) {
             Entry e = { (unsigned)i, boundsArray[i] };
@@ -131,9 +131,9 @@
         }
     }
 
-    void search(const SkRect& query, SkTDArray<unsigned>* results) const SK_OVERRIDE {}
-    size_t bytesUsed() const SK_OVERRIDE { return 0; }
-    SkRect getRootBound() const SK_OVERRIDE { return SkRect::MakeEmpty(); }
+    void search(const SkRect& query, SkTDArray<unsigned>* results) const override {}
+    size_t bytesUsed() const override { return 0; }
+    SkRect getRootBound() const override { return SkRect::MakeEmpty(); }
 
     struct Entry {
         unsigned opIndex;
@@ -290,12 +290,12 @@
         }
 
         void onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
-                         const SkPaint* paint) SK_OVERRIDE {
+                         const SkPaint* paint) override {
             fDrawImageCalled = true;
         }
 
         void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
-                             const SkPaint* paint) SK_OVERRIDE {
+                             const SkPaint* paint) override {
             fDrawImageRectCalled = true;
         }
 
diff --git a/tests/RecordReplaceDrawTest.cpp b/tests/RecordReplaceDrawTest.cpp
index 55f47ce..473683b 100644
--- a/tests/RecordReplaceDrawTest.cpp
+++ b/tests/RecordReplaceDrawTest.cpp
@@ -5,10 +5,10 @@
  * found in the LICENSE file.
  */
 
-#if SK_SUPPORT_GPU
-
 #include "Test.h"
 
+#if SK_SUPPORT_GPU
+
 #include "GrContextFactory.h"
 #include "GrLayerCache.h"
 #include "GrRecordReplaceDraw.h"
@@ -26,7 +26,7 @@
 public:
     JustOneDraw() : fCalls(0) {}
 
-    bool abort() SK_OVERRIDE { return fCalls++ > 0; }
+    bool abort() override { return fCalls++ > 0; }
 private:
     int fCalls;
 };
@@ -124,7 +124,8 @@
     desc.fHeight = kHeight;
     desc.fSampleCnt = 0;
 
-    SkAutoTUnref<GrTexture> texture(context->createTexture(desc, false, NULL, 0));
+    SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc,
+        false, NULL, 0));
     layer->setTexture(texture, SkIRect::MakeWH(kWidth, kHeight));
 
     SkAutoTUnref<SkBBoxHierarchy> bbh;
diff --git a/tests/RecorderTest.cpp b/tests/RecorderTest.cpp
index 4fac1af..e67de58 100644
--- a/tests/RecorderTest.cpp
+++ b/tests/RecorderTest.cpp
@@ -89,28 +89,6 @@
     REPORTER_ASSERT(r, paint.getShader()->unique());
 }
 
-DEF_TEST(Recorder_RefPictures, r) {
-    SkAutoTUnref<SkPicture> pic;
-
-    {
-        SkPictureRecorder pr;
-        SkCanvas* canvas = pr.beginRecording(100, 100);
-        canvas->drawColor(SK_ColorRED);
-        pic.reset(pr.endRecording());
-    }
-    REPORTER_ASSERT(r, pic->unique());
-
-    {
-        SkRecord record;
-        SkRecorder recorder(&record, 100, 100);
-        recorder.drawPicture(pic);
-        // the recorder should now also be an owner
-        REPORTER_ASSERT(r, !pic->unique());
-    }
-    // the recorder destructor should have released us (back to unique)
-    REPORTER_ASSERT(r, pic->unique());
-}
-
 DEF_TEST(Recorder_drawImage_takeReference, reporter) {
 
     SkAutoTUnref<SkImage> image;
diff --git a/tests/RefCntTest.cpp b/tests/RefCntTest.cpp
index cf4acc6..b73618d 100644
--- a/tests/RefCntTest.cpp
+++ b/tests/RefCntTest.cpp
@@ -11,21 +11,6 @@
 #include "SkWeakRefCnt.h"
 #include "Test.h"
 
-class InstCounterClass {
-public:
-    InstCounterClass() { fCount = gInstCounter++; }
-    InstCounterClass(const InstCounterClass& src) {
-        fCount = src.fCount;
-        gInstCounter += 1;
-    }
-    virtual ~InstCounterClass() { gInstCounter -= 1; }
-
-    static int gInstCounter;
-    int fCount;
-};
-
-int InstCounterClass::gInstCounter;
-
 static void bounce_ref(void* data) {
     SkRefCnt* ref = static_cast<SkRefCnt*>(data);
     for (int i = 0; i < 100000; ++i) {
diff --git a/tests/RegionTest.cpp b/tests/RegionTest.cpp
index ae58ae6..acb8180 100644
--- a/tests/RegionTest.cpp
+++ b/tests/RegionTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkPath.h"
 #include "SkRandom.h"
 #include "SkRegion.h"
 #include "Test.h"
@@ -91,6 +92,13 @@
     REPORTER_ASSERT(reporter, !empty.contains(empty2));
     REPORTER_ASSERT(reporter, !valid.contains(empty));
     REPORTER_ASSERT(reporter, !empty.contains(valid));
+
+    SkPath emptyPath;
+    emptyPath.moveTo(1, 5);
+    emptyPath.close();
+    SkRegion openClip;
+    openClip.setRect(-16000, -16000, 16000, 16000);
+    empty.setPath(emptyPath, openClip);  // should not assert
 }
 
 enum {
diff --git a/tests/ResourceCacheTest.cpp b/tests/ResourceCacheTest.cpp
index b6e1eb7..71fb4e3 100644
--- a/tests/ResourceCacheTest.cpp
+++ b/tests/ResourceCacheTest.cpp
@@ -5,6 +5,9 @@
  * found in the LICENSE file.
  */
 
+// Include here to ensure SK_SUPPORT_GPU is set correctly before it is examined.
+#include "SkTypes.h"
+
 #if SK_SUPPORT_GPU
 
 #include "GrContext.h"
@@ -73,31 +76,34 @@
     smallDesc.fHeight = 4;
     smallDesc.fSampleCnt = 0;
 
+    GrTextureProvider* cache = context->textureProvider();
     // Test that two budgeted RTs with the same desc share a stencil buffer.
-    SkAutoTUnref<GrTexture> smallRT0(context->createTexture(smallDesc, true));
+    SkAutoTUnref<GrTexture> smallRT0(cache->createTexture(smallDesc, true));
     if (smallRT0 && smallRT0->asRenderTarget()) {
-        smallRT0->asRenderTarget()->renderTargetPriv().attachStencilBuffer();
+        smallRT0->asRenderTarget()->renderTargetPriv().attachStencilAttachment();
     }
 
-    SkAutoTUnref<GrTexture> smallRT1(context->createTexture(smallDesc, true));
+    SkAutoTUnref<GrTexture> smallRT1(cache->createTexture(smallDesc, true));
     if (smallRT1 && smallRT1->asRenderTarget()) {
-        smallRT1->asRenderTarget()->renderTargetPriv().attachStencilBuffer();
+        smallRT1->asRenderTarget()->renderTargetPriv().attachStencilAttachment();
     }
 
-    REPORTER_ASSERT(reporter, smallRT0 && smallRT1 &&
-                              smallRT0->asRenderTarget() && smallRT1->asRenderTarget() &&
-                              smallRT0->asRenderTarget()->renderTargetPriv().getStencilBuffer() ==
-                              smallRT1->asRenderTarget()->renderTargetPriv().getStencilBuffer());
+    REPORTER_ASSERT(reporter,
+                    smallRT0 && smallRT1 &&
+                    smallRT0->asRenderTarget() && smallRT1->asRenderTarget() &&
+                    smallRT0->asRenderTarget()->renderTargetPriv().getStencilAttachment() ==
+                    smallRT1->asRenderTarget()->renderTargetPriv().getStencilAttachment());
 
     // An unbudgeted RT with the same desc should also share.
-    SkAutoTUnref<GrTexture> smallRT2(context->createTexture(smallDesc, false));
+    SkAutoTUnref<GrTexture> smallRT2(cache->createTexture(smallDesc, false));
     if (smallRT2 && smallRT2->asRenderTarget()) {
-        smallRT2->asRenderTarget()->renderTargetPriv().attachStencilBuffer();
+        smallRT2->asRenderTarget()->renderTargetPriv().attachStencilAttachment();
     }
-    REPORTER_ASSERT(reporter, smallRT0 && smallRT2 &&
-                              smallRT0->asRenderTarget() && smallRT2->asRenderTarget() &&
-                              smallRT0->asRenderTarget()->renderTargetPriv().getStencilBuffer() ==
-                              smallRT2->asRenderTarget()->renderTargetPriv().getStencilBuffer());
+    REPORTER_ASSERT(reporter,
+                    smallRT0 && smallRT2 &&
+                    smallRT0->asRenderTarget() && smallRT2->asRenderTarget() &&
+                    smallRT0->asRenderTarget()->renderTargetPriv().getStencilAttachment() ==
+                    smallRT2->asRenderTarget()->renderTargetPriv().getStencilAttachment());
 
     // An RT with a much larger size should not share.
     GrSurfaceDesc bigDesc;
@@ -106,22 +112,23 @@
     bigDesc.fWidth = 400;
     bigDesc.fHeight = 200;
     bigDesc.fSampleCnt = 0;
-    SkAutoTUnref<GrTexture> bigRT(context->createTexture(bigDesc, false));
+    SkAutoTUnref<GrTexture> bigRT(cache->createTexture(bigDesc, false));
     if (bigRT && bigRT->asRenderTarget()) {
-        bigRT->asRenderTarget()->renderTargetPriv().attachStencilBuffer();
+        bigRT->asRenderTarget()->renderTargetPriv().attachStencilAttachment();
     }
-    REPORTER_ASSERT(reporter, smallRT0 && bigRT &&
-                              smallRT0->asRenderTarget() && bigRT->asRenderTarget() &&
-                              smallRT0->asRenderTarget()->renderTargetPriv().getStencilBuffer() !=
-                              bigRT->asRenderTarget()->renderTargetPriv().getStencilBuffer());
+    REPORTER_ASSERT(reporter,
+                    smallRT0 && bigRT &&
+                    smallRT0->asRenderTarget() && bigRT->asRenderTarget() &&
+                    smallRT0->asRenderTarget()->renderTargetPriv().getStencilAttachment() !=
+                    bigRT->asRenderTarget()->renderTargetPriv().getStencilAttachment());
 
     if (context->getMaxSampleCount() >= 4) {
         // An RT with a different sample count should not share. 
         GrSurfaceDesc smallMSAADesc = smallDesc;
         smallMSAADesc.fSampleCnt = 4;
-        SkAutoTUnref<GrTexture> smallMSAART0(context->createTexture(smallMSAADesc, false));
+        SkAutoTUnref<GrTexture> smallMSAART0(cache->createTexture(smallMSAADesc, false));
         if (smallMSAART0 && smallMSAART0->asRenderTarget()) {
-            smallMSAART0->asRenderTarget()->renderTargetPriv().attachStencilBuffer();
+            smallMSAART0->asRenderTarget()->renderTargetPriv().attachStencilAttachment();
         }
 #ifdef SK_BUILD_FOR_ANDROID
         if (!smallMSAART0) {
@@ -132,35 +139,35 @@
         REPORTER_ASSERT(reporter,
                         smallRT0 && smallMSAART0 &&
                         smallRT0->asRenderTarget() && smallMSAART0->asRenderTarget() &&
-                        smallRT0->asRenderTarget()->renderTargetPriv().getStencilBuffer() !=
-                        smallMSAART0->asRenderTarget()->renderTargetPriv().getStencilBuffer());
+                        smallRT0->asRenderTarget()->renderTargetPriv().getStencilAttachment() !=
+                        smallMSAART0->asRenderTarget()->renderTargetPriv().getStencilAttachment());
         // A second MSAA RT should share with the first MSAA RT.
-        SkAutoTUnref<GrTexture> smallMSAART1(context->createTexture(smallMSAADesc, false));
+        SkAutoTUnref<GrTexture> smallMSAART1(cache->createTexture(smallMSAADesc, false));
         if (smallMSAART1 && smallMSAART1->asRenderTarget()) {
-            smallMSAART1->asRenderTarget()->renderTargetPriv().attachStencilBuffer();
+            smallMSAART1->asRenderTarget()->renderTargetPriv().attachStencilAttachment();
         }
         REPORTER_ASSERT(reporter,
                         smallMSAART0 && smallMSAART1 &&
                         smallMSAART0->asRenderTarget() &&
                         smallMSAART1->asRenderTarget() &&
-                        smallMSAART0->asRenderTarget()->renderTargetPriv().getStencilBuffer() ==
-                        smallMSAART1->asRenderTarget()->renderTargetPriv().getStencilBuffer());
+                        smallMSAART0->asRenderTarget()->renderTargetPriv().getStencilAttachment() ==
+                        smallMSAART1->asRenderTarget()->renderTargetPriv().getStencilAttachment());
         // But not one with a larger sample count should not. (Also check that the request for 4
         // samples didn't get rounded up to >= 8 or else they could share.).
         if (context->getMaxSampleCount() >= 8 && smallMSAART0 && smallMSAART0->asRenderTarget() &&
             smallMSAART0->asRenderTarget()->numSamples() < 8) {
             smallMSAADesc.fSampleCnt = 8;
-            smallMSAART1.reset(context->createTexture(smallMSAADesc, false));
-            SkAutoTUnref<GrTexture> smallMSAART1(context->createTexture(smallMSAADesc, false));
+            smallMSAART1.reset(cache->createTexture(smallMSAADesc, false));
+            SkAutoTUnref<GrTexture> smallMSAART1(cache->createTexture(smallMSAADesc, false));
             if (smallMSAART1 && smallMSAART1->asRenderTarget()) {
-                smallMSAART1->asRenderTarget()->renderTargetPriv().attachStencilBuffer();
+                smallMSAART1->asRenderTarget()->renderTargetPriv().attachStencilAttachment();
             }
             REPORTER_ASSERT(reporter,
-                            smallMSAART0 && smallMSAART1 &&
-                            smallMSAART0->asRenderTarget() &&
-                            smallMSAART1->asRenderTarget() &&
-                            smallMSAART0->asRenderTarget()->renderTargetPriv().getStencilBuffer() !=
-                            smallMSAART1->asRenderTarget()->renderTargetPriv().getStencilBuffer());
+                        smallMSAART0 && smallMSAART1 &&
+                        smallMSAART0->asRenderTarget() &&
+                        smallMSAART1->asRenderTarget() &&
+                        smallMSAART0->asRenderTarget()->renderTargetPriv().getStencilAttachment() !=
+                        smallMSAART1->asRenderTarget()->renderTargetPriv().getStencilAttachment());
         }
     }
 }
@@ -248,7 +255,7 @@
         this->registerWithCache();
     }
 
-    size_t onGpuMemorySize() const SK_OVERRIDE { return fSize; }
+    size_t onGpuMemorySize() const override { return fSize; }
 
     TestResource* fToDelete;
     size_t fSize;
@@ -1023,6 +1030,88 @@
     }
 }
 
+static void test_flush(skiatest::Reporter* reporter) {
+    Mock mock(1000000, 1000000);
+    GrContext* context = mock.context();
+    GrResourceCache* cache = mock.cache();
+
+    // The current cache impl will round the max flush count to the next power of 2. So we choose a
+    // power of two here to keep things simpler.
+    static const int kFlushCount = 16;
+    cache->setLimits(1000000, 1000000, kFlushCount);
+
+    {
+        // Insert a resource and send a flush notification kFlushCount times.
+        for (int i = 0; i < kFlushCount; ++i) {
+            TestResource* r = SkNEW_ARGS(TestResource, (context->getGpu()));
+            GrUniqueKey k;
+            make_unique_key<1>(&k, i);
+            r->resourcePriv().setUniqueKey(k);
+            r->unref();
+            cache->notifyFlushOccurred();
+        }
+
+        // Send flush notifications to the cache. Each flush should purge the oldest resource.
+        for (int i = 0; i < kFlushCount - 1; ++i) {
+            // The first resource was purged after the last flush in the initial loop, hence the -1.
+            REPORTER_ASSERT(reporter, kFlushCount - i - 1 == cache->getResourceCount());
+            for (int j = 0; j < i; ++j) {
+                GrUniqueKey k;
+                make_unique_key<1>(&k, j);
+                GrGpuResource* r = cache->findAndRefUniqueResource(k);
+                REPORTER_ASSERT(reporter, !SkToBool(r));
+                SkSafeUnref(r);
+            }
+            cache->notifyFlushOccurred();
+        }
+
+        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+        cache->purgeAllUnlocked();
+    }
+
+    // Do a similar test but where we leave refs on some resources to prevent them from being
+    // purged.
+    {
+        GrGpuResource* refedResources[kFlushCount >> 1];
+        for (int i = 0; i < kFlushCount; ++i) {
+            TestResource* r = SkNEW_ARGS(TestResource, (context->getGpu()));
+            GrUniqueKey k;
+            make_unique_key<1>(&k, i);
+            r->resourcePriv().setUniqueKey(k);
+            // Leave a ref on every other resource, beginning with the first.
+            if (SkToBool(i & 0x1)) {
+                refedResources[i/2] = r;
+            } else {
+                r->unref();
+            }
+            cache->notifyFlushOccurred();
+        }
+
+        for (int i = 0; i < kFlushCount; ++i) {
+            // Should get a resource purged every other flush.
+            REPORTER_ASSERT(reporter, kFlushCount - i/2 - 1 == cache->getResourceCount());
+            cache->notifyFlushOccurred();
+        }
+
+        // Unref all the resources that we kept refs on in the first loop.
+        for (int i = 0; i < kFlushCount >> 1; ++i) {
+            refedResources[i]->unref();
+        }
+
+        // When we unref'ed them their timestamps got updated. So nothing should be purged until we
+        // get kFlushCount additional flushes. Then everything should be purged.
+        for (int i = 0; i < kFlushCount; ++i) {
+            REPORTER_ASSERT(reporter, kFlushCount >> 1 == cache->getResourceCount());
+            cache->notifyFlushOccurred();
+        }
+        REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+
+        cache->purgeAllUnlocked();
+    }
+
+    REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
+}
+
 static void test_large_resource_count(skiatest::Reporter* reporter) {
     // Set the cache size to double the resource count because we're going to create 2x that number
     // resources, using two different key domains. Add a little slop to the bytes because we resize
@@ -1118,6 +1207,7 @@
     test_cache_chained_purge(reporter);
     test_resource_size_changed(reporter);
     test_timestamp_wrap(reporter);
+    test_flush(reporter);
     test_large_resource_count(reporter);
 }
 
diff --git a/tests/RoundRectTest.cpp b/tests/RoundRectTest.cpp
index 9920d10..8d8f76f 100644
--- a/tests/RoundRectTest.cpp
+++ b/tests/RoundRectTest.cpp
@@ -9,12 +9,29 @@
 #include "SkRRect.h"
 #include "Test.h"
 
-static void test_tricky_radii_crbug_458522(skiatest::Reporter* reporter) {
-    SkRRect rr;
-    const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
-    const SkScalar rad = 12814;
-    const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } };
-    rr.setRectRadii(bounds, vec);
+static void test_tricky_radii(skiatest::Reporter* reporter) {
+    {
+        // crbug.com/458522
+        SkRRect rr;
+        const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
+        const SkScalar rad = 12814;
+        const SkVector vec[] = { { rad, rad }, { 0, rad }, { rad, rad }, { 0, rad } };
+        rr.setRectRadii(bounds, vec);
+    }
+
+    {
+        // crbug.com//463920
+        SkRect r = SkRect::MakeLTRB(0, 0, 1009, 33554432.0);
+        SkVector radii[4] = {
+            { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554432.0 }, { 110.0f, 5.0f }
+        };
+        SkRRect rr;
+        rr.setRectRadii(r, radii);
+
+        REPORTER_ASSERT(reporter, (double) rr.radii(SkRRect::kUpperRight_Corner).fY +
+                                  (double) rr.radii(SkRRect::kLowerRight_Corner).fY <=
+                                  rr.height());
+    }
 }
 
 static void test_empty_crbug_458524(skiatest::Reporter* reporter) {
@@ -660,6 +677,6 @@
     test_round_rect_contains_rect(reporter);
     test_round_rect_transform(reporter);
     test_issue_2696(reporter);
-    test_tricky_radii_crbug_458522(reporter);
+    test_tricky_radii(reporter);
     test_empty_crbug_458524(reporter);
 }
diff --git a/tests/SerializationTest.cpp b/tests/SerializationTest.cpp
index f6a8a7d..f7e2983 100644
--- a/tests/SerializationTest.cpp
+++ b/tests/SerializationTest.cpp
@@ -317,14 +317,8 @@
 }
 
 static void TestPictureTypefaceSerialization(skiatest::Reporter* reporter) {
-    // Load typeface form file.
-    // This test cannot run if there is no resource path.
-    SkString resourcePath = GetResourcePath();
-    if (resourcePath.isEmpty()) {
-        SkDebugf("Could not run fontstream test because resourcePath not specified.");
-        return;
-    }
-    SkString filename = SkOSPath::Join(resourcePath.c_str(), "test.ttc");
+    // Load typeface form file to test CreateFromFile with index.
+    SkString filename = GetResourcePath("/fonts/test.ttc");
     SkTypeface* typeface = SkTypeface::CreateFromFile(filename.c_str(), 1);
     if (!typeface) {
         SkDebugf("Could not run fontstream test because test.ttc not found.");
diff --git a/tests/Sk4xTest.cpp b/tests/Sk4xTest.cpp
deleted file mode 100644
index 1cecd4f..0000000
--- a/tests/Sk4xTest.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-#include "Test.h"
-#include "Sk4x.h"
-
-#define ASSERT_EQ(a, b) REPORTER_ASSERT(r, a.equal(b).allTrue())
-#define ASSERT_NE(a, b) REPORTER_ASSERT(r, a.notEqual(b).allTrue())
-
-DEF_TEST(Sk4x_Construction, r) {
-    Sk4f uninitialized;
-    Sk4f zero(0,0,0,0);
-    Sk4f foo(1,2,3,4),
-         bar(foo),
-         baz = bar;
-    ASSERT_EQ(foo, bar);
-    ASSERT_EQ(bar, baz);
-    ASSERT_EQ(baz, foo);
-}
-
-struct AlignedFloats {
-    Sk4f forces16ByteAlignment;   // On 64-bit machines, the stack starts 128-bit aligned,
-    float fs[5];                  // but not necessarily so on 32-bit.  Adding an Sk4f forces it.
-};
-
-DEF_TEST(Sk4x_LoadStore, r) {
-    AlignedFloats aligned;
-    // fs will be 16-byte aligned, fs+1 not.
-    float* fs = aligned.fs;
-    for (int i = 0; i < 5; i++) {  // set to 5,6,7,8,9
-        fs[i] = float(i+5);
-    }
-
-    Sk4f foo = Sk4f::Load(fs);
-    Sk4f bar = Sk4f::LoadAligned(fs);
-    ASSERT_EQ(foo, bar);
-
-    foo = Sk4f::Load(fs+1);
-    ASSERT_NE(foo, bar);
-
-    foo.storeAligned(fs);
-    bar.store(fs+1);
-    REPORTER_ASSERT(r, fs[0] == 6 &&
-                       fs[1] == 5 &&
-                       fs[2] == 6 &&
-                       fs[3] == 7 &&
-                       fs[4] == 8);
-}
-
-DEF_TEST(Sk4x_Conversions, r) {
-    // Assuming IEEE floats.
-    Sk4f zerof(0,0,0,0);
-    Sk4i zeroi(0,0,0,0);
-    ASSERT_EQ(zeroi, zerof.cast<Sk4i>());
-    ASSERT_EQ(zeroi, zerof.reinterpret<Sk4i>());
-    ASSERT_EQ(zerof, zeroi.cast<Sk4f>());
-    ASSERT_EQ(zerof, zeroi.reinterpret<Sk4f>());
-
-    Sk4f twof(2,2,2,2);
-    Sk4i twoi(2,2,2,2);
-    ASSERT_EQ(twoi, twof.cast<Sk4i>());
-    ASSERT_NE(twoi, twof.reinterpret<Sk4i>());
-    ASSERT_EQ(twof, twoi.cast<Sk4f>());
-    ASSERT_NE(twof, twoi.reinterpret<Sk4f>());
-}
-
-DEF_TEST(Sk4x_Bits, r) {
-    ASSERT_EQ(Sk4i(0,0,0,0).bitNot(), Sk4i(-1,-1,-1,-1));
-
-    Sk4i a(2,3,4,5),
-         b(1,3,5,7);
-    ASSERT_EQ(Sk4i(0,3,4,5), a.bitAnd(b));
-    ASSERT_EQ(Sk4i(3,3,5,7), a.bitOr(b));
-}
-
-DEF_TEST(Sk4x_Arith, r) {
-    ASSERT_EQ(Sk4f(4,6,8,10),    Sk4f(1,2,3,4).add(Sk4f(3,4,5,6)));
-    ASSERT_EQ(Sk4f(-2,-2,-2,-2), Sk4f(1,2,3,4).subtract(Sk4f(3,4,5,6)));
-    ASSERT_EQ(Sk4f(3,8,15,24),   Sk4f(1,2,3,4).multiply(Sk4f(3,4,5,6)));
-
-    float third = 1.0f/3.0f;
-    ASSERT_EQ(Sk4f(1*third, 0.5f, 0.6f, 2*third), Sk4f(1,2,3,4).divide(Sk4f(3,4,5,6)));
-    ASSERT_EQ(Sk4i(4,6,8,10),    Sk4i(1,2,3,4).add(Sk4i(3,4,5,6)));
-    ASSERT_EQ(Sk4i(-2,-2,-2,-2), Sk4i(1,2,3,4).subtract(Sk4i(3,4,5,6)));
-    ASSERT_EQ(Sk4i(3,8,15,24),   Sk4i(1,2,3,4).multiply(Sk4i(3,4,5,6)));
-}
-
-DEF_TEST(Sk4x_ImplicitPromotion, r) {
-    ASSERT_EQ(Sk4f(2,4,6,8), Sk4f(1,2,3,4).multiply(Sk4f(2.0f)));
-}
-
-DEF_TEST(Sk4x_Sqrt, r) {
-    Sk4f squares(4, 16, 25, 121),
-           roots(2,  4,  5,  11);
-    // .sqrt() should be pretty precise.
-    Sk4f error = roots.subtract(squares.sqrt());
-    REPORTER_ASSERT(r, error.greaterThanEqual(Sk4f(0.0f)).allTrue());
-    REPORTER_ASSERT(r, error.lessThan(Sk4f(0.000001f)).allTrue());
-
-    // .rsqrt() isn't so precise (for SSE), but should be pretty close.
-    error = roots.subtract(squares.multiply(squares.rsqrt()));
-    REPORTER_ASSERT(r, error.greaterThanEqual(Sk4f(0.0f)).allTrue());
-    REPORTER_ASSERT(r, error.lessThan(Sk4f(0.01f)).allTrue());
-}
-
-DEF_TEST(Sk4x_Comparison, r) {
-    ASSERT_EQ(Sk4f(1,2,3,4), Sk4f(1,2,3,4));
-    ASSERT_NE(Sk4f(4,3,2,1), Sk4f(1,2,3,4));
-
-    ASSERT_EQ(Sk4i(-1,-1,0,-1), Sk4f(1,2,5,4).equal(Sk4f(1,2,3,4)));
-
-    ASSERT_EQ(Sk4i(-1,-1,-1,-1), Sk4f(1,2,3,4).lessThan(Sk4f(2,3,4,5)));
-    ASSERT_EQ(Sk4i(-1,-1,-1,-1), Sk4f(1,2,3,4).lessThanEqual(Sk4f(2,3,4,5)));
-    ASSERT_EQ(Sk4i(0,0,0,0),     Sk4f(1,2,3,4).greaterThan(Sk4f(2,3,4,5)));
-    ASSERT_EQ(Sk4i(0,0,0,0),     Sk4f(1,2,3,4).greaterThanEqual(Sk4f(2,3,4,5)));
-
-    ASSERT_EQ(Sk4i(1,2,3,4), Sk4i(1,2,3,4));
-    ASSERT_NE(Sk4i(4,3,2,1), Sk4i(1,2,3,4));
-
-    ASSERT_EQ(Sk4i(-1,-1,0,-1), Sk4i(1,2,5,4).equal(Sk4i(1,2,3,4)));
-
-    ASSERT_EQ(Sk4i(-1,-1,-1,-1), Sk4i(1,2,3,4).lessThan(Sk4i(2,3,4,5)));
-    ASSERT_EQ(Sk4i(-1,-1,-1,-1), Sk4i(1,2,3,4).lessThanEqual(Sk4i(2,3,4,5)));
-    ASSERT_EQ(Sk4i(0,0,0,0),     Sk4i(1,2,3,4).greaterThan(Sk4i(2,3,4,5)));
-    ASSERT_EQ(Sk4i(0,0,0,0),     Sk4i(1,2,3,4).greaterThanEqual(Sk4i(2,3,4,5)));
-}
-
-DEF_TEST(Sk4x_MinMax, r) {
-    ASSERT_EQ(Sk4f(1,2,2,1), Sk4f::Min(Sk4f(1,2,3,4), Sk4f(4,3,2,1)));
-    ASSERT_EQ(Sk4f(4,3,3,4), Sk4f::Max(Sk4f(1,2,3,4), Sk4f(4,3,2,1)));
-    ASSERT_EQ(Sk4i(1,2,2,1), Sk4i::Min(Sk4i(1,2,3,4), Sk4i(4,3,2,1)));
-    ASSERT_EQ(Sk4i(4,3,3,4), Sk4i::Max(Sk4i(1,2,3,4), Sk4i(4,3,2,1)));
-}
-
-DEF_TEST(Sk4x_Swizzle, r) {
-    ASSERT_EQ(Sk4f(3,4,1,2), Sk4f(1,2,3,4).zwxy());
-    ASSERT_EQ(Sk4f(1,2,5,6), Sk4f::XYAB(Sk4f(1,2,3,4), Sk4f(5,6,7,8)));
-    ASSERT_EQ(Sk4f(3,4,7,8), Sk4f::ZWCD(Sk4f(1,2,3,4), Sk4f(5,6,7,8)));
-    ASSERT_EQ(Sk4i(3,4,1,2), Sk4i(1,2,3,4).zwxy());
-    ASSERT_EQ(Sk4i(1,2,5,6), Sk4i::XYAB(Sk4i(1,2,3,4), Sk4i(5,6,7,8)));
-    ASSERT_EQ(Sk4i(3,4,7,8), Sk4i::ZWCD(Sk4i(1,2,3,4), Sk4i(5,6,7,8)));
-}
diff --git a/tests/SkNxTest.cpp b/tests/SkNxTest.cpp
new file mode 100644
index 0000000..f880150
--- /dev/null
+++ b/tests/SkNxTest.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkNx.h"
+#include "SkRandom.h"
+#include "Test.h"
+
+template <int N, typename T>
+static void test_Nf(skiatest::Reporter* r) {
+
+    auto assert_nearly_eq = [&](double eps, const SkNf<N,T>& v, T a, T b, T c, T d) {
+        auto close = [=](T a, T b) { return fabs(a-b) <= eps; };
+        T vals[4];
+        v.store(vals);
+        bool ok = close(vals[0], a) && close(vals[1], b)
+               && close(v.template kth<0>(), a) && close(v.template kth<1>(), b);
+        REPORTER_ASSERT(r, ok);
+        if (N == 4) {
+            ok = close(vals[2], c) && close(vals[3], d)
+              && close(v.template kth<2>(), c) && close(v.template kth<3>(), d);
+            REPORTER_ASSERT(r, ok);
+        }
+    };
+    auto assert_eq = [&](const SkNf<N,T>& v, T a, T b, T c, T d) {
+        return assert_nearly_eq(0, v, a,b,c,d);
+    };
+
+    T vals[] = {3, 4, 5, 6};
+    SkNf<N,T> a = SkNf<N,T>::Load(vals),
+              b(a),
+              c = a;
+    SkNf<N,T> d;
+    d = a;
+
+    assert_eq(a, 3, 4, 5, 6);
+    assert_eq(b, 3, 4, 5, 6);
+    assert_eq(c, 3, 4, 5, 6);
+    assert_eq(d, 3, 4, 5, 6);
+
+    assert_eq(a+b, 6, 8, 10, 12);
+    assert_eq(a*b, 9, 16, 25, 36);
+    assert_eq(a*b-b, 6, 12, 20, 30);
+    assert_eq((a*b).sqrt(), 3, 4, 5, 6);
+    assert_eq(a/b, 1, 1, 1, 1);
+    assert_eq(-a, -3, -4, -5, -6);
+
+    SkNf<N,T> fours(4);
+
+    assert_eq(fours.sqrt(), 2,2,2,2);
+    assert_nearly_eq(0.001, fours.rsqrt0(), 0.5, 0.5, 0.5, 0.5);
+    assert_nearly_eq(0.001, fours.rsqrt1(), 0.5, 0.5, 0.5, 0.5);
+    assert_nearly_eq(0.001, fours.rsqrt2(), 0.5, 0.5, 0.5, 0.5);
+
+    assert_eq(              fours.      invert(), 0.25, 0.25, 0.25, 0.25);
+    assert_nearly_eq(0.001, fours.approxInvert(), 0.25, 0.25, 0.25, 0.25);
+
+    assert_eq(SkNf<N,T>::Min(a, fours), 3, 4, 4, 4);
+    assert_eq(SkNf<N,T>::Max(a, fours), 4, 4, 5, 6);
+
+    // Test some comparisons.  This is not exhaustive.
+    REPORTER_ASSERT(r, (a == b).allTrue());
+    REPORTER_ASSERT(r, (a+b == a*b-b).anyTrue());
+    REPORTER_ASSERT(r, !(a+b == a*b-b).allTrue());
+    REPORTER_ASSERT(r, !(a+b == a*b).anyTrue());
+    REPORTER_ASSERT(r, !(a != b).anyTrue());
+    REPORTER_ASSERT(r, (a < fours).anyTrue());
+    REPORTER_ASSERT(r, (a <= fours).anyTrue());
+    REPORTER_ASSERT(r, !(a > fours).allTrue());
+    REPORTER_ASSERT(r, !(a >= fours).allTrue());
+}
+
+DEF_TEST(SkNf, r) {
+    test_Nf<2, float>(r);
+    test_Nf<2, double>(r);
+
+    test_Nf<4, float>(r);
+    test_Nf<4, double>(r);
+}
+
+template <int N, typename T>
+void test_Ni(skiatest::Reporter* r) {
+    auto assert_eq = [&](const SkNi<N,T>& v, T a, T b, T c, T d, T e, T f, T g, T h) {
+        T vals[8];
+        v.store(vals);
+
+        switch (N) {
+          case 8: REPORTER_ASSERT(r, vals[4] == e && vals[5] == f && vals[6] == g && vals[7] == h);
+          case 4: REPORTER_ASSERT(r, vals[2] == c && vals[3] == d);
+          case 2: REPORTER_ASSERT(r, vals[0] == a && vals[1] == b);
+        }
+        switch (N) {
+          case 8: REPORTER_ASSERT(r, v.template kth<4>() == e && v.template kth<5>() == f &&
+                                     v.template kth<6>() == g && v.template kth<7>() == h);
+          case 4: REPORTER_ASSERT(r, v.template kth<2>() == c && v.template kth<3>() == d);
+          case 2: REPORTER_ASSERT(r, v.template kth<0>() == a && v.template kth<1>() == b);
+        }
+    };
+
+    T vals[] = { 1,2,3,4,5,6,7,8 };
+    SkNi<N,T> a = SkNi<N,T>::Load(vals),
+              b(a),
+              c = a;
+    SkNi<N,T> d;
+    d = a;
+
+    assert_eq(a, 1,2,3,4,5,6,7,8);
+    assert_eq(b, 1,2,3,4,5,6,7,8);
+    assert_eq(c, 1,2,3,4,5,6,7,8);
+    assert_eq(d, 1,2,3,4,5,6,7,8);
+
+    assert_eq(a+a, 2,4,6,8,10,12,14,16);
+    assert_eq(a*a, 1,4,9,16,25,36,49,64);
+    assert_eq(a*a-a, 0,2,6,12,20,30,42,56);
+
+    assert_eq(a >> 2, 0,0,0,1,1,1,1,2);
+    assert_eq(a << 1, 2,4,6,8,10,12,14,16);
+
+    REPORTER_ASSERT(r, a.template kth<1>() == 2);
+}
+
+DEF_TEST(SkNi, r) {
+    test_Ni<2, uint16_t>(r);
+    test_Ni<4, uint16_t>(r);
+    test_Ni<8, uint16_t>(r);
+
+    test_Ni<2, int>(r);
+    test_Ni<4, int>(r);
+    test_Ni<8, int>(r);
+}
+
+DEF_TEST(SkNi_min, r) {
+    // Exhaustively check the 8x8 bit space.
+    for (int a = 0; a < (1<<8); a++) {
+    for (int b = 0; b < (1<<8); b++) {
+        REPORTER_ASSERT(r, Sk16b::Min(Sk16b(a), Sk16b(b)).kth<0>() == SkTMin(a, b));
+    }}
+
+    // Exhausting the 16x16 bit space is kind of slow, so only do that in release builds.
+#ifdef SK_DEBUG
+    SkRandom rand;
+    for (int i = 0; i < (1<<16); i++) {
+        uint16_t a = rand.nextU() >> 16,
+                 b = rand.nextU() >> 16;
+        REPORTER_ASSERT(r, Sk8h::Min(Sk8h(a), Sk8h(b)).kth<0>() == SkTMin(a, b));
+    }
+#else
+    for (int a = 0; a < (1<<16); a++) {
+    for (int b = 0; b < (1<<16); b++) {
+        REPORTER_ASSERT(r, Sk8h::Min(Sk8h(a), Sk8h(b)).kth<0>() == SkTMin(a, b));
+    }}
+#endif
+}
diff --git a/tests/SkResourceCacheTest.cpp b/tests/SkResourceCacheTest.cpp
index fae0816..bb12d66 100644
--- a/tests/SkResourceCacheTest.cpp
+++ b/tests/SkResourceCacheTest.cpp
@@ -39,7 +39,7 @@
     SkScalar yScaledSize = SkIntToScalar(kBitmapSize) * yScale;
     canvas->clipRect(SkRect::MakeLTRB(0, 0, xScaledSize, yScaledSize));
     SkPaint paint;
-    paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
+    paint.setFilterQuality(kHigh_SkFilterQuality);
 
     canvas->drawBitmapRect(bitmap,
                            SkRect::MakeLTRB(0, 0, xScaledSize, yScaledSize),
diff --git a/tests/SkpSkGrTest.cpp b/tests/SkpSkGrTest.cpp
index c2f1a79..2c3c5b7 100644
--- a/tests/SkpSkGrTest.cpp
+++ b/tests/SkpSkGrTest.cpp
@@ -146,7 +146,7 @@
         fTestFun = testFun;
     }
 
-    void run() SK_OVERRIDE {
+    void run() override {
         SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
         (*fTestFun)(&fState);
     }
diff --git a/tests/StreamTest.cpp b/tests/StreamTest.cpp
index ff22ecf..3641f3b 100644
--- a/tests/StreamTest.cpp
+++ b/tests/StreamTest.cpp
@@ -5,7 +5,9 @@
  * found in the LICENSE file.
  */
 
+#include "Resources.h"
 #include "SkData.h"
+#include "SkFrontBufferedStream.h"
 #include "SkOSFile.h"
 #include "SkRandom.h"
 #include "SkStream.h"
@@ -190,3 +192,84 @@
     TestPackedUInt(reporter);
     TestNullData();
 }
+
+/**
+ *  Tests peeking and then reading the same amount. The two should provide the
+ *  same results.
+ *  Returns whether the stream could peek.
+ */
+static bool compare_peek_to_read(skiatest::Reporter* reporter,
+                                 SkStream* stream, size_t bytesToPeek) {
+    // The rest of our tests won't be very interesting if bytesToPeek is zero.
+    REPORTER_ASSERT(reporter, bytesToPeek > 0);
+    SkAutoMalloc peekStorage(bytesToPeek);
+    SkAutoMalloc readStorage(bytesToPeek);
+    void* peekPtr = peekStorage.get();
+    void* readPtr = peekStorage.get();
+
+    if (!stream->peek(peekPtr, bytesToPeek)) {
+        return false;
+    }
+    const size_t bytesRead = stream->read(readPtr, bytesToPeek);
+
+    // bytesRead should only be less than attempted if the stream is at the
+    // end.
+    REPORTER_ASSERT(reporter, bytesRead == bytesToPeek || stream->isAtEnd());
+
+    // peek and read should behave the same, except peek returned to the
+    // original position, so they read the same data.
+    REPORTER_ASSERT(reporter, !memcmp(peekPtr, readPtr, bytesRead));
+
+    return true;
+}
+
+static void test_peeking_stream(skiatest::Reporter* r, SkStream* stream, size_t limit) {
+    size_t peeked = 0;
+    for (size_t i = 1; !stream->isAtEnd(); i++) {
+        const bool couldPeek = compare_peek_to_read(r, stream, i);
+        if (!couldPeek) {
+            REPORTER_ASSERT(r, peeked + i > limit);
+            // No more peeking is supported.
+            break;
+        }
+        peeked += i;
+    }
+}
+
+static void test_peeking_front_buffered_stream(skiatest::Reporter* r,
+                                               const SkStream& original,
+                                               size_t bufferSize) {
+    SkStream* dupe = original.duplicate();
+    REPORTER_ASSERT(r, dupe != NULL);
+    SkAutoTDelete<SkStream> bufferedStream(SkFrontBufferedStream::Create(dupe, bufferSize));
+    REPORTER_ASSERT(r, bufferedStream != NULL);
+    test_peeking_stream(r, bufferedStream, bufferSize);
+}
+
+// This test uses file system operations that don't work out of the 
+// box on iOS. It's likely that we don't need them on iOS. Ignoring for now. 
+// TODO(stephana): Re-evaluate if we need this in the future. 
+#ifndef SK_BUILD_FOR_IOS
+DEF_TEST(StreamPeek, reporter) {
+    // Test a memory stream.
+    const char gAbcs[] = "abcdefghijklmnopqrstuvwxyz";
+    SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
+    test_peeking_stream(reporter, &memStream, memStream.getLength());
+
+    // Test an arbitrary file stream. file streams do not support peeking.
+    SkFILEStream fileStream(GetResourcePath("baby_tux.webp").c_str());
+    REPORTER_ASSERT(reporter, fileStream.isValid());
+    if (!fileStream.isValid()) {
+        return;
+    }
+    SkAutoMalloc storage(fileStream.getLength());
+    for (size_t i = 1; i < fileStream.getLength(); i++) {
+        REPORTER_ASSERT(reporter, !fileStream.peek(storage.get(), i));
+    }
+
+    // Now test some FrontBufferedStreams
+    for (size_t i = 1; i < memStream.getLength(); i++) {
+        test_peeking_front_buffered_stream(reporter, memStream, i);
+    }
+}
+#endif 
diff --git a/tests/StrokeTest.cpp b/tests/StrokeTest.cpp
index 80ca317..43b293f 100644
--- a/tests/StrokeTest.cpp
+++ b/tests/StrokeTest.cpp
@@ -9,6 +9,7 @@
 #include "SkPath.h"
 #include "SkRect.h"
 #include "SkStroke.h"
+#include "SkStrokeRec.h"
 #include "Test.h"
 
 static bool equal(const SkRect& a, const SkRect& b) {
@@ -74,7 +75,7 @@
 
         bool isMiter = SkPaint::kMiter_Join == joins[i];
         SkRect nested[2];
-        REPORTER_ASSERT(reporter, fillPath.isNestedRects(nested) == isMiter);
+        REPORTER_ASSERT(reporter, fillPath.isNestedFillRects(nested) == isMiter);
         if (isMiter) {
             SkRect inner(r);
             inner.inset(width/2, width/2);
@@ -84,7 +85,83 @@
     }
 }
 
+static void test_strokerec_equality(skiatest::Reporter* reporter) {
+    {
+        SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
+        SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+
+        // Test that style mismatch is detected.
+        s2.setHairlineStyle();
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+
+        s1.setHairlineStyle();
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+
+        // ResScale is not part of equality.
+        s1.setResScale(2.1f);
+        s2.setResScale(1.2f);
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+        s1.setFillStyle();
+        s2.setFillStyle();
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+        s1.setStrokeStyle(1.0f, false);
+        s2.setStrokeStyle(1.0f, false);
+        s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
+        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+    }
+
+    // Stroke parameters on fill or hairline style are not part of equality.
+    {
+        SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
+        SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
+        for (int i = 0; i < 2; ++i) {
+            s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
+            s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.1f);
+            REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+            s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 2.9f);
+            REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+            s2.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 2.9f);
+            REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+            s1.setHairlineStyle();
+            s2.setHairlineStyle();
+        }
+    }
+
+    // Stroke parameters on stroke style are part of equality.
+    {
+        SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
+        SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
+        s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
+        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
+        s1.setStrokeStyle(1.0f, false);
+
+        s2.setStrokeStyle(1.0f, true);
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+
+        s2.setStrokeStyle(2.1f, false);
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+
+        s2.setStrokeStyle(1.0f, false);
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+
+        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.1f);
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 2.9f);
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+        s2.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 2.9f);
+        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
+
+        // Sets fill.
+        s1.setStrokeStyle(0.0f, true);
+        s2.setStrokeStyle(0.0f, true);
+        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
+    }
+}
+
 DEF_TEST(Stroke, reporter) {
     test_strokecubic(reporter);
     test_strokerect(reporter);
+    test_strokerec_equality(reporter);
 }
diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp
index 9ce9d5f..a3ac216 100644
--- a/tests/SurfaceTest.cpp
+++ b/tests/SurfaceTest.cpp
@@ -67,11 +67,8 @@
 #include "SkImageGenerator.h"
 
 class EmptyGenerator : public SkImageGenerator {
-protected:
-    bool onGetInfo(SkImageInfo* info) SK_OVERRIDE {
-        *info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType);
-        return true;
-    }
+public:
+    EmptyGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(0, 0)) {}
 };
 
 static void test_empty_image(skiatest::Reporter* reporter) {
@@ -93,6 +90,45 @@
     }
 }
 
+#if SK_SUPPORT_GPU
+static void test_wrapped_texture_surface(skiatest::Reporter* reporter, GrContext* ctx) {
+    if (NULL == ctx) {
+        return;
+    }
+    // Test the wrapped factory for SkSurface by creating a texture using ctx and then treat it as
+    // an external texture and wrap it in a SkSurface.
+
+    GrSurfaceDesc texDesc;
+    texDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    texDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    texDesc.fWidth = texDesc.fHeight = 100;
+    texDesc.fSampleCnt = 0;
+    texDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
+    SkAutoTUnref<GrSurface> dummySurface(ctx->textureProvider()->createTexture(texDesc, false));
+
+    REPORTER_ASSERT(reporter, dummySurface && dummySurface->asTexture() &&
+                              dummySurface->asRenderTarget());
+    if (!dummySurface || !dummySurface->asTexture() || !dummySurface->asRenderTarget()) {
+        return;
+    }
+    
+    GrBackendObject textureHandle = dummySurface->asTexture()->getTextureHandle();
+
+    GrBackendTextureDesc wrappedDesc;
+    wrappedDesc.fConfig = dummySurface->config();
+    wrappedDesc.fWidth = dummySurface->width();
+    wrappedDesc.fHeight = dummySurface->height();
+    wrappedDesc.fOrigin = dummySurface->origin();
+    wrappedDesc.fSampleCnt = dummySurface->asRenderTarget()->numSamples();
+    wrappedDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
+    wrappedDesc.fTextureHandle = textureHandle;
+
+    SkAutoTUnref<SkSurface> surface(SkSurface::NewWrappedRenderTarget(ctx, wrappedDesc, NULL));
+    REPORTER_ASSERT(reporter, surface);
+}
+#endif
+
+
 static void test_image(skiatest::Reporter* reporter) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
     size_t rowBytes = info.minRowBytes();
@@ -215,6 +251,9 @@
     GrContext* ctx = NULL;
 #if SK_SUPPORT_GPU
     ctx = factory->get(GrContextFactory::kNative_GLContextType);
+    if (NULL == ctx) {
+        return;
+    }
 #endif
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
@@ -572,8 +611,75 @@
                 TestGetTexture(reporter, kGpuScratch_SurfaceType, context);
                 test_empty_surface(reporter, context);
                 test_surface_budget(reporter, context);
+                test_wrapped_texture_surface(reporter, context);
             }
         }
     }
 #endif
 }
+
+#if SK_SUPPORT_GPU
+static SkImage* make_desc_image(GrContext* ctx, int w, int h, GrBackendObject texID, bool doCopy) {
+    GrBackendTextureDesc desc;
+    desc.fConfig = kSkia8888_GrPixelConfig;
+    // need to be a rendertarget for now...
+    desc.fFlags = kRenderTarget_GrBackendTextureFlag;
+    desc.fWidth = w;
+    desc.fHeight = h;
+    desc.fSampleCnt = 0;
+    desc.fTextureHandle = texID;
+    return doCopy ? SkImage::NewFromTextureCopy(ctx, desc) : SkImage::NewFromTexture(ctx, desc);
+}
+
+static void test_image_color(skiatest::Reporter* reporter, SkImage* image, SkPMColor expected) {
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
+    SkPMColor pixel;
+    REPORTER_ASSERT(reporter, image->readPixels(info, &pixel, sizeof(pixel), 0, 0));
+    REPORTER_ASSERT(reporter, pixel == expected);
+}
+
+DEF_GPUTEST(SkImage_NewFromTexture, reporter, factory) {
+    GrContext* ctx = factory->get(GrContextFactory::kNative_GLContextType);
+    if (!ctx) {
+        REPORTER_ASSERT(reporter, false);
+        return;
+    }
+    GrTextureProvider* provider = ctx->textureProvider();
+    
+    const int w = 10;
+    const int h = 10;
+    SkPMColor storage[w * h];
+    const SkPMColor expected0 = SkPreMultiplyColor(SK_ColorRED);
+    sk_memset32(storage, expected0, w * h);
+    
+    GrSurfaceDesc desc;
+    desc.fFlags = kRenderTarget_GrSurfaceFlag;  // needs to be a rendertarget for readpixels();
+    desc.fOrigin = kDefault_GrSurfaceOrigin;
+    desc.fWidth = w;
+    desc.fHeight = h;
+    desc.fConfig = kSkia8888_GrPixelConfig;
+    desc.fSampleCnt = 0;
+    
+    SkAutoTUnref<GrTexture> tex(provider->createTexture(desc, false, storage, w * 4));
+    if (!tex) {
+        REPORTER_ASSERT(reporter, false);
+        return;
+    }
+    
+    GrBackendObject srcTex = tex->getTextureHandle();
+    SkAutoTUnref<SkImage> refImg(make_desc_image(ctx, w, h, srcTex, false));
+    SkAutoTUnref<SkImage> cpyImg(make_desc_image(ctx, w, h, srcTex, true));
+
+    test_image_color(reporter, refImg, expected0);
+    test_image_color(reporter, cpyImg, expected0);
+
+    // Now lets jam new colors into our "external" texture, and see if the images notice
+    const SkPMColor expected1 = SkPreMultiplyColor(SK_ColorBLUE);
+    sk_memset32(storage, expected1, w * h);
+    tex->writePixels(0, 0, w, h, kSkia8888_GrPixelConfig, storage, GrContext::kFlushWrites_PixelOp);
+
+    // We expect the ref'd image to see the new color, but cpy'd one should still see the old color
+    test_image_color(reporter, refImg, expected1);
+    test_image_color(reporter, cpyImg, expected0);
+}
+#endif
diff --git a/tests/SwizzlerTest.cpp b/tests/SwizzlerTest.cpp
new file mode 100644
index 0000000..7ed1c39
--- /dev/null
+++ b/tests/SwizzlerTest.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSwizzler.h"
+#include "Test.h"
+
+// These are the values that we will look for to indicate that the fill was successful
+static const uint8_t kFillIndex = 0x1;
+static const uint32_t kFillColor = 0x22334455;
+
+static void check_fill(skiatest::Reporter* r,
+                       const SkImageInfo& imageInfo,
+                       uint32_t startRow,
+                       uint32_t endRow,
+                       size_t rowBytes,
+                       uint32_t offset,
+                       uint32_t colorOrIndex,
+                       SkPMColor* colorTable) {
+
+    // Calculate the total size of the image in bytes.  Use the smallest possible size.
+    // The offset value tells us to adjust the pointer from the memory we allocate in order
+    // to test on different memory alignments.  If offset is nonzero, we need to increase the
+    // size of the memory we allocate in order to make sure that we have enough.  We are
+    // still allocating the smallest possible size.
+    const size_t totalBytes = imageInfo.getSafeSize(rowBytes) + offset;
+
+    // Create fake image data where every byte has a value of 0
+    SkAutoTDeleteArray<uint8_t> storage(SkNEW_ARRAY(uint8_t, totalBytes));
+    memset(storage.get(), 0, totalBytes);
+    // Adjust the pointer in order to test on different memory alignments
+    uint8_t* imageData = storage.get() + offset;
+    uint8_t* imageStart = imageData + rowBytes * startRow;
+
+    // Fill image with the fill value starting at the indicated row
+    SkSwizzler::Fill(imageStart, imageInfo, rowBytes, endRow - startRow + 1, colorOrIndex,
+            colorTable);
+
+    // Ensure that the pixels are filled properly
+    // The bots should catch any memory corruption
+    uint8_t* indexPtr = imageData + startRow * rowBytes;
+    uint8_t* grayPtr = indexPtr;
+    uint32_t* colorPtr = (uint32_t*) indexPtr;
+    for (uint32_t y = startRow; y <= endRow; y++) {
+        for (int32_t x = 0; x < imageInfo.width(); x++) {
+            switch (imageInfo.colorType()) {
+                case kIndex_8_SkColorType:
+                    REPORTER_ASSERT(r, kFillIndex == indexPtr[x]);
+                    break;
+                case kN32_SkColorType:
+                    REPORTER_ASSERT(r, kFillColor == colorPtr[x]);
+                    break;
+                case kGray_8_SkColorType:
+                    // We always fill kGray with black
+                    REPORTER_ASSERT(r, (uint8_t) kFillColor == grayPtr[x]);
+                    break;
+                default:
+                    REPORTER_ASSERT(r, false);
+                    break;
+            }
+        }
+        indexPtr += rowBytes;
+        colorPtr = (uint32_t*) indexPtr;
+    }
+}
+
+// Test Fill() with different combinations of dimensions, alignment, and padding
+DEF_TEST(SwizzlerFill, r) {
+    // Set up a color table
+    SkPMColor colorTable[kFillIndex + 1];
+    colorTable[kFillIndex] = kFillColor;
+    // Apart from the fill index, we will leave the other colors in the color table uninitialized.
+    // If we incorrectly try to fill with this uninitialized memory, the bots will catch it.
+
+    // Test on an invalid width and representative widths
+    const uint32_t widths[] = { 0, 10, 50 };
+
+    // In order to call Fill(), there must be at least one row to fill
+    // Test on the smallest possible height and representative heights
+    const uint32_t heights[] = { 1, 5, 10 };
+
+    // Test on interesting possibilities for row padding
+    const uint32_t paddings[] = { 0, 1, 2, 3, 4 };
+
+    // Iterate over test dimensions
+    for (uint32_t width : widths) {
+        for (uint32_t height : heights) {
+
+            // Create image info objects
+            const SkImageInfo colorInfo = SkImageInfo::MakeN32(width, height,
+                kUnknown_SkAlphaType);
+            const SkImageInfo indexInfo = colorInfo.makeColorType(kIndex_8_SkColorType);
+            const SkImageInfo grayInfo = colorInfo.makeColorType(kGray_8_SkColorType);
+
+            for (uint32_t padding : paddings) {
+
+                // Calculate row bytes
+                size_t colorRowBytes = SkColorTypeBytesPerPixel(kN32_SkColorType) * width +
+                        padding;
+                size_t indexRowBytes = width + padding;
+                size_t grayRowBytes = indexRowBytes;
+
+                // If there is padding, we can invent an offset to change the memory alignment
+                for (uint32_t offset = 0; offset <= padding; offset++) {
+
+                    // Test all possible start rows with all possible end rows
+                    for (uint32_t startRow = 0; startRow < height; startRow++) {
+                        for (uint32_t endRow = startRow; endRow < height; endRow++) {
+
+                            // Fill with an index that we use to look up a color
+                            check_fill(r, colorInfo, startRow, endRow, colorRowBytes, offset,
+                                    kFillIndex, colorTable);
+
+                            // Fill with a color
+                            check_fill(r, colorInfo, startRow, endRow, colorRowBytes, offset,
+                                    kFillColor, NULL);
+
+                            // Fill with an index
+                            check_fill(r, indexInfo, startRow, endRow, indexRowBytes, offset,
+                                    kFillIndex, NULL);
+
+                            // Fill a grayscale image
+                            check_fill(r, grayInfo, startRow, endRow, grayRowBytes, offset,
+                                    kFillColor, NULL);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tests/TSetTest.cpp b/tests/TSetTest.cpp
deleted file mode 100644
index 3f82648..0000000
--- a/tests/TSetTest.cpp
+++ /dev/null
@@ -1,135 +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.
- */
-
-#include "SkTSet.h"
-#include "Test.h"
-
-// Tests the SkTSet<T> class template.
-// Functions that just call SkTDArray are not tested.
-
-static void TestTSet_basic(skiatest::Reporter* reporter) {
-    SkTSet<int> set0;
-    REPORTER_ASSERT(reporter,  set0.isEmpty());
-    REPORTER_ASSERT(reporter, !set0.contains(-1));
-    REPORTER_ASSERT(reporter, !set0.contains(0));
-    REPORTER_ASSERT(reporter, !set0.contains(1));
-    REPORTER_ASSERT(reporter,  set0.count() == 0);
-
-    REPORTER_ASSERT(reporter,  set0.add(0));
-    REPORTER_ASSERT(reporter, !set0.isEmpty());
-    REPORTER_ASSERT(reporter, !set0.contains(-1));
-    REPORTER_ASSERT(reporter,  set0.contains(0));
-    REPORTER_ASSERT(reporter, !set0.contains(1));
-    REPORTER_ASSERT(reporter,  set0.count() == 1);
-    REPORTER_ASSERT(reporter, !set0.add(0));
-    REPORTER_ASSERT(reporter,  set0.count() == 1);
-
-#ifdef SK_DEBUG
-    set0.validate();
-#endif
-}
-
-#define COUNT 1732
-#define PRIME1 10007
-#define PRIME2 1733
-
-// Generates a series of positive unique pseudo-random numbers.
-static int f(int i) {
-    return (long(i) * PRIME1) % PRIME2;
-}
-
-// Will expose contains() too.
-static void TestTSet_advanced(skiatest::Reporter* reporter) {
-    SkTSet<int> set0;
-
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, !set0.contains(f(i)));
-        if (i > 0) {
-            REPORTER_ASSERT(reporter,  set0.contains(f(0)));
-            REPORTER_ASSERT(reporter,  set0.contains(f(i / 2)));
-            REPORTER_ASSERT(reporter,  set0.contains(f(i - 1)));
-        }
-        REPORTER_ASSERT(reporter, !set0.contains(f(i)));
-        REPORTER_ASSERT(reporter,  set0.count() == i);
-        REPORTER_ASSERT(reporter,  set0.add(f(i)));
-        REPORTER_ASSERT(reporter,  set0.contains(f(i)));
-        REPORTER_ASSERT(reporter,  set0.count() == i + 1);
-        REPORTER_ASSERT(reporter, !set0.add(f(i)));
-    }
-
-    // Test deterministic output
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, set0[i] == f(i));
-    }
-
-    // Test copy constructor too.
-    SkTSet<int> set1 = set0;
-
-    REPORTER_ASSERT(reporter, set0.count() == set1.count());
-    REPORTER_ASSERT(reporter, !set1.contains(-1000));
-
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, set1.contains(f(i)));
-        REPORTER_ASSERT(reporter, set1[i] == f(i));
-    }
-
-    // Test operator= too.
-    SkTSet<int> set2;
-    set2 = set0;
-
-    REPORTER_ASSERT(reporter, set0.count() == set2.count());
-    REPORTER_ASSERT(reporter, !set2.contains(-1000));
-
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, set2.contains(f(i)));
-        REPORTER_ASSERT(reporter, set2[i] == f(i));
-    }
-
-#ifdef SK_DEBUG
-    set0.validate();
-    set1.validate();
-    set2.validate();
-#endif
-}
-
-static void TestTSet_merge(skiatest::Reporter* reporter) {
-    SkTSet<int> set;
-    SkTSet<int> setOdd;
-
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, set.add(2 * i));
-        REPORTER_ASSERT(reporter, setOdd.add(2 * i + 1));
-    }
-    // mergeInto returns the number of duplicates. Expected 0.
-    REPORTER_ASSERT(reporter, set.mergeInto(setOdd) == 0);
-    REPORTER_ASSERT(reporter, set.count() == 2 * COUNT);
-
-    // mergeInto should now find all new numbers duplicate.
-    REPORTER_ASSERT(reporter, set.mergeInto(setOdd) == setOdd.count());
-    REPORTER_ASSERT(reporter, set.count() == 2 * COUNT);
-
-    for (int i = 0; i < 2 * COUNT; i++) {
-        REPORTER_ASSERT(reporter, set.contains(i));
-    }
-
-    // check deterministic output
-    for (int i = 0; i < COUNT; i++) {
-        REPORTER_ASSERT(reporter, set[i] == 2 * i);
-        REPORTER_ASSERT(reporter, set[COUNT + i] == 2 * i + 1);
-    }
-
-#ifdef SK_DEBUG
-    set.validate();
-    setOdd.validate();
-#endif
-}
-
-DEF_TEST(TSet, reporter) {
-    TestTSet_basic(reporter);
-    TestTSet_advanced(reporter);
-    TestTSet_merge(reporter);
-}
diff --git a/tests/TemplatesTest.cpp b/tests/TemplatesTest.cpp
new file mode 100644
index 0000000..31859f3
--- /dev/null
+++ b/tests/TemplatesTest.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTemplates.h"
+#include "Test.h"
+
+// Tests for some of the helpers in SkTemplates.h
+static void test_automalloc_realloc(skiatest::Reporter* reporter) {
+    SkAutoSTMalloc<1, int> array;
+
+    // test we have a valid pointer, should not crash
+    array[0] = 1;
+    REPORTER_ASSERT(reporter, array[0] == 1);
+
+    // using realloc for init
+    array.realloc(1);
+
+    array[0] = 1;
+    REPORTER_ASSERT(reporter, array[0] == 1);
+
+    // verify realloc can grow
+    array.realloc(2);
+    REPORTER_ASSERT(reporter, array[0] == 1);
+
+    // realloc can shrink
+    array.realloc(1);
+    REPORTER_ASSERT(reporter, array[0] == 1);
+
+    // should not crash
+    array.realloc(0);
+
+    // grow and shrink again
+    array.realloc(10);
+    for (int i = 0; i < 10; i++) {
+        array[i] = 10 - i;
+    }
+    array.realloc(20);
+    for (int i = 0; i < 10; i++) {
+        REPORTER_ASSERT(reporter, array[i] == 10 - i);
+    }
+    array.realloc(10);
+    for (int i = 0; i < 10; i++) {
+        REPORTER_ASSERT(reporter, array[i] == 10 - i);
+    }
+
+    array.realloc(1);
+    REPORTER_ASSERT(reporter, array[0] = 10);
+
+    // resets mixed with realloc, below stack alloc size
+    array.reset(0);
+    array.realloc(1);
+    array.reset(1);
+
+    array[0] = 1;
+    REPORTER_ASSERT(reporter, array[0] == 1);
+
+    // reset and realloc > stack size
+    array.reset(2);
+    array.realloc(3);
+    array[0] = 1;
+    REPORTER_ASSERT(reporter, array[0] == 1);
+    array.realloc(1);
+    REPORTER_ASSERT(reporter, array[0] == 1);
+}
+
+DEF_TEST(Templates, reporter) {
+    test_automalloc_realloc(reporter);
+}
diff --git a/tests/TessellatingPathRendererTests.cpp b/tests/TessellatingPathRendererTests.cpp
index 0635e7f..bc2d0d1 100644
--- a/tests/TessellatingPathRendererTests.cpp
+++ b/tests/TessellatingPathRendererTests.cpp
@@ -5,6 +5,8 @@
  * found in the LICENSE file.
  */
 
+#include "SkPath.h"
+
 #if SK_SUPPORT_GPU
 #include "GrContextFactory.h"
 #include "GrTessellatingPathRenderer.h"
@@ -234,21 +236,23 @@
     GrTessellatingPathRenderer tess;
     GrPipelineBuilder pipelineBuilder;
     pipelineBuilder.setRenderTarget(rt);
-    SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+    GrStrokeInfo stroke(SkStrokeRec::kFill_InitStyle);
     tess.drawPath(dt, &pipelineBuilder, SK_ColorWHITE, SkMatrix::I(), path, stroke, false);
 }
 
 DEF_GPUTEST(TessellatingPathRendererTests, reporter, factory) {
     GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
+    if (NULL == context) {
+        return;
+    }
     GrSurfaceDesc desc;
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
     desc.fWidth = 800;
     desc.fHeight = 800;
     desc.fConfig = kSkia8888_GrPixelConfig;
     desc.fOrigin = kTopLeft_GrSurfaceOrigin;
-    SkAutoTUnref<GrTexture> texture(
-        context->refScratchTexture(desc, GrContext::kExact_ScratchTexMatch)
-    );
+    SkAutoTUnref<GrTexture> texture(context->textureProvider()->refScratchTexture(desc,
+        GrTextureProvider::kExact_ScratchTexMatch));
     GrTestTarget tt;
     context->getTestTarget(&tt);
     GrRenderTarget* rt = texture->asRenderTarget();
diff --git a/tests/Time.cpp b/tests/Time.cpp
index 0139654..60dcd55 100644
--- a/tests/Time.cpp
+++ b/tests/Time.cpp
@@ -41,4 +41,16 @@
     REPORTER_ASSERT(r, dateTime.fMinute <= 59);
 
     REPORTER_ASSERT(r, dateTime.fSecond <= 60);  // leap seconds are 23:59:60
+
+    // The westernmost timezone is -12:00.
+    // The easternmost timezone is +14:00.
+    REPORTER_ASSERT(r, abs(SkToInt(dateTime.fTimeZoneMinutes)) <= 14 * 60);
+
+    SkString timeStamp;
+    dateTime.toISO8601(&timeStamp);
+    REPORTER_ASSERT(r, timeStamp.size() > 0);
+    if (r->verbose()) {  // `dm --veryVerbose`
+        SkDebugf("\nCurrent Time (ISO-8601 format): \"%s\"\n",
+                 timeStamp.c_str());
+    }
 }
diff --git a/tests/VarAllocTest.cpp b/tests/VarAllocTest.cpp
index 18e4214..b1a0cb3 100644
--- a/tests/VarAllocTest.cpp
+++ b/tests/VarAllocTest.cpp
@@ -6,8 +6,8 @@
     char* p = va.alloc(128, SK_MALLOC_THROW);
     sk_bzero(p, 128);  // Just checking this is safe.
 
-#ifndef SK_BUILD_FOR_ANDROID
-    // This method will always return 0 on Android.
+#if !defined(SK_BUILD_FOR_ANDROID) && !defined(__UCLIBC__)
+    // This method will always return 0 on Android and UCLIBC platforms.
     REPORTER_ASSERT(r, va.approxBytesAllocated() >= 128);
 #endif
 }
diff --git a/tests/WritePixelsTest.cpp b/tests/WritePixelsTest.cpp
index 2d24a77..d1c328e 100644
--- a/tests/WritePixelsTest.cpp
+++ b/tests/WritePixelsTest.cpp
@@ -319,7 +319,7 @@
             desc.fConfig = kSkia8888_GrPixelConfig;
             desc.fOrigin = kGpu_TopLeft_DevType == c.fDevType ?
                 kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
-            SkAutoTUnref<GrTexture> texture(grCtx->createTexture(desc, false));
+            SkAutoTUnref<GrTexture> texture(grCtx->textureProvider()->createTexture(desc, false));
             return SkSurface::NewRenderTargetDirect(texture->asRenderTarget());
 #endif
     }
diff --git a/tests/skia_test.cpp b/tests/skia_test.cpp
index 74029d4..3379e39 100644
--- a/tests/skia_test.cpp
+++ b/tests/skia_test.cpp
@@ -84,12 +84,12 @@
       struct TestReporter : public skiatest::Reporter {
       public:
           TestReporter() : fError(false), fTestCount(0) {}
-          void bumpTestCount() SK_OVERRIDE { ++fTestCount; }
-          bool allowExtendedTest() const SK_OVERRIDE {
+          void bumpTestCount() override { ++fTestCount; }
+          bool allowExtendedTest() const override {
               return FLAGS_extendedTest;
           }
-          bool verbose() const SK_OVERRIDE { return FLAGS_veryVerbose; }
-          void reportFailed(const skiatest::Failure& failure) SK_OVERRIDE {
+          bool verbose() const override { return FLAGS_veryVerbose; }
+          void reportFailed(const skiatest::Failure& failure) override {
               SkDebugf("\nFAILED: %s", failure.toString().c_str());
               fError = true;
           }
@@ -229,7 +229,7 @@
     return (status.failCount() == 0) ? 0 : 1;
 }
 
-#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
+#if !defined(SK_BUILD_FOR_IOS)
 int main(int argc, char** argv) {
     SkCommandLineFlags::Parse(argc, argv);
     return test_main();
diff --git a/third_party/giflib/unistd.h b/third_party/giflib/unistd.h
new file mode 100644
index 0000000..4453217
--- /dev/null
+++ b/third_party/giflib/unistd.h
@@ -0,0 +1,7 @@
+// Copyright 2015 Google Inc.
+
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file does nothing.  There are files in the external giflib that include
+// <unistd.h>.  This file exists so that the include does not fail on Windows.
diff --git a/third_party/ktx/ktx.cpp b/third_party/ktx/ktx.cpp
index 42b3ca1..5e24581 100644
--- a/third_party/ktx/ktx.cpp
+++ b/third_party/ktx/ktx.cpp
@@ -18,23 +18,23 @@
 
 static inline uint32_t compressed_fmt_to_gl_define(SkTextureCompressor::Format fmt) {
     static const uint32_t kGLDefineMap[SkTextureCompressor::kFormatCnt] = {
-        GR_GL_COMPRESSED_LUMINANCE_LATC1,  // kLATC_Format
-        GR_GL_COMPRESSED_R11,              // kR11_EAC_Format
-        GR_GL_COMPRESSED_RGB8_ETC1,        // kETC1_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_4x4,    // kASTC_4x4_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_5x4,    // kASTC_5x4_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_5x5,    // kASTC_5x5_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_6x5,    // kASTC_6x5_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_6x6,    // kASTC_6x6_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_8x5,    // kASTC_8x5_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_8x6,    // kASTC_8x6_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_8x8,    // kASTC_8x8_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_10x5,   // kASTC_10x5_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_10x6,   // kASTC_10x6_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_10x8,   // kASTC_10x8_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_10x10,  // kASTC_10x10_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_12x10,  // kASTC_12x10_Format
-        GR_GL_COMPRESSED_RGBA_ASTC_12x12,  // kASTC_12x12_Format
+        GR_GL_COMPRESSED_LUMINANCE_LATC1,      // kLATC_Format
+        GR_GL_COMPRESSED_R11_EAC,              // kR11_EAC_Format
+        GR_GL_COMPRESSED_ETC1_RGB8,            // kETC1_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_4x4_KHR,    // kASTC_4x4_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_5x4_KHR,    // kASTC_5x4_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_5x5_KHR,    // kASTC_5x5_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_6x5_KHR,    // kASTC_6x5_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_6x6_KHR,    // kASTC_6x6_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_8x5_KHR,    // kASTC_8x5_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_8x6_KHR,    // kASTC_8x6_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_8x8_KHR,    // kASTC_8x8_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_10x5_KHR,   // kASTC_10x5_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_10x6_KHR,   // kASTC_10x6_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_10x8_KHR,   // kASTC_10x8_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_10x10_KHR,  // kASTC_10x10_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_12x10_KHR,  // kASTC_12x10_Format
+        GR_GL_COMPRESSED_RGBA_ASTC_12x12_KHR,  // kASTC_12x12_Format
     };
 
     GR_STATIC_ASSERT(0 == SkTextureCompressor::kLATC_Format);
@@ -385,7 +385,7 @@
     hdr.fGLType = 0;
     hdr.fGLTypeSize = 1;
     hdr.fGLFormat = 0;
-    hdr.fGLInternalFormat = GR_GL_COMPRESSED_RGB8_ETC1;
+    hdr.fGLInternalFormat = GR_GL_COMPRESSED_ETC1_RGB8;
     hdr.fGLBaseInternalFormat = GR_GL_RGB;
     hdr.fPixelWidth = width;
     hdr.fPixelHeight = height;
diff --git a/third_party/libpng/README.google b/third_party/libpng/README.google
index 4578d35..cacd08b 100644
--- a/third_party/libpng/README.google
+++ b/third_party/libpng/README.google
@@ -3,14 +3,16 @@
 License: libpng license
 License File: LICENSE, pulled out of png.h
 Description: png compression/decompression library
-Local Modifications: Created pnglibconf.h from pnglibconf.h.prebuilt (just a
-    rename). Pulled LICENSE into its own file.
+Local Modifications: (1) Created pnglibconf.h from pnglibconf.h.prebuilt (a
+    rename with a few additional settings enabled). (2) Created pngprefix.h
+    that is required when using the PNG_PREFIX define. (3) Pulled LICENSE into
+    its own file.
 
 FAQ:
 Q: Why does this directory exist?
 A: libpng is pulled in through DEPS, but in order to build it using ninja, we
-   need to create pnglibconf.h. In https://codereview.chromium.org/930283002/,
-   we originally tried creating the file as an action, but this apparently led
-   to a race condition on Windows, where some builds failed to create the file
-   in time for other files to include it. By checking pnglibconf.h directly
-   into Skia, we eliminate the race condition.
+   need to create pnglibconf.h and pngprefix.h.  We originally tried creating
+   the file as an action in https://codereview.chromium.org/930283002/, but this
+   apparently led to a race condition on Windows, where some builds failed to
+   create the file in time for other files to include it. By checking these
+   files directly into Skia, we eliminate the race condition.
diff --git a/third_party/libpng/pnglibconf.h b/third_party/libpng/pnglibconf.h
index da3b229..ea3694b 100644
--- a/third_party/libpng/pnglibconf.h
+++ b/third_party/libpng/pnglibconf.h
@@ -207,4 +207,11 @@
 #define PNG_sCAL_PRECISION 5
 #define PNG_sRGB_PROFILE_CHECKS 2
 /* end of settings */
+
+/* custom settings */
+#define PNG_ARM_NEON_API_SUPPORTED
+#define PNG_ARM_NEON_CHECK_SUPPORTED
+#define PNG_PREFIX skia_
+/* end of custom settings */
+
 #endif /* PNGLCONF_H */
diff --git a/third_party/libpng/pngprefix.h b/third_party/libpng/pngprefix.h
new file mode 100644
index 0000000..8ffd725
--- /dev/null
+++ b/third_party/libpng/pngprefix.h
@@ -0,0 +1,447 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * This file was created in part using the configuration script provided with
+ * the libpng sources.  However it was missing some symbols which caused the
+ * linker to fail so those were added manually by scanning the library looking
+ * for exported symbols missing the skia_ prefix and adding them to the end of
+ * this file.
+ *
+ * ./third_party/externals/libpng/configure --with-libpng-prefix=skia_
+ */
+
+#define png_sRGB_table skia_png_sRGB_table
+#define png_sRGB_base skia_png_sRGB_base
+#define png_sRGB_delta skia_png_sRGB_delta
+#define png_zstream_error skia_png_zstream_error
+#define png_free_buffer_list skia_png_free_buffer_list
+#define png_fixed skia_png_fixed
+#define png_user_version_check skia_png_user_version_check
+#define png_malloc_base skia_png_malloc_base
+#define png_malloc_array skia_png_malloc_array
+#define png_realloc_array skia_png_realloc_array
+#define png_create_png_struct skia_png_create_png_struct
+#define png_destroy_png_struct skia_png_destroy_png_struct
+#define png_free_jmpbuf skia_png_free_jmpbuf
+#define png_zalloc skia_png_zalloc
+#define png_zfree skia_png_zfree
+#define png_default_read_data skia_png_default_read_data
+#define png_push_fill_buffer skia_png_push_fill_buffer
+#define png_default_write_data skia_png_default_write_data
+#define png_default_flush skia_png_default_flush
+#define png_reset_crc skia_png_reset_crc
+#define png_write_data skia_png_write_data
+#define png_read_sig skia_png_read_sig
+#define png_read_chunk_header skia_png_read_chunk_header
+#define png_read_data skia_png_read_data
+#define png_crc_read skia_png_crc_read
+#define png_crc_finish skia_png_crc_finish
+#define png_crc_error skia_png_crc_error
+#define png_calculate_crc skia_png_calculate_crc
+#define png_flush skia_png_flush
+#define png_write_IHDR skia_png_write_IHDR
+#define png_write_PLTE skia_png_write_PLTE
+#define png_compress_IDAT skia_png_compress_IDAT
+#define png_write_IEND skia_png_write_IEND
+#define png_write_gAMA_fixed skia_png_write_gAMA_fixed
+#define png_write_sBIT skia_png_write_sBIT
+#define png_write_cHRM_fixed skia_png_write_cHRM_fixed
+#define png_write_sRGB skia_png_write_sRGB
+#define png_write_iCCP skia_png_write_iCCP
+#define png_write_sPLT skia_png_write_sPLT
+#define png_write_tRNS skia_png_write_tRNS
+#define png_write_bKGD skia_png_write_bKGD
+#define png_write_hIST skia_png_write_hIST
+#define png_write_tEXt skia_png_write_tEXt
+#define png_write_zTXt skia_png_write_zTXt
+#define png_write_iTXt skia_png_write_iTXt
+#define png_set_text_2 skia_png_set_text_2
+#define png_write_oFFs skia_png_write_oFFs
+#define png_write_pCAL skia_png_write_pCAL
+#define png_write_pHYs skia_png_write_pHYs
+#define png_write_tIME skia_png_write_tIME
+#define png_write_sCAL_s skia_png_write_sCAL_s
+#define png_write_finish_row skia_png_write_finish_row
+#define png_write_start_row skia_png_write_start_row
+#define png_combine_row skia_png_combine_row
+#define png_do_read_interlace skia_png_do_read_interlace
+#define png_do_write_interlace skia_png_do_write_interlace
+#define png_read_filter_row skia_png_read_filter_row
+#define png_read_filter_row_up_neon skia_png_read_filter_row_up_neon
+#define png_read_filter_row_sub3_neon skia_png_read_filter_row_sub3_neon
+#define png_read_filter_row_sub4_neon skia_png_read_filter_row_sub4_neon
+#define png_read_filter_row_avg3_neon skia_png_read_filter_row_avg3_neon
+#define png_read_filter_row_avg4_neon skia_png_read_filter_row_avg4_neon
+#define png_read_filter_row_paeth3_neon skia_png_read_filter_row_paeth3_neon
+#define png_read_filter_row_paeth4_neon skia_png_read_filter_row_paeth4_neon
+#define png_write_find_filter skia_png_write_find_filter
+#define png_read_IDAT_data skia_png_read_IDAT_data
+#define png_read_finish_IDAT skia_png_read_finish_IDAT
+#define png_read_finish_row skia_png_read_finish_row
+#define png_read_start_row skia_png_read_start_row
+#define png_read_transform_info skia_png_read_transform_info
+#define png_do_read_filler skia_png_do_read_filler
+#define png_do_read_swap_alpha skia_png_do_read_swap_alpha
+#define png_do_write_swap_alpha skia_png_do_write_swap_alpha
+#define png_do_read_invert_alpha skia_png_do_read_invert_alpha
+#define png_do_write_invert_alpha skia_png_do_write_invert_alpha
+#define png_do_strip_channel skia_png_do_strip_channel
+#define png_do_swap skia_png_do_swap
+#define png_do_packswap skia_png_do_packswap
+#define png_do_rgb_to_gray skia_png_do_rgb_to_gray
+#define png_do_gray_to_rgb skia_png_do_gray_to_rgb
+#define png_do_unpack skia_png_do_unpack
+#define png_do_unshift skia_png_do_unshift
+#define png_do_invert skia_png_do_invert
+#define png_do_scale_16_to_8 skia_png_do_scale_16_to_8
+#define png_do_chop skia_png_do_chop
+#define png_do_quantize skia_png_do_quantize
+#define png_do_bgr skia_png_do_bgr
+#define png_do_pack skia_png_do_pack
+#define png_do_shift skia_png_do_shift
+#define png_do_compose skia_png_do_compose
+#define png_do_gamma skia_png_do_gamma
+#define png_do_encode_alpha skia_png_do_encode_alpha
+#define png_do_expand_palette skia_png_do_expand_palette
+#define png_do_expand skia_png_do_expand
+#define png_do_expand_16 skia_png_do_expand_16
+#define png_handle_IHDR skia_png_handle_IHDR
+#define png_handle_PLTE skia_png_handle_PLTE
+#define png_handle_IEND skia_png_handle_IEND
+#define png_handle_bKGD skia_png_handle_bKGD
+#define png_handle_cHRM skia_png_handle_cHRM
+#define png_handle_gAMA skia_png_handle_gAMA
+#define png_handle_hIST skia_png_handle_hIST
+#define png_handle_iCCP skia_png_handle_iCCP
+#define png_handle_iTXt skia_png_handle_iTXt
+#define png_handle_oFFs skia_png_handle_oFFs
+#define png_handle_pCAL skia_png_handle_pCAL
+#define png_handle_pHYs skia_png_handle_pHYs
+#define png_handle_sBIT skia_png_handle_sBIT
+#define png_handle_sCAL skia_png_handle_sCAL
+#define png_handle_sPLT skia_png_handle_sPLT
+#define png_handle_sRGB skia_png_handle_sRGB
+#define png_handle_tEXt skia_png_handle_tEXt
+#define png_handle_tIME skia_png_handle_tIME
+#define png_handle_tRNS skia_png_handle_tRNS
+#define png_handle_zTXt skia_png_handle_zTXt
+#define png_check_chunk_name skia_png_check_chunk_name
+#define png_handle_unknown skia_png_handle_unknown
+#define png_chunk_unknown_handling skia_png_chunk_unknown_handling
+#define png_do_read_transformations skia_png_do_read_transformations
+#define png_do_write_transformations skia_png_do_write_transformations
+#define png_init_read_transformations skia_png_init_read_transformations
+#define png_push_read_chunk skia_png_push_read_chunk
+#define png_push_read_sig skia_png_push_read_sig
+#define png_push_check_crc skia_png_push_check_crc
+#define png_push_crc_skip skia_png_push_crc_skip
+#define png_push_crc_finish skia_png_push_crc_finish
+#define png_push_save_buffer skia_png_push_save_buffer
+#define png_push_restore_buffer skia_png_push_restore_buffer
+#define png_push_read_IDAT skia_png_push_read_IDAT
+#define png_process_IDAT_data skia_png_process_IDAT_data
+#define png_push_process_row skia_png_push_process_row
+#define png_push_handle_unknown skia_png_push_handle_unknown
+#define png_push_have_info skia_png_push_have_info
+#define png_push_have_end skia_png_push_have_end
+#define png_push_have_row skia_png_push_have_row
+#define png_push_read_end skia_png_push_read_end
+#define png_process_some_data skia_png_process_some_data
+#define png_read_push_finish_row skia_png_read_push_finish_row
+#define png_push_handle_tEXt skia_png_push_handle_tEXt
+#define png_push_read_tEXt skia_png_push_read_tEXt
+#define png_push_handle_zTXt skia_png_push_handle_zTXt
+#define png_push_read_zTXt skia_png_push_read_zTXt
+#define png_push_handle_iTXt skia_png_push_handle_iTXt
+#define png_push_read_iTXt skia_png_push_read_iTXt
+#define png_do_read_intrapixel skia_png_do_read_intrapixel
+#define png_do_write_intrapixel skia_png_do_write_intrapixel
+#define png_colorspace_set_gamma skia_png_colorspace_set_gamma
+#define png_colorspace_sync_info skia_png_colorspace_sync_info
+#define png_colorspace_sync skia_png_colorspace_sync
+#define png_colorspace_set_chromaticities skia_png_colorspace_set_chromaticities
+#define png_colorspace_set_endpoints skia_png_colorspace_set_endpoints
+#define png_colorspace_set_sRGB skia_png_colorspace_set_sRGB
+#define png_colorspace_set_ICC skia_png_colorspace_set_ICC
+#define png_icc_check_length skia_png_icc_check_length
+#define png_icc_check_header skia_png_icc_check_header
+#define png_icc_check_tag_table skia_png_icc_check_tag_table
+#define png_icc_set_sRGB skia_png_icc_set_sRGB
+#define png_colorspace_set_rgb_coefficients skia_png_colorspace_set_rgb_coefficients
+#define png_check_IHDR skia_png_check_IHDR
+#define png_do_check_palette_indexes skia_png_do_check_palette_indexes
+#define png_fixed_error skia_png_fixed_error
+#define png_safecat skia_png_safecat
+#define png_format_number skia_png_format_number
+#define png_warning_parameter skia_png_warning_parameter
+#define png_warning_parameter_unsigned skia_png_warning_parameter_unsigned
+#define png_warning_parameter_signed skia_png_warning_parameter_signed
+#define png_formatted_warning skia_png_formatted_warning
+#define png_app_warning skia_png_app_warning
+#define png_app_error skia_png_app_error
+#define png_chunk_report skia_png_chunk_report
+#define png_ascii_from_fp skia_png_ascii_from_fp
+#define png_ascii_from_fixed skia_png_ascii_from_fixed
+#define png_check_fp_number skia_png_check_fp_number
+#define png_check_fp_string skia_png_check_fp_string
+#define png_muldiv skia_png_muldiv
+#define png_muldiv_warn skia_png_muldiv_warn
+#define png_reciprocal skia_png_reciprocal
+#define png_reciprocal2 skia_png_reciprocal2
+#define png_gamma_significant skia_png_gamma_significant
+#define png_gamma_correct skia_png_gamma_correct
+#define png_gamma_16bit_correct skia_png_gamma_16bit_correct
+#define png_gamma_8bit_correct skia_png_gamma_8bit_correct
+#define png_destroy_gamma_table skia_png_destroy_gamma_table
+#define png_build_gamma_table skia_png_build_gamma_table
+#define png_safe_error skia_png_safe_error
+#define png_safe_warning skia_png_safe_warning
+#define png_safe_execute skia_png_safe_execute
+#define png_image_error skia_png_image_error
+
+#define png_access_version_number skia_png_access_version_number
+#define png_build_grayscale_palette skia_png_build_grayscale_palette
+#define png_convert_to_rfc1123 skia_png_convert_to_rfc1123
+#define png_convert_to_rfc1123_buffer skia_png_convert_to_rfc1123_buffer
+#define png_create_info_struct skia_png_create_info_struct
+#define png_data_freer skia_png_data_freer
+#define png_destroy_info_struct skia_png_destroy_info_struct
+#define png_free_data skia_png_free_data
+#define png_get_copyright skia_png_get_copyright
+#define png_get_header_ver skia_png_get_header_ver
+#define png_get_header_version skia_png_get_header_version
+#define png_get_io_ptr skia_png_get_io_ptr
+#define png_get_libpng_ver skia_png_get_libpng_ver
+#define png_handle_as_unknown skia_png_handle_as_unknown
+#define png_image_free skia_png_image_free
+#define png_info_init_3 skia_png_info_init_3
+#define png_init_io skia_png_init_io
+#define png_reset_zstream skia_png_reset_zstream
+#define png_save_int_32 skia_png_save_int_32
+#define png_set_option skia_png_set_option
+#define png_set_sig_bytes skia_png_set_sig_bytes
+#define png_sig_cmp skia_png_sig_cmp
+#define png_benign_error skia_png_benign_error
+#define png_chunk_benign_error skia_png_chunk_benign_error
+#define png_chunk_error skia_png_chunk_error
+#define png_chunk_warning skia_png_chunk_warning
+#define png_error skia_png_error
+#define png_get_error_ptr skia_png_get_error_ptr
+#define png_longjmp skia_png_longjmp
+#define png_set_error_fn skia_png_set_error_fn
+#define png_set_longjmp_fn skia_png_set_longjmp_fn
+#define png_warning skia_png_warning
+#define png_get_bit_depth skia_png_get_bit_depth
+#define png_get_bKGD skia_png_get_bKGD
+#define png_get_channels skia_png_get_channels
+#define png_get_cHRM skia_png_get_cHRM
+#define png_get_cHRM_fixed skia_png_get_cHRM_fixed
+#define png_get_cHRM_XYZ skia_png_get_cHRM_XYZ
+#define png_get_cHRM_XYZ_fixed skia_png_get_cHRM_XYZ_fixed
+#define png_get_chunk_cache_max skia_png_get_chunk_cache_max
+#define png_get_chunk_malloc_max skia_png_get_chunk_malloc_max
+#define png_get_color_type skia_png_get_color_type
+#define png_get_compression_buffer_size skia_png_get_compression_buffer_size
+#define png_get_compression_type skia_png_get_compression_type
+#define png_get_filter_type skia_png_get_filter_type
+#define png_get_gAMA skia_png_get_gAMA
+#define png_get_gAMA_fixed skia_png_get_gAMA_fixed
+#define png_get_hIST skia_png_get_hIST
+#define png_get_iCCP skia_png_get_iCCP
+#define png_get_IHDR skia_png_get_IHDR
+#define png_get_image_height skia_png_get_image_height
+#define png_get_image_width skia_png_get_image_width
+#define png_get_interlace_type skia_png_get_interlace_type
+#define png_get_io_chunk_type skia_png_get_io_chunk_type
+#define png_get_io_state skia_png_get_io_state
+#define png_get_oFFs skia_png_get_oFFs
+#define png_get_palette_max skia_png_get_palette_max
+#define png_get_pCAL skia_png_get_pCAL
+#define png_get_pHYs skia_png_get_pHYs
+#define png_get_pHYs_dpi skia_png_get_pHYs_dpi
+#define png_get_pixel_aspect_ratio skia_png_get_pixel_aspect_ratio
+#define png_get_pixel_aspect_ratio_fixed skia_png_get_pixel_aspect_ratio_fixed
+#define png_get_pixels_per_inch skia_png_get_pixels_per_inch
+#define png_get_pixels_per_meter skia_png_get_pixels_per_meter
+#define png_get_PLTE skia_png_get_PLTE
+#define png_get_rgb_to_gray_status skia_png_get_rgb_to_gray_status
+#define png_get_rowbytes skia_png_get_rowbytes
+#define png_get_rows skia_png_get_rows
+#define png_get_sBIT skia_png_get_sBIT
+#define png_get_sCAL skia_png_get_sCAL
+#define png_get_sCAL_fixed skia_png_get_sCAL_fixed
+#define png_get_sCAL_s skia_png_get_sCAL_s
+#define png_get_signature skia_png_get_signature
+#define png_get_sPLT skia_png_get_sPLT
+#define png_get_sRGB skia_png_get_sRGB
+#define png_get_text skia_png_get_text
+#define png_get_tIME skia_png_get_tIME
+#define png_get_tRNS skia_png_get_tRNS
+#define png_get_unknown_chunks skia_png_get_unknown_chunks
+#define png_get_user_chunk_ptr skia_png_get_user_chunk_ptr
+#define png_get_user_height_max skia_png_get_user_height_max
+#define png_get_user_width_max skia_png_get_user_width_max
+#define png_get_valid skia_png_get_valid
+#define png_get_x_offset_inches skia_png_get_x_offset_inches
+#define png_get_x_offset_inches_fixed skia_png_get_x_offset_inches_fixed
+#define png_get_x_offset_microns skia_png_get_x_offset_microns
+#define png_get_x_offset_pixels skia_png_get_x_offset_pixels
+#define png_get_x_pixels_per_inch skia_png_get_x_pixels_per_inch
+#define png_get_x_pixels_per_meter skia_png_get_x_pixels_per_meter
+#define png_get_y_offset_inches skia_png_get_y_offset_inches
+#define png_get_y_offset_inches_fixed skia_png_get_y_offset_inches_fixed
+#define png_get_y_offset_microns skia_png_get_y_offset_microns
+#define png_get_y_offset_pixels skia_png_get_y_offset_pixels
+#define png_get_y_pixels_per_inch skia_png_get_y_pixels_per_inch
+#define png_get_y_pixels_per_meter skia_png_get_y_pixels_per_meter
+#define png_calloc skia_png_calloc
+#define png_free skia_png_free
+#define png_free_default skia_png_free_default
+#define png_get_mem_ptr skia_png_get_mem_ptr
+#define png_malloc skia_png_malloc
+#define png_malloc_default skia_png_malloc_default
+#define png_malloc_warn skia_png_malloc_warn
+#define png_set_mem_fn skia_png_set_mem_fn
+#define png_get_progressive_ptr skia_png_get_progressive_ptr
+#define png_process_data skia_png_process_data
+#define png_process_data_pause skia_png_process_data_pause
+#define png_process_data_skip skia_png_process_data_skip
+#define png_progressive_combine_row skia_png_progressive_combine_row
+#define png_set_progressive_read_fn skia_png_set_progressive_read_fn
+#define png_create_read_struct skia_png_create_read_struct
+#define png_create_read_struct_2 skia_png_create_read_struct_2
+#define png_destroy_read_struct skia_png_destroy_read_struct
+#define png_image_begin_read_from_file skia_png_image_begin_read_from_file
+#define png_image_begin_read_from_memory skia_png_image_begin_read_from_memory
+#define png_image_begin_read_from_stdio skia_png_image_begin_read_from_stdio
+#define png_image_finish_read skia_png_image_finish_read
+#define png_read_end skia_png_read_end
+#define png_read_image skia_png_read_image
+#define png_read_info skia_png_read_info
+#define png_read_png skia_png_read_png
+#define png_read_row skia_png_read_row
+#define png_read_rows skia_png_read_rows
+#define png_read_update_info skia_png_read_update_info
+#define png_set_read_status_fn skia_png_set_read_status_fn
+#define png_start_read_image skia_png_start_read_image
+#define png_set_read_fn skia_png_set_read_fn
+#define png_set_alpha_mode skia_png_set_alpha_mode
+#define png_set_alpha_mode_fixed skia_png_set_alpha_mode_fixed
+#define png_set_background skia_png_set_background
+#define png_set_background_fixed skia_png_set_background_fixed
+#define png_set_crc_action skia_png_set_crc_action
+#define png_set_expand skia_png_set_expand
+#define png_set_expand_16 skia_png_set_expand_16
+#define png_set_expand_gray_1_2_4_to_8 skia_png_set_expand_gray_1_2_4_to_8
+#define png_set_gamma skia_png_set_gamma
+#define png_set_gamma_fixed skia_png_set_gamma_fixed
+#define png_set_gray_to_rgb skia_png_set_gray_to_rgb
+#define png_set_palette_to_rgb skia_png_set_palette_to_rgb
+#define png_set_quantize skia_png_set_quantize
+#define png_set_read_user_transform_fn skia_png_set_read_user_transform_fn
+#define png_set_rgb_to_gray skia_png_set_rgb_to_gray
+#define png_set_rgb_to_gray_fixed skia_png_set_rgb_to_gray_fixed
+#define png_set_scale_16 skia_png_set_scale_16
+#define png_set_strip_16 skia_png_set_strip_16
+#define png_set_strip_alpha skia_png_set_strip_alpha
+#define png_set_tRNS_to_alpha skia_png_set_tRNS_to_alpha
+#define png_get_int_32 skia_png_get_int_32
+#define png_get_uint_16 skia_png_get_uint_16
+#define png_get_uint_31 skia_png_get_uint_31
+#define png_get_uint_32 skia_png_get_uint_32
+#define png_permit_mng_features skia_png_permit_mng_features
+#define png_set_benign_errors skia_png_set_benign_errors
+#define png_set_bKGD skia_png_set_bKGD
+#define png_set_check_for_invalid_index skia_png_set_check_for_invalid_index
+#define png_set_cHRM skia_png_set_cHRM
+#define png_set_cHRM_fixed skia_png_set_cHRM_fixed
+#define png_set_cHRM_XYZ skia_png_set_cHRM_XYZ
+#define png_set_cHRM_XYZ_fixed skia_png_set_cHRM_XYZ_fixed
+#define png_set_chunk_cache_max skia_png_set_chunk_cache_max
+#define png_set_chunk_malloc_max skia_png_set_chunk_malloc_max
+#define png_set_compression_buffer_size skia_png_set_compression_buffer_size
+#define png_set_gAMA skia_png_set_gAMA
+#define png_set_gAMA_fixed skia_png_set_gAMA_fixed
+#define png_set_hIST skia_png_set_hIST
+#define png_set_iCCP skia_png_set_iCCP
+#define png_set_IHDR skia_png_set_IHDR
+#define png_set_invalid skia_png_set_invalid
+#define png_set_keep_unknown_chunks skia_png_set_keep_unknown_chunks
+#define png_set_oFFs skia_png_set_oFFs
+#define png_set_pCAL skia_png_set_pCAL
+#define png_set_pHYs skia_png_set_pHYs
+#define png_set_PLTE skia_png_set_PLTE
+#define png_set_read_user_chunk_fn skia_png_set_read_user_chunk_fn
+#define png_set_rows skia_png_set_rows
+#define png_set_sBIT skia_png_set_sBIT
+#define png_set_sCAL skia_png_set_sCAL
+#define png_set_sCAL_fixed skia_png_set_sCAL_fixed
+#define png_set_sCAL_s skia_png_set_sCAL_s
+#define png_set_sPLT skia_png_set_sPLT
+#define png_set_sRGB skia_png_set_sRGB
+#define png_set_sRGB_gAMA_and_cHRM skia_png_set_sRGB_gAMA_and_cHRM
+#define png_set_text skia_png_set_text
+#define png_set_tIME skia_png_set_tIME
+#define png_set_tRNS skia_png_set_tRNS
+#define png_set_unknown_chunk_location skia_png_set_unknown_chunk_location
+#define png_set_unknown_chunks skia_png_set_unknown_chunks
+#define png_set_user_limits skia_png_set_user_limits
+#define png_get_current_pass_number skia_png_get_current_pass_number
+#define png_get_current_row_number skia_png_get_current_row_number
+#define png_get_user_transform_ptr skia_png_get_user_transform_ptr
+#define png_set_add_alpha skia_png_set_add_alpha
+#define png_set_bgr skia_png_set_bgr
+#define png_set_filler skia_png_set_filler
+#define png_set_interlace_handling skia_png_set_interlace_handling
+#define png_set_invert_alpha skia_png_set_invert_alpha
+#define png_set_invert_mono skia_png_set_invert_mono
+#define png_set_packing skia_png_set_packing
+#define png_set_packswap skia_png_set_packswap
+#define png_set_shift skia_png_set_shift
+#define png_set_swap skia_png_set_swap
+#define png_set_swap_alpha skia_png_set_swap_alpha
+#define png_set_user_transform_info skia_png_set_user_transform_info
+#define png_set_write_fn skia_png_set_write_fn
+#define png_convert_from_struct_tm skia_png_convert_from_struct_tm
+#define png_convert_from_time_t skia_png_convert_from_time_t
+#define png_create_write_struct skia_png_create_write_struct
+#define png_create_write_struct_2 skia_png_create_write_struct_2
+#define png_destroy_write_struct skia_png_destroy_write_struct
+#define png_image_write_to_file skia_png_image_write_to_file
+#define png_image_write_to_stdio skia_png_image_write_to_stdio
+#define png_set_compression_level skia_png_set_compression_level
+#define png_set_compression_mem_level skia_png_set_compression_mem_level
+#define png_set_compression_method skia_png_set_compression_method
+#define png_set_compression_strategy skia_png_set_compression_strategy
+#define png_set_compression_window_bits skia_png_set_compression_window_bits
+#define png_set_filter skia_png_set_filter
+#define png_set_filter_heuristics skia_png_set_filter_heuristics
+#define png_set_filter_heuristics_fixed skia_png_set_filter_heuristics_fixed
+#define png_set_flush skia_png_set_flush
+#define png_set_text_compression_level skia_png_set_text_compression_level
+#define png_set_text_compression_mem_level skia_png_set_text_compression_mem_level
+#define png_set_text_compression_method skia_png_set_text_compression_method
+#define png_set_text_compression_strategy skia_png_set_text_compression_strategy
+#define png_set_text_compression_window_bits skia_png_set_text_compression_window_bits
+#define png_set_write_status_fn skia_png_set_write_status_fn
+#define png_set_write_user_transform_fn skia_png_set_write_user_transform_fn
+#define png_write_end skia_png_write_end
+#define png_write_flush skia_png_write_flush
+#define png_write_image skia_png_write_image
+#define png_write_info skia_png_write_info
+#define png_write_info_before_PLTE skia_png_write_info_before_PLTE
+#define png_write_png skia_png_write_png
+#define png_write_row skia_png_write_row
+#define png_write_rows skia_png_write_rows
+#define png_save_uint_16 skia_png_save_uint_16
+#define png_save_uint_32 skia_png_save_uint_32
+#define png_write_chunk skia_png_write_chunk
+#define png_write_chunk_data skia_png_write_chunk_data
+#define png_write_chunk_start skia_png_write_chunk_start
+#define png_write_chunk_end skia_png_write_chunk_end
+#define png_write_sig skia_png_write_sig
diff --git a/tools/Android.mk b/tools/Android.mk
deleted file mode 100644
index e869952..0000000
--- a/tools/Android.mk
+++ /dev/null
@@ -1,78 +0,0 @@
-
-###############################################################################
-#
-# THIS FILE IS AUTOGENERATED BY GYP_TO_ANDROID.PY. DO NOT EDIT.
-#
-###############################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_CFLAGS += \
-	-fPIC \
-	-Wno-c++11-extensions \
-	-Wno-unused-parameter \
-	-U_FORTIFY_SOURCE \
-	-D_FORTIFY_SOURCE=1
-
-LOCAL_CPPFLAGS := \
-	-Wno-invalid-offsetof
-
-LOCAL_SRC_FILES := \
-	../bench/BenchLogger.cpp \
-	../bench/ResultsWriter.cpp \
-	PictureBenchmark.cpp \
-	bench_pictures_main.cpp \
-	timer/Timer.cpp \
-	timer/TimerData.cpp \
-	timer/GpuTimer.cpp \
-	timer/SysTimer_posix.cpp \
-	CrashHandler.cpp \
-	flags/SkCommandLineFlags.cpp \
-	image_expectations.cpp \
-	LazyDecodeBitmap.cpp \
-	PictureRenderer.cpp \
-	PictureRenderingFlags.cpp \
-	CopyTilesRenderer.cpp \
-	../src/pipe/utils/SamplePipeControllers.cpp \
-	picture_utils.cpp \
-	../src/gpu/GrTest.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	liblog \
-	libskia \
-	libGLESv2 \
-	libEGL
-
-LOCAL_STATIC_LIBRARIES := \
-	libjsoncpp
-
-LOCAL_C_INCLUDES := \
-	$(LOCAL_PATH)/../src/core \
-	$(LOCAL_PATH)/../src/gpu \
-	$(LOCAL_PATH)/../include/config \
-	$(LOCAL_PATH)/../include/core \
-	$(LOCAL_PATH)/../include/pathops \
-	$(LOCAL_PATH)/../include/pipe \
-	$(LOCAL_PATH)/../include/effects \
-	$(LOCAL_PATH)/../include/images \
-	$(LOCAL_PATH)/../include/ports \
-	$(LOCAL_PATH)/../src/sfnt \
-	$(LOCAL_PATH)/../include/utils \
-	$(LOCAL_PATH)/../src/utils \
-	$(LOCAL_PATH)/../include/gpu \
-	$(LOCAL_PATH)/../src/images \
-	$(LOCAL_PATH)/../src/lazy \
-	$(LOCAL_PATH)/../src/pipe/utils \
-	$(LOCAL_PATH)/flags \
-	$(LOCAL_PATH)/../bench \
-	$(LOCAL_PATH)/timer \
-	$(LOCAL_PATH)/../third_party/externals/jsoncpp-chromium/overrides/include \
-	$(LOCAL_PATH)/../third_party/externals/jsoncpp/include
-
-LOCAL_MODULE_TAGS := \
-	tests
-
-LOCAL_MODULE := \
-	skia_bench_pictures
-
-include $(BUILD_NATIVE_TEST)
diff --git a/tools/CopyTilesRenderer.h b/tools/CopyTilesRenderer.h
index 507e0fd..3d9a1eb 100644
--- a/tools/CopyTilesRenderer.h
+++ b/tools/CopyTilesRenderer.h
@@ -32,15 +32,15 @@
                           const SkString* mismatchPath,
                           const SkString* inputFilename,
                           bool useChecksumBasedFilenames,
-                          bool useMultiPictureDraw) SK_OVERRIDE;
+                          bool useMultiPictureDraw) override;
 
         /**
          *  Similar to TiledPictureRenderer, this will draw a PNG for each tile. However, the
          *  numbering (and actual tiles) will be different.
          */
-        bool render(SkBitmap** out) SK_OVERRIDE;
+        bool render(SkBitmap** out) override;
 
-        bool supportsTimingIndividualTiles() SK_OVERRIDE { return false; }
+        bool supportsTimingIndividualTiles() override { return false; }
 
     private:
         int fXTilesPerLargeTile;
@@ -49,7 +49,7 @@
         int fLargeTileWidth;
         int fLargeTileHeight;
 
-        SkString getConfigNameInternal() SK_OVERRIDE;
+        SkString getConfigNameInternal() override;
 
         typedef TiledPictureRenderer INHERITED;
     };
diff --git a/tools/CrashHandler.cpp b/tools/CrashHandler.cpp
index 2ab6a9d..e26772b 100644
--- a/tools/CrashHandler.cpp
+++ b/tools/CrashHandler.cpp
@@ -1,3 +1,10 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "CrashHandler.h"
 
 #include "SkTypes.h"
@@ -47,7 +54,7 @@
             _Exit(sig);
         }
 
-    #elif defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_NACL)  // NACL doesn't have backtrace.
+    #elif defined(SK_BUILD_FOR_UNIX)
 
         // We'd use libunwind here too, but it's a pain to get installed for
         // both 32 and 64 bit on bots.  Doesn't matter much: catchsegv is best anyway.
@@ -67,7 +74,7 @@
 
     #endif
 
-    #if (defined(SK_BUILD_FOR_MAC) || (defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_NACL)))
+    #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)
         #include <signal.h>
 
         void SetupCrashHandler() {
diff --git a/tools/LazyDecodeBitmap.cpp b/tools/LazyDecodeBitmap.cpp
index 0dca16b..3b6f17e 100644
--- a/tools/LazyDecodeBitmap.cpp
+++ b/tools/LazyDecodeBitmap.cpp
@@ -22,19 +22,17 @@
 
 //  Fits SkPicture::InstallPixelRefProc call signature.
 //  Used in SkPictureData::CreateFromStream
-bool sk_tools::LazyDecodeBitmap(const void* src,
-                                size_t length,
-                                SkBitmap* dst) {
+bool sk_tools::LazyDecodeBitmap(const void* src, size_t length, SkBitmap* dst) {
     SkAutoDataUnref data(SkData::NewWithCopy(src, length));
     if (NULL == data.get()) {
         return false;
     }
 
     SkAutoTDelete<SkImageGenerator> gen(SkImageGenerator::NewFromData(data));
-    SkImageInfo info;
-    if ((NULL == gen.get()) || !gen->getInfo(&info)) {
+    if (NULL == gen.get()) {
         return false;
     }
+    const SkImageInfo info = gen->getInfo();
     SkDiscardableMemory::Factory* pool = NULL;
     if ((!FLAGS_useVolatileCache) || (info.width() * info.height() < 32 * 1024)) {
         // how to do switching with SkDiscardableMemory.
diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp
index 6acd13c..93701ba 100644
--- a/tools/PictureRenderer.cpp
+++ b/tools/PictureRenderer.cpp
@@ -37,9 +37,10 @@
 #include "SkTDArray.h"
 #include "SkThreadUtils.h"
 #include "SkTypes.h"
+#include "sk_tool_utils.h"
 
 static inline SkScalar scalar_log2(SkScalar x) {
-    static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2));
+    static const SkScalar log2_conversion_factor = SkScalarInvert(SkScalarLog(2));
 
     return SkScalarLog(x) * log2_conversion_factor;
 }
@@ -155,7 +156,7 @@
                 desc.fWidth = width;
                 desc.fHeight = height;
                 desc.fSampleCnt = fSampleCount;
-                target.reset(fGrContext->createTexture(desc, false, NULL, 0));
+                target.reset(fGrContext->textureProvider()->createTexture(desc, false, NULL, 0));
             }
 
             uint32_t flags = fUseDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0;
@@ -358,19 +359,6 @@
     return NULL;
 }
 
-// Encodes to PNG, unless there is already encoded data, in which case that gets
-// used.
-// FIXME: Share with PictureTest.cpp?
-
-class PngPixelSerializer : public SkPixelSerializer {
-public:
-    bool onUseEncodedData(const void*, size_t) SK_OVERRIDE { return true; }
-    SkData* onEncodePixels(const SkImageInfo& info, const void* pixels,
-                           size_t rowBytes) SK_OVERRIDE {
-        return SkImageEncoder::EncodeData(info, pixels, rowBytes, SkImageEncoder::kPNG_Type, 100);
-    }
-};
-
 bool RecordPictureRenderer::render(SkBitmap** out) {
     SkAutoTDelete<SkBBHFactory> factory(this->getFactory());
     SkPictureRecorder recorder;
@@ -385,7 +373,7 @@
         // Record the new picture as a new SKP with PNG encoded bitmaps.
         SkString skpPath = SkOSPath::Join(fWritePath.c_str(), fInputFilename.c_str());
         SkFILEWStream stream(skpPath.c_str());
-        PngPixelSerializer serializer;
+        sk_tool_utils::PngPixelSerializer serializer;
         picture->serialize(&stream, &serializer);
         return true;
     }
diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h
index d52b988..e143468 100644
--- a/tools/PictureRenderer.h
+++ b/tools/PictureRenderer.h
@@ -501,17 +501,17 @@
     RecordPictureRenderer(const GrContext::Options &opts) : INHERITED(opts) { }
 #endif
 
-    bool render(SkBitmap** out = NULL) SK_OVERRIDE;
+    bool render(SkBitmap** out = NULL) override;
 
-    SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
+    SkString getPerIterTimeFormat() override { return SkString("%.4f"); }
 
-    SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
+    SkString getNormalTimeFormat() override { return SkString("%6.4f"); }
 
 protected:
-    SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
+    SkCanvas* setupCanvas(int width, int height) override;
 
 private:
-    SkString getConfigNameInternal() SK_OVERRIDE;
+    SkString getConfigNameInternal() override;
 
     typedef PictureRenderer INHERITED;
 };
@@ -522,10 +522,10 @@
     PipePictureRenderer(const GrContext::Options &opts) : INHERITED(opts) { }
 #endif
 
-    bool render(SkBitmap** out = NULL) SK_OVERRIDE;
+    bool render(SkBitmap** out = NULL) override;
 
 private:
-    SkString getConfigNameInternal() SK_OVERRIDE;
+    SkString getConfigNameInternal() override;
 
     typedef PictureRenderer INHERITED;
 };
@@ -541,12 +541,12 @@
                       const SkString* mismatchPath,
                       const SkString* inputFilename,
                       bool useChecksumBasedFilenames,
-                      bool useMultiPictureDraw) SK_OVERRIDE;
+                      bool useMultiPictureDraw) override;
 
-    bool render(SkBitmap** out = NULL) SK_OVERRIDE;
+    bool render(SkBitmap** out = NULL) override;
 
 private:
-    SkString getConfigNameInternal() SK_OVERRIDE;
+    SkString getConfigNameInternal() override;
 
     typedef PictureRenderer INHERITED;
 };
@@ -564,16 +564,16 @@
                       const SkString* mismatchPath,
                       const SkString* inputFilename,
                       bool useChecksumBasedFilenames,
-                      bool useMultiPictureDraw) SK_OVERRIDE;
+                      bool useMultiPictureDraw) override;
 
     /**
      * Renders to tiles, rather than a single canvas.
      * If fWritePath was provided, a separate file is
      * created for each tile, named "path0.png", "path1.png", etc.
      */
-    bool render(SkBitmap** out = NULL) SK_OVERRIDE;
+    bool render(SkBitmap** out = NULL) override;
 
-    void end() SK_OVERRIDE;
+    void end() override;
 
     void setTileWidth(int width) {
         fTileWidth = width;
@@ -620,7 +620,7 @@
         return fTileMinPowerOf2Width;
     }
 
-    TiledPictureRenderer* getTiledRenderer() SK_OVERRIDE { return this; }
+    TiledPictureRenderer* getTiledRenderer() override { return this; }
 
     virtual bool supportsTimingIndividualTiles() { return true; }
 
@@ -655,8 +655,8 @@
 protected:
     SkTDArray<SkIRect> fTileRects;
 
-    SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
-    SkString getConfigNameInternal() SK_OVERRIDE;
+    SkCanvas* setupCanvas(int width, int height) override;
+    SkString getConfigNameInternal() override;
 
 private:
     int    fTileWidth;
@@ -692,18 +692,18 @@
     PlaybackCreationRenderer(const GrContext::Options &opts) : INHERITED(opts) { }
 #endif
 
-    void setup() SK_OVERRIDE;
+    void setup() override;
 
-    bool render(SkBitmap** out = NULL) SK_OVERRIDE;
+    bool render(SkBitmap** out = NULL) override;
 
-    SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
+    SkString getPerIterTimeFormat() override { return SkString("%.4f"); }
 
-    SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
+    SkString getNormalTimeFormat() override { return SkString("%6.4f"); }
 
 private:
     SkAutoTDelete<SkPictureRecorder> fRecorder;
 
-    SkString getConfigNameInternal() SK_OVERRIDE;
+    SkString getConfigNameInternal() override;
 
     typedef PictureRenderer INHERITED;
 };
diff --git a/tools/PictureResultsWriter.h b/tools/PictureResultsWriter.h
index d3d346f..6737102 100644
--- a/tools/PictureResultsWriter.h
+++ b/tools/PictureResultsWriter.h
@@ -56,22 +56,22 @@
         fWriters.push_back(newWriter);
     }
     virtual ~PictureResultsMultiWriter() {}
-    void bench(const char name[], int32_t x, int32_t y) SK_OVERRIDE {
+    void bench(const char name[], int32_t x, int32_t y) override {
         for(int i=0; i<fWriters.count(); ++i) {
             fWriters[i]->bench(name, x, y);
         }
     }
-    void logRenderer(sk_tools::PictureRenderer *pr) SK_OVERRIDE {
+    void logRenderer(sk_tools::PictureRenderer *pr) override {
         for(int i=0; i<fWriters.count(); ++i) {
             fWriters[i]->logRenderer(pr);
         }
     }
-    void tileMeta(int x, int y, int tx, int ty) SK_OVERRIDE {
+    void tileMeta(int x, int y, int tx, int ty) override {
         for(int i=0; i<fWriters.count(); ++i) {
             fWriters[i]->tileMeta(x, y, tx, ty);
         }
     }
-    void addTileFlag(PictureResultsWriter::TileFlags flag) SK_OVERRIDE {
+    void addTileFlag(PictureResultsWriter::TileFlags flag) override {
         for(int i=0; i<fWriters.count(); ++i) {
             fWriters[i]->addTileFlag(flag);
         }
@@ -81,13 +81,13 @@
             const char format[],
             const TimerData::Result result,
             uint32_t timerTypes,
-            int numInnerLoops = 1) SK_OVERRIDE {
+            int numInnerLoops = 1) override {
         for(int i=0; i<fWriters.count(); ++i) {
             fWriters[i]->tileData(data, format, result, timerTypes,
                                  numInnerLoops);
         }
     }
-   void end() SK_OVERRIDE {
+   void end() override {
         for(int i=0; i<fWriters.count(); ++i) {
             fWriters[i]->end();
         }
@@ -109,18 +109,18 @@
 public:
     PictureResultsLoggerWriter(BenchLogger* log)
           : fLogger(log), fCurrentLine() {}
-    void bench(const char name[], int32_t x, int32_t y) SK_OVERRIDE {
+    void bench(const char name[], int32_t x, int32_t y) override {
         SkString result;
         result.printf("running bench [%i %i] %s ", x, y, name);
         this->logProgress(result.c_str());
     }
-    void logRenderer(sk_tools::PictureRenderer* renderer) SK_OVERRIDE {
+    void logRenderer(sk_tools::PictureRenderer* renderer) override {
         fCurrentLine = renderer->getConfigName();
     }
-    void tileMeta(int x, int y, int tx, int ty) SK_OVERRIDE {
+    void tileMeta(int x, int y, int tx, int ty) override {
         fCurrentLine.appendf(": tile [%i,%i] out of [%i,%i]", x, y, tx, ty);
     }
-    void addTileFlag(PictureResultsWriter::TileFlags flag) SK_OVERRIDE {
+    void addTileFlag(PictureResultsWriter::TileFlags flag) override {
         if(flag == PictureResultsWriter::kPurging) {
             fCurrentLine.append(" <withPurging>");
         } else if(flag == PictureResultsWriter::kAvg) {
@@ -132,13 +132,13 @@
             const char format[],
             const TimerData::Result result,
             uint32_t timerTypes,
-            int numInnerLoops = 1) SK_OVERRIDE {
+            int numInnerLoops = 1) override {
         SkString results = data->getResult(format, result,
                 fCurrentLine.c_str(), timerTypes, numInnerLoops);
         results.append("\n");
         this->logProgress(results.c_str());
     }
-    void end() SK_OVERRIDE {}
+    void end() override {}
 private:
     BenchLogger* fLogger;
     SkString fCurrentLine;
@@ -189,23 +189,23 @@
         fBuilderData = this->makeBuilderJson();
     }
 
-    void bench(const char name[], int32_t x, int32_t y) SK_OVERRIDE {
+    void bench(const char name[], int32_t x, int32_t y) override {
         fBenchName = SkString(name);
     }
-    void logRenderer(sk_tools::PictureRenderer* pr) SK_OVERRIDE {
+    void logRenderer(sk_tools::PictureRenderer* pr) override {
         fParams = pr->getJSONConfig();
         fConfigString = pr->getConfigName();
     }
     // Apparently tiles aren't used, so tileMeta is empty
-    void tileMeta(int x, int y, int tx, int ty) SK_OVERRIDE {}
+    void tileMeta(int x, int y, int tx, int ty) override {}
     // Flags aren't used, so addTileFlag is empty
-    void addTileFlag(PictureResultsWriter::TileFlags flag) SK_OVERRIDE {}
+    void addTileFlag(PictureResultsWriter::TileFlags flag) override {}
     virtual void tileData(
             TimerData* data,
             const char format[],
             const TimerData::Result result,
             uint32_t timerTypes,
-            int numInnerLoops = 1) SK_OVERRIDE {
+            int numInnerLoops = 1) override {
         Json::Value newData = data->getJSON(timerTypes, result, numInnerLoops);
         Json::Value combinedParams(fBuilderData);
         for(Json::ValueIterator iter = fParams.begin(); iter != fParams.end();
@@ -241,7 +241,7 @@
             fStream.writeText(Json::FastWriter().write(data).c_str());
         }
     }
-    void end() SK_OVERRIDE {
+    void end() override {
        fStream.flush();
     }
 private:
diff --git a/tools/ProcStats.cpp b/tools/ProcStats.cpp
index c9f9f3f..a772db4 100644
--- a/tools/ProcStats.cpp
+++ b/tools/ProcStats.cpp
@@ -7,10 +7,7 @@
 
 #include "ProcStats.h"
 
-#if defined(SK_BUILD_FOR_UNIX) || \
-    defined(SK_BUILD_FOR_MAC) || \
-    defined(SK_BUILD_FOR_ANDROID)
-
+#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) || defined(SK_BUILD_FOR_ANDROID)
     #include <sys/resource.h>
     int sk_tools::getMaxResidentSetSizeMB() {
         struct rusage ru;
@@ -21,7 +18,6 @@
         return static_cast<int>(ru.ru_maxrss / 1024);  // Linux reports kilobytes.
     #endif
     }
-
 #elif defined(SK_BUILD_FOR_WIN32)
     #include <windows.h>
     #include <psapi.h>
@@ -30,10 +26,44 @@
         GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
         return static_cast<int>(info.PeakWorkingSetSize / 1024 / 1024);  // Windows reports bytes.
     }
-
 #else
-    int sk_tools::getMaxResidentSetSizeMB() {
-        return -1;
+    int sk_tools::getMaxResidentSetSizeMB() { return -1; }
+#endif
+
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+    #include <mach/mach.h>
+    int sk_tools::getCurrResidentSetSizeMB() {
+        mach_task_basic_info info;
+        mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
+        if (KERN_SUCCESS !=
+                task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &count)) {
+            return -1;
+        }
+        return info.resident_size / 1024 / 1024;  // Darwin reports bytes.
+    }
+#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)  // N.B. /proc is Linux-only.
+    #include <unistd.h>
+    #include <stdio.h>
+    int sk_tools::getCurrResidentSetSizeMB() {
+        const long pageSize = sysconf(_SC_PAGESIZE);
+        long long rssPages = 0;
+        if (FILE* statm = fopen("/proc/self/statm", "r")) {
+            // statm contains: program-size rss shared text lib data dirty, all in page counts.
+            int rc = fscanf(statm, "%*d %lld", &rssPages);
+            fclose(statm);
+            if (rc != 1) {
+                return -1;
+            }
+        }
+        return rssPages * pageSize / 1024 / 1024;
     }
 
+#elif defined(SK_BUILD_FOR_WIN32)
+    int sk_tools::getCurrResidentSetSizeMB() {
+        PROCESS_MEMORY_COUNTERS info;
+        GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
+        return static_cast<int>(info.WorkingSetSize / 1024 / 1024);  // Windows reports bytes.
+    }
+#else
+    int sk_tools::getCurrResidentSetSizeMB() { return -1; }
 #endif
diff --git a/tools/ProcStats.h b/tools/ProcStats.h
index 14b98b7..344b304 100644
--- a/tools/ProcStats.h
+++ b/tools/ProcStats.h
@@ -15,11 +15,17 @@
 namespace sk_tools {
 
 /**
- *  If not implemented for this OS, returns -1.  Otherwise, return
- *  the maximum resident set size, as reported by getrusage().
+ *  If implemented, returns the maximum resident set size in MB.
+ *  If not, returns -1.
  */
 int getMaxResidentSetSizeMB();
 
+/**
+ *  If implemented, returns the current resident set size in MB.
+ *  If not, returns -1.
+ */
+int getCurrResidentSetSizeMB();
+
 }  // namespace sk_tools
 
 #endif  // ProcStats_DEFINED
diff --git a/tools/Resources.cpp b/tools/Resources.cpp
index 43cc21d..4767edd 100644
--- a/tools/Resources.cpp
+++ b/tools/Resources.cpp
@@ -11,6 +11,8 @@
 #include "SkData.h"
 #include "SkImageGenerator.h"
 #include "SkOSFile.h"
+#include "SkStream.h"
+#include "SkTypeface.h"
 
 DEFINE_string2(resourcePath, i, "resources", "Directory with test resources: images, fonts, etc.");
 
@@ -24,7 +26,29 @@
 
 bool GetResourceAsBitmap(const char* resource, SkBitmap* dst) {
     SkString resourcePath = GetResourcePath(resource);
-    SkAutoTUnref<SkData> resourceData(
-            SkData::NewFromFileName(resourcePath.c_str()));
+    SkAutoTUnref<SkData> resourceData(SkData::NewFromFileName(resourcePath.c_str()));
     return resourceData && SkInstallDiscardablePixelRef(resourceData, dst);
 }
+
+SkStreamAsset* GetResourceAsStream(const char* resource) {
+    SkString resourcePath = GetResourcePath(resource);
+    SkAutoTDelete<SkFILEStream> stream(new SkFILEStream(resourcePath.c_str()));
+    if (stream->isValid()) {
+        return stream.detach();
+    } else {
+        SkDebugf("Resource %s not found.\n", resource);
+        return NULL;
+    }
+}
+
+SkTypeface* GetResourceAsTypeface(const char* resource) {
+    SkAutoTDelete<SkStreamAsset> stream(GetResourceAsStream(resource));
+    if (!stream) {
+        return NULL;
+    }
+    SkAutoTUnref<SkTypeface> typeface(SkTypeface::CreateFromStream(stream.detach()));
+    if (!typeface) {
+        SkDebugf("Resource %s not a valid font.", resource);
+    }
+    return typeface.detach();
+}
diff --git a/tools/Resources.h b/tools/Resources.h
index 99de77a..c9785a5 100644
--- a/tools/Resources.h
+++ b/tools/Resources.h
@@ -11,10 +11,14 @@
 #include "SkString.h"
 
 class SkBitmap;
+class SkStreamAsset;
+class SkTypeface;
 
 SkString GetResourcePath(const char* resource = "");
 void SetResourcePath(const char* );
 
 bool GetResourceAsBitmap(const char* resource, SkBitmap* dst);
+SkStreamAsset* GetResourceAsStream(const char* resource);
+SkTypeface* GetResourceAsTypeface(const char* resource);
 
 #endif  // Resources_DEFINED
diff --git a/tools/chrome_fuzz.cpp b/tools/chrome_fuzz.cpp
new file mode 100644
index 0000000..94d276f
--- /dev/null
+++ b/tools/chrome_fuzz.cpp
@@ -0,0 +1,82 @@
+// Copyright 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.
+
+#include "SkCanvas.h"
+#include "SkFlattenableSerialization.h"
+#include "SkImageFilter.h"
+#include "SkOSFile.h"
+#include "SkString.h"
+
+static const int kBitmapSize = 24;
+
+static bool read_test_case(const char* filename, SkString* testdata) {
+  SkFILE* file = sk_fopen(filename, kRead_SkFILE_Flag);
+  if (!file) {
+    SkDebugf("couldn't open file %s\n", filename);
+    return false;
+  }
+  size_t len = sk_fgetsize(file);
+  if (!len) {
+    SkDebugf("couldn't read file %s\n", filename);
+    return false;
+  }
+  testdata->resize(len);
+  (void) sk_fread(testdata->writable_str(), len, file);
+  return true;
+}
+
+static void run_test_case(const SkString& testdata, const SkBitmap& bitmap,
+                 SkCanvas* canvas) {
+  // This call shouldn't crash or cause ASAN to flag any memory issues
+  // If nothing bad happens within this call, everything is fine
+  SkFlattenable* flattenable = SkValidatingDeserializeFlattenable(
+        testdata.c_str(), testdata.size(), SkImageFilter::GetFlattenableType());
+
+  // Adding some info, but the test passed if we got here without any trouble
+  if (flattenable != NULL) {
+    SkDebugf("Valid stream detected.\n");
+    // Let's see if using the filters can cause any trouble...
+    SkPaint paint;
+    paint.setImageFilter(static_cast<SkImageFilter*>(flattenable))->unref();
+    canvas->save();
+    canvas->clipRect(SkRect::MakeXYWH(
+        0, 0, SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize)));
+
+    // This call shouldn't crash or cause ASAN to flag any memory issues
+    // If nothing bad happens within this call, everything is fine
+    canvas->drawBitmap(bitmap, 0, 0, &paint);
+
+    SkDebugf("Filter DAG rendered successfully.\n");
+    canvas->restore();
+  } else {
+    SkDebugf("Invalid stream detected.\n");
+  }
+}
+
+static bool read_and_run_test_case(const char* filename, const SkBitmap& bitmap,
+                        SkCanvas* canvas) {
+  SkString testdata;
+  SkDebugf("Test case: %s\n", filename);
+  // read_test_case will print a useful error message if it fails.
+  if (!read_test_case(filename, &testdata))
+    return false;
+  run_test_case(testdata, bitmap, canvas);
+  return true;
+}
+
+int main(int argc, char** argv) {
+  int ret = 0;
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(kBitmapSize, kBitmapSize);
+  SkCanvas canvas(bitmap);
+  canvas.clear(0x00000000);
+  for (int i = 1; i < argc; i++)
+    if (!read_and_run_test_case(argv[i], bitmap, &canvas))
+      ret = 2;
+  // Cluster-Fuzz likes "#EOF" as the last line of output to help distinguish
+  // successful runs from crashes.
+  SkDebugf("#EOF\n");
+  return ret;
+}
+
diff --git a/tools/dm_flags.json b/tools/dm_flags.json
index 32a5c2c..b82ce33 100644
--- a/tools/dm_flags.json
+++ b/tools/dm_flags.json
@@ -1,11 +1,17 @@
 {
-  "Test-Android-GalaxyS3-Mali400-Arm7-Debug": [
+  "Pretend-iOS-Bot": [
+    "--matrix", 
+    "0", 
+    "1", 
+    "1", 
+    "0", 
     "--config", 
     "565", 
     "8888", 
     "gpu", 
-    "msaa4", 
-    "nvprmsaa4", 
+    "upright-matrix-8888", 
+    "upright-matrix-gpu", 
+    "pdf", 
     "serialize-8888", 
     "tiles_rt-8888", 
     "pipe-8888", 
@@ -13,140 +19,294 @@
     "--blacklist", 
     "gpu", 
     "_", 
+    "_", 
     "PANO_20121023_214540.jpg", 
     "msaa", 
     "_", 
+    "_", 
     "PANO_20121023_214540.jpg", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v4.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v5.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565pal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32-111110.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32bf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32abf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal4rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "Hopstarter-Mac-Folders-Apple.ico", 
     "gpu", 
     "skp", 
     "_", 
-    "gpu", 
-    "image", 
-    "_", 
-    "gpu", 
-    "subset", 
     "_", 
     "msaa", 
     "skp", 
     "_", 
-    "msaa", 
-    "image", 
     "_", 
     "gpu", 
+    "image", 
+    "decode", 
+    "_", 
+    "msaa", 
+    "image", 
+    "decode", 
+    "_", 
+    "gpu", 
+    "image", 
     "subset", 
     "_", 
+    "msaa", 
+    "image", 
+    "subset", 
+    "_", 
+    "msaa16", 
+    "gm", 
+    "_", 
+    "tilemodesProcess", 
     "--match", 
-    "~WritePixels", 
-    "~tabl_mozilla_0", 
-    "~desk_yahoonews_0", 
-    "--nocpu"
+    "~WritePixels"
   ], 
-  "Test-Android-Nexus7-Tegra3-Arm7-Release": [
+  "Test-Android-GCC-GalaxyS3-GPU-Mali400-Arm7-Debug": [
     "--config", 
     "565", 
     "8888", 
     "gpu", 
     "msaa4", 
-    "nvprmsaa4", 
     "serialize-8888", 
     "tiles_rt-8888", 
     "pipe-8888", 
     "tiles_rt-gpu", 
+    "--threads", 
+    "0", 
     "--blacklist", 
     "gpu", 
     "_", 
+    "_", 
     "PANO_20121023_214540.jpg", 
     "msaa", 
     "_", 
+    "_", 
     "PANO_20121023_214540.jpg", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v4.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v5.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565pal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32-111110.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32bf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32abf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal4rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "Hopstarter-Mac-Folders-Apple.ico", 
     "gpu", 
     "skp", 
     "_", 
-    "gpu", 
-    "image", 
+    "_", 
+    "msaa", 
+    "skp", 
+    "_", 
     "_", 
     "gpu", 
+    "image", 
+    "decode", 
+    "_", 
+    "msaa", 
+    "image", 
+    "decode", 
+    "_", 
+    "gpu", 
+    "image", 
     "subset", 
     "_", 
     "msaa", 
-    "skp", 
-    "_", 
-    "msaa", 
     "image", 
-    "_", 
-    "gpu", 
-    "subset", 
-    "_", 
-    "--match", 
-    "~tabl_mozilla_0", 
-    "~desk_yahoonews_0", 
-    "--nocpu"
-  ], 
-  "Test-Android-NexusPlayer-PowerVR-x86-Release": [
-    "--config", 
-    "565", 
-    "8888", 
-    "gpu", 
-    "--blacklist", 
-    "gpu", 
-    "_", 
-    "PANO_20121023_214540.jpg", 
-    "msaa", 
-    "_", 
-    "PANO_20121023_214540.jpg", 
-    "gpu", 
-    "skp", 
-    "_", 
-    "gpu", 
-    "image", 
-    "_", 
-    "gpu", 
-    "subset", 
-    "_", 
-    "msaa", 
-    "skp", 
-    "_", 
-    "msaa", 
-    "image", 
-    "_", 
-    "gpu", 
-    "subset", 
-    "_", 
-    "--match", 
-    "~tabl_mozilla_0", 
-    "~desk_yahoonews_0", 
-    "~ResourceCache"
-  ], 
-  "Test-Android-Xoom-Tegra2-Arm7-Release": [
-    "--config", 
-    "565", 
-    "8888", 
-    "gpu", 
-    "msaa4", 
-    "nvprmsaa4", 
-    "--blacklist", 
-    "gpu", 
-    "_", 
-    "PANO_20121023_214540.jpg", 
-    "msaa", 
-    "_", 
-    "PANO_20121023_214540.jpg", 
-    "gpu", 
-    "skp", 
-    "_", 
-    "gpu", 
-    "image", 
-    "_", 
-    "gpu", 
-    "subset", 
-    "_", 
-    "msaa", 
-    "skp", 
-    "_", 
-    "msaa", 
-    "image", 
-    "_", 
-    "gpu", 
     "subset", 
     "_", 
     "--match", 
@@ -154,87 +314,1077 @@
     "~tabl_mozilla_0", 
     "~desk_yahoonews_0"
   ], 
-  "Test-ChromeOS-Alex-GMA3150-x86-Debug": [
+  "Test-Android-GCC-GalaxyS4-GPU-SGX544-Arm7-Release": [
     "--config", 
     "565", 
     "8888", 
     "gpu", 
-    "msaa16", 
-    "nvprmsaa16", 
-    "pdf", 
     "serialize-8888", 
     "tiles_rt-8888", 
     "pipe-8888", 
     "tiles_rt-gpu", 
-    "--blacklist", 
-    "gpu", 
-    "_", 
-    "PANO_20121023_214540.jpg", 
-    "msaa", 
-    "_", 
-    "PANO_20121023_214540.jpg", 
     "--threads", 
-    "1"
+    "0", 
+    "--blacklist", 
+    "gpu", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "msaa", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v4.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v5.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565pal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32-111110.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32bf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32abf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal4rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "Hopstarter-Mac-Folders-Apple.ico", 
+    "gpu", 
+    "skp", 
+    "_", 
+    "_", 
+    "msaa", 
+    "skp", 
+    "_", 
+    "_", 
+    "gpu", 
+    "image", 
+    "decode", 
+    "_", 
+    "msaa", 
+    "image", 
+    "decode", 
+    "_", 
+    "gpu", 
+    "image", 
+    "subset", 
+    "_", 
+    "msaa", 
+    "image", 
+    "subset", 
+    "_", 
+    "tiles_rt-gpu", 
+    "gm", 
+    "_", 
+    "imagefilterscropped", 
+    "--match", 
+    "~tabl_mozilla_0", 
+    "~desk_yahoonews_0"
   ], 
-  "Test-Ubuntu12-ShuttleA-GTX550Ti-x86_64-Release-Valgrind_GPU": [
+  "Test-Android-GCC-Nexus7-GPU-Tegra3-Arm7-Release": [
     "--config", 
     "565", 
     "8888", 
     "gpu", 
+    "serialize-8888", 
+    "tiles_rt-8888", 
+    "pipe-8888", 
+    "tiles_rt-gpu", 
+    "--blacklist", 
+    "gpu", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "msaa", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v4.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v5.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565pal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32-111110.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32bf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32abf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal4rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "Hopstarter-Mac-Folders-Apple.ico", 
+    "gpu", 
+    "skp", 
+    "_", 
+    "_", 
+    "msaa", 
+    "skp", 
+    "_", 
+    "_", 
+    "gpu", 
+    "image", 
+    "decode", 
+    "_", 
+    "msaa", 
+    "image", 
+    "decode", 
+    "_", 
+    "gpu", 
+    "image", 
+    "subset", 
+    "_", 
+    "msaa", 
+    "image", 
+    "subset", 
+    "_", 
+    "--match", 
+    "~tabl_mozilla_0", 
+    "~desk_yahoonews_0"
+  ], 
+  "Test-Android-GCC-Nexus9-GPU-TegraK1-Arm64-Debug": [
+    "--config", 
+    "565", 
+    "8888", 
+    "gpu", 
+    "nvprmsaa4", 
+    "msaa4", 
+    "serialize-8888", 
+    "tiles_rt-8888", 
+    "pipe-8888", 
+    "tiles_rt-gpu", 
+    "--blacklist", 
+    "gpu", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "msaa", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v4.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v5.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565pal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32-111110.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32bf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32abf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal4rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "Hopstarter-Mac-Folders-Apple.ico", 
+    "gpu", 
+    "skp", 
+    "_", 
+    "_", 
+    "msaa", 
+    "skp", 
+    "_", 
+    "_", 
+    "gpu", 
+    "image", 
+    "decode", 
+    "_", 
+    "msaa", 
+    "image", 
+    "decode", 
+    "_", 
+    "gpu", 
+    "image", 
+    "subset", 
+    "_", 
+    "msaa", 
+    "image", 
+    "subset", 
+    "_", 
+    "--match", 
+    "~tabl_mozilla_0", 
+    "~desk_yahoonews_0"
+  ], 
+  "Test-Android-GCC-NexusPlayer-CPU-SSSE3-x86-Release": [
+    "--config", 
+    "565", 
+    "8888", 
+    "gpu", 
+    "--blacklist", 
+    "gpu", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "msaa", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v4.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v5.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565pal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32-111110.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32bf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32abf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal4rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "Hopstarter-Mac-Folders-Apple.ico", 
+    "gpu", 
+    "skp", 
+    "_", 
+    "_", 
+    "msaa", 
+    "skp", 
+    "_", 
+    "_", 
+    "gpu", 
+    "image", 
+    "decode", 
+    "_", 
+    "msaa", 
+    "image", 
+    "decode", 
+    "_", 
+    "gpu", 
+    "image", 
+    "subset", 
+    "_", 
+    "msaa", 
+    "image", 
+    "subset", 
+    "_", 
+    "--match", 
+    "~tabl_mozilla_0", 
+    "~desk_yahoonews_0", 
+    "~ResourceCache"
+  ], 
+  "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-TSAN": [
+    "--matrix", 
+    "0", 
+    "1", 
+    "1", 
+    "0", 
+    "--config", 
+    "565", 
+    "8888", 
+    "gpu", 
+    "upright-matrix-8888", 
+    "upright-matrix-gpu", 
+    "sp-8888", 
     "msaa16", 
-    "nvprmsaa16", 
     "pdf", 
     "serialize-8888", 
     "tiles_rt-8888", 
     "pipe-8888", 
     "tiles_rt-gpu", 
-    "--nocpu", 
     "--blacklist", 
     "gpu", 
     "_", 
+    "_", 
     "PANO_20121023_214540.jpg", 
     "msaa", 
     "_", 
-    "PANO_20121023_214540.jpg", 
-    "pdf", 
     "_", 
-    ".webp", 
+    "PANO_20121023_214540.jpg", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v4.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v5.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565pal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32-111110.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32bf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32abf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal4rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "Hopstarter-Mac-Folders-Apple.ico", 
     "--match", 
-    "~Threaded"
+    "~Math"
   ], 
-  "Test-Ubuntu14-GCE-NoGPU-x86_64-Release-Valgrind_CPU": [
+  "Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-Valgrind": [
+    "--matrix", 
+    "0", 
+    "1", 
+    "1", 
+    "0", 
     "--config", 
     "565", 
     "8888", 
     "gpu", 
+    "upright-matrix-8888", 
+    "upright-matrix-gpu", 
+    "sp-8888", 
     "msaa16", 
-    "nvprmsaa16", 
     "pdf", 
     "serialize-8888", 
     "tiles_rt-8888", 
     "pipe-8888", 
     "tiles_rt-gpu", 
-    "--nogpu", 
     "--blacklist", 
     "gpu", 
     "_", 
+    "_", 
     "PANO_20121023_214540.jpg", 
     "msaa", 
     "_", 
+    "_", 
     "PANO_20121023_214540.jpg", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v4.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v5.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565pal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32-111110.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32bf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32abf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal4rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "Hopstarter-Mac-Folders-Apple.ico", 
     "pdf", 
     "_", 
+    "_", 
     ".webp", 
+    "pdf", 
+    "gm", 
+    "_", 
+    "fontmgr_iter", 
+    "pdf", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "pdf", 
+    "skp", 
+    "_", 
+    "worldjournal", 
+    "pdf", 
+    "skp", 
+    "_", 
+    "desk_baidu.skp", 
+    "pdf", 
+    "skp", 
+    "_", 
+    "desk_wikipedia.skp", 
     "--match", 
     "~Threaded"
   ], 
-  "Test-Win7-ShuttleA-HD2000-x86-Debug-ANGLE": [
+  "Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind": [
+    "--matrix", 
+    "0", 
+    "1", 
+    "1", 
+    "0", 
     "--config", 
     "565", 
     "8888", 
     "gpu", 
-    "msaa16", 
+    "upright-matrix-8888", 
+    "upright-matrix-gpu", 
     "nvprmsaa16", 
+    "msaa16", 
+    "pdf", 
+    "serialize-8888", 
+    "tiles_rt-8888", 
+    "pipe-8888", 
+    "tiles_rt-gpu", 
+    "--blacklist", 
+    "gpu", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "msaa", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v4.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v5.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565pal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32-111110.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32bf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32abf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal4rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "Hopstarter-Mac-Folders-Apple.ico", 
+    "pdf", 
+    "_", 
+    "_", 
+    ".webp", 
+    "pdf", 
+    "gm", 
+    "_", 
+    "fontmgr_iter", 
+    "pdf", 
+    "_", 
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "pdf", 
+    "skp", 
+    "_", 
+    "worldjournal", 
+    "pdf", 
+    "skp", 
+    "_", 
+    "desk_baidu.skp", 
+    "pdf", 
+    "skp", 
+    "_", 
+    "desk_wikipedia.skp", 
+    "--match", 
+    "~Threaded"
+  ], 
+  "Test-Win7-MSVC-ShuttleA-GPU-HD2000-x86-Debug-ANGLE": [
+    "--matrix", 
+    "0", 
+    "1", 
+    "1", 
+    "0", 
+    "--config", 
+    "565", 
+    "8888", 
+    "gpu", 
+    "upright-matrix-8888", 
+    "upright-matrix-gpu", 
+    "msaa16", 
     "pdf", 
     "serialize-8888", 
     "tiles_rt-8888", 
@@ -244,9 +1394,131 @@
     "--blacklist", 
     "gpu", 
     "_", 
+    "_", 
     "PANO_20121023_214540.jpg", 
     "msaa", 
     "_", 
-    "PANO_20121023_214540.jpg"
+    "_", 
+    "PANO_20121023_214540.jpg", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v4.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8v5.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb16-565pal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32-111110.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb32bf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgba32abf.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal4rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "pal8rletrns.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "rgb24largepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8os2v2-16.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "pal8oversizepal.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "4bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "8bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "24bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "subset", 
+    "32bpp-pixeldata-cropped.bmp", 
+    "_", 
+    "image", 
+    "decode", 
+    "Hopstarter-Mac-Folders-Apple.ico", 
+    "_", 
+    "image", 
+    "decode", 
+    "_", 
+    "_", 
+    "image", 
+    "subset", 
+    "_", 
+    "msaa16", 
+    "gm", 
+    "_", 
+    "colorwheelnative", 
+    "pdf", 
+    "gm", 
+    "_", 
+    "fontmgr_iter_factory"
   ]
 }
\ No newline at end of file
diff --git a/tools/dm_flags.py b/tools/dm_flags.py
index d00f74c..22ec8da 100755
--- a/tools/dm_flags.py
+++ b/tools/dm_flags.py
@@ -1,8 +1,15 @@
+#
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+
 #!/usr/bin/env python
 
 usage = '''
 Write extra flags to outfile for DM based on the bot name:
-  $ python dm_flags.py outfile Test-Mac10.9-MacMini6.2-HD4000-x86_64-Release
+  $ python dm_flags.py outfile Test-Ubuntu-GCC-GCE-CPU-AVX2-x86-Debug
 Or run self-tests:
   $ python dm_flags.py test
 '''
@@ -23,21 +30,36 @@
   args = []
 
   configs = ['565', '8888', 'gpu']
-  # The S4 crashes and the NP produces a long error stream when we run with
-  # MSAA.
-  if ('GalaxyS4'    not in bot and
-      'NexusPlayer' not in bot):
+
+  if 'Android' not in bot:
+    configs.extend(('upright-matrix-8888', 'upright-matrix-gpu'))
+    args.extend('--matrix 0 1 1 0'.split(' '))
+
+  if '-GCE-' in bot:
+    configs.append('sp-8888')
+
+  if 'TegraK1' in bot or 'GTX550Ti' in bot or 'GTX660' in bot or 'GT610' in bot:
     if 'Android' in bot:
-      configs.extend(['msaa4', 'nvprmsaa4'])
+      configs.append('nvprmsaa4')
     else:
-      configs.extend(['msaa16', 'nvprmsaa16'])
+      configs.append('nvprmsaa16')
+
+  # The S4 crashes and the NP produces a long error stream when we run with
+  # MSAA.  The Tegra2 and Tegra3 just don't support it.
+  if ('GalaxyS4'    not in bot and
+      'NexusPlayer' not in bot and
+      'Tegra3'      not in bot and
+      'iOS'         not in bot):
+    if 'Android' in bot:
+      configs.append('msaa4')
+    else:
+      configs.append('msaa16')
   # Runs out of memory on Android bots and Daisy.  Everyone else seems fine.
   if 'Android' not in bot and 'Daisy' not in bot:
     configs.append('pdf')
 
-  # Xoom and NP are running out of RAM when we run all these modes.  skia:3255
-  if ('Xoom'        not in bot and
-      'NexusPlayer' not in bot):
+  # NP is running out of RAM when we run all these modes.  skia:3255
+  if 'NexusPlayer' not in bot:
     configs.extend(mode + '-8888' for mode in
                    ['serialize', 'tiles_rt', 'pipe'])
     configs.append('tiles_rt-gpu')
@@ -46,38 +68,94 @@
   args.append('--config')
   args.extend(configs)
 
+  if 'GalaxyS' in bot:
+    args.extend(('--threads', '0'))
+
   blacklist = []
   # This image is too large to be a texture for many GPUs.
-  blacklist.extend('gpu _ PANO_20121023_214540.jpg'.split(' '))
-  blacklist.extend('msaa _ PANO_20121023_214540.jpg'.split(' '))
+  blacklist.extend('gpu _ _ PANO_20121023_214540.jpg'.split(' '))
+  blacklist.extend('msaa _ _ PANO_20121023_214540.jpg'.split(' '))
+
+  # Several of the newest version bmps fail on SkImageDecoder
+  blacklist.extend('_ image decode pal8os2v2.bmp'.split(' '))
+  blacklist.extend('_ image decode pal8v4.bmp'.split(' '))
+  blacklist.extend('_ image decode pal8v5.bmp'.split(' '))
+  blacklist.extend('_ image decode rgb16-565.bmp'.split(' '))
+  blacklist.extend('_ image decode rgb16-565pal.bmp'.split(' '))
+  blacklist.extend('_ image decode rgb32-111110.bmp'.split(' '))
+  blacklist.extend('_ image decode rgb32bf.bmp'.split(' '))
+  blacklist.extend('_ image decode rgba32.bmp'.split(' '))
+  blacklist.extend('_ image decode rgba32abf.bmp'.split(' '))
+  blacklist.extend('_ image decode rgb24largepal.bmp'.split(' '))
+  blacklist.extend('_ image decode pal8os2v2-16.bmp'.split(' '))
+  blacklist.extend('_ image decode pal8oversizepal.bmp'.split(' '))
+  blacklist.extend('_ image decode pal4rletrns.bmp'.split(' '))
+  blacklist.extend('_ image decode pal8rletrns.bmp'.split(' '))
+  blacklist.extend('_ image decode 4bpp-pixeldata-cropped.bmp'.split(' '))
+  blacklist.extend('_ image decode 8bpp-pixeldata-cropped.bmp'.split(' '))
+  blacklist.extend('_ image decode 24bpp-pixeldata-cropped.bmp'.split(' '))
+  blacklist.extend('_ image decode 32bpp-pixeldata-cropped.bmp'.split(' '))
+  blacklist.extend('_ image subset rgb24largepal.bmp'.split(' '))
+  blacklist.extend('_ image subset pal8os2v2-16.bmp'.split(' '))
+  blacklist.extend('_ image subset pal8oversizepal.bmp'.split(' '))
+  blacklist.extend('_ image subset 4bpp-pixeldata-cropped.bmp'.split(' '))
+  blacklist.extend('_ image subset 8bpp-pixeldata-cropped.bmp'.split(' '))
+  blacklist.extend('_ image subset 24bpp-pixeldata-cropped.bmp'.split(' '))
+  blacklist.extend('_ image subset 32bpp-pixeldata-cropped.bmp'.split(' '))
+
+  # New ico files that fail on SkImageDecoder
+  blacklist.extend('_ image decode Hopstarter-Mac-Folders-Apple.ico'.split(' '))
+
+  # Leon doesn't care about this, so why run it?
+  if 'Win' in bot:
+    blacklist.extend('_ image decode _'.split(' '))
+    blacklist.extend('_ image subset _'.split(' '))
+
+  # Certain gm's on win7 gpu and pdf are never finishing and keeping the test
+  # running forever
+  if 'Win7' in bot:
+    blacklist.extend('msaa16 gm _ colorwheelnative'.split(' '))
+    blacklist.extend('pdf gm _ fontmgr_iter_factory'.split(' '))
 
   # Drawing SKPs or images into GPU canvases is a New Thing.
   # It seems like we're running out of RAM on some Android bots, so start off
   # with a very wide blacklist disabling all these tests on all Android bots.
   if 'Android' in bot:  # skia:3255
-    blacklist.extend('gpu skp _ gpu image _ gpu subset _'.split(' '))
-    blacklist.extend('msaa skp _ msaa image _ gpu subset _'.split(' '))
+    blacklist.extend('gpu skp _ _ msaa skp _ _'.split(' '))
+    blacklist.extend('gpu image decode _ msaa image decode _'.split(' '))
+    blacklist.extend('gpu image subset _ msaa image subset _'.split(' '))
 
-  # PDF + .webp -> jumps depending on uninitialized memory.  skia:3505
   if 'Valgrind' in bot:
-    blacklist.extend('pdf _ .webp'.split(' '))
-    if 'Valgrind_GPU' in bot:
-      args.append('--nocpu')
-    elif 'Valgrind_CPU' in bot:
-      args.append('--nogpu')
+    # PDF + .webp -> jumps depending on uninitialized memory.  skia:3505
+    blacklist.extend('pdf _ _ .webp'.split(' '))
+    # These take 18+ hours to run.
+    blacklist.extend('pdf gm _ fontmgr_iter'.split(' '))
+    blacklist.extend('pdf _ _ PANO_20121023_214540.jpg'.split(' '))
+    blacklist.extend('pdf skp _ worldjournal'.split(' '))
+    blacklist.extend('pdf skp _ desk_baidu.skp'.split(' '))
+    blacklist.extend('pdf skp _ desk_wikipedia.skp'.split(' '))
+
+  if 'iOS' in bot:
+    blacklist.extend('gpu skp _ _ msaa skp _ _'.split(' '))
+    blacklist.extend('gpu image decode _ msaa image decode _'.split(' '))
+    blacklist.extend('gpu image subset _ msaa image subset _'.split(' '))
+    blacklist.extend('msaa16 gm _ tilemodesProcess'.split(' '))
+
+  if 'GalaxyS4' in bot:
+    # This occasionally runs forever. skia:3802
+    blacklist.extend('tiles_rt-gpu gm _ imagefilterscropped'.split(' '))
 
   if blacklist:
     args.append('--blacklist')
     args.extend(blacklist)
 
   match = []
-  if 'Alex' in bot:  # skia:2793
-    # This machine looks to be running out of heap.
-    # Running with fewer threads may help.
-    args.extend(['--threads', '1'])
   if 'Valgrind' in bot: # skia:3021
     match.append('~Threaded')
-  if 'Xoom' in bot or 'GalaxyS3' in bot:  # skia:1699
+  if 'TSAN' in bot: # skia:3562
+    match.append('~Math')
+
+  if 'GalaxyS3' in bot:  # skia:1699
     match.append('~WritePixels')
 
   # skia:3249: these images flakily don't decode on Android.
@@ -88,17 +166,13 @@
   if 'NexusPlayer' in bot:
     match.append('~ResourceCache')
 
+  if 'iOS' in bot:
+    match.append('~WritePixels')
+
   if match:
     args.append('--match')
     args.extend(match)
 
-  # Though their GPUs are interesting, these don't test anything on
-  # the CPU that other ARMv7+NEON bots don't test faster (N5).
-  if ('Nexus10'  in bot or
-      'Nexus7'   in bot or
-      'GalaxyS3' in bot or
-      'GalaxyS4' in bot):
-    args.append('--nocpu')
   return args
 cov_end = lineno()   # Don't care about code coverage past here.
 
@@ -107,14 +181,16 @@
   import coverage  # This way the bots don't need coverage.py to be installed.
   args = {}
   cases = [
-    'Test-Android-GalaxyS3-Mali400-Arm7-Debug',
-    'Test-Android-Nexus7-Tegra3-Arm7-Release',
-    'Test-Android-NexusPlayer-PowerVR-x86-Release',
-    'Test-Android-Xoom-Tegra2-Arm7-Release',
-    'Test-ChromeOS-Alex-GMA3150-x86-Debug',
-    'Test-Ubuntu12-ShuttleA-GTX550Ti-x86_64-Release-Valgrind_GPU',
-    'Test-Ubuntu14-GCE-NoGPU-x86_64-Release-Valgrind_CPU',
-    'Test-Win7-ShuttleA-HD2000-x86-Debug-ANGLE',
+    'Pretend-iOS-Bot',
+    'Test-Android-GCC-Nexus9-GPU-TegraK1-Arm64-Debug',
+    'Test-Android-GCC-GalaxyS3-GPU-Mali400-Arm7-Debug',
+    'Test-Android-GCC-GalaxyS4-GPU-SGX544-Arm7-Release',
+    'Test-Android-GCC-Nexus7-GPU-Tegra3-Arm7-Release',
+    'Test-Android-GCC-NexusPlayer-CPU-SSSE3-x86-Release',
+    'Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind',
+    'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-TSAN',
+    'Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-Valgrind',
+    'Test-Win7-MSVC-ShuttleA-GPU-HD2000-x86-Debug-ANGLE',
   ]
 
   cov = coverage.coverage()
diff --git a/tools/embed_resources.py b/tools/embed_resources.py
new file mode 100644
index 0000000..56d5982
--- /dev/null
+++ b/tools/embed_resources.py
@@ -0,0 +1,80 @@
+#!/usr/bin/python
+
+'''
+Copyright 2015 Google Inc.
+
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+'''
+
+import argparse
+
+
+def bytes_from_file(f, chunksize=8192):
+  while True:
+    chunk = f.read(chunksize)
+    if chunk:
+      for b in chunk:
+        yield ord(b)
+    else:
+      break
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      formatter_class=argparse.RawDescriptionHelpFormatter,
+      description='Convert resource files to embedded read only data.',
+      epilog='''The output (when compiled and linked) can be used as:
+struct SkEmbeddedResource {const uint8_t* data; const size_t size;};
+struct SkEmbeddedHeader {const SkEmbeddedResource* entries; const int count;};
+extern "C" SkEmbeddedHeader const NAME;''')
+  parser.add_argument('--align', default=1, type=int,
+                      help='minimum alignment (in bytes) of resource data')
+  parser.add_argument('--name', default='_resource', type=str,
+                      help='the name of the c identifier to export')
+  parser.add_argument('--input', required=True, type=argparse.FileType('rb'),
+                      nargs='+', help='list of resource files to embed')
+  parser.add_argument('--output', required=True, type=argparse.FileType('w'),
+                      help='the name of the cpp file to output')
+  args = parser.parse_args()
+
+  out = args.output.write;
+  out('#include "SkTypes.h"\n')
+
+  # Write the resources.
+  index = 0
+  for f in args.input:
+    out('static const uint8_t resource{0:d}[] SK_STRUCT_ALIGN({1:d}) = {{\n'
+        .format(index, args.align))
+    bytes_written = 0
+    bytes_on_line = 0
+    for b in bytes_from_file(f):
+      out(hex(b) + ',')
+      bytes_written += 1
+      bytes_on_line += 1
+      if bytes_on_line >= 32:
+        out('\n')
+        bytes_on_line = 0
+    out('};\n')
+    out('static const size_t resource{0:d}_size = {1:d};\n'
+        .format(index, bytes_written))
+    index += 1
+
+  # Write the resource entries.
+  out('struct SkEmbeddedResource { const uint8_t* d; const size_t s; };\n')
+  out('static const SkEmbeddedResource header[] = {\n')
+  index = 0
+  for f in args.input:
+    out('  {{ resource{0:d}, resource{0:d}_size }},\n'.format(index))
+    index += 1
+  out('};\n')
+  out('static const int header_count = {0:d};\n'.format(index))
+
+  # Export the resource header.
+  out('struct SkEmbeddedHeader {const SkEmbeddedResource* e; const int c;};\n')
+  out('extern "C" const SkEmbeddedHeader {0:s} = {{ header, header_count }};\n'
+      .format(args.name))
+
+
+if __name__ == "__main__":
+  main()
diff --git a/tools/flags/SkCommandLineFlags.cpp b/tools/flags/SkCommandLineFlags.cpp
index 6f1491b..a7fe6bd 100644
--- a/tools/flags/SkCommandLineFlags.cpp
+++ b/tools/flags/SkCommandLineFlags.cpp
@@ -11,6 +11,8 @@
 
 DEFINE_bool(undefok, false, "Silently ignore unknown flags instead of crashing.");
 
+template <typename T> static void ignore_result(const T&) {}
+
 bool SkFlagInfo::CreateStringFlag(const char* name, const char* shortName,
                                   SkCommandLineFlags::StringArray* pStrings,
                                   const char* defaultValue, const char* helpString) {
@@ -140,10 +142,10 @@
 }
 
 // Maximum line length for the help message.
-#define LINE_LENGTH 80
+#define LINE_LENGTH 72
 
 static void print_help_for_flag(const SkFlagInfo* flag) {
-    SkDebugf("\t--%s", flag->name().c_str());
+    SkDebugf("    --%s", flag->name().c_str());
     const SkString& shortName = flag->shortName();
     if (shortName.size() > 0) {
         SkDebugf(" or -%s", shortName.c_str());
@@ -160,7 +162,7 @@
     while (currLine < stop) {
         if (strlen(currLine) < LINE_LENGTH) {
             // Only one line length's worth of text left.
-            SkDebugf("\t\t%s\n", currLine);
+            SkDebugf("        %s\n", currLine);
             break;
         }
         int lineBreak = SkStrFind(currLine, "\n");
@@ -180,12 +182,12 @@
                 // Skip the space on the next line
                 gap = 1;
             }
-            SkDebugf("\t\t%.*s\n", spaceIndex, currLine);
+            SkDebugf("        %.*s\n", spaceIndex, currLine);
             currLine += spaceIndex + gap;
         } else {
             // the line break is within the limit. Break there.
             lineBreak++;
-            SkDebugf("\t\t%.*s", lineBreak, currLine);
+            SkDebugf("        %.*s", lineBreak, currLine);
             currLine += lineBreak;
         }
     }
@@ -256,7 +258,7 @@
             if (helpFlags.count() > 0) {
                 SkDebugf("Requested help for unrecognized flags:\n");
                 for (int k = 0; k < helpFlags.count(); k++) {
-                    SkDebugf("\t--%s\n", helpFlags[k]);
+                    SkDebugf("    --%s\n", helpFlags[k]);
                 }
             }
             helpPrinted = true;
@@ -284,7 +286,8 @@
                             // Add all arguments until another flag is reached.
                             while (i+1 < argc) {
                                 char* end = NULL;
-                                (void)strtod(argv[i+1], &end); // Negative numbers aren't flags.
+                                // Negative numbers aren't flags.
+                                ignore_result(strtod(argv[i+1], &end));
                                 if (end == argv[i+1] && SkStrStartsWith(argv[i+1], '-')) {
                                     break;
                                 }
@@ -313,8 +316,10 @@
                         i++;  // skip YES
                 } else
 #endif
-                if (!FLAGS_undefok) {
-                    SkDebugf("Got unknown flag \"%s\". Exiting.\n", argv[i]);
+                if (FLAGS_undefok) {
+                    SkDebugf("FYI: ignoring unknown flag '%s'.\n", argv[i]);
+                } else {
+                    SkDebugf("Got unknown flag '%s'. Exiting.\n", argv[i]);
                     exit(-1);
                 }
             }
diff --git a/tools/flags/SkCommonFlags.cpp b/tools/flags/SkCommonFlags.cpp
index d05f764..1a9018b 100644
--- a/tools/flags/SkCommonFlags.cpp
+++ b/tools/flags/SkCommonFlags.cpp
@@ -7,9 +7,10 @@
 
 #include "SkCommonFlags.h"
 
-DEFINE_string(config, "565 8888 gpu nonrendering angle nvprmsaa4 hwui ",
-              "Options: 565 8888 pdf gpu nonrendering msaa4 msaa16 nvprmsaa4 nvprmsaa16 "
-              "gpudft gpunull gpudebug angle mesa (and many more)");
+DEFINE_string(config, "565 8888 gpu nonrendering angle hwui ", "Options: "
+              "565 8888 angle debug gpu gpudebug gpudft gpunull hwui mesa "
+              "msaa16 msaa4 nonrendering null nullgpu nvprmsaa16 nvprmsaa4 "
+              "pdf skp svg xps (and maybe more)");
 
 DEFINE_bool(cpu, true, "master switch for running CPU-bound work.");
 
diff --git a/tools/iOSShell.cpp b/tools/iOSShell.cpp
index 8656e64..1f5f2da 100644
--- a/tools/iOSShell.cpp
+++ b/tools/iOSShell.cpp
@@ -61,7 +61,6 @@
 
 IOS_launch_type set_cmd_line_args(int argc, char *argv[], const char* resourceDir) {
     SkCommandLineFlags::Parse(argc, argv);
-    SetResourcePath(resourceDir);
     if (FLAGS_nanobench) {
         return nanobench_main() ? kError_iOSLaunchType : kTool_iOSLaunchType;
     }
diff --git a/tools/iOSShell.h b/tools/iOSShell.h
index 9a9608f..eaecba5 100644
--- a/tools/iOSShell.h
+++ b/tools/iOSShell.h
@@ -20,10 +20,10 @@
     virtual ~ShellWindow();
 
 protected:
-    void onSizeChange() SK_OVERRIDE;
+    void onSizeChange() override;
 
     virtual bool onDispatchClick(int x, int y, Click::State, void* owner,
-                                 unsigned modi) SK_OVERRIDE;
+                                 unsigned modi) override;
 
 private:
     typedef SkOSWindow INHERITED;
diff --git a/tools/install_dependencies.sh b/tools/install_dependencies.sh
index ae98884..037b894 100755
--- a/tools/install_dependencies.sh
+++ b/tools/install_dependencies.sh
@@ -10,21 +10,35 @@
 
 set -e
 
+# Return 0 iff all package name arguments are installed.
+dpkg_all_installed() {
+    for arg; do
+        if !(dpkg-query -W -f'${Status}' "$arg" 2>/dev/null | \
+            grep -q "ok installed"); then
+            return 1
+        fi
+    done
+    return 0
+}
+
 if command -v lsb_release > /dev/null ; then
     case $(lsb_release -i -s) in
         Ubuntu)
-            sudo apt-get install \
-                build-essential \
-		libfreetype6-dev \
-		libfontconfig-dev \
-		libpng12-dev \
-		libgif-dev \
-		libqt4-dev \
-		clang
-	    if [ $(lsb_release -r -s) = '14.04' ] ; then
-		sudo apt-get install \
-		    ninja-build
-	    fi
+            PACKAGES=$(cat<<-EOF
+		build-essential
+		libfreetype6-dev
+		libfontconfig-dev
+		libpng12-dev
+		libgif-dev
+		libqt4-dev
+		EOF
+            )
+           if [ $(lsb_release -r -s) = '14.04' ] ; then
+               PACKAGES="${PACKAGES} ninja-build"
+            fi
+            if ! dpkg_all_installed $PACKAGES; then
+                sudo apt-get install $PACKAGES
+            fi
             exit
             ;;
     esac
diff --git a/tools/lua/scrape.lua b/tools/lua/scrape.lua
index 6270188..4f4adbf 100644
--- a/tools/lua/scrape.lua
+++ b/tools/lua/scrape.lua
@@ -59,7 +59,7 @@
     end
 
     if false and t.verb == "drawPath" then
-        local pred, r1, r2, d1, d2 = t.path:isNestedRects()
+        local pred, r1, r2, d1, d2 = t.path:isNestedFillRects()
         
         if pred then
             print("drawRect_Nested", tostr(r1), tostr(r2), d1, d2)
diff --git a/tools/nanobench_flags.json b/tools/nanobench_flags.json
index c98c2e1..e90f217 100644
--- a/tools/nanobench_flags.json
+++ b/tools/nanobench_flags.json
@@ -1,5 +1,5 @@
 {
-  "Perf-Android-GalaxyS3-Mali400-Arm7-Release": [
+  "Perf-Android-GCC-NexusPlayer-GPU-PowerVR-x86-Release": [
     "--scales", 
     "1.0", 
     "1.1", 
@@ -10,13 +10,11 @@
     "nonrendering", 
     "angle", 
     "hwui", 
-    "msaa4", 
-    "nvprmsaa4", 
     "--match", 
     "~blurroundrect", 
     "~patch_grid", 
     "~desk_carsvg", 
-    "--nocpu"
+    "~desk_unicodetable"
   ], 
   "Perf-Android-Nexus7-Tegra3-Arm7-Release": [
     "--scales", 
@@ -34,7 +32,7 @@
     "--match", 
     "skp"
   ], 
-  "Test-Ubuntu12-ShuttleA-GTX550Ti-x86_64-Release-Valgrind_GPU": [
+  "Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind": [
     "--scales", 
     "1.0", 
     "1.1", 
@@ -50,10 +48,9 @@
     "--loops", 
     "1", 
     "--samples", 
-    "1", 
-    "--nocpu"
+    "1"
   ], 
-  "Test-Ubuntu14-GCE-NoGPU-x86_64-Release-Valgrind_CPU": [
+  "Test-Win7-MSVC-ShuttleA-GPU-HD2000-x86-Debug-ANGLE": [
     "--scales", 
     "1.0", 
     "1.1", 
@@ -66,25 +63,10 @@
     "hwui", 
     "msaa16", 
     "nvprmsaa16", 
-    "--loops", 
-    "1", 
-    "--samples", 
-    "1", 
-    "--nogpu"
-  ], 
-  "Test-Win7-ShuttleA-HD2000-x86-Debug-ANGLE": [
-    "--scales", 
-    "1.0", 
-    "1.1", 
-    "--config", 
-    "565", 
-    "8888", 
-    "gpu", 
-    "nonrendering", 
-    "angle", 
-    "hwui", 
-    "msaa16", 
-    "nvprmsaa16", 
+    "--benchTileW", 
+    "256", 
+    "--benchTileH", 
+    "256", 
     "--match", 
     "~gradient", 
     "~etc1bitmap"
diff --git a/tools/nanobench_flags.py b/tools/nanobench_flags.py
index 81cd8ce..fb6d863 100755
--- a/tools/nanobench_flags.py
+++ b/tools/nanobench_flags.py
@@ -1,8 +1,15 @@
+#
+# Copyright 2015 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+
 #!/usr/bin/env python
 
 usage = '''
 Write extra flags to outfile for nanobench based on the bot name:
-  $ python nanobench_flags.py outfile Perf-Android-GalaxyS3-Mali400-Arm7-Release
+  $ python nanobench_flags.py outfile Perf-Android-GCC-GalaxyS3-GPU-Mali400-Arm7-Release
 Or run self-tests:
   $ python nanobench_flags.py test
 '''
@@ -40,10 +47,10 @@
     # Don't care about Valgrind performance.
     args.extend(['--loops',   '1'])
     args.extend(['--samples', '1'])
-    if 'Valgrind_GPU' in bot:
-      args.append('--nocpu')
-    elif 'Valgrind_CPU' in bot:
-      args.append('--nogpu')
+
+  if 'HD2000' in bot:
+    args.extend(['--benchTileW', '256'])
+    args.extend(['--benchTileH', '256'])
 
   match = []
   if 'Android' in bot:
@@ -55,14 +62,13 @@
     match.extend(['~gradient', '~etc1bitmap'])  # skia:2895
   if 'Nexus7' in bot:
     match = ['skp']  # skia:2774
+  if 'NexusPlayer' in bot:
+    match.append('~desk_unicodetable')
+
   if match:
     args.append('--match')
     args.extend(match)
 
-
-  if ('GalaxyS3' in bot or
-      'GalaxyS4' in bot):
-    args.append('--nocpu')
   return args
 cov_end = lineno()   # Don't care about code coverage past here.
 
@@ -71,11 +77,10 @@
   import coverage  # This way the bots don't need coverage.py to be installed.
   args = {}
   cases = [
-    'Perf-Android-GalaxyS3-Mali400-Arm7-Release',
     'Perf-Android-Nexus7-Tegra3-Arm7-Release',
-    'Test-Ubuntu14-GCE-NoGPU-x86_64-Release-Valgrind_CPU',
-    'Test-Ubuntu12-ShuttleA-GTX550Ti-x86_64-Release-Valgrind_GPU',
-    'Test-Win7-ShuttleA-HD2000-x86-Debug-ANGLE',
+    'Perf-Android-GCC-NexusPlayer-GPU-PowerVR-x86-Release',
+    'Test-Ubuntu-GCC-ShuttleA-GPU-GTX550Ti-x86_64-Release-Valgrind',
+    'Test-Win7-MSVC-ShuttleA-GPU-HD2000-x86-Debug-ANGLE',
   ]
 
   cov = coverage.coverage()
diff --git a/tools/pathops_sorter.htm b/tools/pathops_sorter.htm
index a77abd6..3411505 100644
--- a/tools/pathops_sorter.htm
+++ b/tools/pathops_sorter.htm
@@ -6,1180 +6,27 @@
     <title></title>
 <div style="height:0">
 
-<div id="quad1">
-{{3.13,2.74}, {1.08,4.62}, {3.71,0.94}} 
-{{3.13,2.74}, {7.99,2.75}, {8.27,1.96}} 
-</div>
-
-<div id="quad2">
-{{4.838888984361574,4.399276078363981}, {5.947577332875065,2.02910379790342}, {3.8092258119951885,2.108659563498883}}
-{{4.838888984361574,4.399276078363981}, {6.192910293864926,1.7797920604914939}, {3.3638348513490293,1.4969462106891218}}
-</div>
-
-<div id="quad3">
-{{4.838888984361574,4.399276078363981}, {5.962263714769107,1.654601059605365}, {3.8789861259918847,2.8650082310420126}}
-{{4.838888984361574,4.399276078363981}, {6.192910293864926,1.7797920604914939}, {3.3638348513490293,1.4969462106891218}}
-</div>
-
-<div id="quad4">
-{{4.838888984361574,4.399276078363981}, {5.77868394109359,1.852867215174923}, {3.915702080726988,2.1820914729690903}}
-{{4.838888984361574,4.399276078363981}, {6.681232491841801,2.5287975370876032}, {3.3638348513490293,1.4969462106891218}}
-</div>
-
-<div id="quad5">
-{{4.838888984361574,4.399276078363981}, {6.082937568878361,1.9951156645288415}, {3.915702080726988,2.1820914729690903}}
-{{4.838888984361574,4.399276078363981}, {6.681232491841801,2.5287975370876032}, {3.3638348513490293,1.4969462106891218}}
-</div>
-
-<div id="quad6">
-{{4.898159171592373,4.367665311840888}, {6.695396170263287,1.769888953051804}, {3.6312051820191513,2.727377195492444}}
-{{4.898159171592373,4.367665311840888}, {6.961778044734251,2.4813813873029633}, {3.3638348513490293,1.4969462106891218}}
-</div>
-
-<div id="quad7">
-{{4.838888984361574,4.399276078363981}, {3.012741870322956,2.449520433298304}, {5.140619283496844,2.110967248292131}}
-{{4.838888984361574,4.399276078363981}, {2.804962246947524,2.232446600933607}, {6.60393841996606,2.077794045550955}}
-</div>
-
-<div id="quad8">
-{{4.838888984361574,4.399276078363981}, {3.1707957029384213,2.607574265913769}, {4.626944327496585,2.2848264641691425}}
-{{4.838888984361574,4.399276078363981}, {2.804962246947524,2.232446600933607}, {6.60393841996606,2.077794045550955}}
-</div>
-
-<div id="quad9">
-{{4.838888984361574,4.399276078363981}, {3.463749932092156,2.935940544745428}, {5.161344349908893,2.4940794849932386}}
-{{4.838888984361574,4.399276078363981}, {2.804962246947524,2.232446600933607}, {6.60393841996606,2.077794045550955}}
-</div>
-
-<div id="quad10">
-{{4.838888984361574,4.399276078363981}, {5.82508561259808,2.495362604119041}, {3.4377993053488463,2.7132154732530362}}
-{{4.838888984361574,4.399276078363981}, {6.192910293864926,1.7797920604914939}, {2.435268584733173,1.817005221735438}}
-</div>
-
-<div id="cubic1">
-{{0,0}, {1,0}, {0,1}, {1,1}}
-{{0,0}, {2,0}, {0,2}, {2,2}}
-</div>
-
-<div id="cubic2" >
-{{0.4655213647959181,1.5608657525510201}, {0.6599868463010203,0.4290098852040817}, {2.473652742346939,1.2464524872448977}, {1.8511738679846936,0.5344786352040818}}
-{{0.4655213647959181,1.5608657525510201}, {0.3250358737244896,0.819226323341837}, {1.4399214764030612,0.3318817761479596}, {1.2703414571528546,0.9081465322144181}}
-</div> 
-
-<div id="quad11">
-{{-378.22698974609375, -987.8935546875}, {-47.53326416015625, 482.7139892578125}, {-626.4708251953125, -338.62969970703125}}
-{{-378.22698974609375, -987.8935546875}, {-847.94854736328125, -861.42230224609375}, {-390.9146728515625, 402.08740234375}}
-</div>
-
-<div id="quad12">
-{{-173.3448486328125, -962.89422607421875}, {-778.321533203125, -161.47637939453125}, {-196.77374267578125, -736.40155029296875}}
-{{-173.3448486328125, -962.89422607421875}, {652.3017578125, -400.67816162109375}, {-386.7855224609375, 361.1614990234375}}
-</div>
-
-<div id="quad13">
-{{{-968.181396484375, 544.0128173828125}, {592.2825927734375, 870.552490234375}, {593.435302734375, 557.8828125}}}
-{{{-968.181396484375, 544.0128173828125}, {593.677001953125, 865.5810546875}, {-66.57171630859375, -847.849853515625}}}
-</div>
-
-<div id="quad14">
-{{{769.693115234375, -626.35089111328125}, {6.60491943359375, -210.43756103515625}, {-898.26654052734375, -17.76312255859375}}}
-{{{769.693115234375, -626.35089111328125}, {192.8486328125, 609.8062744140625}, {888.317626953125, -551.27215576171875}}}
-</div>
-
-<div id="quad15">
-{{{187.410400390625, -343.557373046875}, {-752.7930908203125, 431.57177734375}, {387.663330078125, 701.281982421875}}}
-{{{187.410400390625, -343.557373046875}, {-86.16302490234375, -366.027099609375}, {-468.3883056640625, -25.736572265625}}}
-</div>
-
-<div id="quad16">
-{{{-353.9388427734375, 76.8973388671875}, {-36.00189208984375, 282.289306640625}, {-531.37969970703125, 683.95751953125}}}
-{{{-353.9388427734375, 76.8973388671875}, {-779.3529052734375, 509.6165771484375}, {-662.34088134765625, 124.4027099609375}}}
-</div>
-
-<div id="quad17">
-{{{-657.0289306640625, 681.611083984375}, {-991.8365478515625, 964.4644775390625}, {-843.3585205078125, 904.47998046875}}}
-{{{-657.0289306640625, 681.611083984375}, {-763.1571044921875, 39.1097412109375}, {618.2041015625, 840.6429443359375}}}
-</div>
-
-<div id="quad18">
-{{{-609.406005859375, -684.37506103515625}, {766.4923095703125, 583.657958984375}, {-912.6832275390625, -949.553466796875}}}
-{{{-609.406005859375, -684.37506103515625}, {774.140380859375, 82.2415771484375}, {540.9007568359375, -136.982666015625}}}
-</div>
-
-<div id="quad19">
-{{{-657.0289306640625, 681.611083984375}, {-991.8365478515625, 964.4644775390625}, {-843.3585205078125, 904.47998046875}}}
-{{{-657.0289306640625, 681.611083984375}, {-763.1571044921875, 39.1097412109375}, {618.2041015625, 840.6429443359375}}}
-</div>
-
-<div id="quad20">
-{{{123.2955322265625, -577.799560546875}, {-491.892578125, 704.91748046875}, {478.03759765625, -951.92333984375}}}
-{{{123.2955322265625, -577.799560546875}, {-550.6966552734375, 812.216796875}, {-816.3184814453125, -705.0025634765625}}}
-</div>
-
-<div id="quad21">
-{{{123.2955322265625, -577.799560546875}, {-481.892578125, 704.91748046875}, {478.03759765625, -951.92333984375}}}
-{{{123.2955322265625, -577.799560546875}, {-550.6966552734375, 812.216796875}, {-816.3184814453125, -705.0025634765625}}}
-</div>
-
-<div id="quad22">
-{{{187.410400390625, -343.557373046875}, {-752.7930908203125, 431.57177734375}, {387.663330078125, 701.281982421875}}}
-{{{187.410400390625, -343.557373046875}, {-86.16302490234375, -366.027099609375}, {-468.3883056640625, -25.736572265625}}}
-</div>
-
-<div id="quad23">
-{{{-341.26922607421875, 964.1964111328125}, {883.2567138671875, 812.7301025390625}, {286.0372314453125, 94.979248046875}}}
-{{{-341.26922607421875, 964.1964111328125}, {-158.90765380859375, 597.1875}, {-282.2255859375, 262.430908203125}}}
-</div>
-
-<div id="quad24">
-{{{123.2955322265625, -577.799560546875}, {-481.892578125, 704.91748046875}, {478.03759765625, -951.92333984375}}}
-{{{123.2955322265625, -577.799560546875}, {-550.6966552734375, 812.216796875}, {-816.3184814453125, -705.0025634765625}}}
-{{{417.3499131065152, -577.799560546875}, {417.3499131065152, -699.60087482901156}, {331.22337542585541, -785.72740374616797}}}
-</div>
-
-<div id="quad25">
-{{{922.6107177734375, 291.412109375}, {-939.361572265625, 589.8492431640625}, {-515.70941162109375, 120.2764892578125}}}
-{{{922.6107177734375, 291.412109375}, {148.5115966796875, -751.42095947265625}, {-347.47503662109375, 331.1798095703125}}}
-{{{922.6107177734375, -143.9114969433939}, {742.29377357777753, -143.9114969433939}, {614.79044900323777, -16.408159395199732}}}
-{{{487.2871114550436, 291.412109375}, {487.2871114550436, 471.72905357065997}, {614.79044900323777, 599.23237814519973}}}
-</div>
-
-<div id="quad26">
-{{{187.410400390625, -343.557373046875}, {-752.7930908203125, 431.57177734375}, {387.663330078125, 701.281982421875}}}
-{{{187.410400390625, -343.557373046875}, {-86.16302490234375, -366.027099609375}, {-468.3883056640625, -25.736572265625}}}
-{{{33.221887415632978, -343.557373046875}, {33.221887415632978, -279.69039894717827}, {78.38265915086852, -234.52963180711851}}}
-</div>
-
-<div id="quad27">
-{{{-173.3448486328125, -962.89422607421875}, {-778.321533203125, -161.47637939453125}, {-196.77374267578125, -736.40155029296875}}}
-{{{-173.3448486328125, -962.89422607421875}, {652.3017578125, -400.67816162109375}, {-386.7855224609375, 361.1614990234375}}}
-{{{-270.84959533883426, -865.38947936819704}, {-230.46180860703427, -825.00168852687921}, {-173.3448486328125, -825.00168852687921}}}
-{{{-75.840101926790737, -865.38947936819704}, {-35.4523110854729, -905.77726609999695}, {-35.4523110854729, -962.89422607421875}}}
-</div>
-
-<div id="quad28">
-{{{344.2755126953125, -689.900390625}, {743.6728515625, 512.8448486328125}, {928.598388671875, 111.946044921875}}}
-{{{344.2755126953125, -689.900390625}, {-950.03106689453125, -511.25741577148437}, {850.8173828125, 798.4874267578125}}}
-{{{344.2755126953125, -689.900390625}, {850.8173828125, 798.4874267578125}}}
-{{{344.2755126953125, -689.900390625}, {391.39917554828793, -551.43545842779145}}}
-</div>
-
-<div id="quad29">
-{{{351.8946533203125, 512.8131103515625}, {-294.22332763671875, 183.2200927734375}, {624.4842529296875, 862.0753173828125}}}
-{{{351.8946533203125, 512.8131103515625}, {489.1907958984375, -543.4212646484375}, {-432.7445068359375, 812.5205078125}}}
-</div>
-
-<div id="quad30">
-{{{627.6910400390625, 81.144287109375}, {168.9248046875, -211.72735595703125}, {-61.57086181640625, 915.171875}}}
-{{{627.6910400390625, 81.144287109375}, {918.159423828125, -325.468994140625}, {359.0523681640625, 817.4888916015625}}}
-{{{235.78221371860315, 81.144287109375}, {235.78221371860315, 243.47824037936314}, {350.56965608373537, 358.26567106470213}}},
-</div>
-
-<div id="quad31">
-{{{178.1549072265625, 62.724609375}, {541.3643798828125, 223.823486328125}, {-446.77471923828125, -15.990478515625}}}
-{{{178.1549072265625, 62.724609375}, {-347.14031982421875, -834.27191162109375}, {-495.13888549804687, 96.476806640625}}}
-</div>
-
-<div id="quad32">
-{{{-809.41009521484375, 370.4566650390625}, {622.44677734375, -166.97119140625}, {-285.6748046875, 333.81005859375}}},
-{{{-809.41009521484375, 370.4566650390625}, {-110.36346435546875, -656.96044921875}, {906.4796142578125, 530.2061767578125}}}
-</div>
-
-<div id="quad33">
-{{{-918.58624267578125, 653.6695556640625}, {-639.37548828125, 61.493896484375}, {-198.9605712890625, 243.704345703125}}},
-{{{-918.58624267578125, 653.6695556640625}, {-302.093505859375, -107.10955810546875}, {696.4962158203125, 600.738525390625}}}
-</div>
-
-<div id="quad34">
-{{{-610.4193115234375, 861.173095703125}, {403.3203125, 215.3988037109375}, {-373.5546875, 179.88134765625}}},
-{{{-610.4193115234375, 861.173095703125}, {-757.244140625, -222.137451171875}, {705.892822265625, 87.4090576171875}}}
-</div>
-
-<div id="quad35">
-{{{282.5767822265625, -529.4022216796875}, {392.0968017578125, 768.1014404296875}, {712.11572265625, 189.19677734375}}},
-{{{282.5767822265625, -529.4022216796875}, {699.360595703125, 465.6171875}, {438.5755615234375, 125.5230712890625}}}
-</div>
-
-<div id="quad36">
-{{{-170.1510009765625, -184.905517578125}, {654.734130859375, 120.339599609375}, {-470.98443603515625, -69.4737548828125}}},
-{{{-170.1510009765625, -184.905517578125}, {-500.9822998046875, -148.40911865234375}, {-446.35821533203125, -840.5694580078125}}}
-</div>
-
-<div id="quad37">
-{{{-119.55023193359375, -39.2008056640625}, {-618.14306640625, -620.1419677734375}, {-779.53790283203125, -681.9923095703125}}},
-{{{-119.55023193359375, -39.2008056640625}, {365.968994140625, 55.4974365234375}, {98.1297607421875, -192.474609375}}}
-</div>
-
-<div id="quad38">
-{{{607.9136962890625, 484.1448974609375}, {280.619140625, 982.736572265625}, {-577.5596923828125, 798.9134521484375}}},
-{{{607.9136962890625, 484.1448974609375}, {374.318115234375, -590.5146484375}, {-258.30438232421875, 592.958984375}}}
-</div>
-
-<div id="quad39">
-{{{-491.48846435546875, -470.9105224609375}, {109.7149658203125, -989.5384521484375}, {-275.900390625, 657.1920166015625}}},
-{{{-491.48846435546875, -470.9105224609375}, {-796.935791015625, 191.326171875}, {-852.120849609375, 62.06005859375}}}
-</div>
-
-<div id="quad40">
-{{{-872.76458740234375, -163.30078125}, {723.6697998046875, 177.8204345703125}, {206.470703125, 147.9564208984375}}},
-{{{-872.76458740234375, -163.30078125}, {556.937744140625, 715.4345703125}, {627.348388671875, 77.0643310546875}}}
-</div>
-
-<div id="quad108">
-{{{282.5767822265625, -529.4022216796875}, {392.0968017578125, 768.1014404296875}, {712.11572265625, 189.19677734375}}},
-{{{282.5767822265625, -529.4022216796875}, {699.360595703125, 465.6171875}, {438.5755615234375, 125.5230712890625}}}
-</div>
-
-<div id="quad159">
-{{{-868.3076171875, -212.74591064453125}, {-208.84014892578125, -57.353515625}, {393.79736328125, -986.03607177734375}}},
-{{{-868.3076171875, -212.74591064453125}, {371.0980224609375, -960.9017333984375}, {-236.2821044921875, -441.20074462890625}}}
-</div>
-
-<div id="quad212">
-{{{-610.4193115234375, 861.173095703125}, {403.3203125, 215.3988037109375}, {-373.5546875, 179.88134765625}}},
-{{{-610.4193115234375, 861.173095703125}, {-757.244140625, -222.137451171875}, {705.892822265625, 87.4090576171875}}}
-</div>
-
-<div id="quad232">
-{{{766.497802734375, 675.660400390625}, {639.0235595703125, 351.4776611328125}, {345.9315185546875, 624.685791015625}}},
-{{{766.497802734375, 675.660400390625}, {-901.72650146484375, 923.99169921875}, {755.665283203125, 416.728759765625}}}
-</div>
-
-<div id="quad379">
-{{{-872.76458740234375, -163.30078125}, {723.6697998046875, 177.8204345703125}, {206.470703125, 147.9564208984375}}},
-{{{-872.76458740234375, -163.30078125}, {556.937744140625, 715.4345703125}, {627.348388671875, 77.0643310546875}}}
-</div>
-
-<div id="quad413">
-{{{-127.60784912109375, 384.614990234375}, {-184.46685791015625, 717.5728759765625}, {-981.56524658203125, -827.18109130859375}}},
-{{{-127.60784912109375, 384.614990234375}, {-125.78131103515625, 751.187744140625}, {562.529541015625, -277.5535888671875}}}
-</div>
-
-<div id="quad179">
-{{{-595.956298828125, -113.24383544921875}, {-730.611572265625, 481.5323486328125}, {505.58447265625, -504.9130859375}}},
-{{{-595.956298828125, -113.24383544921875}, {-971.0836181640625, -849.73907470703125}, {-32.39227294921875, -906.3277587890625}}}
-</div>
-
-<div id="quad584">
-{{{-406.65435791015625, 599.96630859375}, {-566.71881103515625, -400.65362548828125}, {-486.0682373046875, 100.34326171875}}},
-{{{-406.65435791015625, 599.96630859375}, {799.783935546875, 992.77783203125}, {180.6688232421875, -490.0054931640625}}}
-</div>
-
-<div id="quad653">
-{{{-46.6143798828125, 164.224853515625}, {-161.7724609375, 327.61376953125}, {168.5106201171875, -948.4150390625}}},
-{{{-46.6143798828125, 164.224853515625}, {412.9364013671875, -199.26715087890625}, {-278.044677734375, 472.3961181640625}}}
-</div>
-
-<div id="quad809">
-{{{-176.8541259765625, -275.9761962890625}, {-723.969482421875, -7.4718017578125}, {931.6959228515625, 231.6737060546875}}},
-{{{-176.8541259765625, -275.9761962890625}, {-250.86737060546875, -748.8143310546875}, {-96.77099609375, -287.76336669921875}}}
-</div>
-
-<div id="quad14a">
-{{{-609.406005859375, -684.37506103515625}, {766.4923095703125, 583.657958984375}, {-912.6832275390625, -949.553466796875}}},
-{{{-609.406005859375, -684.37506103515625}, {774.140380859375, 82.2415771484375}, {540.9007568359375, -136.982666015625}}}
-</div>
-
-<div id="quad22a">
-{{{-728.5626220703125, 141.134521484375}, {749.9122314453125, -645.93359375}, {67.1751708984375, -285.85528564453125}}},
-{{{-728.5626220703125, 141.134521484375}, {-841.0341796875, -988.058349609375}, {34.87939453125, -489.359130859375}}}
-{{{276.48354206343231, -395.24293552482953}, {-728.5626220703125, 141.134521484375}}}
-{{{fX=97.702285839737073, -301.95147049201717}, {-728.5626220703125, 141.134521484375}}}
-{{{fX=-52.525628917174856, -536.31069276053427}, {-728.5626220703125, 141.134521484375}}}
-{{{fX=-5.2463328209585285, -511.63085965304060}, {-728.5626220703125, 141.134521484375}}}
-</div>
-
-<div id="quad77">
-{{{383.7933349609375, -397.5057373046875}, {480.7408447265625, 92.927490234375}, {690.7930908203125, -267.44964599609375}}},
-{{{383.7933349609375, -397.5057373046875}, {83.3685302734375, 619.781005859375}, {688.14111328125, 416.241455078125}}}
-</div>
-
-<div id="quad94">
-{{{627.6910400390625, 81.144287109375}, {168.9248046875, -211.72735595703125}, {-61.57086181640625, 915.171875}}},
-{{{627.6910400390625, 81.144287109375}, {918.159423828125, -325.468994140625}, {359.0523681640625, 817.4888916015625}}}
-{{{564.43435948662466, 47.034527772832369}, {627.6910400390625, 81.144287109375}}}
-{{{699.34014109378302, 79.147174806567705}, {627.6910400390625, 81.144287109375}}}
-</div>
-
-<div id="quad4a">
-{{{187.410400390625, -343.557373046875}, {-752.7930908203125, 431.57177734375}, {387.663330078125, 701.281982421875}}},
-{{{187.410400390625, -343.557373046875}, {-86.16302490234375, -366.027099609375}, {-468.3883056640625, -25.736572265625}}}
-</div>
-
-<div id="quad0">
-{{{-708.0077926931004413, -154.6166947224404566}, {-701.0429781735874712, -128.8517387364408933}, {505.58447265625, -504.9130859375}}},
-{{{-708.0077926931004413, -154.6166947224404566}, {-721.5125661899801344, -174.4028951148648048}, {-32.39227294921875, -906.3277587890625}}}
-{{{-707.8363172079705237, -154.25350453766481}, {-708.0077926931004413, -154.6166947224404566}}}
-{{{-708.1792267111628689, -154.9799046892118213}, {-708.0077926931004413, -154.6166947224404566}}}
-</div>
-
-<div id="quad999">
-{{{-708.00779269310044, -154.61669472244046}, {-707.92342686353186, -154.30459999551294}, {505.58447265625, -504.9130859375}}},
-{{{-708.00779269310044, -154.61669472244046}, {-708.1713780141481, -154.85636789757655}, {-32.39227294921875, -906.3277587890625}}}
-{{{-708.0077672218041, -154.61664072892336}, {-708.00779269310044, -154.61669472244046}}}
-{{{-708.00781827681976, -154.61674895426012}, {-708.00779269310044, -154.61669472244046}}}
-</div>
-
-<div id="quad113">
-{{{425.018310546875, -866.61865234375}, {-918.76531982421875, 209.05322265625}, {964.34716796875, 199.52587890625}}},
-{{{425.018310546875, -866.61865234375}, {703.10693359375, -955.0738525390625}, {-952.24664306640625, -717.94775390625}}}
-</div>
-
-<div id="quad136">
-{{{178.1549072265625, 62.724609375}, {541.3643798828125, 223.823486328125}, {-446.77471923828125, -15.990478515625}}},
-{{{178.1549072265625, 62.724609375}, {-347.14031982421875, -834.27191162109375}, {-495.138885498046875, 96.476806640625}}}
-</div>
-
-<div id="quad206">
-{{{-503.007415771484375, -318.59490966796875}, {-798.330810546875, -881.21630859375}, {-127.2027587890625, 769.6160888671875}}},
-{{{-503.007415771484375, -318.59490966796875}, {-153.6217041015625, -776.896728515625}, {-378.43701171875, -296.3197021484375}}}
-{{{-468.9176053311167607, -89.39573455985038208}, {-503.007415771484375, -318.59490966796875}}}
-{{{-356.1573846604815685, -497.6768266540607328}, {-503.007415771484375, -318.59490966796875}}}
-{{{-559.0376987487186398, -420.2054253473417589}, {-503.007415771484375, -318.59490966796875}}}
-{{{-431.6586315464865606, -409.8353728177644371}, {-503.007415771484375, -318.59490966796875}}}
-</div>
-
-<div id="quad640">
-{{{412.260498046875, 49.193603515625}, {838.97900390625, 86.9951171875}, {427.7896728515625, -605.6881103515625}}},
-{{{412.260498046875, 49.193603515625}, {-995.54583740234375, 990.032470703125}, {-881.18670654296875, 461.211669921875}}}
-</div>
-
-<div id="quad3160">
-{{{426.645751953125, 813.79150390625}, {-387.23828125, -588.89483642578125}, {792.4261474609375, -704.4637451171875}}},
-{{{426.645751953125, 813.79150390625}, {19.24896240234375, -416.09906005859375}, {233.8497314453125, 350.778564453125}}}
-</div>
-
-<div id="quad35237">
-{{{-770.8492431640625, 948.2369384765625}, {-853.37066650390625, 972.0301513671875}, {-200.62042236328125, -26.7174072265625}}},
-{{{-770.8492431640625, 948.2369384765625}, {513.602783203125, 578.8681640625}, {960.641357421875, -813.69757080078125}}}
-</div>
-
-<div id="quad37226">
-{{{563.8267822265625, -107.4566650390625}, {-44.67724609375, -136.57452392578125}, {492.3856201171875, -268.79644775390625}}},
-{{{563.8267822265625, -107.4566650390625}, {708.049072265625, -100.77789306640625}, {-48.88226318359375, 967.9022216796875}}}
-</div>
-
-<div id="quad67242">
-{{{598.857421875, 846.345458984375}, {-644.095703125, -316.12921142578125}, {-97.64599609375, 20.6158447265625}}},
-{{{598.857421875, 846.345458984375}, {715.7142333984375, 955.3599853515625}, {-919.9478759765625, 691.611328125}}}
-</div>
-
-<div id="quad208">
-{{{481.1463623046875, -687.09613037109375}, {643.64697265625, -951.9462890625}, {162.5869140625, 698.7342529296875}}},
-{{{481.1463623046875, -687.09613037109375}, {171.8175048828125, -919.07977294921875}, {153.3433837890625, -587.43072509765625}}}
-</div>
-
-<div id="quad8a">
-{{{344.2755126953125, -689.900390625}, {743.6728515625, 512.8448486328125}, {928.598388671875, 111.946044921875}}},
-{{{344.2755126953125, -689.900390625}, {-950.03106689453125, -511.25741577148437}, {850.8173828125, 798.4874267578125}}}
-</div>
-
-<div id="quad8b">
-{{{344.2755126953125, -689.900390625}, {928.598388671875, 111.946044921875}, {743.6728515625, 512.8448486328125}}},
-{{{344.2755126953125, -689.900390625}, {-950.03106689453125, -511.25741577148437}, {850.8173828125, 798.4874267578125}}}
-</div>
-
-<div id="quad8741">
-{{{944.9024658203125, 939.454345703125}, {-971.06219482421875, -914.24395751953125}, {-878.764404296875, -297.61602783203125}}},
-{{{944.9024658203125, 939.454345703125}, {-838.96612548828125, -785.837646484375}, {-126.80029296875, 921.1981201171875}}}
-{{{107.03238931174118, 218.460612766889}, {944.9024658203125, 939.454345703125}}}
-{{{-292.72752350740279, 99.917575976335598}, {944.9024658203125, 939.454345703125}}}
-</div>
-
-<div id="quad89987">
-{{{939.4808349609375, 914.355224609375}, {-357.7921142578125, 590.842529296875}, {736.8936767578125, -350.717529296875}}},
-{{{939.4808349609375, 914.355224609375}, {-182.85418701171875, 634.4552001953125}, {-509.62615966796875, 576.1182861328125}}}
-</div>
-
-<div id="simplifyQuadratic36">
-{{{1.9474306106567383, 2.3777823448181152}, {1.9234547048814592, 2.2418855043499213}, {1.8885438442230225, 2.1114561557769775}}}
-{{{1.9474306106567383, 2.3777823448181152}, {2.0764266380046235, 2.2048800651418379}, {1.8888888359069824, 2.1111111640930176}}}
-</div>
-
-<div id="simplifyQuadratic58">
-{{326.236786,205.854996}, {329.104431,231.663818}, {351.512085,231.663818}}
-{{303.12088,141.299606}, {330.463562,217.659027}}
-</div>
-
-<div id="simplifyQuadratic58a">
-{{{326.23678588867188, 205.85499572753906}, {328.04376176056422, 222.11778818951981}, {337.6092529296875, 228.13298034667969}
-{{{303.12088012695312, 141.29960632324219}, {330.46356201171875, 217.65902709960937}
-</div>
-
-<div id="quadratic58again">
-{{322.935669,231.030273}, {312.832214,220.393295}, {312.832214,203.454178}}
-{{322.12738,233.397751}, {295.718353,159.505829}}
-</div>
-
-<div id="simplifyQuadratic56">
-{{{380.29449462890625, 140.44486999511719}, {387.29080200195312, 136.67460632324219}, {396.0399169921875, 136.67460632324219}}}
-{{{380.29449462890625, 140.44486999511719}, {388.29925537109375, 136.67460632324219}, {398.16494750976562, 136.67460632324219}}}
-{{{380.29449462890625, 140.44486999511719}, {387.692810, 137.858429}}}
-</div>
-
-<div id="simplifyQuadratic56a">
-{{{380.29449462890625, 140.44486999511719}, {387.29079954793264, 136.67460632324219}, {396.0399169921875, 136.67460632324219}}}
-{{{380.29449462890625, 140.44486999511719}, {388.29925767018653, 136.67460632324219}, {398.16494750976562, 136.67460632324219}}}
-{{fX=380.29449462890625 fY=140.44486999511719 }, {fX=398.16494750976562 fY=136.67460632324219 }} }
-{{fX=380.29449462890625 fY=140.44486999511719 }, {fX=396.03991699218750 fY=136.67460632324219 }}
-</div>
-
-<div id="simplifyQuadratic27">
-{{{1, 1}, {1, 0.666666687f}, {0.888888896f, 0.444444448f}}}
-{{{1, 1}, {1, 0.5f}, {0, 0}}}
-{{fX=1.0000000000000000 fY=1.0000000000000000 }, {fX=0.00000000000000000 fY=0.00000000000000000 }} }
-{{fX=1.0000000000000000 fY=1.0000000000000000 }, {fX=0.88888889551162720 fY=0.44444444775581360 }} }
-</div>
-
-<div id="cubicOp7d">
-{{{0.7114982008934021, 1.6617077589035034}, {0.51239079236984253, 1.4952657222747803}, {0.27760171890258789, 1.2776017189025879}, {0, 1}}}
-{{{0.7114982008934021, 1.6617077589035034}, {0.20600014925003052, 1.7854888439178467}, {9.8686491813063348e-017, 1.9077447652816772}, {0, 1}}}
-</div>
-
-<div id="cubicOp25i">
-{{{3.3856770992279053, 1.6298094987869263}, {3.777235186270762, 1.2744716237277114}, {3.7191683314895783, 1.4127666421509713}, {3.3995792865753174, 1.6371387243270874}}}
-{{{3.3856770992279053, 1.6298094987869263}, {3.3902986605112582, 1.6322361865810757}, {3.3949326825525121, 1.6346792563210237}, {3.3995792865753174, 1.6371387243270874}}}
-{{3.3856770992279053, 1.6298094987869263 }, {3.3995792865753174, 1.6371387243270874 }}
-</div>
-
-<div id="eldorado1">
-{{{1006.69513f, 291}, {1023.26367f, 291}, {1033.84021f, 304.431458f}, {1030.31836f, 321}}}
-{{{1030.318359375, 321}, {1036.695068359375, 291}}}
-{{fX=1030.3183593750000 fY=321.00000000000000 }, {fX=1006.6951293945312 fY=291.00000000000000 }} }
-</div>
-
-<div id="carpetplanet1">
-{{fX=67.000000000000000, 913.00000000000000 }, {194.00000000000000, 1041.0000000000000 }} }
-{{fX=67.000000000000000, 913.00000000000000 }, {67.662002563476562, 926.00000000000000 }} }
-{{{67, 913}, {67, 917.388977f}, {67.223999f, 921.726013f}, {67.6620026f, 926}}}
-{{{67, 913}, {67, 983.692017f}, {123.860001f, 1041}, {194, 1041}}}
-{{{67, 913}, {67.17070902440698, 919.69443917507760}}}
-</div>
-
-<div id="cubicOp104">
-{{{2.25, 2.5}, {4.5, 1}}}
-{{{2.25, 2.5}, {3.0833333333333321, 1.9999999999999973}, {4.0277778307596899, 1.2777777777777759}, {4.8611111640930176, 1}}}
-{{{2.25, 2.5}, {1.9476099234472042, 2.6814340459316774}, {1.6598502000264239, 2.8336073904096661}, {1.3973386287689209, 2.9246666431427002}}}
-{{{2.25, 2.5}, {1.2674896717071533, 3.1550068855285645}}}
-</div>
-
-<div id="cubicOp105">
-{{{2.4060275554656982, 3.4971563816070557}, {2.9702522134213849, 4.2195279679982622}, {3.8172613958721247, 5.0538091166976979}, {5, 6}}}
-{{{2.4060275554656982, 3.4971563816070557}, {3.4194286958002023, 3.5574883660881684}, {4.0077197935900575, 2.6628073781813661}, {2.2602717876434326, 0.33545622229576111}}}
-</div>
-
-<div id="cubicOp106">
-{{{0.80825299024581909, 1.9691258668899536}, {0.8601454496383667, 1.9885541200637817}, {0.92434978485107422, 2}, {1, 2}}}
-{{{0.80825299024581909, 1.9691258668899536}, {2.2400102615356445, 3.5966837406158447}, {2.5486805438995361, 3.362929105758667}, {2.494147777557373, 2.5976591110229492}}}
-{{{0.80825299024581909, 1.9691258668899536}, {2.494147777557373, 2.5976591110229492}}}
-{{{0.80825299024581909, 1.9691258668899536}, {1, 2}}}
-</div>
-
-<div id="cubicOp109">
-{{{5, 4}, {5.443139240552143931, 3.556860759447856069}, {5.297161243696338673, 3.702838775882067335}, {4.649086475372314453, 3.654751062393188477}}}
-{{{5, 4}, {4.876459521889748849, 3.876459521889748849}, {4.759596556247283949, 3.761504502886134915}, {4.649086475372314453, 3.654751062393188477}}}
-</div>
-
-<div id="skpwww_joomla_org_23">
-{{{421, 378}, {421, 380.209137f}, {418.761414f, 382}, {416, 382}}}
-{{{320, 378}, {421, 378.000031f}}}
-{{{421, 378.000031f}, {421, 383}}}
-{{{416, 383}, {418.761414f, 383}, {421, 380.761414f}, {421, 378}}}
-</div>
-
-<div id="xop1i">
-{{5.000,1.000}, {5.191,0.809}, {5.163,0.837}, {4.993,1.000}}
-{{5.000,1.000}, {4.968,1.024}}
-{{5.000,1.000}, {4.998,1.000}, {4.995,1.000}, {4.993,1.000}}
-</div>
-
-<div id="xop1u">
-{{3.500,3.500}, {3.000,4.000}, {2.500,4.500}, {1.000,4.000}}
-{{3.500,3.500}, {3.113,3.887}, {2.725,4.275}, {2.338,3.732}}
-</div>
-
-<div id="xOp2i">
-{{{2, 3}, {1.3475509011665685, 4.9573472965002949}, {2.8235509286078759, 3.5091759365574173}, {3.6505906581878662, 1.9883773326873779}}}
-{{{2, 3}, {2.4604574005585795, 2.654656949581065}, {3.0269255632437986, 2.3093137214344743}, {3.6505906581878662, 1.9883773326873779}}}
-{{{2, 3}, {1.0000000000000013, 3.7500000000000004}, {0.500000000000001, 4.5}, {1, 5}}}
-</div>
-
-<div id="testQuadratic56">
-{{{380.29449462890625, 140.44486999511719}, {379.59701460635523, 140.8207374882179}, {378.91729736328125, 141.23385620117187}}}
-{{{380.29449462890625, 140.44486999511719}, {387.29079954793264, 136.67460632324219}, {396.0399169921875, 136.67460632324219}}}
-{{{380.29449462890625, 140.44486999511719}, {388.29925767018653, 136.67460632324219}, {398.16494750976562, 136.67460632324219}}}
-</div>
-
-<div id="testQuad15">
-{{{1, 3}, {1, 1}}}
-{{{1, 3}, {0, 0}}}
-{{{1, 3}, {2, 0}, {0, 0}}}
-</div>
-
-<div id="testQuad21">
-{{{0, 0}, {1, 1}}}
-{{{0, 0}, {3, 0}, {2, 3}}}
-{{{0, 0}, {2, 3}}}
-{{{0, 0}, {2, 1}}}
-</div>
-
-<div id="testQuad22">
-{{{0, 0}, {1.2000000476837158, 0.80000001192092896}}}
-{{{0, 0}, {2, 0}}}
-{{{0, 0}, {0, 1}, {3, 2}}}
-{{{0, 0}, {1, 1}}}
-</div>
-
-<div id="testQuad23">
-{{{1, 3}, {1.9090908914804459, 1.1818182170391081}, {0.33884298801422119, 1.0165289640426636}}}
-{{{1, 3}, {0.33884298801422119, 1.0165289640426636}}}
-{{{1, 3}, {3, 0}}}
-</div>
-
-<div id="cubicOp35d">
-{{{2.211416482925415, 1.6971458196640015}, {1.2938009262874868, 2.8273619288830005}, {0.64690048634813535, 3.5876019453925414}, {0, 1}}}
-{{{2.211416482925415, 1.6971458196640015}, {1.0082962512969971, 1.997925877571106}}}
-{{{2.211416482925415, 1.6971458196640015}, {5, 1}}}
-</div>
-
-<div id="skpnational_com_au81">
-{{{1110.7071533203125, 817.29290771484375}, {1110.9998779296875, 817.58587646484375}, {1111, 818}}}
-{{{1110.7071533203125, 817.29290771484375}, {1110.526180767307778, 817.1119214508684081}, {1110.276144384999725, 817}, {1110, 817}}}
-{{{1110.7071533203125, 817.29290771484375}, {1110.888097894721341, 817.4738660071997174}, {1111, 817.7238677851287321}, {1111, 818}}}
-{{{1110.7071533203125, 817.29290771484375}, {1110.4140625, 817.0001220703125}, {1110, 817}}}
-</div>
-
-<div id="cubicOp85d">
-{{{1.0648664236068726, 2.9606373310089111}, {0.80208036362614099, 2.7936484180272374}, {0.49170560730211144, 2.2292640182552783}, {0, 1}}}
-{{{1.0648664236068726, 2.9606373310089111}, {0.6261905430171294, 3.2248910899179175}, {0.38860045191888659, 2.9430022595944321}, {0, 1}}}
-{{{1.0648664236068726, 2.9606373310089111}, {1.4282355765013004, 3.191542348791669}, {1.7006143409852195, 2.6626209548338378}, {2.2355968952178955, 2.0810616016387939}}}
-{{{1.0648664236068726, 2.9606373310089111}, {1.3437142856601656, 2.7926622975690494}, {1.7038131304059798, 2.4040122748806132}, {2.2355968952178955, 2.0810616016387939}}}
-</div>
-
-<div id="testQuads22">
-{{{0, 0}, {1.20000004768371582, 0.8000000119209289551}}}
-{{{0, 0}, {2, 0}}}
-{{{0, 0}, {0, 1}, {3, 2}}}
-{{{0, 0}, {1, 1}}}
-</div>
-
-<div id="cubicOp59d">
-{{{4, 1}, {4, 0.37698365082686142}, {4.3881493046286568, 2.4710128800004547}, {3.4716842174530029, 2.9292664527893066}}}
-{{{4, 1}, {0, 1}}}
-</div>
-
-<div id="findFirst1">
-{{{2.5767931938171387, 0.88524383306503296}, {2.4235948002142855, 0.88692501490384834}, {2.2328897699358898, 0.92237007668803672}, {2, 1}}}
-{{{2.5767931938171387, 0.88524383306503296}, {1.6008643534817426, 1.1609015907463158}, {1.1200849961943122, 1.8138386966264941}, {0.75343161821365356, 2.7170474529266357}}}
-{{{2.5767931938171387, 0.88524383306503296}, {4.0492746201577932, 0.86908498848619054}, {2.0567957107800288, 3.9721309710522448}, {0.75343161821365356, 2.7170474529266357}}}
-{{{2.5767931938171387, 0.88524383306503296}, {3.3470152174198557, 0.66768936887879282}, {4.4256496583071421, 0.68512993166142844}, {6, 1}}}
-{{{2.57679319, 0.885243833}, {5.15358639, 0.885243833}}}
-</div>
-
-<div id="testQuads54">
-{{1.000,1.000}, {1.500,0.500}, {1.500,0.250}}
-{{1.000,1.000}, {1.667,0.333}}
-{{1.000,1.000}, {2.000,3.000}}
-</div>
-
-<div id="testQuads45">
-{{{3, 3}, {3, 2.7999999523162842}, {2.880000114440918, 2.6400001049041748}}}
-{{{3, 3}, {3, 2}, {2, 0}}}
-{{{3, 3}, {2, 0}}}
-{{{3, 3}, {2.880000114440918, 2.6400001049041748}}}
-</div>
-
-<div id="testQuads59">
-{{{3, 1}, {3, 0}}}
-{{{3, 1}, {2.6666667461395264, 0.66666668653488159}}}
-{{{3, 1}, {2.8000003099441542, 1.1999996900558463}, {2.6800000667572021, 1.3600000143051147}}}
-{{{3, 1}, {2.6666667461395264, 1.3333333730697632}}}
-</div>
-
-<div id="skpcarrot_is24">
-{{{1020.08099, 672.161987}, {1020.08051, 651.450988}, {1011.68576, 632.700988}, {998.113511, 619.128738}}}
-{{{1020.08099, 672.161987}, {1019.27607, 640.291301}, {998.113511, 619.128738}}}
-{{{1020, 672}, {1020, 651.289307}, {1012.67767, 633.611633}, {998.03302, 618.96698}}}
-{{{1020, 672}, {1020, 640.93396}, {998.03302, 618.96698}}}
-</div>
-
-<div id="skpcarrot_is24a">
-{{{1020, 672}, {1020, 651.289307}, {1012.67767, 633.611633}, {998.03302, 618.96698}}}
-{{{1020, 672}, {1020, 640.93396}, {998.03302, 618.96698}}}
-</div>
-
-<div id="skpcarrot_is24b">
-{{{1020.08099, 672.161987}, {1020.08051, 651.450988}, {1011.68576, 632.700988}, {998.113511, 619.128738}}}
-{{{1020.08099, 672.161987}, {1019.27607, 640.291301}, {998.113511, 619.128738}}}
-</div>
-
-<div id="skpcarrot_is24c">
-{{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}},
-{{{1020,672}, {1020,640.93396}, {998.03302,618.96698}}},
-</div>
-
-<div id="skpcarrot_is24d">
-{{{1020.08099, 672.161987}, {1019.27607, 640.291301}, {998.113511, 619.128738}}}
-{{{1020, 672}, {1020, 640.93396}, {998.03302, 618.96698}}}
-</div>
-
-<div id="skpcarrot_is24e">
-{{{{1020.08099,672.161987}, {1020.08002,630.73999}, {986.502014,597.161987}, {945.080994,597.161987}}},
-{{{1020.08099, 672.161987}, {1019.27607, 640.291301}, {998.113511, 619.128738}}}
-{{{1020, 672}, {1020, 640.93396}, {998.03302, 618.96698}}}
-</div>
-
-<div id="slop1">
-{{{-378.22698974609375, -987.8935546875}, {-47.53326416015625, 482.7139892578125}, {-626.4708251953125, -338.62969970703125}, {-847.94854736328125, -861.42230224609375}}}
-{{{-378.61790442466736, -987.49146723747253}, {-282.51787429804051, -556.39065286764685}, {-278.55106873374694, -364.17984985308294}}}
-{{{-305.5273847156202, -615.99979442705023}, {-305.04071954345704, -612.87932617187505}}}
-</div>
-
-qT=0.98917687 cT=0.788725084 dist=312.188493 cross=-40759.4852
-<div id="slop2">
-{{{79.5137939,-249.867188}, {260.778931,-561.349854}, {343.375977,-472.657898}, {610.251465,97.8208008}}}
-{{{312.993284,-406.178762}, {418.053808,-326.9483}, {610.036929,97.2408578}}}
-{{{463.107827,-200.015424}, {602.008878,79.5702581}}}
-</div>
-
-qT=0.0192863061 cT=0.241285225 dist=652.007881 cross=528435.665
-<div id="slop3">
-{{{-895.015015,-523.545288}, {223.166992,-999.644531}, {615.428711,-767.162109}, {605.479736,480.355713}}}
-{{{-894.932414,-523.605499}, {-66.4040558,-889.938889}, {278.515212,-667.684158}}}
-{{{-207.851881,-740.109296}, {-831.819002,-550.955104}}}
-</div>
-
-qT=0.0245724525 cT=0.231316637 dist=407.103004 cross=-46286.5686
-<div id="slop4">
-{{{876.492798,-849.716187}, {519.430908,-288.374207}, {187.2771,314.324341}, {335.363403,533.086548}}}
-{{{876.323133,-849.535824}, {594.868958,-415.229224}, {416.667192,-30.0277669}}}
-{{{638.343827,-458.798274}, {849.023879,-807.14691}}}
-</div>
-
-qT=0.000316393778 cT=0.248252634 dist=489.678412 cross=-57352.7653
-<div id="slop5">
-{{{876.492798,-849.716187}, {519.430908,-288.374207}, {187.2771,314.324341}, {335.363403,533.086548}}}
-{{{876.147506,-849.184429}, {593.963775,-414.437342}, {416.842819,-30.3791618}}}
-{{{622.139843,-430.512844}, {876.135915,-849.166571}}}
-</div>
-
-qT=0.989562776 cT=0.760518485 dist=211.50589 cross=134901.42
-<div id="slop6">
-{{{876.492798,-849.716187}, {519.430908,-288.374207}, {187.2771,314.324341}, {335.363403,533.086548}}}
-{{{416.141554,-30.4534414}, {237.846068,356.664216}, {335.719378,533.692585}}}
-{{{305.345404,315.701195}, {331.440368,525.591152}}}
-</div>
-
-qT=0.0978245708 cT=0.397465904 dist=959.737748 cross=158093.403
-<div id="slop7">
-{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
-{{{629.666617,-82.159942}, {-661.943328,620.81113}, {-723.44072,537.121833}}}
-{{{-347.560585,421.003177}, {507.062151,-15.707855}}}
-</div>
-
-qT=0.169803964 cT=0.389326872 dist=658.039939 cross=107865.424
-<div id="slop8">
-{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
-{{{629.536617,-81.7990275}, {-662.457623,620.485316}, {-723.31072,536.760918}}}
-{{{-330.996421,413.091598}, {257.080063,117.824582}}}
-</div>
-
-qT=0.0863836955 cT=0.387901231 dist=986.24777 cross=157348.113
-<div id="slop9">
-{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
-{{{629.248316,-81.8984216}, {-662.339696,620.351182}, {-723.022419,536.860313}}}
-{{{-328.058099,411.68229}, {549.399512,-38.5985162}}}
-</div>
-
-qT=0.175359403 cT=0.390420692 dist=640.051938 cross=105488.084
-<div id="slop10">
-{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
-{{{629.760605,-81.9577046}, {-661.301606,620.029216}, {-723.534707,536.919596}}}
-{{{-333.243516,414.168229}, {238.961251,127.37878}}}
-</div>
-
-qT=0.0986412358 cT=0.382365595 dist=921.951857 cross=145651.761
-<div id="slop11">
-{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
-{{{629.919588,-82.1841825}, {-662.488256,620.04494}, {-723.693691,537.146073}}}
-{{{-316.541641,406.142013}, {504.067361,-14.0913644}}}
-</div>
-
-qT=0.146746849 cT=0.391456086 dist=750.006927 cross=123679.094
-<div id="slop12">
-{{{895.800171,-222.213013}, {-528.78833,526.47644}, {-967.299927,776.05603}, {-611.228027,319.934814}}}
-{{{629.712675,-82.0366321}, {-661.487948,620.191832}, {-723.486777,536.998523}}}
-{{{-335.364605,415.183549}, {334.079508,77.0194322}}}
-</div>
-
-qT=0.00196158131 cT=0.20357489 dist=466.080185 cross=241741.95
-<div id="slop13">
-{{{-627.671509,-359.277039}, {222.414551,-791.598328}, {390.603027,-581.903687}, {-21.7962036,560.33728}}}
-{{{-627.675958,-359.282959}, {-52.012535,-659.029798}, {116.967835,-524.756101}}}
-{{{-192.427848,-541.033993}, {-622.696937,-361.871356}}}
-</div>
-
-qT=0.948725598 cT=0.744200608 dist=699.694313 cross=179509.878
-<div id="slop14">
-{{{-362.331848,427.292603}, {634.418701,-661.946533}, {438.438599,-626.147278}, {-893.060425,214.243408}}}
-{{{259.978301,-393.549091}, {181.692599,-474.452437}, {-892.389834,213.689096}}}
-{{{-95.1310032,-267.365579}, {-696.89984,89.6307768}}}
-</div>
-
-qT=0.971677129 cT=0.755306143 dist=771.998962 cross=189468.817
-<div id="slop15">
-{{{-362.331848,427.292603}, {634.418701,-661.946533}, {438.438599,-626.147278}, {-893.060425,214.243408}}}
-{{{259.662278,-393.355886}, {181.612843,-473.935297}, {-892.073812,213.495892}}}
-{{{-120.438346,-253.451518}, {-782.461182,143.673352}}}
-</div>
-
-qT=0.571797795 cT=0.773951562 dist=495.560209 cross=221091.889
-<div id="slop16">
-{{{447.192383,-883.210205}, {359.794678,-987.765808}, {755.427612,-754.328735}, {963.672119,746.545776}}}
-{{{635.795655,-580.726915}, {810.704547,-228.491534}, {963.345162,745.921688}}}
-{{{801.470356,-87.7105789}, {646.551495,-558.433498}}}
-</div>
-
-qT=0.579236693 cT=0.782683167 dist=281.750564 cross=65125.1655
-<div id="slop17">
-{{{-931.259155,-883.589966}, {-485.682007,-615.793701}, {-68.5913696,-928.695923}, {431.499268,-810.584778}}}
-{{{-177.087049,-804.265618}, {110.452267,-866.525236}, {430.718323,-810.414444}}}
-{{{116.080189,-836.904702}, {-164.080748,-807.017753}}}
-</div>
-
-qT=0.0102075348 cT=0.2448024 dist=855.408492 cross=463614.179
-<div id="slop18">
-{{{-867.011292,844.139282}, {-136.156799,-281.244263}, {583.27771,-926.761414}, {998.710205,6.19244385}}}
-{{{-866.7221,843.65105}, {-308.756317,-34.8353977}, {183.843514,-346.222431}}}
-{{{-336.612013,120.039627}, {-844.283739,808.5112}}}
-</div>
-
-qT=0.473968306 cT=0.266805943 dist=567.851844 cross=-461509.104
-<div id="slop19">
-{{{-867.011292,844.139282}, {-136.156799,-281.244263}, {583.27771,-926.761414}, {998.710205,6.19244385}}}
-{{{-867.218781,844.133445}, {-310.496711,-35.0458119}, {184.340195,-346.704825}}}
-{{{-290.018097,66.7065093}, {132.536746,-312.639141}}}
-</div>
-
-qT=0.0232589401 cT=0.241085469 dist=789.989464 cross=428119.544
-<div id="slop20">
-{{{-867.011292,844.139282}, {-136.156799,-281.244263}, {583.27771,-926.761414}, {998.710205,6.19244385}}}
-{{{-866.942271,843.928587}, {-309.178151,-34.0018497}, {184.063685,-346.499968}}}
-{{{-344.507162,129.265381}, {-815.30119,763.644082}}}
-</div>
-
-<div id="skpnamecheap_405">
-{{{141.008835f, 837.9646f}, {141.235291f, 1109.05884f}}}
-{{{141, 842}, {141.14502f, 1000}}}
-{{{141.14502f, 1000}, {140, 1000}}}
-</div>
-
-<div id="skpwww_dealnews_com_315">
-{{{969.87066650390625, 4279.810546875}, {967.7166748046875, 4260}}}
-{{{969.87066650390625, 4279.810546875}, {969.866972698829386, 4279.809233889284769}, {969.88751220703125, 4279.81640625}}}
-{{{969.87066650390625, 4279.810546875}, {970, 4281}}}
-{{{969.87066650390625, 4279.810546875}, {968.9217161046863112, 4279.473236623693992}, {968.17156982421875, 4278.53564453125}}}
-{{{969.8706626470486754, 4279.810469740555163}, {969.8790796016525064, 4279.813461598796493}, {969.88751220703125, 4279.81640625}}}
-</div>
-
-<div id="skpwww_dealnews_com_315_a">
-{{{969.8706626470486754, 4279.810469740555163}, {969.8790796016525064, 4279.813461598796493}, {969.88751220703125, 4279.81640625}}}
-{{{969.87066650390625, 4279.810546875}, {969.8790834585100811, 4279.81353873324133}}}
-{{{969.88751220703125, 4279.81640625}, {969.8790796016525064, 4279.813461598796493}}}
-</div>
-
-<div id="testQuads60">
-{{{2, 2}, {1.977731304590550021, 1.97773134708404541}, {1.95645439624786377, 1.95546269416809082}}}
-{{{2, 2}, {2, 3}}}
-{{{2, 2}, {2, 1.960000038146972656}}}
-{{{2, 2}, {1.955341815948486328, 1.955341815948486328}}}
-</div>
-
-<div id="testQuads60_a">
-{{{2, 0}, {1, 1}, {2, 2}}}
-{{{2, 2}, {0, 0}}}
-</div>
-
-<div id="testQuads60_b">
-{{2,1}, {0,2}, {3,2}},
-{{3,2}, {2,3}},
-{{2,3}, {2,1}},
-{{0,0}, {2,0}},
-{{2,0}, {1,1}, {2,2}},
-{{2,2}, {0,0}},
-</div>
-
-<div id="skpelpais_com_18">
-{{183,8507}, {552,8506.99023}},
-{{552,8506.99023}, {552,8508}},
-{{552,8508}, {183,8508}},
-{{183,8508}, {183,8507}},
-op intersect
-{{183,8508}, {183,8506.99023}},
-{{183,8506.99023}, {552,8507}},
-{{552,8507}, {552,8508}},
-</div>
-
-<div id="skpwww_cityads_ru_249">
-{{{1000, 10.4003992f}, {1000, 13.3527431f}}}
-{{{1000, 13.3527431f}, {999.917603f, 13.2607508f}, {999.82843f, 13.1715727f}}}
-{{{1000, 13}, {999.969971f, 37.0299988f}}}
-</div>
-
-<div id="skpwww_maturesupertube_com_21">
-    {{{{3.87867975f, 11831.8789f}, {4.7573595f, 11831}, {6, 11831}}},
-     {{{2, 11830}, {4.5f, 11832.5f}}}},
-</div>
-
-<div id="loop1">
-{{1, 4, 2, 6, 0, 5, 4.5f, 4.33333302f
-{{2, 6, 0, 5, 4.5f, 4.33333302f, 1, 4
-{{{3, 5}, {2.33333325f, 4.33333349f}, {3.83333325f, 3.83333349f}, {2, 4}}}
-{{{2, 4}, {3, 5}, {2.33333325f, 4.33333349f}, {3.83333325f, 3.83333349f}}}
-</div>
-
-<div id="serp1">
-{{{0.55431359440952721, 2.1086271888190544}, {0.1588954256872922, 2.3078315988141811}, {0.57446808656344528, 2.1489361731268914}, {0, 1}}}
-{{{0.55431359440952721, 2.1086271888190544}, {0.1588954256872922, 2.3078315988141811}, {0.57446808656344528, 2.1489361731268914}, {0, 1}}}
-</div>
-<div id="serp2">
-{{{4.0946656649135988, 3.283996994740797}, {4.1983471074380168, 2.1074380165289259}, {4.5454545454545459, 1.3636363636363635}, {4, 3}}}
-{{{4.0946656649135988, 3.283996994740797}, {4.1983471074380168, 2.1074380165289259}, {4.5454545454545459, 1.3636363636363635}, {4, 3}}}
-</div>
-<div id="serp3">
-{{{2.2015477442471254, 1.1371488033013577}, {2.3167674423028526, 0.68323255769714741}, {2.4076432497431028, 0.59235675025689716}, {2, 1}}}
-{{{2.2015477442471254, 1.1371488033013577}, {2.3167674423028526, 0.68323255769714741}, {2.4076432497431028, 0.59235675025689716}, {2, 1}}}
-</div>
-
-<div id="skpwww_seopack_blogspot_com_2153">
-{{{924, 245.472672f}, {1143, 247}}}
-{{{1000, 246}, {927.340759f, 245.505722f}}}
-{{{999.892212f, 246}, {927.340759f, 245.505722f}}}
-</div>
-
-<div id="self1">
-{{{2, 3}, {0, 4}, {3, 2}, {5, 3}}}
-{{{2, 3}, {0, 4}, {3, 2}, {5, 3}}}
-</div>
-
-<div id="skpwww_pindosiya_com_99">
-{{{901.0869140625, 547}, {899, 556}}}
-{{{900.0235595703125, 551.60284423828125}, {900.06072998046875, 551.29705810546875}, {900.15655517578125, 551.0157470703125}}}
-</div>
-
-<div id="cubicLineMiss1">
-{{{-634.60540771484375, -481.262939453125}, {266.2696533203125, -752.70867919921875}, {-751.8370361328125, -317.37921142578125}, {-969.7427978515625, 824.7255859375}}}
-{{{-287.9506133720805678, -557.1376476615772617}, {-285.9506133720805678, -557.1376476615772617}}}
-</div>
-
-<div id="cubicLineMiss2">
-{{{-818.4456787109375, 248.218017578125}, {944.18505859375, -252.2330322265625}, {957.3946533203125, -45.43280029296875}, {-591.766357421875, 868.6187744140625}}}
-{{{435.1963493079119871, -16.42683763243891093}, {437.1963493079119871, -16.42683763243891093}}}
-</div>
-
-<div id="cubicLineMiss3">
-{{{-818.4456787109375, 248.218017578125}, {944.18505859375, -252.2330322265625}, {957.3946533203125, -45.43280029296875}, {-591.766357421875, 868.6187744140625}}}
-{{{397.5007682490800676, -17.35020084021140008}, {399.5007682490800676, -17.35020084021140008}}}
-</div>
-
-<div id="cubicLineMiss4">
-{{{-652.660888671875, -384.6475830078125}, {-551.7723388671875, -925.5025634765625}, {-321.06658935546875, -813.10345458984375}, {142.6982421875, -47.4503173828125}}}
-{{{-478.4372049758064236, -717.868282575075682}, {-476.4372049758064236, -717.868282575075682}}}
-</div>
-
-<div id="cubicLineErr1">
-{{{-954.4322509765625, 827.2216796875}, {-420.24017333984375, -7.80560302734375}, {799.134765625, -971.4295654296875}, {-556.23486328125, 344.400146484375}}}
-
-{{{58.57411390280688579, -302.8879316712078662}, {60.57411390280688579, -302.8879316712078662}}}
-</div>
-
-<div id="cubicLineErr2">
-{{{-634.60540771484375, -481.262939453125}, {266.2696533203125, -752.70867919921875}, {-751.8370361328125, -317.37921142578125}, {-969.7427978515625, 824.7255859375}}}
-{{{-287.95061337208057, -557.13764766157726}, {-285.95061337208057, -557.13764766157726}}}
-{{{-308.65463091760211, -549.4520029924679} -308.65463091760211, -569.4520029924679
-</div>
-
-<div id="skpwww_educationalcraft_com_4">
-{{{974.91998291015625, 1481.7769775390625}, {974.91998291015625, 1481.7760009765625}, {977.3189697265625, 1484.6190185546875}, {975.10699462890625, 1486.97802734375}}}
-{{fX=974.91998291015625 fY=1481.7769775390625 }, {fX=974.92071342468262 fY=1481.7972941398621 }} }
-</div>
-
-<div id="skpwww_educationalcraft_com_4a">
-{{{962.10699462890625, 1485.654052734375}, {962.10699462890625, 1485.654052734375}, {960.58502197265625, 1483.595947265625}, {957.53900146484375, 1482.0970458984375}}}
-{{{963.21502685546875, 1486.6700439453125}, {962.7449951171875, 1486.6700439453125}, {962.10699462890625, 1485.654052734375}, {962.10699462890625, 1485.654052734375}}}
-</div>
-
-<div id="skpwww_educationalcraft_com_4b">
-{{{980.9000244140625, 1474.3280029296875}, {980.9000244140625, 1474.3280029296875}, {978.89300537109375, 1471.95703125}, {981.791015625, 1469.487060546875}}}
-{{{981.791015625, 1469.487060546875}, {981.791015625, 1469.4859619140625}, {983.3580322265625, 1472.72900390625}, {980.9000244140625, 1474.3280029296875}}}
-</div>
-
-<div id="skpwww_aceinfographics_com_106">
-{{{168, 29.6722088f}, {166, 29.6773338f}}}
-{{{166.878677f, 29.6750813f}, {167.388f, 29.6763878f}, {168.019989f, 29.6769352f}}}
-</div>
-
-<div id="skpwww_tcmevents_org_13">
-{{{465.84668f, 547.288391f}, {467.274506f, 552.852356f}, {468.506836f, 560.718567f}}}
-{{{468.506836f, 560.718567f}, {467.336121f, 553.24585f}, {465.951904f, 547.960144f}}
-</div>
-
-<div id="skpwww_kitcheninspirations_wordpress_com_66">
-{{{60.8333359f, 27820.498f}, {47.1666679f, 27820.5f}}}
-{{{60.8333359f, 27820.668f}, {60.8333359f, 27820.498f}}}
-{{{47.1666679f, 27820.498f}, {60.8333359f, 27820.5f}}}
-{{{60.8333359f, 27820.5f}, {60.8333359f, 27820.668f}}}
-</div>
-
-<div id="skpwww_galaxystwo_com_4">
-{{{10105, 2510}, {10123, 2509.98999f}}}
-{{{10105, 2509.98999f}, {10123, 2510}}}
-</div>
-
-<div id="skpwww_wartepop_blogspot_com_br_6">
-{{{124.666672f, 152.333344f}, {125.909309f, 152.333344f}, {126.787994f, 153.309662f}}}
-{{fX=124.66666412353516 fY=152.33334350585937 }, {fX=126.78799438476562 fY=153.30966186523437 }} }
-{{fX=124.66666412353516 fY=152.33334350585937 }, {fX=127.02368927001953 fY=153.30966186523437 }} }
-</div>
-
-<div id="skpwww_wartepop_blogspot_com_br_6a">
-{{{124.666672f, 152.333344f}, {125.909309f, 152.333344f}, {126.787994f, 153.309662f}}}
-{{fX=124.66667175292969 fY=152.33334350585937 }, {fX=126.78799438476562 fY=153.30966186523437 }} }
-{{fX=124.66667175292969 fY=152.33334350585937 }, {fX=127.02368927001953 fY=153.30966186523437 }} }
-</div>
-
-<div id="skpcarrot_is24x">
-{{{1020.08099, 672.16198699999995}, {1020.08002, 630.73999000000003}, {986.50201400000003, 597.16198699999995}, {945.08099400000003, 597.16198699999995}}}
-{{{1020, 672}, {1020, 640.93395999999996}, {998.03301999999996, 618.96698000000004}}}
-</div>
-
-<div id="skpwww_9to5mac_com_64">
-{{{{365.848175,5081.15186}, {368,5103}}},
-{{{367.967712,5102.61084}, {368.278717,5105.71045}}}},
-</div>
-
-<div id="issue2753">
-{{50.6,117.001}, {50.6,117.001}, {164.601,85.2}, {188.201,117.601}},
-{{188.201,117.601}, {188.201,117.601}, {174.801,93}, {39,124.001}},
-computed quadratics set
-{{50.6,117.001}, {52.4926111,116.112083}, {81.0298889,109.956333}},
-{{81.0298889,109.956333}, {109.567167,103.800583}, {142.037778,103.045}},
-{{142.037778,103.045}, {174.508389,102.289417}, {188.201,117.601}},
-computed quadratics set
-{{188.201,117.601}, {189.210269,116.85838}, {179.697259,112.371148}},
-{{179.697259,112.371148}, {170.18425,107.883917}, {138.037741,108.563519}},
-{{138.037741,108.563519}, {105.891231,109.24312}, {39,124.001}},
-</div>
-
-<div id="battle6001">
-{{{0.111722f, -59.999897f}, {0.0895366594f, -59.999939f}, {0.0673542097f, -59.9999695f}, {0.0451717526f, -59.9999847f}}}
-{{{0.0451734141f, -59.9999847f}, {0.0438041016f, -59.9999886f}, {0.0424379632f, -59.9999886f}, {0.0410718247f, -59.9999886f}}}
-</div>
-
-<div id="fuzz763_3084">
-{{{38.6568527f, 27.3431454f}, {41, 29.6862907f}, {41, 33}}}
-{{{39.131218f, 27.8554096f}, {41, 30.0406685f}, {41, 33}}}
-{{{44.6041069f, 27.9369583f}, {41.8078537f, 28.9057903f}, {39.131218f, 27.8554096f}}}
-</div>
-
-<div id="fuzz763_378">
-{{{-52.8062439,14.1493912}, {-53.6638947,10.948595}, {-52.0070419,8.07883835}}
-{{-52.8054848,14.1522331}, {-53.6633072,10.9514809}, {-52.0066071,8.08163643}}
-</div>
-
-<div id="fuzz763_378d">
-{{{-37.351398500000002, 10.0082998}, {-36.493801099999999, 13.209099800000001}, {-38.150600400000002, 16.0788002}}
-{{-37.350898700000002, 10.010299699999999}, {-36.493099200000003, 13.2110004}, {-38.149799299999998, 16.080900199999999}}}
-{{-37.320497331221297, 10.126736679362402}, {-37.320543141534543 fY=10.126556206903867 }}
-{{-37.514829818825397, 14.722977321623326}, {=-37.514249241879924 fY=14.725464892492159 }}
-</div>
-
-<div id="fuzz763_6411089">
-{{38.5810318, 38.7318115}, {38.5877266, 38.7252655}, {38.5931816, 38.7199173}}
-{{38.5931816, 38.7199173}, {38.5880508, 38.7249527}, {38.5810318, 38.7318115}}
+<div id="sect1">
+{{{6, -3}, {0, 1}}} id=3
+{{{1.6714313f, -1.08141601f}, {2.24979973f, -1.14467525f}, {2.27122664f, -0.514151096f}, {0, 1}}} id=5
+{{{0.001119050197303295135, 0.9992539882659912109}, {4.001119136810302734, 6.999254226684570312}}},
 </div>
 
 </div>
 
 <script type="text/javascript">
 
-    var testDivs = [
-        fuzz763_6411089,
-        fuzz763_378d,
-        fuzz763_378,
-        fuzz763_3084,
-        battle6001,
-        issue2753,
-        skpwww_9to5mac_com_64,
-        skpcarrot_is24x,
-        skpwww_wartepop_blogspot_com_br_6,
-        skpwww_wartepop_blogspot_com_br_6a,
-        skpwww_galaxystwo_com_4,
-        skpwww_kitcheninspirations_wordpress_com_66,
-        skpwww_tcmevents_org_13,
-        skpwww_aceinfographics_com_106,
-        skpwww_educationalcraft_com_4b,
-        skpwww_educationalcraft_com_4a,
-        skpwww_educationalcraft_com_4,
-        cubicLineErr2,
-        cubicLineErr1,
-        cubicLineMiss1,
-        cubicLineMiss2,
-        cubicLineMiss3,
-        cubicLineMiss4,
-        skpwww_pindosiya_com_99,
-        self1,
-        skpwww_seopack_blogspot_com_2153,
-        serp1,
-        serp2,
-        serp3,
-        loop1,
-        skpwww_maturesupertube_com_21,
-        skpwww_cityads_ru_249,
-        skpelpais_com_18,
-        testQuads60_b,
-        testQuads60_a,
-        testQuads60,
-        skpwww_dealnews_com_315_a,
-        skpwww_dealnews_com_315,
-        skpnamecheap_405,
-        slop1,
-        slop2,
-        slop3,
-        slop4,
-        slop5,
-        slop6,
-        slop7,
-        slop8,
-        slop9,
-        slop10,
-        slop11,
-        slop12,
-        slop13,
-        slop14,
-        slop15,
-        slop16,
-        slop17,
-        slop18,
-        slop19,
-        slop20,
-        skpcarrot_is24e,
-        skpcarrot_is24d,
-        skpcarrot_is24c,
-        skpcarrot_is24b,
-        skpcarrot_is24a,
-        skpcarrot_is24,
-        testQuads59,
-        testQuads45,
-        testQuads54,
-        findFirst1,
-        cubicOp59d,
-        testQuads22,
-        cubicOp85d,
-        cubicOp104,
-        skpnational_com_au81,
-        cubicOp35d,
-        testQuad23,
-        testQuad22,
-        testQuad21,
-        testQuad15,
-        testQuadratic56,
-        xop1i,
-        xOp2i,
-        xop1u,
-        skpwww_joomla_org_23,
-        cubicOp109,
-        cubicOp106,
-        cubicOp105,
-        carpetplanet1,
-        eldorado1,
-        cubicOp25i,
-        cubicOp7d,
-        simplifyQuadratic27,
-        simplifyQuadratic56a,
-        simplifyQuadratic56,
-        quadratic58again,
-        simplifyQuadratic58a,
-        simplifyQuadratic58,
-        simplifyQuadratic36,
-        quad89987,
-        quad8741,
-        quad8b,
-        quad8a,
-        quad208,
-        quad67242,
-        quad37226,
-        quad35237,
-        quad108,
-        quad212,
-        quad3160,
-        quad640,
-        quad206,
-        quad136,
-        quad113,
-        quad999,
-        quad0,
-        quad179,
-        quad4a,
-        quad94,
-        quad77,
-        quad22a,
-        quad14a,
-        quad809,
-        quad653,
-        quad584,
-        quad413,
-        quad379,
-        quad159,
-        quad232,
-        quad40,
-        quad39,
-        quad38,
-        quad37,
-        quad36,
-        quad35,
-        quad34,
-        quad33,
-        quad32,
-        quad31,
-        quad30,
-        quad29,
-        quad28,
-        quad27,
-        quad26,
-        quad25,
-        quad24,
-        quad23,
-        quad22,
-        quad21,
-        quad20,
-        quad19,
-        quad18,
-        quad17,
-        quad16,
-        quad15,
-        quad14,
-        quad13,
-        quad12,
-        quad11,
-        cubic2,
-        cubic1,
-        quad1,
-        quad2,
-        quad3,
-        quad4,
-        quad5,
-        quad6,
-        quad7,
-        quad8,
-        quad9,
-        quad10,
-    ];
+var testDivs = [
+sect1,
+];
+
+    var decimal_places = 3;
 
     var tests = [];
     var testTitles = [];
     var testIndex = 0;
     var ctx;
+
     var subscale = 1;
     var xmin, xmax, ymin, ymax;
     var scale;
@@ -1190,18 +37,21 @@
     var screenWidth, screenHeight;
     var drawnPts;
     var curveT = 0;
+    var curveW = -1;
 
     var lastX, lastY;
     var activeCurve = [];
     var activePt;
+    var ids = [];
 
-    var decimal_places = 3;
-
+    var focus_on_selection = 0;
     var draw_t = false;
+    var draw_w = false;
     var draw_closest_t = false;
     var draw_cubic_red = false;
     var draw_derivative = false;
-    var draw_endpoints = true;
+    var draw_endpoints = 2;
+    var draw_id = 0;
     var draw_midpoint = 0;
     var draw_mouse_xy = false;
     var draw_order = false;
@@ -1218,6 +68,12 @@
         var curves = [];
         for (var c in curveStrs) {
             var curveStr = curveStrs[c];
+            var idPart = curveStr.split("id=");
+            var id = -1;
+            if (idPart.length == 2) {
+                id = parseInt(idPart[1]);
+                curveStr = idPart[0];
+            }
             var points = curveStr.match(pattern);
             var pts = [];
             for (var wd in points) {
@@ -1225,8 +81,13 @@
                 if (isNaN(num)) continue;
                 pts.push(num);
             }
-            if (pts.length > 2)
+            if (pts.length > 2) {
                 curves.push(pts);
+            }
+            if (id >= 0) {
+                ids.push(id);
+                ids.push(pts);
+            }
         }
         if (curves.length >= 1) {
             tests.push(curves);
@@ -1256,7 +117,7 @@
         ymax = -Infinity;
         for (var curves in test) {
             var curve = test[curves];
-            var last = curve.length;
+            var last = curve.length - (curve.length % 2 == 1 ? 1 : 0);
             for (var idx = 0; idx < last; idx += 2) {
                 xmin = Math.min(xmin, curve[idx]);
                 xmax = Math.max(xmax, curve[idx]);
@@ -1264,7 +125,7 @@
                 ymax = Math.max(ymax, curve[idx + 1]);
             }
         }
-        xmin -= 1;
+        xmin -= Math.min(1, Math.max(xmax - xmin, ymax - ymin));
         var testW = xmax - xmin;
         var testH = ymax - ymin;
         subscale = 1;
@@ -1313,6 +174,17 @@
         var c = t;
         dxy.x = a * curve[0] + b * curve[2] + c * curve[4];
         dxy.y = a * curve[1] + b * curve[3] + c * curve[5];
+    } else if (curve.length == 7) {
+        var p20x = curve[4] - curve[0];
+        var p20y = curve[5] - curve[1];
+        var p10xw = (curve[2] - curve[0]) * curve[6];
+        var p10yw = (curve[3] - curve[1]) * curve[6];
+        var coeff0x = curve[6] * p20x - p20x;
+        var coeff0y = curve[6] * p20y - p20y;
+        var coeff1x = p20x - 2 * p10xw;
+        var coeff1y = p20y - 2 * p10yw;
+        dxy.x = t * (t * coeff0x + coeff1x) + p10xw;
+        dxy.y = t * (t * coeff0y + coeff1y) + p10yw;
     } else if (curve.length == 8) {
         var one_t = 1 - t;
         var a = curve[0];
@@ -1437,7 +309,7 @@
             roots.push(r);
             if (approximately_zero(R2 - Q3)) {
                 r = -A / 2 - adiv3;
-                if (!approximately_zero(s[0] - r)) {
+                if (!approximately_zero(roots[0] - r)) {
                     roots.push(r);
                 }
             }
@@ -1499,7 +371,8 @@
         var adj = endPt[0] - startPt[0];
         var opp = endPt[1] - startPt[1];
         var r = [];
-        for (var n = 0; n < curve.length / 2; ++n) {
+        var len = (curve.length == 7 ? 6 : curve.length) / 2;
+        for (var n = 0; n < len; ++n) {
             r[n] = (curve[n * 2 + 1] - startPt[1]) * adj - (curve[n * 2] - startPt[0]) * opp;
         }
         if (curve.length == 6) {
@@ -1510,6 +383,14 @@
             B -= C;  // B = -(b - c)
             return quad_roots(A, 2 * B, C);
         }
+        if (curve.length == 7) {
+            var A = r[2];
+            var B = r[1] * curve[6];
+            var C = r[0];
+            A += C - 2 * B;  // A = a - 2*b + c
+            B -= C;  // B = -(b - c)
+            return quad_roots(A, 2 * B, C);
+        }
         var A = r[3];       // d
         var B = r[2] * 3;   // 3*c
         var C = r[1] * 3;   // 3*b
@@ -1530,6 +411,13 @@
         if (curve.length == 6) {
             return one_t2 * curve[0] + 2 * one_t * t * curve[2] + t2 * curve[4];
         }
+        if (curve.length == 7) {
+            var numer = one_t2 * curve[0] + 2 * one_t * t * curve[2] * curve[6]
+                    + t2 * curve[4];
+            var denom = one_t2            + 2 * one_t * t            * curve[6]
+                    + t2;
+            return numer / denom;
+        }
         var a = one_t2 * one_t;
         var b = 3 * one_t2 * t;
         var c = 3 * one_t * t2;
@@ -1547,6 +435,13 @@
         if (curve.length == 6) {
             return one_t2 * curve[1] + 2 * one_t * t * curve[3] + t2 * curve[5];
         }
+        if (curve.length == 7) {
+            var numer = one_t2 * curve[1] + 2 * one_t * t * curve[3] * curve[6]
+                    + t2 * curve[5];
+            var denom = one_t2            + 2 * one_t * t            * curve[6]
+                    + t2;
+            return numer / denom;
+        }
         var a = one_t2 * one_t;
         var b = 3 * one_t2 * t;
         var c = 3 * one_t * t2;
@@ -1636,6 +531,77 @@
         return closest / 16;
     }
 
+    var kMaxConicToQuadPOW2 = 5;
+
+    function computeQuadPOW2(curve, tol) {
+        var a = curve[6] - 1;
+        var k = a / (4 * (2 + a));
+        var x = k * (curve[0] - 2 * curve[2] + curve[4]);
+        var y = k * (curve[1] - 2 * curve[3] + curve[5]);
+
+        var error = Math.sqrt(x * x + y * y);
+        var pow2;
+        for (pow2 = 0; pow2 < kMaxConicToQuadPOW2; ++pow2) {
+            if (error <= tol) {
+                break;
+            }
+            error *= 0.25;
+        }
+        return pow2;
+    }
+
+    function subdivide_w_value(w) {
+        return Math.sqrt(0.5 + w * 0.5);
+    }
+
+    function chop(curve, part1, part2) {
+        var w = curve[6];
+        var scale = 1 / (1 + w);
+        part1[0] = curve[0];
+        part1[1] = curve[1];
+        part1[2] = (curve[0] + curve[2] * w) * scale;
+        part1[3] = (curve[1] + curve[3] * w) * scale;
+        part1[4] = part2[0] = (curve[0] + (curve[2] * w) * 2 + curve[4]) * scale * 0.5;
+        part1[5] = part2[1] = (curve[1] + (curve[3] * w) * 2 + curve[5]) * scale * 0.5;
+        part2[2] = (curve[2] * w + curve[4]) * scale;
+        part2[3] = (curve[3] * w + curve[5]) * scale;
+        part2[4] = curve[4];
+        part2[5] = curve[5];
+        part1[6] = part2[6] = subdivide_w_value(w);
+    }
+
+    function subdivide(curve, level, pts) {
+        if (0 == level) {
+            pts.push(curve[2]);
+            pts.push(curve[3]);
+            pts.push(curve[4]);
+            pts.push(curve[5]);
+        } else {
+            var part1 = [], part2 = [];
+            chop(curve, part1, part2);
+            --level;
+            subdivide(part1, level, pts);
+            subdivide(part2, level, pts);
+        }
+    }
+
+    function chopIntoQuadsPOW2(curve, pow2, pts) {
+        subdivide(curve, pow2, pts);
+        return 1 << pow2;
+    }
+
+    function drawConic(curve, srcLeft, srcTop, scale) {
+        var tol = 1 / scale;
+        var pow2 = computeQuadPOW2(curve, tol);
+        var pts = [];
+        chopIntoQuadsPOW2(curve, pow2, pts);
+        for (var i = 0; i < pts.length; i += 4) {
+            ctx.quadraticCurveTo(
+                (pts[i + 0] - srcLeft) * scale, (pts[i + 1] - srcTop) * scale,
+                (pts[i + 2] - srcLeft) * scale, (pts[i + 3] - srcTop) * scale);
+        }
+    }
+
     function draw(test, title) {
         ctx.font = "normal 50px Arial";
         ctx.textAlign = "left";
@@ -1664,7 +630,7 @@
             if (curve.length == 4) {
                 endPt.push(curve[2]);
                 endPt.push(curve[3]);
-            } else if (curve.length == 6) {
+            } else if (curve.length == 6 || curve.length == 7) {
                 endPt.push(curve[4]);
                 endPt.push(curve[5]);
             } else if (curve.length == 8) {
@@ -1696,7 +662,7 @@
         var maxWidth = Math.max(xmax - xmin, ymax - ymin);
         for (var curves in test) {
             var curve = test[curves];
-            if (curve.length == 6 || curve.length == 8) {
+            if (curve.length >= 6 && curve.length <= 8) {
                 var opp = curves == 0 || curves == 1 ? 0 : 1;
                 var sects = ray_curve_intersect(origin, hullEnds[opp], curve);
                 intersect.push(sects);
@@ -1713,7 +679,7 @@
                         var iDist = Math.sqrt(ix * ix + iy * iy);
                         var eDist = Math.sqrt(ex * ex + ey * ey);
                         var delta = Math.abs(iDist - eDist) / maxWidth;
-                        if (delta > (curve.length == 6 ? 1e-5 : 1e-4)) {
+                        if (delta > (curve.length != 8 ? 1e-5 : 1e-4)) {
                             useIntersect ^= true;
                         }
                     }
@@ -1755,8 +721,8 @@
   //      console.log("midLeft=" + midLeft + " startCross=" + startCross);
         var intersectIndex = 0;
         for (var curves in test) {
-            var curve = test[curves];
-            if (curve.length != 4 && curve.length != 6 && curve.length != 8) {
+            var curve = test[draw_id != 2 ? curves : test.length - curves - 1];
+            if (curve.length != 4 && curve.length != 6 && curve.length != 7 && curve.length != 8) {
                 continue;
             }
             ctx.lineWidth = 1;
@@ -1769,10 +735,12 @@
                 drawLine(curve[0], curve[1], curve[2], curve[3]);
                 if (draw_tangents != 2) {
                     if (curve.length > 4) drawLine(curve[2], curve[3], curve[4], curve[5]);
-                    if (curve.length > 6) drawLine(curve[4], curve[5], curve[6], curve[7]);
+                    if (curve.length == 8) drawLine(curve[4], curve[5], curve[6], curve[7]);
                 }
                 if (draw_tangents != 1) {
-                    if (curve.length == 6) drawLine(curve[0], curve[1], curve[4], curve[5]);
+                    if (curve.length == 6 || curve.length == 7) {
+                        drawLine(curve[0], curve[1], curve[4], curve[5]);
+                    }
                     if (curve.length == 8) drawLine(curve[0], curve[1], curve[6], curve[7]);
                 }
             }
@@ -1784,6 +752,8 @@
                 ctx.quadraticCurveTo(
                     (curve[2] - srcLeft) * scale, (curve[3] - srcTop) * scale,
                     (curve[4] - srcLeft) * scale, (curve[5] - srcTop) * scale);
+            } else if (curve.length == 7) {
+                drawConic(curve, srcLeft, srcTop, scale);
             } else {
                 ctx.bezierCurveTo(
                     (curve[2] - srcLeft) * scale, (curve[3] - srcTop) * scale,
@@ -1796,11 +766,16 @@
                 ctx.strokeStyle = "rgba(0,0,255, 1)";
             }
             ctx.stroke();
-            if (draw_endpoints) {
+            if (draw_endpoints > 0) {
                 drawPoint(curve[0], curve[1]);
-                drawPoint(curve[2], curve[3]);
-                if (curve.length > 4) drawPoint(curve[4], curve[5]);
-                if (curve.length > 6) drawPoint(curve[6], curve[7]);
+                if (draw_endpoints > 1 || curve.length == 4) {
+                    drawPoint(curve[2], curve[3]);
+                }
+                if (curve.length == 6 || curve.length == 7 ||
+                        (draw_endpoints > 1 && curve.length == 8)) {
+                    drawPoint(curve[4], curve[5]);
+                }
+                if (curve.length == 8) drawPoint(curve[6], curve[7]);
             }
             if (draw_midpoint != 0) {
                 if ((curves == 0) == (midLeft == 0)) {
@@ -1848,7 +823,7 @@
             }
             if (draw_ray_intersect != 0) {
                 ctx.strokeStyle = "rgba(75,45,199, 0.6)";
-                if (curve.length == 6 || curve.length == 8) {
+                if (curve.length >= 6 && curve.length <= 8) {
                     var intersections = intersect[intersectIndex];
                     for (var i in intersections) {
                         var intersection = intersections[i];
@@ -1902,10 +877,39 @@
             if (draw_t) {
                 drawPointAtT(curve);
             }
+            if (draw_id != 0) {
+                var id = -1;
+                for (var i = 0; i < ids.length; i += 2) {
+                    if (ids[i + 1] == curve) {
+                        id = ids[i];
+                        break;
+                    }
+                }
+                if (id >= 0) {
+                    var px = x_at_t(curve, 0.5);
+                    var py = y_at_t(curve, 0.5);
+                    var _px = (px - srcLeft) * scale;
+                    var _py = (py - srcTop) * scale;
+                    ctx.beginPath();
+                    ctx.arc(_px, _py, 15, 0, Math.PI * 2, true);
+                    ctx.closePath();
+                    ctx.fillStyle = "white";
+                    ctx.fill();
+                    ctx.strokeStyle = "rgba(255,0,0, 1)";
+                    ctx.fillStyle = "rgba(255,0,0, 1)";
+                    ctx.stroke();
+                    ctx.font = "normal 16px Arial";
+                    ctx.textAlign = "center";
+                    ctx.fillText(id, _px, _py + 5);
+                }
+            }
         }
         if (draw_t) {
             drawCurveTControl();
         }
+        if (draw_w) {
+            drawCurveWControl();
+        }
     }
 
     function drawCurveTControl() {
@@ -1928,6 +932,42 @@
         ctx.fillText(num, screenWidth - 78, ty);
     }
 
+    function drawCurveWControl() {
+        var w = -1;
+        var choice = 0;
+        for (var curves in tests[testIndex]) {
+            var curve = tests[testIndex][curves];
+            if (curve.length != 7) {
+                continue;
+            }
+            if (choice == curveW) {
+                w = curve[6];
+                break;
+            }
+            ++choice;
+        }
+        if (w < 0) {
+            return;
+        }
+        ctx.lineWidth = 2;
+        ctx.strokeStyle = "rgba(0,0,0, 0.3)";
+        ctx.beginPath();
+        ctx.rect(screenWidth - 40, 40, 28, screenHeight - 80);
+        ctx.stroke();
+        var ty = 40 + w * (screenHeight - 80);
+        ctx.beginPath();
+        ctx.moveTo(screenWidth - 40, ty);
+        ctx.lineTo(screenWidth - 45, ty - 5);
+        ctx.lineTo(screenWidth - 45, ty + 5);
+        ctx.lineTo(screenWidth - 40, ty);
+        ctx.fillStyle = "rgba(0,0,0, 0.6)";
+        ctx.fill();
+        var num = w.toFixed(decimal_places);
+        ctx.font = "normal 10px Arial";
+        ctx.textAlign = "left";
+        ctx.fillText(num, screenWidth - 38, ty);
+    }
+
     function ptInTControl() {
         var e = window.event;
         var tgt = e.target || e.srcElement;
@@ -1948,12 +988,66 @@
         return true;
     }
 
+    function ptInWControl() {
+        var e = window.event;
+        var tgt = e.target || e.srcElement;
+        var left = tgt.offsetLeft;
+        var top = tgt.offsetTop;
+        var x = (e.clientX - left);
+        var y = (e.clientY - top);
+        if (x < screenWidth - 40 || x > screenWidth - 10) {
+            return false;
+        }
+        if (y < 40 || y > screenHeight - 80) {
+            return false;
+        }
+        var w = (y - 40) / (screenHeight - 120);
+        if (w < 0 || w > 1) {
+            throw "stop execution";
+        }
+        var choice = 0;
+        for (var curves in tests[testIndex]) {
+            var curve = tests[testIndex][curves];
+            if (curve.length != 7) {
+                continue;
+            }
+            if (choice == curveW) {
+                curve[6] = w;
+                break;
+            }
+            ++choice;
+        }
+        return true;
+    }
+
     function drawTop() {
         init(tests[testIndex]);
         redraw();
     }
 
     function redraw() {
+        if (focus_on_selection > 0) {
+            var focusXmin = focusYmin = Infinity;
+            var focusXmax = focusYmax = -Infinity;
+            var choice = 0;
+            for (var curves in tests[testIndex]) {
+                if (++choice != focus_on_selection) {
+                    continue;
+                }
+                var curve = tests[testIndex][curves];
+                var last = curve.length - (curve.length % 2 == 1 ? 1 : 0);
+                for (var idx = 0; idx < last; idx += 2) {
+                    focusXmin = Math.min(focusXmin, curve[idx]);
+                    focusXmax = Math.max(focusXmax, curve[idx]);
+                    focusYmin = Math.min(focusYmin, curve[idx + 1]);
+                    focusYmax = Math.max(focusYmax, curve[idx + 1]);
+                }
+            }
+            focusXmin -= Math.min(1, Math.max(focusXmax - focusXmin, focusYmax - focusYmin));
+            if (focusXmin < focusXmax && focusYmin < focusYmax) {
+                setScale(focusXmin, focusXmax, focusYmin, focusYmax);
+            }
+        }
         ctx.beginPath();
         ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
         ctx.fillStyle = "white";
@@ -1963,6 +1057,7 @@
 
     function doKeyPress(evt) {
         var char = String.fromCharCode(evt.charCode);
+        var focusWasOn = false;
         switch (char) {
             case '0':
             case '1':
@@ -1978,15 +1073,29 @@
                 redraw();
                 break;
             case '-':
-                scale /= 2;
+                focusWasOn = focus_on_selection;
+                if (focusWasOn) {
+                    focus_on_selection = false;
+                    scale /= 1.2;
+                } else {
+                    scale /= 2;
+                }
                 calcLeftTop();
                 redraw();
+                focus_on_selection = focusWasOn;
                 break;
             case '=':
             case '+':
-                scale *= 2;
+                focusWasOn = focus_on_selection;
+                if (focusWasOn) {
+                    focus_on_selection = false;
+                    scale *= 1.2;
+                } else {
+                    scale *= 2;
+                }
                 calcLeftTop();
                 redraw();
+                focus_on_selection = focusWasOn;
                 break;
             case 'b':
                 draw_cubic_red ^= true;
@@ -2012,7 +1121,7 @@
                 redraw();
                 break;
             case 'e':
-                draw_endpoints ^= true;
+                draw_endpoints = (draw_endpoints + 1) % 3;
                 redraw();
                 break;
             case 'f':
@@ -2096,6 +1205,26 @@
                 draw_tangents = (draw_tangents + 1) % 4;
                 redraw();
                 break;
+            case 'w':
+                ++curveW;
+                var choice = 0;
+                draw_w = false;
+                for (var curves in tests[testIndex]) {
+                    var curve = tests[testIndex][curves];
+                    if (curve.length != 7) {
+                        continue;
+                    }
+                    if (choice == curveW) {
+                        draw_w = true;
+                        break;
+                    }
+                    ++choice;
+                }
+                if (!draw_w) {
+                    curveW = -1;
+                }
+                redraw();
+                break;
             case 'x':
                 draw_point_xy ^= true;
                 redraw();
@@ -2108,6 +1237,18 @@
                 retina_scale ^= true;
                 drawTop();
                 break;
+            case '`':
+                ++focus_on_selection;
+                if (focus_on_selection >= tests[testIndex].length) {
+                    focus_on_selection = 0;
+                }
+                setScale(xmin, xmax, ymin, ymax);
+                redraw();
+                break;
+            case '.':
+                draw_id = (draw_id + 1) % 3;
+                redraw();
+                break;
         }
     }
 
@@ -2121,7 +1262,11 @@
                 }
                 if (--testIndex < 0)
                     testIndex = tests.length - 1;
-                drawTop();
+                if (evt.ctrlKey) {
+                    redraw();
+                } else {
+                    drawTop();
+                }
                 preventDefault = true;
                 break;
             case 39: // right arrow
@@ -2130,7 +1275,11 @@
                 }
                 if (++testIndex >= tests.length)
                     testIndex = 0;
-                drawTop();
+                if (evt.ctrlKey) {
+                    redraw();
+                } else {
+                    drawTop();
+                }
                 preventDefault = true;
                 break;
         }
@@ -2156,7 +1305,7 @@
     }
 
     function handleMouseClick() {
-        if (!draw_t || !ptInTControl()) {
+        if ((!draw_t || !ptInTControl()) && (!draw_w || !ptInWControl())) {
             calcXY();
         } else {
             redraw();
@@ -2169,10 +1318,11 @@
         activePt = -1;
         for (var curves in test) {
             var testCurve = test[curves];
-            if (testCurve.length != 4 && testCurve.length != 6 && testCurve.length != 8) {
+            if (testCurve.length != 4 && (testCurve.length < 6 || testCurve.length > 8)) {
                 continue;
             }
-            for (var i = 0; i < testCurve.length; i += 2) {
+            var testMax = testCurve.length == 7 ? 6 : testCurve.length;
+            for (var i = 0; i < testMax; i += 2) {
                 var testX = testCurve[i];
                 var testY = testCurve[i + 1];
                 var dx = testX - mouseX;
diff --git a/tools/pathops_visualizer.htm b/tools/pathops_visualizer.htm
index f5482fd..ad60633 100644
--- a/tools/pathops_visualizer.htm
+++ b/tools/pathops_visualizer.htm
@@ -1,471 +1,759 @@
 <html>
 <head>
 <div height="0" hidden="true">
-<div id="fuzz763_1026368">
-  RunTestSet [fuzz763_1026368]
 
-{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}},
-{{33,25}, {36.3137093,25}, {38.6568527,27.3431454}},
-{{38.6568527,27.3431454}, {41,29.6862907}, {41,33}},
-{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}},
-{{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}},
-{{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}},
-{{38.6341171,38.6795731}, {38.6284409,38.6852493}, {38.6227531,38.6909218}},
-{{38.6227531,38.6909218}, {36.2775421,41.0320053}, {32.9638329,41.0290833}},
-{{32.9638329,41.0290833}, {29.6501274,41.0261612}, {27.3090477,38.6809464}},
-{{27.3090477,38.6809464}, {24.9679718,36.3357391}, {24.9708939,33.0220299}},
-{{24.9708939,33.0220299}, {24.973814,29.7083225}, {27.319025,27.3672428}},
-{{27.319025,27.3672428}, {27.3209743,27.3652973}, {27.3229256,27.3633518}},
-{{27.3229256,27.3633518}, {27.324995,27.3612823}, {27.3270645,27.3592148}},
-{{27.3270645,27.3592148}, {27.3312511,27.355032}, {27.3354416,27.3508568}},
-{{27.3354416,27.3508568}, {27.3332844,27.3530178}, {27.331131,27.3551788}},
-{{27.331131,27.3551788}, {27.3369579,27.3493824}, {27.3431988,27.3431988}},
-{{27.3431988,27.3431988}, {27.3431454,27.3431454}},
-{{38.6398277,38.6738319}, {38.6447258,38.6689186}},
-{{38.6447258,38.6689186}, {38.6447449,38.6689377}},
-{{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}},
+<div id="issue3651_7">
+seg=1 {{{68.1999969f, 74.8000031f}, {68.4444427f, 74.8000031f}, {68.6888885f, 74.8039017f}, {68.9333344f, 74.8078003f}}}
+seg=2 {{{68.9333344f, 74.8078003f}, {69.422226f, 74.8155975f}, {69.9111099f, 74.8233948f}, {70.4000015f, 74.8000031f}}}
+seg=3 {{{70.4000015f, 74.8000031f}, {70.7796326f, 74.7818375f}, {71.1592636f, 74.7372665f}, {71.5388947f, 74.6926956f}}}
+seg=4 {{{71.5388947f, 74.6926956f}, {71.8925934f, 74.6511688f}, {72.2462997f, 74.609642f}, {72.5999985f, 74.5894699f}}}
+seg=5 {{{72.5999985f, 74.5894699f}, {73.1301117f, 74.5592346f}, {73.6602249f, 74.5564957f}, {74.1903381f, 74.5537567f}}}
+seg=6 {{{74.1903381f, 74.5537567f}, {74.3935623f, 74.5527115f}, {74.5967789f, 74.5516663f}, {74.8000031f, 74.5490646f}}}
+seg=7 {{{74.8000031f, 74.5490646f}, {75.0211792f, 74.5462341f}, {75.242363f, 74.5398636f}, {75.4635391f, 74.5335007f}}}
+seg=8 {{{75.4635391f, 74.5335007f}, {75.9756927f, 74.5187607f}, {76.4878464f, 74.5040207f}, {77, 74.5332108f}}}
+seg=9 {{{77, 74.5332108f}, {77.3702316f, 74.5543137f}, {77.7404709f, 74.610733f}, {78.1107025f, 74.66716f}}}
+seg=10 {{{78.1107025f, 74.66716f}, {78.4738007f, 74.722496f}, {78.8368988f, 74.777832f}, {79.1999969f, 74.7998505f}}}
+seg=11 {{{79.1999969f, 74.7998505f}, {79.689003f, 74.8295059f}, {80.1780014f, 74.819664f}, {80.6670074f, 74.8098221f}}}
+seg=12 {{{80.6670074f, 74.8098221f}, {80.9113388f, 74.8049088f}, {81.1556702f, 74.7999954f}, {81.4000015f, 74.8000031f}}}
+seg=13 {{{81.4000015f, 74.8000031f}, {81.6438522f, 74.8000107f}, {81.8877106f, 74.8010635f}, {82.1315613f, 74.8021164f}}}
+seg=14 {{{82.1315613f, 74.8021164f}, {82.6210403f, 74.8042297f}, {83.1105194f, 74.8063507f}, {83.5999985f, 74.8000031f}}}
+seg=15 {{{83.5999985f, 74.8000031f}, {83.9259109f, 74.7957764f}, {84.2518234f, 74.7840347f}, {84.5777359f, 74.7722931f}}}
+seg=16 {{{84.5777359f, 74.7722931f}, {84.9851608f, 74.7576141f}, {85.3925781f, 74.7429352f}, {85.8000031f, 74.7429352f}}}
+seg=17 {{{85.8000031f, 74.7429352f}, {86.2074203f, 74.7429352f}, {86.6148453f, 74.7576141f}, {87.0222626f, 74.7722931f}}}
+seg=18 {{{87.0222626f, 74.7722931f}, {87.348175f, 74.7840347f}, {87.6740875f, 74.7957764f}, {88, 74.8000031f}}}
+seg=19 {{{88, 74.8000031f}, {88.1576462f, 74.8020477f}, {88.3152924f, 74.8048859f}, {88.4729385f, 74.8077316f}}}
+seg=20 {{{88.4729385f, 74.8077316f}, {89.0486221f, 74.8181076f}, {89.6243134f, 74.8284836f}, {90.1999969f, 74.8000031f}}}
+seg=21 {{{90.1999969f, 74.8000031f}, {90.5620499f, 74.7820892f}, {90.9241028f, 74.7369537f}, {91.2861557f, 74.6918182f}}}
+seg=22 {{{91.2861557f, 74.6918182f}, {91.6574402f, 74.6455307f}, {92.028717f, 74.5992432f}, {92.4000015f, 74.5823135f}}}
+seg=23 {{{92.4000015f, 74.5823135f}, {92.9111481f, 74.5590057f}, {93.4223022f, 74.5721283f}, {93.9334488f, 74.5852509f}}}
+seg=24 {{{93.9334488f, 74.5852509f}, {94.155632f, 74.59095f}, {94.3778152f, 74.5966568f}, {94.5999985f, 74.5993652f}}}
+seg=25 {{{94.5999985f, 74.5993652f}, {94.8669891f, 74.602623f}, {95.1339798f, 74.6110153f}, {95.4009705f, 74.6194f}}}
+seg=26 {{{95.4009705f, 74.6194f}, {95.8673172f, 74.6340485f}, {96.3336563f, 74.6486969f}, {96.8000031f, 74.6360168f}}}
+seg=27 {{{96.8000031f, 74.6360168f}, {97.2396164f, 74.624054f}, {97.6792297f, 74.5860596f}, {98.1188431f, 74.5480652f}}}
+seg=28 {{{98.1188431f, 74.5480652f}, {98.4125595f, 74.5226822f}, {98.7062836f, 74.4972992f}, {99, 74.4796829f}}}
+seg=29 {{{99, 74.4796829f}, {99.7333298f, 74.4356995f}, {100.466667f, 74.3924866f}, {101.199997f, 74.3721085f}}}
+seg=30 {{{101.199997f, 74.3721085f}, {101.6632f, 74.3592453f}, {102.126396f, 74.3594589f}, {102.5896f, 74.3596725f}}}
+seg=31 {{{102.5896f, 74.3596725f}, {102.859734f, 74.3597946f}, {103.129868f, 74.3599167f}, {103.400002f, 74.3574448f}}}
+seg=32 {{{103.400002f, 74.3574448f}, {103.499123f, 74.3565369f}, {103.598244f, 74.3556519f}, {103.697365f, 74.3547668f}}}
+seg=33 {{{103.697365f, 74.3547668f}, {104.331573f, 74.3491211f}, {104.96579f, 74.3434753f}, {105.599998f, 74.3318787f}}}
+seg=34 {{{105.599998f, 74.3318787f}, {105.951164f, 74.3254547f}, {106.30233f, 74.3153458f}, {106.653496f, 74.3052368f}}}
+seg=35 {{{106.653496f, 74.3052368f}, {107.035667f, 74.2942352f}, {107.417831f, 74.2832336f}, {107.800003f, 74.2769852f}}}
+seg=36 {{{107.800003f, 74.2769852f}, {107.904305f, 74.2752838f}, {108.008606f, 74.2734833f}, {108.112907f, 74.2716827f}}}
+seg=37 {{{108.112907f, 74.2716827f}, {108.741936f, 74.2608261f}, {109.370972f, 74.2499771f}, {110, 74.2599411f}}}
+seg=38 {{{110, 74.2599411f}, {110.73333f, 74.2715607f}, {111.466667f, 74.3080215f}, {112.199997f, 74.3467178f}}}
+seg=39 {{{112.199997f, 74.3467178f}, {112.524193f, 74.3638306f}, {112.848389f, 74.3887024f}, {113.172585f, 74.4135742f}}}
+seg=40 {{{113.172585f, 74.4135742f}, {113.581726f, 74.4449615f}, {113.99086f, 74.4763565f}, {114.400002f, 74.4921417f}}}
+seg=41 {{{114.400002f, 74.4921417f}, {115.133331f, 74.5204391f}, {115.866669f, 74.5265198f}, {116.599998f, 74.5165176f}}}
+seg=42 {{{116.599998f, 74.5165176f}, {117.013039f, 74.5108871f}, {117.426071f, 74.4911652f}, {117.839111f, 74.4714432f}}}
+seg=43 {{{117.839111f, 74.4714432f}, {118.159409f, 74.4561462f}, {118.479706f, 74.4408493f}, {118.800003f, 74.4321289f}}}
+seg=44 {{{118.800003f, 74.4321289f}, {118.915619f, 74.428978f}, {119.031235f, 74.4256439f}, {119.146851f, 74.4223099f}}}
+seg=45 {{{119.146851f, 74.4223099f}, {119.764565f, 74.4045105f}, {120.382286f, 74.3867035f}, {121, 74.3966675f}}}
+seg=46 {{{121, 74.3966675f}, {121.552635f, 74.405571f}, {122.105278f, 74.4385529f}, {122.657913f, 74.4715347f}}}
+seg=47 {{{122.657913f, 74.4715347f}, {122.838608f, 74.4823227f}, {123.019302f, 74.4931107f}, {123.199997f, 74.5030518f}}}
+seg=48 {{{123.199997f, 74.5030518f}, {123.480431f, 74.5184784f}, {123.760864f, 74.5394821f}, {124.041298f, 74.5604858f}}}
+seg=49 {{{124.041298f, 74.5604858f}, {124.494202f, 74.5943985f}, {124.947098f, 74.6283112f}, {125.400002f, 74.6387558f}}}
+seg=50 {{{125.400002f, 74.6387558f}, {126.133331f, 74.6556625f}, {126.866669f, 74.6397476f}, {127.599998f, 74.6044846f}}}
+seg=51 {{{127.599998f, 74.6044846f}, {128.17691f, 74.5767441f}, {128.75383f, 74.5233994f}, {129.33075f, 74.4700623f}}}
+seg=52 {{{129.33075f, 74.4700623f}, {129.487167f, 74.4555969f}, {129.643585f, 74.4411316f}, {129.800003f, 74.4271774f}}}
+seg=53 {{{129.800003f, 74.4271774f}, {130.08493f, 74.4017639f}, {130.369843f, 74.3696442f}, {130.65477f, 74.3375244f}}}
+seg=54 {{{130.65477f, 74.3375244f}, {131.10318f, 74.2869797f}, {131.55159f, 74.2364349f}, {132, 74.2120285f}}}
+seg=55 {{{132, 74.2120285f}, {132.682098f, 74.1748962f}, {133.364182f, 74.1806335f}, {134.04628f, 74.1863708f}}}
+seg=56 {{{134.04628f, 74.1863708f}, {134.199997f, 74.1876526f}}}
+seg=57 {{{134.199997f, 74.1876526f}, {134.740479f, 74.1920013f}, {135.28096f, 74.2102814f}, {135.821426f, 74.2285538f}}}
+seg=58 {{{135.821426f, 74.2285538f}, {136.014282f, 74.2350769f}, {136.207138f, 74.2416f}, {136.399994f, 74.2474899f}}}
+seg=59 {{{136.399994f, 74.2474899f}, {136.723831f, 74.2573776f}, {137.047668f, 74.2692032f}, {137.371506f, 74.2810364f}}}
+seg=60 {{{137.371506f, 74.2810364f}, {137.781006f, 74.2959976f}, {138.190506f, 74.3109589f}, {138.600006f, 74.3219833f}}}
+seg=61 {{{138.600006f, 74.3219833f}, {138.775055f, 74.3266983f}, {138.950119f, 74.3321457f}, {139.125183f, 74.3375931f}}}
+seg=62 {{{139.125183f, 74.3375931f}, {139.683456f, 74.3549652f}, {140.24173f, 74.3723373f}, {140.800003f, 74.3659744f}}}
+seg=63 {{{140.800003f, 74.3659744f}, {141.173523f, 74.3617172f}, {141.547028f, 74.3401871f}, {141.920547f, 74.3186493f}}}
+seg=64 {{{141.920547f, 74.3186493f}, {142.280365f, 74.297905f}, {142.640182f, 74.2771606f}, {143, 74.2718658f}}}
+seg=65 {{{143, 74.2718658f}, {143.733337f, 74.2610703f}, {144.46666f, 74.2575989f}, {145.199997f, 74.3011856f}}}
+seg=66 {{{145.199997f, 74.3011856f}, {145.574966f, 74.3234711f}, {145.949936f, 74.3711548f}, {146.32489f, 74.4188385f}}}
+seg=67 {{{146.32489f, 74.4188385f}, {146.683258f, 74.4644165f}, {147.041626f, 74.5099869f}, {147.399994f, 74.5333862f}}}
+seg=68 {{{147.399994f, 74.5333862f}, {148.133331f, 74.5812607f}, {148.866669f, 74.6018982f}, {149.600006f, 74.5884552f}}}
+seg=69 {{{149.600006f, 74.5884552f}, {149.982086f, 74.5814514f}, {150.364182f, 74.5504913f}, {150.746277f, 74.5195313f}}}
+seg=70 {{{150.746277f, 74.5195313f}, {151.097519f, 74.491066f}, {151.448761f, 74.4626007f}, {151.800003f, 74.4527512f}}}
+seg=71 {{{151.800003f, 74.4527512f}, {152.343307f, 74.4375076f}, {152.886597f, 74.4468765f}, {153.429901f, 74.4562454f}}}
+seg=72 {{{153.429901f, 74.4562454f}, {153.619934f, 74.4595261f}, {153.809967f, 74.4627991f}, {154, 74.4650269f}}}
+seg=73 {{{154, 74.4650269f}, {154.295746f, 74.4684906f}, {154.591476f, 74.4761581f}, {154.887222f, 74.4838257f}}}
+seg=74 {{{154.887222f, 74.4838257f}, {155.324814f, 74.4951782f}, {155.762405f, 74.5065308f}, {156.199997f, 74.5042419f}}}
+seg=75 {{{156.199997f, 74.5042419f}, {156.559143f, 74.5023651f}, {156.918289f, 74.487793f}, {157.27742f, 74.4732208f}}}
+seg=76 {{{157.27742f, 74.4732208f}, {157.651611f, 74.4580307f}, {158.025803f, 74.4428406f}, {158.399994f, 74.4420166f}}}
+seg=77 {{{158.399994f, 74.4420166f}, {159.133331f, 74.4403992f}, {159.866669f, 74.448494f}, {160.600006f, 74.4945221f}}}
+seg=78 {{{160.600006f, 74.4945221f}, {160.952393f, 74.5166473f}, {161.304794f, 74.5602722f}, {161.657196f, 74.6038971f}}}
+seg=79 {{{161.657196f, 74.6038971f}, {162.038132f, 74.6510468f}, {162.419067f, 74.698204f}, {162.800003f, 74.7182007f}}}
+seg=80 {{{162.800003f, 74.7182007f}, {163.53334f, 74.7566986f}, {164.266663f, 74.7636337f}, {165, 74.7255325f}}}
+seg=81 {{{165, 74.7255325f}, {165.356293f, 74.7070236f}, {165.712585f, 74.65802f}, {166.068878f, 74.6090164f}}}
+seg=82 {{{166.068878f, 74.6090164f}, {166.445923f, 74.5571594f}, {166.822952f, 74.5053024f}, {167.199997f, 74.4895782f}}}
+seg=83 {{{167.199997f, 74.4895782f}, {167.933334f, 74.4590073f}, {168.666672f, 74.4903488f}, {169.399994f, 74.5420837f}}}
+seg=84 {{{169.399994f, 74.5420837f}, {169.752808f, 74.5669785f}, {170.105621f, 74.6176682f}, {170.458435f, 74.6683578f}}}
+seg=85 {{{170.458435f, 74.6683578f}, {170.838959f, 74.7230225f}, {171.219482f, 74.7776947f}, {171.600006f, 74.8000031f}}}
+seg=86 {{{171.600006f, 74.8000031f}, {172.333328f, 74.8429871f}, {173.066666f, 74.8376999f}, {173.800003f, 74.8000031f}}}
+seg=87 {{{173.800003f, 74.8000031f}, {174.500809f, 74.7639694f}, {175.201599f, 74.6750717f}, {175.902405f, 74.5861664f}}}
+seg=88 {{{175.902405f, 74.5861664f}, {176, 74.5737915f}}}
+seg=89 {{{176, 74.5737915f}, {176.306427f, 74.5349655f}, {176.612839f, 74.4810333f}, {176.919266f, 74.4270935f}}}
+seg=90 {{{176.919266f, 74.4270935f}, {177.346176f, 74.3519516f}, {177.773087f, 74.2768097f}, {178.199997f, 74.2425385f}}}
+seg=91 {{{178.199997f, 74.2425385f}, {178.933334f, 74.18367f}, {179.666672f, 74.1942673f}, {180.399994f, 74.2205505f}}}
+seg=92 {{{180.399994f, 74.2205505f}, {180.822174f, 74.2356796f}, {181.244354f, 74.2772751f}, {181.666534f, 74.3188705f}}}
+seg=93 {{{181.666534f, 74.3188705f}, {181.977692f, 74.3495255f}, {182.288849f, 74.3801804f}, {182.600006f, 74.400238f}}}
+seg=94 {{{182.600006f, 74.400238f}, {183.153549f, 74.435936f}, {183.707108f, 74.4587555f}, {184.260666f, 74.481575f}}}
+seg=95 {{{184.260666f, 74.481575f}, {184.440445f, 74.4889832f}, {184.620224f, 74.4963913f}, {184.800003f, 74.5042419f}}}
+seg=96 {{{184.800003f, 74.5042419f}, {185.53334f, 74.5362625f}, {186.266663f, 74.5669022f}, {187, 74.5923843f}}}
+seg=97 {{{187, 74.5923843f}, {187.218643f, 74.5999832f}, {187.437286f, 74.6105118f}, {187.65593f, 74.6210403f}}}
+seg=98 {{{187.65593f, 74.6210403f}, {188.170624f, 74.6458359f}, {188.685303f, 74.6706314f}, {189.199997f, 74.6571732f}}}
+seg=99 {{{189.199997f, 74.6571732f}, {189.560562f, 74.6477432f}, {189.921127f, 74.6077042f}, {190.281693f, 74.5676651f}}}
+seg=100 {{{190.281693f, 74.5676651f}, {190.654465f, 74.526268f}, {191.027237f, 74.4848709f}, {191.399994f, 74.4773026f}}}
+seg=101 {{{191.399994f, 74.4773026f}, {191.942627f, 74.4662857f}, {192.48526f, 74.5001678f}, {193.027893f, 74.5340576f}}}
+seg=102 {{{193.027893f, 74.5340576f}, {193.218597f, 74.5459671f}, {193.409302f, 74.5578766f}, {193.600006f, 74.5678329f}}}
+seg=103 {{{193.600006f, 74.5678329f}, {193.897125f, 74.5833511f}, {194.19426f, 74.6063919f}, {194.491394f, 74.6294403f}}}
+seg=104 {{{194.491394f, 74.6294403f}, {194.927597f, 74.663269f}, {195.3638f, 74.6970978f}, {195.800003f, 74.7071152f}}}
+seg=105 {{{195.800003f, 74.7071152f}, {196.363327f, 74.7200546f}, {196.926636f, 74.7021637f}, {197.48996f, 74.6842728f}}}
+seg=106 {{{197.48996f, 74.6842728f}, {197.659973f, 74.6788712f}, {197.829987f, 74.6734695f}, {198, 74.6689148f}}}
+seg=107 {{{198, 74.6689148f}, {198.539948f, 74.6544571f}, {199.07988f, 74.6331635f}, {199.619827f, 74.6118698f}}}
+seg=108 {{{199.619827f, 74.6118698f}, {199.813217f, 74.6042404f}, {200.006607f, 74.596611f}, {200.199997f, 74.5892944f}}}
+seg=109 {{{200.199997f, 74.5892944f}, {200.468765f, 74.5791321f}, {200.737534f, 74.5671921f}, {201.006287f, 74.5552597f}}}
+seg=110 {{{201.006287f, 74.5552597f}, {201.470856f, 74.5346298f}, {201.935425f, 74.5139999f}, {202.399994f, 74.5025177f}}}
+seg=111 {{{202.399994f, 74.5025177f}, {202.888885f, 74.4904327f}, {203.377777f, 74.4879456f}, {203.866669f, 74.4854507f}}}
+seg=112 {{{203.866669f, 74.4854507f}, {204.111115f, 74.4842072f}, {204.35556f, 74.4829636f}, {204.600006f, 74.4805222f}}}
+seg=113 {{{204.600006f, 74.4805222f}, {204.84462f, 74.4780731f}, {205.089233f, 74.476059f}, {205.333847f, 74.4740448f}}}
+seg=114 {{{205.333847f, 74.4740448f}, {205.822556f, 74.4700241f}, {206.311279f, 74.4659958f}, {206.800003f, 74.4585342f}}}
+seg=115 {{{206.800003f, 74.4585342f}, {207.53334f, 74.4473343f}, {208.266663f, 74.4324722f}, {209, 74.413353f}}}
+seg=116 {{{209, 74.413353f}, {209.350708f, 74.404213f}, {209.701416f, 74.3919983f}, {210.052124f, 74.3797836f}}}
+seg=117 {{{210.052124f, 74.3797836f}, {210.434753f, 74.3664627f}, {210.817368f, 74.3531418f}, {211.199997f, 74.3438034f}}}
+seg=118 {{{211.199997f, 74.3438034f}, {211.933334f, 74.3259048f}, {212.666672f, 74.3128586f}, {213.399994f, 74.305954f}}}
+seg=119 {{{213.399994f, 74.305954f}, {214.133331f, 74.2990494f}, {214.866669f, 74.2958145f}, {215.600006f, 74.3023758f}}}
+seg=120 {{{215.600006f, 74.3023758f}, {216.076187f, 74.3066406f}, {216.552383f, 74.317627f}, {217.02858f, 74.3286133f}}}
+seg=121 {{{217.02858f, 74.3286133f}, {217.285721f, 74.334549f}, {217.542862f, 74.340477f}, {217.800003f, 74.3453522f}}}
+seg=122 {{{217.800003f, 74.3453522f}, {218.041779f, 74.3499298f}, {218.283554f, 74.358696f}, {218.52533f, 74.3674622f}}}
+seg=123 {{{218.52533f, 74.3674622f}, {219.016891f, 74.3852844f}, {219.508438f, 74.4031067f}, {220, 74.3857574f}}}
+seg=124 {{{220, 74.3857574f}, {220.409988f, 74.3712845f}, {220.819977f, 74.3263474f}, {221.229965f, 74.2814102f}}}
+seg=125 {{{221.229965f, 74.2814102f}, {221.553314f, 74.2459717f}, {221.876648f, 74.2105331f}, {222.199997f, 74.190033f}}}
+seg=126 {{{222.199997f, 74.190033f}, {222.933334f, 74.1435471f}, {223.666672f, 74.1037674f}, {224.399994f, 74.1068344f}}}
+seg=127 {{{224.399994f, 74.1068344f}, {224.885803f, 74.1088638f}, {225.371613f, 74.1381073f}, {225.857422f, 74.1673508f}}}
+seg=128 {{{225.857422f, 74.1673508f}, {226.10495f, 74.182251f}, {226.352478f, 74.1971512f}, {226.600006f, 74.2084503f}}}
+seg=129 {{{226.600006f, 74.2084503f}, {226.839722f, 74.2193909f}, {227.079437f, 74.2324677f}, {227.319153f, 74.2455521f}}}
+seg=130 {{{227.319153f, 74.2455521f}, {227.812759f, 74.2724838f}, {228.306381f, 74.2994156f}, {228.800003f, 74.3076782f}}}
+seg=131 {{{228.800003f, 74.3076782f}, {229.309921f, 74.3162155f}, {229.819824f, 74.3054352f}, {230.329742f, 74.2946548f}}}
+seg=132 {{{230.329742f, 74.2946548f}, {230.553162f, 74.2899323f}, {230.776581f, 74.2852097f}, {231, 74.2821121f}}}
+seg=133 {{{231, 74.2821121f}, {231.290405f, 74.2780838f}, {231.580811f, 74.2724228f}, {231.871216f, 74.2667542f}}}
+seg=134 {{{231.871216f, 74.2667542f}, {232.314148f, 74.2581177f}, {232.757065f, 74.2494812f}, {233.199997f, 74.2466507f}}}
+seg=135 {{{233.199997f, 74.2466507f}, {233.343719f, 74.2457352f}, {233.487442f, 74.2448959f}, {233.631165f, 74.2442474f}}}
+seg=136 {{{233.631165f, 74.2442474f}, {234.220779f, 74.2415924f}, {234.810394f, 74.2421951f}, {235.399994f, 74.2539825f}}}
+seg=137 {{{235.399994f, 74.2539825f}, {235.845032f, 74.2628784f}, {236.29007f, 74.2817612f}, {236.735107f, 74.3006439f}}}
+seg=138 {{{236.735107f, 74.3006439f}, {237.023407f, 74.3128738f}, {237.311707f, 74.3251038f}, {237.600006f, 74.3346176f}}}
+seg=139 {{{237.600006f, 74.3346176f}, {238.333328f, 74.3588257f}, {239.066666f, 74.3803329f}, {239.800003f, 74.3992233f}}}
+seg=140 {{{239.800003f, 74.3992233f}, {240.082169f, 74.4064941f}, {240.364334f, 74.4108353f}, {240.6465f, 74.4151764f}}}
+seg=141 {{{240.6465f, 74.4151764f}, {241.097672f, 74.4221191f}, {241.548828f, 74.4290695f}, {242, 74.4479828f}}}
+seg=142 {{{242, 74.4479828f}, {242.416458f, 74.4654388f}, {242.832916f, 74.4942093f}, {243.249374f, 74.5229874f}}}
+seg=143 {{{243.249374f, 74.5229874f}, {243.566254f, 74.5448837f}, {243.883118f, 74.5667801f}, {244.199997f, 74.5836868f}}}
+seg=144 {{{244.199997f, 74.5836868f}, {244.933334f, 74.6228104f}, {245.666672f, 74.6546402f}, {246.399994f, 74.6827393f}}}
+seg=145 {{{246.399994f, 74.6827393f}, {247.133331f, 74.7108383f}, {247.866669f, 74.7327499f}, {248.600006f, 74.7522964f}}}
+seg=146 {{{248.600006f, 74.7522964f}, {248.714218f, 74.7553406f}}}
+seg=147 {{{248.714218f, 74.7553406f}, {249.40947f, 74.7739029f}, {250.104736f, 74.7924576f}, {250.800003f, 74.8000031f}}}
+seg=148 {{{250.800003f, 74.8000031f}, {251.288895f, 74.8053055f}, {251.777771f, 74.8035355f}, {252.266663f, 74.8017731f}}}
+seg=149 {{{252.266663f, 74.8017731f}, {252.511108f, 74.8008881f}, {252.755554f, 74.8000031f}, {253, 74.8000031f}}}
+seg=150 {{{253, 74.8000031f}, {68.1999969f, 74.8000031f}}}
 op union
-{{41,33}, {41,36.3137093}, {38.6568527,38.6568527}},
-{{38.6568527,38.6568527}, {36.3137093,41}, {33,41}},
-{{33,41}, {29.6862907,41}, {27.3431454,38.6568527}},
-{{27.3431454,38.6568527}, {25,36.3137093}, {25,33}},
-{{25,33}, {25,29.6862907}, {27.3431454,27.3431454}},
-{{27.3431454,27.3431454}, {29.6862907,25}, {33,25}},
-{{33,25}, {36.3137093,25}, {38.6568527,27.3431454}},
-{{38.6568527,27.3431454}, {41,29.6862907}, {41,33}},
-debugShowQuadIntersection wtTs[0]=1 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{33,25}} wnTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}
-debugShowQuadLineIntersection wtTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{27.3431454,27.3431454}} wnTs[0]=1 {{27.3431988,27.3431988}, {27.3431454,27.3431454}}
-debugShowQuadIntersection wtTs[0]=1 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} {{38.6568527,27.3431454}} wnTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}
-debugShowQuadIntersection wtTs[0]=1 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}} {{41,33}} wnTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}
-debugShowQuadIntersection wtTs[0]=1 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{38.6568527,38.6568527}} wnTs[0]=0 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}}
-debugShowQuadIntersection wtTs[0]=1 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6447449,38.6689377}} wnTs[0]=0 {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}}
-debugShowQuadIntersection wtTs[0]=1 {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}} {{38.6341171,38.6795731}} wnTs[0]=0 {{38.6341171,38.6795731}, {38.6284409,38.6852493}, {38.6227531,38.6909218}}
-debugShowQuadIntersection wtTs[0]=1 {{38.6341171,38.6795731}, {38.6284409,38.6852493}, {38.6227531,38.6909218}} {{38.6227531,38.6909218}} wnTs[0]=0 {{38.6227531,38.6909218}, {36.2775421,41.0320053}, {32.9638329,41.0290833}}
-debugShowQuadIntersection wtTs[0]=1 {{38.6227531,38.6909218}, {36.2775421,41.0320053}, {32.9638329,41.0290833}} {{32.9638329,41.0290833}} wnTs[0]=0 {{32.9638329,41.0290833}, {29.6501274,41.0261612}, {27.3090477,38.6809464}}
-debugShowQuadIntersection wtTs[0]=1 {{32.9638329,41.0290833}, {29.6501274,41.0261612}, {27.3090477,38.6809464}} {{27.3090477,38.6809464}} wnTs[0]=0 {{27.3090477,38.6809464}, {24.9679718,36.3357391}, {24.9708939,33.0220299}}
-debugShowQuadIntersection wtTs[0]=1 {{27.3090477,38.6809464}, {24.9679718,36.3357391}, {24.9708939,33.0220299}} {{24.9708939,33.0220299}} wnTs[0]=0 {{24.9708939,33.0220299}, {24.973814,29.7083225}, {27.319025,27.3672428}}
-debugShowQuadIntersection wtTs[0]=1 {{24.9708939,33.0220299}, {24.973814,29.7083225}, {27.319025,27.3672428}} {{27.319025,27.3672428}} wnTs[0]=0 {{27.319025,27.3672428}, {27.3209743,27.3652973}, {27.3229256,27.3633518}}
-debugShowQuadIntersection wtTs[0]=1 {{27.319025,27.3672428}, {27.3209743,27.3652973}, {27.3229256,27.3633518}} {{27.3229256,27.3633518}} wnTs[0]=0 {{27.3229256,27.3633518}, {27.324995,27.3612823}, {27.3270645,27.3592148}}
-debugShowQuadIntersection wtTs[0]=1 {{27.3229256,27.3633518}, {27.324995,27.3612823}, {27.3270645,27.3592148}} {{27.3270645,27.3592148}} wnTs[0]=0 {{27.3270645,27.3592148}, {27.3312511,27.355032}, {27.3354416,27.3508568}}
-debugShowQuadIntersection wtTs[0]=1 {{27.3270645,27.3592148}, {27.3312511,27.355032}, {27.3354416,27.3508568}} {{27.3354416,27.3508568}} wnTs[0]=0 {{27.3354416,27.3508568}, {27.3332844,27.3530178}, {27.331131,27.3551788}}
-debugShowQuadIntersection no intersect {{27.3270645,27.3592148}, {27.3312511,27.355032}, {27.3354416,27.3508568}} {{27.331131,27.3551788}, {27.3369579,27.3493824}, {27.3431988,27.3431988}}
-debugShowQuadIntersection wtTs[0]=1 {{27.3354416,27.3508568}, {27.3332844,27.3530178}, {27.331131,27.3551788}} {{27.331131,27.3551788}} wnTs[0]=0 {{27.331131,27.3551788}, {27.3369579,27.3493824}, {27.3431988,27.3431988}}
-debugShowQuadLineIntersection wtTs[0]=1 {{27.331131,27.3551788}, {27.3369579,27.3493824}, {27.3431988,27.3431988}} {{27.3431988,27.3431988}} wnTs[0]=0 {{27.3431988,27.3431988}, {27.3431454,27.3431454}}
-debugShowQuadIntersection wtTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{27.3431454,27.3431454}} wnTs[0]=1 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
-debugShowQuadIntersection wtTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{27.3431454,27.3431454}} wtTs[1]=1 {{33,25}} wnTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} wnTs[1]=1
-debugShowQuadIntersection wtTs[0]=1 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{33,25}} wnTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}
-debugShowQuadIntersection wtTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} {{33,25}} wnTs[0]=1 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}
-debugShowQuadIntersection wtTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} {{33,25}} wtTs[1]=1 {{38.6568527,27.3431454}} wnTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} wnTs[1]=1
-debugShowQuadIntersection wtTs[0]=1 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} {{38.6568527,27.3431454}} wnTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}
-debugShowQuadIntersection wtTs[0]=1 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}} {{41,33}} wnTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}
-debugShowQuadIntersection wtTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}} {{38.6568527,27.3431454}} wnTs[0]=1 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}
-debugShowQuadIntersection wtTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}} {{38.6568527,27.3431454}} wtTs[1]=1 {{41,33}} wnTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}} wnTs[1]=1
-debugShowQuadIntersection wtTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{41,33}} wtTs[1]=1 {{38.6568527,38.6568527}} wnTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} wnTs[1]=1
-debugShowQuadIntersection wtTs[0]=1 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{38.6568527,38.6568527}} wnTs[0]=0 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
-debugShowQuadIntersection wtTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{41,33}} wnTs[0]=1 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}
-debugShowQuadIntersection wtTs[0]=0 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6568527,38.6568527}} wnTs[0]=1 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}}
-debugShowQuadIntersection wtTs[0]=0 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6568527,38.6568527}} wnTs[0]=0 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
-debugShowQuadIntersection wtTs[0]=0.0149880862 {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}} {{38.6445847,38.6690979}} wnTs[0]=0.00261623 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
-debugShowQuadIntersection no intersect {{38.6341171,38.6795731}, {38.6284409,38.6852493}, {38.6227531,38.6909218}} {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
-debugShowQuadIntersection no intersect {{38.6227531,38.6909218}, {36.2775421,41.0320053}, {32.9638329,41.0290833}} {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
-debugShowQuadIntersection no intersect {{38.6227531,38.6909218}, {36.2775421,41.0320053}, {32.9638329,41.0290833}} {{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}
-debugShowQuadIntersection no intersect {{32.9638329,41.0290833}, {29.6501274,41.0261612}, {27.3090477,38.6809464}} {{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}
-debugShowQuadIntersection no intersect {{27.3090477,38.6809464}, {24.9679718,36.3357391}, {24.9708939,33.0220299}} {{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}
-debugShowQuadIntersection no intersect {{24.9708939,33.0220299}, {24.973814,29.7083225}, {27.319025,27.3672428}} {{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}
-debugShowQuadIntersection no intersect {{24.9708939,33.0220299}, {24.973814,29.7083225}, {27.319025,27.3672428}} {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
-debugShowQuadIntersection no intersect {{27.319025,27.3672428}, {27.3209743,27.3652973}, {27.3229256,27.3633518}} {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
-debugShowQuadIntersection no intersect {{27.3229256,27.3633518}, {27.324995,27.3612823}, {27.3270645,27.3592148}} {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
-debugShowQuadIntersection no intersect {{27.3270645,27.3592148}, {27.3312511,27.355032}, {27.3354416,27.3508568}} {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
-debugShowQuadIntersection wtTs[0]=0 {{27.3354416,27.3508568}, {27.3332844,27.3530178}, {27.331131,27.3551788}} {{27.3354416,27.3508568}} wtTs[1]=1 {{27.331131,27.3551788}} wnTs[0]=0.998355 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}} wnTs[1]=0.99743327
-debugShowQuadIntersection wtTs[0]=0.0266527086 {{27.331131,27.3551788}, {27.3369579,27.3493824}, {27.3431988,27.3431988}} {{27.3314419,27.3548698}} wnTs[0]=0.997499 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
-debugShowQuadLineIntersection wtTs[0]=1 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}} {{27.3431454,27.3431454}} wnTs[0]=1 {{27.3431988,27.3431988}, {27.3431454,27.3431454}}
-debugShowQuadLineIntersection wtTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{27.3431454,27.3431454}} wnTs[0]=1 {{27.3431988,27.3431988}, {27.3431454,27.3431454}}
-debugShowQuadLineIntersection no intersect {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6398277,38.6738319}, {38.6447258,38.6689186}}
-debugShowQuadLineIntersection wtTs[0]=1 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6447449,38.6689377}} wnTs[0]=1 {{38.6447258,38.6689186}, {38.6447449,38.6689377}}
-debugShowQuadIntersection wtTs[0]=1 {{38.6568527,38.6568527}, {38.6510239,38.6626816}, {38.6447449,38.6689377}} {{38.6447449,38.6689377}} wnTs[0]=0 {{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}}
-debugShowQuadLineIntersection no intersect {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}} {{38.6398277,38.6738319}, {38.6447258,38.6689186}}
-debugShowQuadLineIntersection wtTs[0]=0 {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}} {{38.6447449,38.6689377}} wnTs[0]=1 {{38.6447258,38.6689186}, {38.6447449,38.6689377}}
-debugShowQuadIntersection wtTs[0]=0 {{38.6447449,38.6689377}, {38.6394348,38.6742554}, {38.6341171,38.6795731}} {{38.6447449,38.6689377}} wnTs[0]=0 {{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}}
-debugShowQuadIntersection wtTs[0]=1 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{38.6568527,38.6568527}} wnTs[0]=0 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}}
-debugShowQuadIntersection wtTs[0]=0 {{41,33}, {41,36.3137093}, {38.6568527,38.6568527}} {{41,33}} wnTs[0]=1 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}
-debugShowQuadIntersection wtTs[0]=1 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}} {{33,41}} wnTs[0]=0 {{33,41}, {29.6862907,41}, {27.3431454,38.6568527}}
-debugShowQuadIntersection wtTs[0]=1 {{33,41}, {29.6862907,41}, {27.3431454,38.6568527}} {{27.3431454,38.6568527}} wnTs[0]=0 {{27.3431454,38.6568527}, {25,36.3137093}, {25,33}}
-debugShowQuadIntersection wtTs[0]=1 {{27.3431454,38.6568527}, {25,36.3137093}, {25,33}} {{25,33}} wnTs[0]=0 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}}
-debugShowQuadIntersection wtTs[0]=1 {{25,33}, {25,29.6862907}, {27.3431454,27.3431454}} {{27.3431454,27.3431454}} wnTs[0]=0 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}}
-debugShowQuadIntersection wtTs[0]=1 {{27.3431454,27.3431454}, {29.6862907,25}, {33,25}} {{33,25}} wnTs[0]=0 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}}
-debugShowQuadIntersection wtTs[0]=1 {{33,25}, {36.3137093,25}, {38.6568527,27.3431454}} {{38.6568527,27.3431454}} wnTs[0]=0 {{38.6568527,27.3431454}, {41,29.6862907}, {41,33}}
-debugShowQuadLineIntersection no intersect {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}} {{38.6398277,38.6738319}, {38.6447258,38.6689186}}
-debugShowQuadLineIntersection no intersect {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}} {{38.6447258,38.6689186}, {38.6447449,38.6689377}}
-debugShowQuadIntersection wtTs[0]=0.00258220891 {{38.6568527,38.6568527}, {36.3137093,41}, {33,41}} {{38.6447449,38.6689377}} wtTs[1]=0.00362998223 {{38.6398277,38.6738319}} wnTs[0]=0 {{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}} wnTs[1]=1
-debugShowLineIntersection wtTs[0]=1 {{38.6398277,38.6738319}, {38.6447258,38.6689186}} {{38.6447258,38.6689186}} wnTs[0]=0 {{38.6447258,38.6689186}, {38.6447449,38.6689377}}
-debugShowQuadLineIntersection wtTs[0]=1 {{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}} {{38.6398277,38.6738319}} wnTs[0]=0 {{38.6398277,38.6738319}, {38.6447258,38.6689186}}
-debugShowQuadLineIntersection wtTs[0]=0 {{38.6447449,38.6689377}, {38.6422882,38.6713867}, {38.6398277,38.6738319}} {{38.6447449,38.6689377}} wnTs[0]=1 {{38.6447258,38.6689186}, {38.6447449,38.6689377}}
-SkOpSegment::debugShowTs - id=0 [o=24,16 t=0 27.3431454,27.3431454 w=1 o=0] [o=26,1 t=1 33,25 w=1 o=0]
-SkOpSegment::debugShowTs o id=25 [o=24,16 t=0 27.3431454,27.3431454 w=1 o=0] [o=26,1 t=1 33,25 w=1 o=0] operand
-SkOpSegment::debugShowTs + id=0 [o=24,16 t=0 27.3431454,27.3431454 w=1 o=0] [o=26,1 t=1 33,25 w=1 o=0]
-SkOpSegment::debugShowTs o id=25 [o=24,16 t=0 27.3431454,27.3431454 w=1 o=0] [o=26,1 t=1 33,25 w=1 o=0] operand
-SkOpSegment::debugShowTs - id=1 [o=25,0 t=0 33,25 w=1 o=0] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0]
-SkOpSegment::debugShowTs o id=26 [o=25,0 t=0 33,25 w=1 o=0] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0] operand
-SkOpSegment::debugShowTs + id=1 [o=25,0 t=0 33,25 w=1 o=0] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0]
-SkOpSegment::debugShowTs o id=26 [o=25,0 t=0 33,25 w=1 o=0] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0] operand
-SkOpSegment::debugShowTs - id=2 [o=26,1 t=0 38.6568527,27.3431454 w=1 o=0] [o=20,3 t=1 41,33 w=1 o=0]
-SkOpSegment::debugShowTs o id=27 [o=26,1 t=0 38.6568527,27.3431454 w=1 o=0] [o=20,3 t=1 41,33 w=1 o=0] operand
-SkOpSegment::debugShowTs + id=2 [o=26,1 t=0 38.6568527,27.3431454 w=1 o=0] [o=20,3 t=1 41,33 w=1 o=0]
-SkOpSegment::debugShowTs o id=27 [o=26,1 t=0 38.6568527,27.3431454 w=1 o=0] [o=20,3 t=1 41,33 w=1 o=0] operand
-SkOpSegment::debugShowTs - id=3 [o=27,2 t=0 41,33 w=1 o=0] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0]
-SkOpSegment::debugShowTs o id=20 [o=27,2 t=0 41,33 w=1 o=0] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0] operand
-SkOpSegment::debugShowTs + id=3 [o=27,2 t=0 41,33 w=1 o=0] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0]
-SkOpSegment::debugShowTs o id=20 [o=27,2 t=0 41,33 w=1 o=0] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0] operand
-SkOpSegment::debugShowTs - id=14 [o=13 t=0 27.3354416,27.3508568 w=1 o=0] [o=15 t=1 27.331131,27.3551788 w=1 o=0]
-SkOpSegment::debugShowTs o id=24 [o=23 t=0 25,33 w=1 o=0] [o=15 t=0.997 27.3314419,27.3548698 w=1 o=0] [o=25,16,0 t=1 27.3431454,27.3431454 w=1 o=0] operand
-SkOpSegment::addTPair addTPair this=14 0 other=24 0.998354892
-SkOpSegment::addTPair id=14 lower=0 upper=1 other=24 oLower=2 oUpper=2
-SkOpSegment::addTPair addTPair this=24 0.99743327 other=14 1
-SkOpSegment::addTPair id=24 lower=1 upper=1 other=14 oLower=2 oUpper=3
-SkOpSegment::debugShowTs + id=14 [o=24,13 t=0 27.3354416,27.3508568 w=1 o=0] [o=24,15 t=1 27.331131,27.3551788 w=1 o=0]
-SkOpSegment::debugShowTs o id=24 [o=23 t=0 25,33 w=1 o=0] [o=14 t=0.997 27.331131,27.3551788 w=1 o=0] [o=15 t=0.997 27.3314419,27.3548698 w=1 o=0] [o=14 t=0.998 27.3354416,27.3508568 w=1 o=0] [o=25,16,0 t=1 27.3431454,27.3431454 w=1 o=0] operand
-SkOpSegment::debugShowTs - id=21 [o=20,4,3 t=0 38.6568527,38.6568527 w=1 o=0] [o=5 t=0.00262 38.6445847,38.6690979 w=1 o=0] [o=22 t=1 33,41 w=1 o=0] operand
-SkOpSegment::debugShowTs o id=19 [o=18,5,4 t=0 38.6447449,38.6689377 w=1 o=0] [o=17 t=1 38.6398277,38.6738319 w=1 o=0]
-SkOpSegment::addTPair addTPair this=21 0.00258220891 other=19 0
-SkOpSegment::addTPair id=21 lower=3 upper=3 other=19 oLower=0 oUpper=3
-SkOpSegment::addTPair addTPair this=19 1 other=21 0.00362998223
-SkOpSegment::addTPair id=19 lower=4 upper=5 other=21 oLower=5 oUpper=5
-SkOpSegment::debugShowTs + id=21 [o=20,4,3 t=0 38.6568527,38.6568527 w=1 o=0] [o=19 t=0.00258 38.6447449,38.6689377 w=1 o=0] [o=5 t=0.00262 38.6445847,38.6690979 w=1 o=0] [o=19 t=0.00363 38.6398277,38.6738319 w=1 o=0] [o=22 t=1 33,41 w=1 o=0] operand
-SkOpSegment::debugShowTs o id=19 [o=21,18,5,4 t=0 38.6447449,38.6689377 w=1 o=0] [o=21,17 t=1 38.6398277,38.6738319 w=1 o=0]
-SkOpContour::calcCoincidentWinding count=5
-SkOpSegment::debugShowTs p id=0 [o=24,16 t=0 27.3431454,27.3431454 w=1 o=1] [o=26,1 t=1 33,25 w=1 o=0]
-SkOpSegment::debugShowTs o id=25 [o=24,16 t=0 27.3431454,27.3431454 w=0 o=0] [o=26,1 t=1 33,25 w=1 o=0] operand done
-SkOpSegment::debugShowTs p id=1 [o=25,0 t=0 33,25 w=1 o=1] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0]
-SkOpSegment::debugShowTs o id=26 [o=25,0 t=0 33,25 w=0 o=0] [o=27,2 t=1 38.6568527,27.3431454 w=1 o=0] operand done
-SkOpSegment::debugShowTs p id=2 [o=26,1 t=0 38.6568527,27.3431454 w=1 o=1] [o=20,3 t=1 41,33 w=1 o=0]
-SkOpSegment::debugShowTs o id=27 [o=26,1 t=0 38.6568527,27.3431454 w=0 o=0] [o=20,3 t=1 41,33 w=1 o=0] operand done
-SkOpSegment::debugShowTs p id=3 [o=27,2 t=0 41,33 w=1 o=1] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0]
-SkOpSegment::debugShowTs o id=20 [o=27,2 t=0 41,33 w=0 o=0] [o=21,4 t=1 38.6568527,38.6568527 w=1 o=0] operand done
-SkOpSegment::debugShowTs p id=14 [o=24,13 t=0 27.3354416,27.3508568 w=1 o=-1] [o=24,15 t=1 27.331131,27.3551788 w=1 o=0]
-SkOpSegment::debugShowTs o id=24 [o=23 t=0 25,33 w=0 o=0] [o=14 t=0.997 27.331131,27.3551788 w=0 o=0] [o=15 t=0.997 27.3314419,27.3548698 w=0 o=0] [o=14 t=0.998 27.3354416,27.3508568 w=1 o=0] [o=25,16,0 t=1 27.3431454,27.3431454 w=1 o=0] operand
-SkOpContour::calcCoincidentWinding count=1
-SkOpSegment::debugShowTs p id=21 [o=20,4,3 t=0 38.6568527,38.6568527 w=1 o=0] [o=19 t=0.00258 38.6447449,38.6689377 w=1 o=1] [o=5 t=0.00262 38.6445847,38.6690979 w=1 o=1] [o=19 t=0.00363 38.6398277,38.6738319 w=1 o=0] [o=22 t=1 33,41 w=1 o=0] operand
-SkOpSegment::debugShowTs o id=19 [o=21,18,5,4 t=0 38.6447449,38.6689377 w=0 o=0] [o=21,17 t=1 38.6398277,38.6738319 w=1 o=0] done
-SkOpSegment::checkEnds id=4 missing t=1 other=21 otherT=0.00258220891 pt=(38.6447449,38.6689377)
-SkOpSegment::addTPair addTPair this=4 1 other=21 0.00258220891
-SkOpSegment::addTPair id=4 lower=3 upper=6 other=21 oLower=3 oUpper=4
-SkOpSegment::checkEnds id=5 missing t=0 other=21 otherT=0.00258220891 pt=(38.6447449,38.6689377)
-SkOpSegment::addTPair addTPair this=5 0 other=21 0.00258220891
-SkOpSegment::addTPair id=5 lower=0 upper=3 other=21 oLower=3 oUpper=5
-SkOpSegment::checkEnds id=13 missing t=1 other=24 otherT=0.998354892 pt=(27.3354416,27.3508568)
-SkOpSegment::addTPair addTPair this=13 1 other=24 0.998354892
-SkOpSegment::addTPair id=13 lower=1 upper=2 other=24 oLower=3 oUpper=4
-SkOpSegment::checkEnds id=15 missing t=0 other=24 otherT=0.99743327 pt=(27.331131,27.3551788)
-SkOpSegment::addTPair addTPair this=15 0 other=24 0.99743327
-SkOpSegment::addTPair id=15 lower=0 upper=1 other=24 oLower=1 oUpper=2
-SkOpSegment::checkEnds id=21 missing t=0.00362998223 other=17 otherT=0 pt=(38.6398277,38.6738319)
-SkOpSegment::addTPair addTPair this=21 0.00362998223 other=17 0
-SkOpSegment::addTPair id=21 lower=7 upper=8 other=17 oLower=0 oUpper=1
-SkOpSegment::addTPair addTPair this=0 0 other=25 0
-SkOpSegment::addTPair id=0 lower=0 upper=2 other=25 oLower=0 oUpper=2
-SkOpSegment::addTPair addTPair duplicate this=0 0 other=25 0
-SkOpSegment::addTPair addTPair this=3 1 other=20 1
-SkOpSegment::addTPair id=3 lower=2 upper=4 other=20 oLower=2 oUpper=4
-SkOpSegment::addTPair addTPair duplicate this=3 1 other=20 1
-SkOpSegment::addTPair addTPair this=21 0.00258220891 other=18 1
-SkOpSegment::addTPair id=21 lower=3 upper=6 other=18 oLower=1 oUpper=4
-SkOpSegment::addTPair addTPair duplicate this=18 1 other=21 0.00258220891
-SkOpContour::joinCoincidence count=5
-SkOpContour::joinCoincidence count=1
-SkOpSegment::sortAngles [0] tStart=0 [1]
-SkOpAngle::after [0/1] 2/1 tStart=0 tEnd=1 < [16/1] 27/27 tStart=1 tEnd=0 < [24/2] 17/21 tStart=1 tEnd=0.998354892  F 4
-SkOpSegment::sortAngles [0] tStart=1 [4]
-SkOpSegment::sortAngles [1] tStart=1 [3]
-SkOpSegment::sortAngles [2] tStart=1 [3]
-SkOpSegment::sortAngles [3] tStart=1 [3]
-SkOpAngle::after [3/2] 1/5 tStart=1 tEnd=0 < [4/1] 18/17 tStart=0 tEnd=1 < [21/1] 21/17 tStart=0 tEnd=0.00258220891  T 11
-SkOpSegment::sortAngles [4] tStart=1 [3]
-SkOpAngle::after [4/2] 1/1 tStart=1 tEnd=0 < [21/3] 21/21 tStart=0.00258220891 tEnd=0.0026162254 < [21/2] 1/1 tStart=0.00258220891 tEnd=0  T 5
-SkOpAngle::after [4/2] 1/1 tStart=1 tEnd=0 < [18/1] 11/11 tStart=1 tEnd=0 < [21/3] 21/21 tStart=0.00258220891 tEnd=0.0026162254  T 4
-SkOpAngle::after [4/2] 1/1 tStart=1 tEnd=0 < [5/1] 21/21 tStart=0 tEnd=0.0149880862 < [18/1] 11/11 tStart=1 tEnd=0  F 4
-SkOpAngle::after [18/1] 11/11 tStart=1 tEnd=0 < [5/1] 21/21 tStart=0 tEnd=0.0149880862 < [21/3] 21/21 tStart=0.00258220891 tEnd=0.0026162254  T 7
-SkOpSegment::sortAngles [5] tStart=0.0149880862 [4]
-SkOpAngle::after [5/2] 1/1 tStart=0.0149880862 tEnd=0 < [21/4] 1/1 tStart=0.0026162254 tEnd=0.00258220891 < [5/3] 21/21 tStart=0.0149880862 tEnd=1  T 12
-SkOpAngle::after [5/2] 1/1 tStart=0.0149880862 tEnd=0 < [21/5] 17/17 tStart=0.0026162254 tEnd=0.00362998223 < [21/4] 1/1 tStart=0.0026162254 tEnd=0.00258220891  F 5
-SkOpAngle::after [21/4] 1/1 tStart=0.0026162254 tEnd=0.00258220891 < [21/5] 17/17 tStart=0.0026162254 tEnd=0.00362998223 < [5/3] 21/21 tStart=0.0149880862 tEnd=1  T 4
-SkOpSegment::sortAngles [13] tStart=1 [1]
-SkOpAngle::after [13/1] 17/17 tStart=1 tEnd=0 < [14/1] 21/21 tStart=0 tEnd=1 < [24/1] 5/5 tStart=0.998354892 tEnd=1  T 4
-SkOpSegment::sortAngles [14] tStart=1 [3]
-SkOpSegment::sortAngles [15] tStart=0.0266527086 [2]
-SkOpSegment::sortAngles [21] tStart=0.00362998223 [8]
-SkOpAngle::after [21/6] 1/1 tStart=0.00362998223 tEnd=0.0026162254 < [17/1] 5/5 tStart=0 tEnd=1 < [21/7] 17/17 tStart=0.00362998223 tEnd=1  T 4
-SkOpSegment::debugShowActiveSpans id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 (27.3431454,27.3431454) tEnd=1 other=25 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=1
-SkOpSegment::debugShowActiveSpans id=1 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 (33,25) tEnd=1 other=25 otherT=1 otherIndex=4 windSum=? windValue=1 oppValue=1
-SkOpSegment::debugShowActiveSpans id=2 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 (38.6568527,27.3431454) tEnd=1 other=26 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=1
-SkOpSegment::debugShowActiveSpans id=3 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 (41,33) tEnd=1 other=27 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=1
-SkOpSegment::debugShowActiveSpans id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 (38.6568527,38.6568527) tEnd=1 other=21 otherT=0 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0 (38.6447449,38.6689377) tEnd=0.0149880862 other=21 otherT=0.00258220891 otherIndex=4 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 (38.6445847,38.6690979) tEnd=1 other=21 otherT=0.0026162254 otherIndex=7 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 (38.6341171,38.6795731) tEnd=1 other=5 otherT=1 otherIndex=5 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 (38.6227531,38.6909218) tEnd=1 other=6 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 (32.9638329,41.0290833) tEnd=1 other=7 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 (27.3090477,38.6809464) tEnd=1 other=8 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 (24.9708939,33.0220299) tEnd=1 other=9 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 (27.319025,27.3672428) tEnd=1 other=10 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 (27.3229256,27.3633518) tEnd=1 other=11 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 (27.3270645,27.3592148) tEnd=1 other=12 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 (27.3354416,27.3508568) tEnd=1 other=24 otherT=0.998354892 otherIndex=5 windSum=? windValue=1 oppValue=-1
-SkOpSegment::debugShowActiveSpans id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 (27.331131,27.3551788) tEnd=0.0266527086 other=24 otherT=0.99743327 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0.0266527086 (27.3314419,27.3548698) tEnd=1 other=24 otherT=0.997499486 otherIndex=3 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (27.3431988,27.3431988 27.3431454,27.3431454) t=0 (27.3431988,27.3431988) tEnd=1 other=15 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 (38.6568527,38.6568527) tEnd=0.00258220891 other=20 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00258220891 (38.6447449,38.6689377) tEnd=0.0026162254 other=18 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=1
-SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.0026162254 (38.6445847,38.6690979) tEnd=0.00362998223 other=5 otherT=0.0149880862 otherIndex=4 windSum=? windValue=1 oppValue=1
-SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00362998223 (38.6398277,38.6738319) tEnd=1 other=17 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 other=21 otherT=1 otherIndex=10 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 other=22 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 (27.3354416,27.3508568) tEnd=1 other=13 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (38.6398277,38.6738319 38.6447258,38.6689186) t=0 (38.6398277,38.6738319) tEnd=1 other=21 otherT=0.00362998223 otherIndex=8 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [0/2] next=1/1 sect=16/17  s=1 [4] e=0 [2] sgn=1 windVal=1 windSum=? oppVal=1 oppSum=?
-SkOpAngle::dumpOne [1/1] next=0/2 sect=30/29  s=0 [0] e=1 [2] sgn=-1 windVal=1 windSum=? oppVal=1 oppSum=? stop
-SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [1] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [0] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [2] (27.3431454,27.3431454) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=1 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [0] (33,25) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::markWinding id=1 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [1] (33,25) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=2 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [0] (38.6568527,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::markWinding id=2 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [1] (38.6568527,27.3431454) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=3 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [0] (41,33) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::markWinding id=3 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [1] (41,33) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [1] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [0] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-SkOpSegment::markWinding id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [2] (27.3431454,27.3431454) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-SkOpSegment::activeOp id=0 t=0 tEnd=1 op=union miFrom=1 miTo=0 suFrom=1 suTo=0 result=1
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDoneBinary id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [1] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-SkOpSegment::markDoneBinary id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [0] (27.3431454,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-SkOpSegment::markDoneBinary id=0 (27.3431454,27.3431454 29.6862907,25 33,25) t=0 [2] (27.3431454,27.3431454) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-bridgeOp current id=0 from=(27.3431454,27.3431454) to=(33,25)
-path.moveTo(27.3431454,27.3431454);
-path.quadTo(29.6862907,25, 33,25);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDoneBinary id=1 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [0] (33,25) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-SkOpSegment::markDoneBinary id=1 (33,25 36.3137093,25 38.6568527,27.3431454) t=0 [1] (33,25) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-bridgeOp current id=1 from=(33,25) to=(38.6568527,27.3431454)
-path.quadTo(36.3137093,25, 38.6568527,27.3431454);
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp simple
-SkOpSegment::markDoneBinary id=2 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [0] (38.6568527,27.3431454) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-SkOpSegment::markDoneBinary id=2 (38.6568527,27.3431454 41,29.6862907 41,33) t=0 [1] (38.6568527,27.3431454) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-bridgeOp current id=2 from=(38.6568527,27.3431454) to=(41,33)
-path.quadTo(41,29.6862907, 41,33);
-SkOpSegment::markWinding id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [0] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [1] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [2] (38.6568527,38.6568527) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markAngle last id=4 windSum=? small=0
-SkOpSegment::markWinding id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [0] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [1] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [2] (38.6568527,38.6568527) tEnd=0.00258220891 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markAngle last id=21 windSum=? small=0
-SkOpSegment::findNextOp
-SkOpAngle::dumpOne [3/2] next=4/1 sect=1/5  s=1 [4] e=0 [1] sgn=1 windVal=1 windSum=-1 oppVal=1 oppSum=-1
-SkOpAngle::dumpOne [4/1] next=21/1 sect=18/17  s=0 [0] e=1 [3] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=-1
-SkOpAngle::dumpOne [21/1] next=3/2 sect=21/17  s=0 [0] e=0.00258220891 [3] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand
-SkOpSegment::activeOp id=4 t=0 tEnd=1 op=union miFrom=1 miTo=0 suFrom=1 suTo=1 result=0
-SkOpSegment::markDoneBinary id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [0] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDoneBinary id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [1] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDoneBinary id=4 (38.6568527,38.6568527 38.6510239,38.6626816 38.6447449,38.6689377) t=0 [2] (38.6568527,38.6568527) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::findNextOp chase.append id=4 windSum=-2147483647 small=0
-SkOpSegment::activeOp id=21 t=0 tEnd=0.00258220891 op=union miFrom=0 miTo=0 suFrom=1 suTo=0 result=1
-SkOpSegment::findNextOp chase.append id=21 windSum=-2147483647 small=0
-SkOpSegment::markDoneBinary id=3 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [0] (41,33) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-SkOpSegment::markDoneBinary id=3 (41,33 41,36.3137093 38.6568527,38.6568527) t=0 [1] (41,33) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=1
-SkOpSegment::findNextOp from:[3] to:[21] start=0 end=3
-bridgeOp current id=3 from=(41,33) to=(38.6568527,38.6568527)
-path.quadTo(41,36.3137093, 38.6568527,38.6568527);
-SkOpSegment::findNextOp
-SkOpAngle::dumpOne [21/2] next=4/2 sect=1/1  s=0.00258220891 [3] e=0 [0] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand
-SkOpAngle::dumpOne [4/2] next=18/1 sect=1/1  s=1 [6] e=0 [2] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 done
-SkOpAngle::dumpOne [18/1] next=5/1 sect=11/11  s=1 [4] e=0 [0] sgn=1 windVal=1 windSum=? done
-SkOpAngle::dumpOne [5/1] next=21/3 sect=21/21  s=0 [0] e=0.0149880862 [4] sgn=-1 windVal=1 windSum=? unorderable
-SkOpAngle::dumpOne [21/3] next=21/2 sect=21/21  s=0.00258220891 [3] e=0.0026162254 [7] sgn=-1 windVal=1 windSum=? oppVal=1 oppSum=? unorderable operand
-SkOpSegment::activeOp id=4 t=1 tEnd=0 op=union miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
-SkOpSegment::activeOp id=18 t=1 tEnd=0 op=union miFrom=1 miTo=0 suFrom=1 suTo=1 result=0
-SkOpSegment::activeOp id=5 t=0 tEnd=0.0149880862 op=union miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
-SkOpSegment::markDoneBinary id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0 [0] (38.6447449,38.6689377) tEnd=0 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markDoneBinary id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0 [1] (38.6447449,38.6689377) tEnd=0 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markDoneBinary id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0 [2] (38.6447449,38.6689377) tEnd=0 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markDoneBinary id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0 [3] (38.6447449,38.6689377) tEnd=0.0149880862 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::activeOp id=21 t=0.00258220891 tEnd=0.0026162254 op=union miFrom=1 miTo=0 suFrom=1 suTo=0 result=1
-SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [0] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [1] (38.6568527,38.6568527) tEnd=0 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0 [2] (38.6568527,38.6568527) tEnd=0.00258220891 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::findNextOp from:[21] to:[21] start=3 end=7
-bridgeOp current id=21 from=(38.6568527,38.6568527) to=(38.6447449,38.6689377)
-path.quadTo(38.6510239,38.6626816, 38.6447449,38.6689377);
-SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00258220891 [3] (38.6447449,38.6689377) tEnd=0.00258220891 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00258220891 [4] (38.6447449,38.6689377) tEnd=0.00258220891 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00258220891 [5] (38.6447449,38.6689377) tEnd=0.00258220891 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00258220891 [6] (38.6447449,38.6689377) tEnd=0.0026162254 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::debugShowActiveSpans id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 (38.6445847,38.6690979) tEnd=1 other=21 otherT=0.0026162254 otherIndex=7 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 (38.6341171,38.6795731) tEnd=1 other=5 otherT=1 otherIndex=5 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 (38.6227531,38.6909218) tEnd=1 other=6 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 (32.9638329,41.0290833) tEnd=1 other=7 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 (27.3090477,38.6809464) tEnd=1 other=8 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 (24.9708939,33.0220299) tEnd=1 other=9 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 (27.319025,27.3672428) tEnd=1 other=10 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 (27.3229256,27.3633518) tEnd=1 other=11 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 (27.3270645,27.3592148) tEnd=1 other=12 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 (27.3354416,27.3508568) tEnd=1 other=24 otherT=0.998354892 otherIndex=5 windSum=? windValue=1 oppValue=-1
-SkOpSegment::debugShowActiveSpans id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 (27.331131,27.3551788) tEnd=0.0266527086 other=24 otherT=0.99743327 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0.0266527086 (27.3314419,27.3548698) tEnd=1 other=24 otherT=0.997499486 otherIndex=3 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=16 (27.3431988,27.3431988 27.3431454,27.3431454) t=0 (27.3431988,27.3431988) tEnd=1 other=15 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.0026162254 (38.6445847,38.6690979) tEnd=0.00362998223 other=5 otherT=0.0149880862 otherIndex=4 windSum=? windValue=1 oppValue=1
-SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00362998223 (38.6398277,38.6738319) tEnd=1 other=17 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 other=21 otherT=1 otherIndex=10 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 other=22 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 (27.3354416,27.3508568) tEnd=1 other=13 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (38.6398277,38.6738319 38.6447258,38.6689186) t=0 (38.6398277,38.6738319) tEnd=1 other=21 otherT=0.00362998223 otherIndex=8 windSum=? windValue=1 oppValue=0
-SkOpSegment::findTop
-SkOpAngle::dumpOne [0/1] next=24/2 sect=2/1  s=0 [0] e=1 [3] sgn=-1 windVal=1 windSum=-1 oppVal=1 oppSum=-1 done
-SkOpAngle::dumpOne [24/2] next=16/1 sect=17/21  s=1 [8] e=0.998354892 [5] sgn=1 windVal=1 windSum=? operand stop
-SkOpAngle::dumpOne [16/1] next=0/1 sect=27/27  s=1 [3] e=0 [0] sgn=1 windVal=1 windSum=? stop
-SkOpSegment::markWinding id=16 (27.3431988,27.3431988 27.3431454,27.3431454) t=0 [0] (27.3431988,27.3431988) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0.0266527086 [2] (27.3314419,27.3548698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markWinding id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 [0] (27.331131,27.3551788) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 [1] (27.331131,27.3551788) tEnd=0.0266527086 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markAngle last id=15 windSum=-1 small=0
-SkOpSegment::markWinding id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 [4] (27.3354416,27.3508568) tEnd=0.998354892 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 [5] (27.3354416,27.3508568) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markAngle last id=24 windSum=-1 small=0
-SkOpSegment::activeOp id=24 t=0.998354892 tEnd=1 op=union miFrom=0 miTo=0 suFrom=1 suTo=0 result=1
-SkOpSegment::findNextOp
-SkOpAngle::dumpOne [24/2] next=16/1 sect=17/21  s=1 [8] e=0.998354892 [5] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand stop
-SkOpAngle::dumpOne [16/1] next=0/1 sect=27/27  s=1 [3] e=0 [0] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 stop
-SkOpAngle::dumpOne [0/1] next=24/2 sect=2/1  s=0 [0] e=1 [3] sgn=-1 windVal=1 windSum=-1 oppVal=1 oppSum=-1 done
-SkOpSegment::activeOp id=16 t=1 tEnd=0 op=union miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
-SkOpSegment::markDoneBinary id=16 (27.3431988,27.3431988 27.3431454,27.3431454) t=0 [0] (27.3431988,27.3431988) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDoneBinary id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0.0266527086 [2] (27.3314419,27.3548698) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::markDoneBinary id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 [0] (27.331131,27.3551788) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDoneBinary id=15 (27.331131,27.3551788 27.3369579,27.3493824 27.3431988,27.3431988) t=0 [1] (27.331131,27.3551788) tEnd=0.0266527086 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::nextChase mismatched signs
-SkOpSegment::findNextOp chase.append id=15 windSum=-1 small=0
-SkOpSegment::activeOp id=0 t=0 tEnd=1 op=union miFrom=1 miTo=0 suFrom=1 suTo=0 result=1
-SkOpSegment::markDoneBinary id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 [4] (27.3354416,27.3508568) tEnd=0.998354892 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::markDoneBinary id=24 (25,33 25,29.6862907 27.3431454,27.3431454) t=0.998354892 [5] (27.3354416,27.3508568) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::findNextOp from:[24] to:[0] start=0 end=3
-bridgeOp current id=24 from=(27.3354416,27.3508568) to=(27.3431454,27.3431454)
-path.moveTo(27.3354416,27.3508568);
-path.quadTo(27.3392906,27.3470001, 27.3431454,27.3431454);
-SkOpSegment::markWinding id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 [0] (27.3354416,27.3508568) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=-1
-SkOpSegment::markWinding id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 [1] (27.3354416,27.3508568) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=-1
-SkOpSegment::markAngle last id=14 windSum=-1 small=0
-SkOpSegment::debugShowActiveSpans id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 (38.6445847,38.6690979) tEnd=1 other=21 otherT=0.0026162254 otherIndex=7 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 (38.6341171,38.6795731) tEnd=1 other=5 otherT=1 otherIndex=5 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 (38.6227531,38.6909218) tEnd=1 other=6 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 (32.9638329,41.0290833) tEnd=1 other=7 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 (27.3090477,38.6809464) tEnd=1 other=8 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 (24.9708939,33.0220299) tEnd=1 other=9 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 (27.319025,27.3672428) tEnd=1 other=10 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 (27.3229256,27.3633518) tEnd=1 other=11 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 (27.3270645,27.3592148) tEnd=1 other=12 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 (27.3354416,27.3508568) tEnd=1 other=24 otherT=0.998354892 otherIndex=5 windSum=-1 windValue=1 oppValue=-1
-SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.0026162254 (38.6445847,38.6690979) tEnd=0.00362998223 other=5 otherT=0.0149880862 otherIndex=4 windSum=? windValue=1 oppValue=1
-SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00362998223 (38.6398277,38.6738319) tEnd=1 other=17 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 other=21 otherT=1 otherIndex=10 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 other=22 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (38.6398277,38.6738319 38.6447258,38.6689186) t=0 (38.6398277,38.6738319) tEnd=1 other=21 otherT=0.00362998223 otherIndex=8 windSum=? windValue=1 oppValue=0
-SkOpSegment::activeOp id=14 t=1 tEnd=0 op=union miFrom=0 miTo=1 suFrom=1 suTo=0 result=0
-SkOpSegment::markDoneBinary id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 [0] (27.3354416,27.3508568) tEnd=0 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=-1
-SkOpSegment::markDoneBinary id=14 (27.3354416,27.3508568 27.3332844,27.3530178 27.331131,27.3551788) t=0 [1] (27.3354416,27.3508568) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=-1
-bridgeOp chase.append id=14 windSum=-1 small=0
-SkOpSegment::markWinding id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 [0] (27.3270645,27.3592148) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 [0] (27.3229256,27.3633518) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 [0] (27.319025,27.3672428) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 [0] (24.9708939,33.0220299) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 [0] (27.3090477,38.6809464) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 [0] (32.9638329,41.0290833) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 [0] (38.6227531,38.6909218) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 [0] (38.6341171,38.6795731) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markWinding id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 [4] (38.6445847,38.6690979) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
-SkOpSegment::markAngle last id=5 windSum=-1 small=0
-SkOpSegment::debugShowActiveSpans id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 (38.6445847,38.6690979) tEnd=1 other=21 otherT=0.0026162254 otherIndex=7 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 (38.6341171,38.6795731) tEnd=1 other=5 otherT=1 otherIndex=5 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 (38.6227531,38.6909218) tEnd=1 other=6 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 (32.9638329,41.0290833) tEnd=1 other=7 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 (27.3090477,38.6809464) tEnd=1 other=8 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 (24.9708939,33.0220299) tEnd=1 other=9 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 (27.319025,27.3672428) tEnd=1 other=10 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 (27.3229256,27.3633518) tEnd=1 other=11 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 (27.3270645,27.3592148) tEnd=1 other=12 otherT=1 otherIndex=1 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.0026162254 (38.6445847,38.6690979) tEnd=0.00362998223 other=5 otherT=0.0149880862 otherIndex=4 windSum=? windValue=1 oppValue=1
-SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00362998223 (38.6398277,38.6738319) tEnd=1 other=17 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 other=21 otherT=1 otherIndex=10 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 other=22 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (38.6398277,38.6738319 38.6447258,38.6689186) t=0 (38.6398277,38.6738319) tEnd=1 other=21 otherT=0.00362998223 otherIndex=8 windSum=? windValue=1 oppValue=0
-SkOpSegment::activeOp id=13 t=1 tEnd=0 op=union miFrom=0 miTo=1 suFrom=0 suTo=0 result=1
-SkOpSegment::findNextOp simple
-SkOpSegment::markDoneBinary id=13 (27.3270645,27.3592148 27.3312511,27.355032 27.3354416,27.3508568) t=0 [0] (27.3270645,27.3592148) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=13 from=(27.3354416,27.3508568) to=(27.3270645,27.3592148)
-path.moveTo(27.3354416,27.3508568);
-path.quadTo(27.3312511,27.355032, 27.3270645,27.3592148);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDoneBinary id=12 (27.3229256,27.3633518 27.324995,27.3612823 27.3270645,27.3592148) t=0 [0] (27.3229256,27.3633518) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=12 from=(27.3270645,27.3592148) to=(27.3229256,27.3633518)
-path.quadTo(27.324995,27.3612823, 27.3229256,27.3633518);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDoneBinary id=11 (27.319025,27.3672428 27.3209743,27.3652973 27.3229256,27.3633518) t=0 [0] (27.319025,27.3672428) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=11 from=(27.3229256,27.3633518) to=(27.319025,27.3672428)
-path.quadTo(27.3209743,27.3652973, 27.319025,27.3672428);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDoneBinary id=10 (24.9708939,33.0220299 24.973814,29.7083225 27.319025,27.3672428) t=0 [0] (24.9708939,33.0220299) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=10 from=(27.319025,27.3672428) to=(24.9708939,33.0220299)
-path.quadTo(24.973814,29.7083225, 24.9708939,33.0220299);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDoneBinary id=9 (27.3090477,38.6809464 24.9679718,36.3357391 24.9708939,33.0220299) t=0 [0] (27.3090477,38.6809464) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=9 from=(24.9708939,33.0220299) to=(27.3090477,38.6809464)
-path.quadTo(24.9679718,36.3357391, 27.3090477,38.6809464);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDoneBinary id=8 (32.9638329,41.0290833 29.6501274,41.0261612 27.3090477,38.6809464) t=0 [0] (32.9638329,41.0290833) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=8 from=(27.3090477,38.6809464) to=(32.9638329,41.0290833)
-path.quadTo(29.6501274,41.0261612, 32.9638329,41.0290833);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDoneBinary id=7 (38.6227531,38.6909218 36.2775421,41.0320053 32.9638329,41.0290833) t=0 [0] (38.6227531,38.6909218) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=7 from=(32.9638329,41.0290833) to=(38.6227531,38.6909218)
-path.quadTo(36.2775421,41.0320053, 38.6227531,38.6909218);
-SkOpSegment::findNextOp simple
-SkOpSegment::markDoneBinary id=6 (38.6341171,38.6795731 38.6284409,38.6852493 38.6227531,38.6909218) t=0 [0] (38.6341171,38.6795731) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-bridgeOp current id=6 from=(38.6227531,38.6909218) to=(38.6341171,38.6795731)
-path.quadTo(38.6284409,38.6852493, 38.6341171,38.6795731);
-SkOpSegment::findNextOp
-SkOpAngle::dumpOne [5/3] next=5/2 sect=21/21  s=0.0149880862 [4] e=1 [5] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0
-SkOpAngle::dumpOne [5/2] next=21/4 sect=1/1  s=0.0149880862 [4] e=0 [0] sgn=1 windVal=1 windSum=? done unorderable
-SkOpAngle::dumpOne [21/4] next=21/5 sect=1/1  s=0.0026162254 [7] e=0.00258220891 [3] sgn=1 windVal=1 windSum=? oppVal=1 oppSum=? done unorderable operand
-SkOpAngle::dumpOne [21/5] next=5/3 sect=17/17  s=0.0026162254 [7] e=0.00362998223 [8] sgn=-1 windVal=1 windSum=? oppVal=1 oppSum=? operand
-SkOpSegment::activeOp id=5 t=0.0149880862 tEnd=0 op=union miFrom=0 miTo=1 suFrom=0 suTo=0 result=1
-SkOpSegment::activeOp id=21 t=0.0026162254 tEnd=0.00258220891 op=union miFrom=1 miTo=0 suFrom=0 suTo=1 result=0
-SkOpSegment::activeOp id=21 t=0.0026162254 tEnd=0.00362998223 op=union miFrom=0 miTo=1 suFrom=1 suTo=0 result=0
-SkOpSegment::markDoneBinary id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.0026162254 [7] (38.6445847,38.6690979) tEnd=0.00362998223 newWindSum=-2147483647 newOppSum=-2147483647 oppSum=? windSum=? windValue=1 oppValue=1
-SkOpSegment::markDoneBinary id=5 (38.6447449,38.6689377 38.6394348,38.6742554 38.6341171,38.6795731) t=0.0149880862 [4] (38.6445847,38.6690979) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
-SkOpSegment::findNextOp from:[5] to:[5] start=4 end=0
-bridgeOp current id=5 from=(38.6341171,38.6795731) to=(38.6445847,38.6690979)
-path.quadTo(38.6393547,38.6743355, 38.6445847,38.6690979);
-SkOpSegment::debugShowActiveSpans id=21 (38.6568527,38.6568527 36.3137093,41 33,41) t=0.00362998223 (38.6398277,38.6738319) tEnd=1 other=17 otherT=0 otherIndex=0 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=22 (33,41 29.6862907,41 27.3431454,38.6568527) t=0 (33,41) tEnd=1 other=21 otherT=1 otherIndex=10 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=23 (27.3431454,38.6568527 25,36.3137093 25,33) t=0 (27.3431454,38.6568527) tEnd=1 other=22 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
-SkOpSegment::debugShowActiveSpans id=17 (38.6398277,38.6738319 38.6447258,38.6689186) t=0 (38.6398277,38.6738319) tEnd=1 other=21 otherT=0.00362998223 otherIndex=8 windSum=? windValue=1 oppValue=0
-d:\cygwin\puregit\src\pathops\skopsegment.cpp:472: failed assertion "span.fT > 0"
+seg=151 {{{68.1999969f, 74.8000031f}, {68.4444427f, 74.8000031f}, {68.6888885f, 74.7961044f}, {68.9333344f, 74.7922058f}}}
+seg=152 {{{68.9333344f, 74.7922058f}, {69.422226f, 74.7844086f}, {69.9111099f, 74.7766113f}, {70.4000015f, 74.8000031f}}}
+seg=153 {{{70.4000015f, 74.8000031f}, {70.7796326f, 74.8181686f}, {71.1592636f, 74.8627396f}, {71.5388947f, 74.9073105f}}}
+seg=154 {{{71.5388947f, 74.9073105f}, {71.8925934f, 74.9488373f}, {72.2462997f, 74.9903641f}, {72.5999985f, 75.0105362f}}}
+seg=155 {{{72.5999985f, 75.0105362f}, {73.1301117f, 75.0407715f}, {73.6602249f, 75.0435104f}, {74.1903381f, 75.0462494f}}}
+seg=156 {{{74.1903381f, 75.0462494f}, {74.3935623f, 75.0472946f}, {74.5967789f, 75.0483398f}, {74.8000031f, 75.0509415f}}}
+seg=157 {{{74.8000031f, 75.0509415f}, {75.0211792f, 75.053772f}, {75.242363f, 75.0601425f}, {75.4635391f, 75.0665054f}}}
+seg=158 {{{75.4635391f, 75.0665054f}, {75.9756927f, 75.0812454f}, {76.4878464f, 75.0959854f}, {77, 75.0667953f}}}
+seg=159 {{{77, 75.0667953f}, {77.3702316f, 75.0456924f}, {77.7404709f, 74.9892731f}, {78.1107025f, 74.9328461f}}}
+seg=160 {{{78.1107025f, 74.9328461f}, {78.4738007f, 74.8775101f}, {78.8368988f, 74.8221741f}, {79.1999969f, 74.8001556f}}}
+seg=161 {{{79.1999969f, 74.8001556f}, {79.689003f, 74.7705002f}, {80.1780014f, 74.7803421f}, {80.6670074f, 74.790184f}}}
+seg=162 {{{80.6670074f, 74.790184f}, {80.9113388f, 74.7950974f}, {81.1556702f, 74.8000107f}, {81.4000015f, 74.8000031f}}}
+seg=163 {{{81.4000015f, 74.8000031f}, {81.6438522f, 74.7999954f}, {81.8877106f, 74.7989426f}, {82.1315613f, 74.7978897f}}}
+seg=164 {{{82.1315613f, 74.7978897f}, {82.6210403f, 74.7957764f}, {83.1105194f, 74.7936554f}, {83.5999985f, 74.8000031f}}}
+seg=165 {{{83.5999985f, 74.8000031f}, {83.9259109f, 74.8042297f}, {84.2518234f, 74.8159714f}, {84.5777359f, 74.827713f}}}
+seg=166 {{{84.5777359f, 74.827713f}, {84.9851608f, 74.842392f}, {85.3925781f, 74.8570709f}, {85.8000031f, 74.8570709f}}}
+seg=167 {{{85.8000031f, 74.8570709f}, {86.2074203f, 74.8570709f}, {86.6148453f, 74.842392f}, {87.0222626f, 74.827713f}}}
+seg=168 {{{87.0222626f, 74.827713f}, {87.348175f, 74.8159714f}, {87.6740875f, 74.8042297f}, {88, 74.8000031f}}}
+seg=169 {{{88, 74.8000031f}, {88.1576462f, 74.7979584f}, {88.3152924f, 74.7951202f}, {88.4729385f, 74.7922745f}}}
+seg=170 {{{88.4729385f, 74.7922745f}, {89.0486221f, 74.7818985f}, {89.6243134f, 74.7715225f}, {90.1999969f, 74.8000031f}}}
+seg=171 {{{90.1999969f, 74.8000031f}, {90.5620499f, 74.8179169f}, {90.9241028f, 74.8630524f}, {91.2861557f, 74.9081879f}}}
+seg=172 {{{91.2861557f, 74.9081879f}, {91.6574402f, 74.9544754f}, {92.028717f, 75.0007629f}, {92.4000015f, 75.0176926f}}}
+seg=173 {{{92.4000015f, 75.0176926f}, {92.9111481f, 75.0410004f}, {93.4223022f, 75.0278778f}, {93.9334488f, 75.0147552f}}}
+seg=174 {{{93.9334488f, 75.0147552f}, {94.155632f, 75.0090561f}, {94.3778152f, 75.0033493f}, {94.5999985f, 75.0006409f}}}
+seg=175 {{{94.5999985f, 75.0006409f}, {94.8669891f, 74.9973831f}, {95.1339798f, 74.9889908f}, {95.4009705f, 74.9806061f}}}
+seg=176 {{{95.4009705f, 74.9806061f}, {95.8673172f, 74.9659576f}, {96.3336563f, 74.9513092f}, {96.8000031f, 74.9639893f}}}
+seg=177 {{{96.8000031f, 74.9639893f}, {97.2396164f, 74.9759521f}, {97.6792297f, 75.0139465f}, {98.1188431f, 75.0519409f}}}
+seg=178 {{{98.1188431f, 75.0519409f}, {98.4125595f, 75.0773239f}, {98.7062836f, 75.1027069f}, {99, 75.1203232f}}}
+seg=179 {{{99, 75.1203232f}, {99.7333298f, 75.1643066f}, {100.466667f, 75.2075195f}, {101.199997f, 75.2278976f}}}
+seg=180 {{{101.199997f, 75.2278976f}, {101.6632f, 75.2407608f}, {102.126396f, 75.2405472f}, {102.5896f, 75.2403336f}}}
+seg=181 {{{102.5896f, 75.2403336f}, {102.859734f, 75.2402115f}, {103.129868f, 75.2400894f}, {103.400002f, 75.2425613f}}}
+seg=182 {{{103.400002f, 75.2425613f}, {103.499123f, 75.2434692f}, {103.598244f, 75.2443542f}, {103.697365f, 75.2452393f}}}
+seg=183 {{{103.697365f, 75.2452393f}, {104.331573f, 75.250885f}, {104.96579f, 75.2565308f}, {105.599998f, 75.2681274f}}}
+seg=184 {{{105.599998f, 75.2681274f}, {105.951164f, 75.2745514f}, {106.30233f, 75.2846603f}, {106.653496f, 75.2947693f}}}
+seg=185 {{{106.653496f, 75.2947693f}, {107.035667f, 75.3057709f}, {107.417831f, 75.3167725f}, {107.800003f, 75.3230209f}}}
+seg=186 {{{107.800003f, 75.3230209f}, {107.904305f, 75.3247223f}, {108.008606f, 75.3265228f}, {108.112907f, 75.3283234f}}}
+seg=187 {{{108.112907f, 75.3283234f}, {108.741936f, 75.33918f}, {109.370972f, 75.350029f}, {110, 75.340065f}}}
+seg=188 {{{110, 75.340065f}, {110.73333f, 75.3284454f}, {111.466667f, 75.2919846f}, {112.199997f, 75.2532883f}}}
+seg=189 {{{112.199997f, 75.2532883f}, {112.524193f, 75.2361755f}, {112.848389f, 75.2113037f}, {113.172585f, 75.1864319f}}}
+seg=190 {{{113.172585f, 75.1864319f}, {113.581726f, 75.1550446f}, {113.99086f, 75.1236496f}, {114.400002f, 75.1078644f}}}
+seg=191 {{{114.400002f, 75.1078644f}, {115.133331f, 75.079567f}, {115.866669f, 75.0734863f}, {116.599998f, 75.0834885f}}}
+seg=192 {{{116.599998f, 75.0834885f}, {117.013039f, 75.089119f}, {117.426071f, 75.1088409f}, {117.839111f, 75.1285629f}}}
+seg=193 {{{117.839111f, 75.1285629f}, {118.159409f, 75.1438599f}, {118.479706f, 75.1591568f}, {118.800003f, 75.1678772f}}}
+seg=194 {{{118.800003f, 75.1678772f}, {118.915619f, 75.1710281f}, {119.031235f, 75.1743622f}, {119.146851f, 75.1776962f}}}
+seg=195 {{{119.146851f, 75.1776962f}, {119.764565f, 75.1954956f}, {120.382286f, 75.2133026f}, {121, 75.2033386f}}}
+seg=196 {{{121, 75.2033386f}, {121.552635f, 75.1944351f}, {122.105278f, 75.1614532f}, {122.657913f, 75.1284714f}}}
+seg=197 {{{122.657913f, 75.1284714f}, {122.838608f, 75.1176834f}, {123.019302f, 75.1068954f}, {123.199997f, 75.0969543f}}}
+seg=198 {{{123.199997f, 75.0969543f}, {123.480431f, 75.0815277f}, {123.760864f, 75.060524f}, {124.041298f, 75.0395203f}}}
+seg=199 {{{124.041298f, 75.0395203f}, {124.494202f, 75.0056076f}, {124.947098f, 74.9716949f}, {125.400002f, 74.9612503f}}}
+seg=200 {{{125.400002f, 74.9612503f}, {126.133331f, 74.9443436f}, {126.866669f, 74.9602585f}, {127.599998f, 74.9955215f}}}
+seg=201 {{{127.599998f, 74.9955215f}, {128.17691f, 75.023262f}, {128.75383f, 75.0766068f}, {129.33075f, 75.1299438f}}}
+seg=202 {{{129.33075f, 75.1299438f}, {129.487167f, 75.1444092f}, {129.643585f, 75.1588745f}, {129.800003f, 75.1728287f}}}
+seg=203 {{{129.800003f, 75.1728287f}, {130.08493f, 75.1982422f}, {130.369843f, 75.2303619f}, {130.65477f, 75.2624817f}}}
+seg=204 {{{130.65477f, 75.2624817f}, {131.10318f, 75.3130264f}, {131.55159f, 75.3635712f}, {132, 75.3879776f}}}
+seg=205 {{{132, 75.3879776f}, {132.682098f, 75.4251099f}, {133.364182f, 75.4193726f}, {134.04628f, 75.4136353f}}}
+seg=206 {{{134.04628f, 75.4136353f}, {134.199997f, 75.4123535f}}}
+seg=207 {{{134.199997f, 75.4123535f}, {134.740479f, 75.4080048f}, {135.28096f, 75.3897247f}, {135.821426f, 75.3714523f}}}
+seg=208 {{{135.821426f, 75.3714523f}, {136.014282f, 75.3649292f}, {136.207138f, 75.3584061f}, {136.399994f, 75.3525162f}}}
+seg=209 {{{136.399994f, 75.3525162f}, {136.723831f, 75.3426285f}, {137.047668f, 75.3308029f}, {137.371506f, 75.3189697f}}}
+seg=210 {{{137.371506f, 75.3189697f}, {137.781006f, 75.3040085f}, {138.190506f, 75.2890472f}, {138.600006f, 75.2780228f}}}
+seg=211 {{{138.600006f, 75.2780228f}, {138.775055f, 75.2733078f}, {138.950119f, 75.2678604f}, {139.125183f, 75.262413f}}}
+seg=212 {{{139.125183f, 75.262413f}, {139.683456f, 75.2450409f}, {140.24173f, 75.2276688f}, {140.800003f, 75.2340317f}}}
+seg=213 {{{140.800003f, 75.2340317f}, {141.173523f, 75.2382889f}, {141.547028f, 75.259819f}, {141.920547f, 75.2813568f}}}
+seg=214 {{{141.920547f, 75.2813568f}, {142.280365f, 75.3021011f}, {142.640182f, 75.3228455f}, {143, 75.3281403f}}}
+seg=215 {{{143, 75.3281403f}, {143.733337f, 75.3389359f}, {144.46666f, 75.3424072f}, {145.199997f, 75.2988205f}}}
+seg=216 {{{145.199997f, 75.2988205f}, {145.574966f, 75.276535f}, {145.949936f, 75.2288513f}, {146.32489f, 75.1811676f}}}
+seg=217 {{{146.32489f, 75.1811676f}, {146.683258f, 75.1355896f}, {147.041626f, 75.0900192f}, {147.399994f, 75.0666199f}}}
+seg=218 {{{147.399994f, 75.0666199f}, {148.133331f, 75.0187454f}, {148.866669f, 74.9981079f}, {149.600006f, 75.0115509f}}}
+seg=219 {{{149.600006f, 75.0115509f}, {149.982086f, 75.0185547f}, {150.364182f, 75.0495148f}, {150.746277f, 75.0804749f}}}
+seg=220 {{{150.746277f, 75.0804749f}, {151.097519f, 75.1089401f}, {151.448761f, 75.1374054f}, {151.800003f, 75.1472549f}}}
+seg=221 {{{151.800003f, 75.1472549f}, {152.343307f, 75.1624985f}, {152.886597f, 75.1531296f}, {153.429901f, 75.1437607f}}}
+seg=222 {{{153.429901f, 75.1437607f}, {153.619934f, 75.14048f}, {153.809967f, 75.137207f}, {154, 75.1349792f}}}
+seg=223 {{{154, 75.1349792f}, {154.295746f, 75.1315155f}, {154.591476f, 75.123848f}, {154.887222f, 75.1161804f}}}
+seg=224 {{{154.887222f, 75.1161804f}, {155.324814f, 75.1048279f}, {155.762405f, 75.0934753f}, {156.199997f, 75.0957642f}}}
+seg=225 {{{156.199997f, 75.0957642f}, {156.559143f, 75.097641f}, {156.918289f, 75.1122131f}, {157.27742f, 75.1267853f}}}
+seg=226 {{{157.27742f, 75.1267853f}, {157.651611f, 75.1419754f}, {158.025803f, 75.1571655f}, {158.399994f, 75.1579895f}}}
+seg=227 {{{158.399994f, 75.1579895f}, {159.133331f, 75.1596069f}, {159.866669f, 75.1515121f}, {160.600006f, 75.105484f}}}
+seg=228 {{{160.600006f, 75.105484f}, {160.952393f, 75.0833588f}, {161.304794f, 75.0397339f}, {161.657196f, 74.996109f}}}
+seg=229 {{{161.657196f, 74.996109f}, {162.038132f, 74.9489594f}, {162.419067f, 74.9018021f}, {162.800003f, 74.8818054f}}}
+seg=230 {{{162.800003f, 74.8818054f}, {163.53334f, 74.8433075f}, {164.266663f, 74.8363724f}, {165, 74.8744736f}}}
+seg=231 {{{165, 74.8744736f}, {165.356293f, 74.8929825f}, {165.712585f, 74.9419861f}, {166.068878f, 74.9909897f}}}
+seg=232 {{{166.068878f, 74.9909897f}, {166.445923f, 75.0428467f}, {166.822952f, 75.0947037f}, {167.199997f, 75.1104279f}}}
+seg=233 {{{167.199997f, 75.1104279f}, {167.933334f, 75.1409988f}, {168.666672f, 75.1096573f}, {169.399994f, 75.0579224f}}}
+seg=234 {{{169.399994f, 75.0579224f}, {169.752808f, 75.0330276f}, {170.105621f, 74.982338f}, {170.458435f, 74.9316483f}}}
+seg=235 {{{170.458435f, 74.9316483f}, {170.838959f, 74.8769836f}, {171.219482f, 74.8223114f}, {171.600006f, 74.8000031f}}}
+seg=236 {{{171.600006f, 74.8000031f}, {172.333328f, 74.757019f}, {173.066666f, 74.7623062f}, {173.800003f, 74.8000031f}}}
+seg=237 {{{173.800003f, 74.8000031f}, {174.500809f, 74.8360367f}, {175.201599f, 74.9249344f}, {175.902405f, 75.0138397f}}}
+seg=238 {{{175.902405f, 75.0138397f}, {176, 75.0262146f}}}
+seg=239 {{{176, 75.0262146f}, {176.306427f, 75.0650406f}, {176.612839f, 75.1189728f}, {176.919266f, 75.1729126f}}}
+seg=240 {{{176.919266f, 75.1729126f}, {177.346176f, 75.2480545f}, {177.773087f, 75.3231964f}, {178.199997f, 75.3574677f}}}
+seg=241 {{{178.199997f, 75.3574677f}, {178.933334f, 75.4163361f}, {179.666672f, 75.4057388f}, {180.399994f, 75.3794556f}}}
+seg=242 {{{180.399994f, 75.3794556f}, {180.822174f, 75.3643265f}, {181.244354f, 75.322731f}, {181.666534f, 75.2811356f}}}
+seg=243 {{{181.666534f, 75.2811356f}, {181.977692f, 75.2504807f}, {182.288849f, 75.2198257f}, {182.600006f, 75.1997681f}}}
+seg=244 {{{182.600006f, 75.1997681f}, {183.153549f, 75.1640701f}, {183.707108f, 75.1412506f}, {184.260666f, 75.1184311f}}}
+seg=245 {{{184.260666f, 75.1184311f}, {184.440445f, 75.1110229f}, {184.620224f, 75.1036148f}, {184.800003f, 75.0957642f}}}
+seg=246 {{{184.800003f, 75.0957642f}, {185.53334f, 75.0637436f}, {186.266663f, 75.0331039f}, {187, 75.0076218f}}}
+seg=247 {{{187, 75.0076218f}, {187.218643f, 75.0000229f}, {187.437286f, 74.9894943f}, {187.65593f, 74.9789658f}}}
+seg=248 {{{187.65593f, 74.9789658f}, {188.170624f, 74.9541702f}, {188.685303f, 74.9293747f}, {189.199997f, 74.9428329f}}}
+seg=249 {{{189.199997f, 74.9428329f}, {189.560562f, 74.9522629f}, {189.921127f, 74.9923019f}, {190.281693f, 75.032341f}}}
+seg=250 {{{190.281693f, 75.032341f}, {190.654465f, 75.0737381f}, {191.027237f, 75.1151352f}, {191.399994f, 75.1227036f}}}
+seg=251 {{{191.399994f, 75.1227036f}, {191.942627f, 75.1337204f}, {192.48526f, 75.0998383f}, {193.027893f, 75.0659485f}}}
+seg=252 {{{193.027893f, 75.0659485f}, {193.218597f, 75.054039f}, {193.409302f, 75.0421295f}, {193.600006f, 75.0321732f}}}
+seg=253 {{{193.600006f, 75.0321732f}, {193.897125f, 75.016655f}, {194.19426f, 74.9936142f}, {194.491394f, 74.9705658f}}}
+seg=254 {{{194.491394f, 74.9705658f}, {194.927597f, 74.9367371f}, {195.3638f, 74.9029083f}, {195.800003f, 74.8928909f}}}
+seg=255 {{{195.800003f, 74.8928909f}, {196.363327f, 74.8799515f}, {196.926636f, 74.8978424f}, {197.48996f, 74.9157333f}}}
+seg=256 {{{197.48996f, 74.9157333f}, {197.659973f, 74.9211349f}, {197.829987f, 74.9265366f}, {198, 74.9310913f}}}
+seg=257 {{{198, 74.9310913f}, {198.539948f, 74.945549f}, {199.07988f, 74.9668427f}, {199.619827f, 74.9881363f}}}
+seg=258 {{{199.619827f, 74.9881363f}, {199.813217f, 74.9957657f}, {200.006607f, 75.0033951f}, {200.199997f, 75.0107117f}}}
+seg=259 {{{200.199997f, 75.0107117f}, {200.468765f, 75.020874f}, {200.737534f, 75.032814f}, {201.006287f, 75.0447464f}}}
+seg=260 {{{201.006287f, 75.0447464f}, {201.470856f, 75.0653763f}, {201.935425f, 75.0860062f}, {202.399994f, 75.0974884f}}}
+seg=261 {{{202.399994f, 75.0974884f}, {202.888885f, 75.1095734f}, {203.377777f, 75.1120605f}, {203.866669f, 75.1145554f}}}
+seg=262 {{{203.866669f, 75.1145554f}, {204.111115f, 75.115799f}, {204.35556f, 75.1170425f}, {204.600006f, 75.1194839f}}}
+seg=263 {{{204.600006f, 75.1194839f}, {204.84462f, 75.121933f}, {205.089233f, 75.1239471f}, {205.333847f, 75.1259613f}}}
+seg=264 {{{205.333847f, 75.1259613f}, {205.822556f, 75.129982f}, {206.311279f, 75.1340103f}, {206.800003f, 75.1414719f}}}
+seg=265 {{{206.800003f, 75.1414719f}, {207.53334f, 75.1526718f}, {208.266663f, 75.1675339f}, {209, 75.1866531f}}}
+seg=266 {{{209, 75.1866531f}, {209.350708f, 75.1957932f}, {209.701416f, 75.2080078f}, {210.052124f, 75.2202225f}}}
+seg=267 {{{210.052124f, 75.2202225f}, {210.434753f, 75.2335434f}, {210.817368f, 75.2468643f}, {211.199997f, 75.2562027f}}}
+seg=268 {{{211.199997f, 75.2562027f}, {211.933334f, 75.2741013f}, {212.666672f, 75.2871475f}, {213.399994f, 75.2940521f}}}
+seg=269 {{{213.399994f, 75.2940521f}, {214.133331f, 75.3009567f}, {214.866669f, 75.3041916f}, {215.600006f, 75.2976303f}}}
+seg=270 {{{215.600006f, 75.2976303f}, {216.076187f, 75.2933655f}, {216.552383f, 75.2823792f}, {217.02858f, 75.2713928f}}}
+seg=271 {{{217.02858f, 75.2713928f}, {217.285721f, 75.2654572f}, {217.542862f, 75.2595291f}, {217.800003f, 75.2546539f}}}
+seg=272 {{{217.800003f, 75.2546539f}, {218.041779f, 75.2500763f}, {218.283554f, 75.2413101f}, {218.52533f, 75.2325439f}}}
+seg=273 {{{218.52533f, 75.2325439f}, {219.016891f, 75.2147217f}, {219.508438f, 75.1968994f}, {220, 75.2142487f}}}
+seg=274 {{{220, 75.2142487f}, {220.409988f, 75.2287216f}, {220.819977f, 75.2736588f}, {221.229965f, 75.3185959f}}}
+seg=275 {{{221.229965f, 75.3185959f}, {221.553314f, 75.3540344f}, {221.876648f, 75.389473f}, {222.199997f, 75.4099731f}}}
+seg=276 {{{222.199997f, 75.4099731f}, {222.933334f, 75.456459f}, {223.666672f, 75.4962387f}, {224.399994f, 75.4931717f}}}
+seg=277 {{{224.399994f, 75.4931717f}, {224.885803f, 75.4911423f}, {225.371613f, 75.4618988f}, {225.857422f, 75.4326553f}}}
+seg=278 {{{225.857422f, 75.4326553f}, {226.10495f, 75.4177551f}, {226.352478f, 75.4028549f}, {226.600006f, 75.3915558f}}}
+seg=279 {{{226.600006f, 75.3915558f}, {226.839722f, 75.3806152f}, {227.079437f, 75.3675385f}, {227.319153f, 75.354454f}}}
+seg=280 {{{227.319153f, 75.354454f}, {227.812759f, 75.3275223f}, {228.306381f, 75.3005905f}, {228.800003f, 75.2923279f}}}
+seg=281 {{{228.800003f, 75.2923279f}, {229.309921f, 75.2837906f}, {229.819824f, 75.2945709f}, {230.329742f, 75.3053513f}}}
+seg=282 {{{230.329742f, 75.3053513f}, {230.553162f, 75.3100739f}, {230.776581f, 75.3147964f}, {231, 75.317894f}}}
+seg=283 {{{231, 75.317894f}, {231.290405f, 75.3219223f}, {231.580811f, 75.3275833f}, {231.871216f, 75.333252f}}}
+seg=284 {{{231.871216f, 75.333252f}, {232.314148f, 75.3418884f}, {232.757065f, 75.3505249f}, {233.199997f, 75.3533554f}}}
+seg=285 {{{233.199997f, 75.3533554f}, {233.933334f, 75.3580399f}, {234.666672f, 75.3606873f}, {235.399994f, 75.3460236f}}}
+seg=286 {{{235.399994f, 75.3460236f}, {235.845032f, 75.3371277f}, {236.29007f, 75.3182449f}, {236.735107f, 75.2993622f}}}
+seg=287 {{{236.735107f, 75.2993622f}, {237.023407f, 75.2871323f}, {237.311707f, 75.2749023f}, {237.600006f, 75.2653885f}}}
+seg=288 {{{237.600006f, 75.2653885f}, {238.333328f, 75.2411804f}, {239.066666f, 75.2196732f}, {239.800003f, 75.2007828f}}}
+seg=289 {{{239.800003f, 75.2007828f}, {240.082169f, 75.193512f}, {240.364334f, 75.1891708f}, {240.6465f, 75.1848297f}}}
+seg=290 {{{240.6465f, 75.1848297f}, {241.097672f, 75.177887f}, {241.548828f, 75.1709366f}, {242, 75.1520233f}}}
+seg=291 {{{242, 75.1520233f}, {242.416458f, 75.1345673f}, {242.832916f, 75.1057968f}, {243.249374f, 75.0770187f}}}
+seg=292 {{{243.249374f, 75.0770187f}, {243.566254f, 75.0551224f}, {243.883118f, 75.033226f}, {244.199997f, 75.0163193f}}}
+seg=293 {{{244.199997f, 75.0163193f}, {244.933334f, 74.9771957f}, {245.666672f, 74.9453659f}, {246.399994f, 74.9172668f}}}
+seg=294 {{{246.399994f, 74.9172668f}, {247.133331f, 74.8891678f}, {247.866669f, 74.8672562f}, {248.600006f, 74.8477097f}}}
+seg=295 {{{248.600006f, 74.8477097f}, {248.714218f, 74.8446655f}}}
+seg=296 {{{248.714218f, 74.8446655f}, {249.40947f, 74.8261032f}, {250.104736f, 74.8075485f}, {250.800003f, 74.8000031f}}}
+seg=297 {{{250.800003f, 74.8000031f}, {251.288895f, 74.7947006f}, {251.777771f, 74.7964706f}, {252.266663f, 74.798233f}}}
+seg=298 {{{252.266663f, 74.798233f}, {252.511108f, 74.799118f}, {252.755554f, 74.8000031f}, {253, 74.8000031f}}}
+seg=299 {{{253, 74.8000031f}, {68.1999969f, 74.8000031f}}}
+debugShowCubicIntersection wtTs[0]=1 {{{68.1999969,74.8000031}, {68.4444427,74.8000031}, {68.6888885,74.8039017}, {68.9333344,74.8078003}}} {{68.9333344,74.8078003}} wnTs[0]=0 {{{68.9333344,74.8078003}, {69.422226,74.8155975}, {69.9111099,74.8233948}, {70.4000015,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{68.1999969,74.8000031}, {68.4444427,74.8000031}, {68.6888885,74.8039017}, {68.9333344,74.8078003}}} {{68.1999969,74.8000031}} wnTs[0]=1 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{68.9333344,74.8078003}, {69.422226,74.8155975}, {69.9111099,74.8233948}, {70.4000015,74.8000031}}} {{70.4000015,74.8000031}} wnTs[0]=0 {{{70.4000015,74.8000031}, {70.7796326,74.7818375}, {71.1592636,74.7372665}, {71.5388947,74.6926956}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{68.9333344,74.8078003}, {69.422226,74.8155975}, {69.9111099,74.8233948}, {70.4000015,74.8000031}}} {{70.4000015,74.8000031}} wnTs[0]=0.988095 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.988095214 segID=150 spanID=599
+debugShowCubicIntersection wtTs[0]=1 {{{70.4000015,74.8000031}, {70.7796326,74.7818375}, {71.1592636,74.7372665}, {71.5388947,74.6926956}}} {{71.5388947,74.6926956}} wnTs[0]=0 {{{71.5388947,74.6926956}, {71.8925934,74.6511688}, {72.2462997,74.609642}, {72.5999985,74.5894699}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{70.4000015,74.8000031}, {70.7796326,74.7818375}, {71.1592636,74.7372665}, {71.5388947,74.6926956}}} {{70.4000015,74.8000031}} wnTs[0]=0.988095 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{71.5388947,74.6926956}, {71.8925934,74.6511688}, {72.2462997,74.609642}, {72.5999985,74.5894699}}} {{72.5999985,74.5894699}} wnTs[0]=0 {{{72.5999985,74.5894699}, {73.1301117,74.5592346}, {73.6602249,74.5564957}, {74.1903381,74.5537567}}}
+debugShowCubicIntersection wtTs[0]=1 {{{72.5999985,74.5894699}, {73.1301117,74.5592346}, {73.6602249,74.5564957}, {74.1903381,74.5537567}}} {{74.1903381,74.5537567}} wnTs[0]=0 {{{74.1903381,74.5537567}, {74.3935623,74.5527115}, {74.5967789,74.5516663}, {74.8000031,74.5490646}}}
+debugShowCubicIntersection wtTs[0]=1 {{{74.1903381,74.5537567}, {74.3935623,74.5527115}, {74.5967789,74.5516663}, {74.8000031,74.5490646}}} {{74.8000031,74.5490646}} wnTs[0]=0 {{{74.8000031,74.5490646}, {75.0211792,74.5462341}, {75.242363,74.5398636}, {75.4635391,74.5335007}}}
+debugShowCubicIntersection wtTs[0]=1 {{{74.8000031,74.5490646}, {75.0211792,74.5462341}, {75.242363,74.5398636}, {75.4635391,74.5335007}}} {{75.4635391,74.5335007}} wnTs[0]=0 {{{75.4635391,74.5335007}, {75.9756927,74.5187607}, {76.4878464,74.5040207}, {77,74.5332108}}}
+debugShowCubicIntersection wtTs[0]=1 {{{75.4635391,74.5335007}, {75.9756927,74.5187607}, {76.4878464,74.5040207}, {77,74.5332108}}} {{77,74.5332108}} wnTs[0]=0 {{{77,74.5332108}, {77.3702316,74.5543137}, {77.7404709,74.610733}, {78.1107025,74.66716}}}
+debugShowCubicIntersection wtTs[0]=1 {{{77,74.5332108}, {77.3702316,74.5543137}, {77.7404709,74.610733}, {78.1107025,74.66716}}} {{78.1107025,74.66716}} wnTs[0]=0 {{{78.1107025,74.66716}, {78.4738007,74.722496}, {78.8368988,74.777832}, {79.1999969,74.7998505}}}
+debugShowCubicIntersection wtTs[0]=1 {{{78.1107025,74.66716}, {78.4738007,74.722496}, {78.8368988,74.777832}, {79.1999969,74.7998505}}} {{79.1999969,74.7998505}} wnTs[0]=0 {{{79.1999969,74.7998505}, {79.689003,74.8295059}, {80.1780014,74.819664}, {80.6670074,74.8098221}}}
+debugShowCubicIntersection wtTs[0]=1 {{{79.1999969,74.7998505}, {79.689003,74.8295059}, {80.1780014,74.819664}, {80.6670074,74.8098221}}} {{80.6670074,74.8098221}} wnTs[0]=0 {{{80.6670074,74.8098221}, {80.9113388,74.8049088}, {81.1556702,74.7999954}, {81.4000015,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0.0017190524 {{{79.1999969,74.7998505}, {79.689003,74.8295059}, {80.1780014,74.819664}, {80.6670074,74.8098221}}} {{79.2025223,74.8000031}} wnTs[0]=0.940463 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.0017190524 segID=11 spanID=600
+SkOpSegment::addT insert t=0.940462545 segID=150 spanID=601
+debugShowCubicIntersection wtTs[0]=1 {{{80.6670074,74.8098221}, {80.9113388,74.8049088}, {81.1556702,74.7999954}, {81.4000015,74.8000031}}} {{81.4000015,74.8000031}} wnTs[0]=0 {{{81.4000015,74.8000031}, {81.6438522,74.8000107}, {81.8877106,74.8010635}, {82.1315613,74.8021164}}}
+debugShowCubicLineIntersection wtTs[0]=0.99844881 {{{80.6670074,74.8098221}, {80.9113388,74.8049088}, {81.1556702,74.7999954}, {81.4000015,74.8000031}}} {{81.3988647,74.8000031}} wtTs[1]=1 {{81.4000015,74.8000031}} wnTs[0]=0.928578 {{{253,74.8000031}, {68.1999969,74.8000031}}} wnTs[1]=0.928571405
+SkOpSegment::addT insert t=0.99844881 segID=12 spanID=602
+SkOpSegment::addT insert t=0.928577558 segID=150 spanID=603
+SkOpSegment::addT insert t=0.928571405 segID=150 spanID=604
+debugShowCubicIntersection wtTs[0]=1 {{{81.4000015,74.8000031}, {81.6438522,74.8000107}, {81.8877106,74.8010635}, {82.1315613,74.8021164}}} {{82.1315613,74.8021164}} wnTs[0]=0 {{{82.1315613,74.8021164}, {82.6210403,74.8042297}, {83.1105194,74.8063507}, {83.5999985,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{81.4000015,74.8000031}, {81.6438522,74.8000107}, {81.8877106,74.8010635}, {82.1315613,74.8021164}}} {{81.4000015,74.8000031}} wnTs[0]=0.928571 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{82.1315613,74.8021164}, {82.6210403,74.8042297}, {83.1105194,74.8063507}, {83.5999985,74.8000031}}} {{83.5999985,74.8000031}} wnTs[0]=0 {{{83.5999985,74.8000031}, {83.9259109,74.7957764}, {84.2518234,74.7840347}, {84.5777359,74.7722931}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{82.1315613,74.8021164}, {82.6210403,74.8042297}, {83.1105194,74.8063507}, {83.5999985,74.8000031}}} {{83.5999985,74.8000031}} wnTs[0]=0.916667 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.91666666 segID=150 spanID=605
+debugShowCubicIntersection wtTs[0]=1 {{{83.5999985,74.8000031}, {83.9259109,74.7957764}, {84.2518234,74.7840347}, {84.5777359,74.7722931}}} {{84.5777359,74.7722931}} wnTs[0]=0 {{{84.5777359,74.7722931}, {84.9851608,74.7576141}, {85.3925781,74.7429352}, {85.8000031,74.7429352}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{83.5999985,74.8000031}, {83.9259109,74.7957764}, {84.2518234,74.7840347}, {84.5777359,74.7722931}}} {{83.5999985,74.8000031}} wnTs[0]=0.916667 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{84.5777359,74.7722931}, {84.9851608,74.7576141}, {85.3925781,74.7429352}, {85.8000031,74.7429352}}} {{85.8000031,74.7429352}} wnTs[0]=0 {{{85.8000031,74.7429352}, {86.2074203,74.7429352}, {86.6148453,74.7576141}, {87.0222626,74.7722931}}}
+debugShowCubicIntersection wtTs[0]=1 {{{85.8000031,74.7429352}, {86.2074203,74.7429352}, {86.6148453,74.7576141}, {87.0222626,74.7722931}}} {{87.0222626,74.7722931}} wnTs[0]=0 {{{87.0222626,74.7722931}, {87.348175,74.7840347}, {87.6740875,74.7957764}, {88,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{87.0222626,74.7722931}, {87.348175,74.7840347}, {87.6740875,74.7957764}, {88,74.8000031}}} {{88,74.8000031}} wnTs[0]=0 {{{88,74.8000031}, {88.1576462,74.8020477}, {88.3152924,74.8048859}, {88.4729385,74.8077316}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{87.0222626,74.7722931}, {87.348175,74.7840347}, {87.6740875,74.7957764}, {88,74.8000031}}} {{88,74.8000031}} wnTs[0]=0.892857 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.892857128 segID=150 spanID=606
+debugShowCubicIntersection wtTs[0]=1 {{{88,74.8000031}, {88.1576462,74.8020477}, {88.3152924,74.8048859}, {88.4729385,74.8077316}}} {{88.4729385,74.8077316}} wnTs[0]=0 {{{88.4729385,74.8077316}, {89.0486221,74.8181076}, {89.6243134,74.8284836}, {90.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{88,74.8000031}, {88.1576462,74.8020477}, {88.3152924,74.8048859}, {88.4729385,74.8077316}}} {{88,74.8000031}} wnTs[0]=0.892857 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{88.4729385,74.8077316}, {89.0486221,74.8181076}, {89.6243134,74.8284836}, {90.1999969,74.8000031}}} {{90.1999969,74.8000031}} wnTs[0]=0 {{{90.1999969,74.8000031}, {90.5620499,74.7820892}, {90.9241028,74.7369537}, {91.2861557,74.6918182}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{88.4729385,74.8077316}, {89.0486221,74.8181076}, {89.6243134,74.8284836}, {90.1999969,74.8000031}}} {{90.1999969,74.8000031}} wnTs[0]=0.880952 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.880952383 segID=150 spanID=607
+debugShowCubicIntersection wtTs[0]=1 {{{90.1999969,74.8000031}, {90.5620499,74.7820892}, {90.9241028,74.7369537}, {91.2861557,74.6918182}}} {{91.2861557,74.6918182}} wnTs[0]=0 {{{91.2861557,74.6918182}, {91.6574402,74.6455307}, {92.028717,74.5992432}, {92.4000015,74.5823135}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{90.1999969,74.8000031}, {90.5620499,74.7820892}, {90.9241028,74.7369537}, {91.2861557,74.6918182}}} {{90.1999969,74.8000031}} wnTs[0]=0.880952 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{91.2861557,74.6918182}, {91.6574402,74.6455307}, {92.028717,74.5992432}, {92.4000015,74.5823135}}} {{92.4000015,74.5823135}} wnTs[0]=0 {{{92.4000015,74.5823135}, {92.9111481,74.5590057}, {93.4223022,74.5721283}, {93.9334488,74.5852509}}}
+debugShowCubicIntersection wtTs[0]=1 {{{92.4000015,74.5823135}, {92.9111481,74.5590057}, {93.4223022,74.5721283}, {93.9334488,74.5852509}}} {{93.9334488,74.5852509}} wnTs[0]=0 {{{93.9334488,74.5852509}, {94.155632,74.59095}, {94.3778152,74.5966568}, {94.5999985,74.5993652}}}
+debugShowCubicIntersection wtTs[0]=1 {{{93.9334488,74.5852509}, {94.155632,74.59095}, {94.3778152,74.5966568}, {94.5999985,74.5993652}}} {{94.5999985,74.5993652}} wnTs[0]=0 {{{94.5999985,74.5993652}, {94.8669891,74.602623}, {95.1339798,74.6110153}, {95.4009705,74.6194}}}
+debugShowCubicIntersection wtTs[0]=1 {{{94.5999985,74.5993652}, {94.8669891,74.602623}, {95.1339798,74.6110153}, {95.4009705,74.6194}}} {{95.4009705,74.6194}} wnTs[0]=0 {{{95.4009705,74.6194}, {95.8673172,74.6340485}, {96.3336563,74.6486969}, {96.8000031,74.6360168}}}
+debugShowCubicIntersection wtTs[0]=1 {{{95.4009705,74.6194}, {95.8673172,74.6340485}, {96.3336563,74.6486969}, {96.8000031,74.6360168}}} {{96.8000031,74.6360168}} wnTs[0]=0 {{{96.8000031,74.6360168}, {97.2396164,74.624054}, {97.6792297,74.5860596}, {98.1188431,74.5480652}}}
+debugShowCubicIntersection wtTs[0]=1 {{{96.8000031,74.6360168}, {97.2396164,74.624054}, {97.6792297,74.5860596}, {98.1188431,74.5480652}}} {{98.1188431,74.5480652}} wnTs[0]=0 {{{98.1188431,74.5480652}, {98.4125595,74.5226822}, {98.7062836,74.4972992}, {99,74.4796829}}}
+debugShowCubicIntersection wtTs[0]=1 {{{98.1188431,74.5480652}, {98.4125595,74.5226822}, {98.7062836,74.4972992}, {99,74.4796829}}} {{99,74.4796829}} wnTs[0]=0 {{{99,74.4796829}, {99.7333298,74.4356995}, {100.466667,74.3924866}, {101.199997,74.3721085}}}
+debugShowCubicIntersection wtTs[0]=1 {{{99,74.4796829}, {99.7333298,74.4356995}, {100.466667,74.3924866}, {101.199997,74.3721085}}} {{101.199997,74.3721085}} wnTs[0]=0 {{{101.199997,74.3721085}, {101.6632,74.3592453}, {102.126396,74.3594589}, {102.5896,74.3596725}}}
+debugShowCubicIntersection wtTs[0]=1 {{{101.199997,74.3721085}, {101.6632,74.3592453}, {102.126396,74.3594589}, {102.5896,74.3596725}}} {{102.5896,74.3596725}} wnTs[0]=0 {{{102.5896,74.3596725}, {102.859734,74.3597946}, {103.129868,74.3599167}, {103.400002,74.3574448}}}
+debugShowCubicIntersection wtTs[0]=1 {{{102.5896,74.3596725}, {102.859734,74.3597946}, {103.129868,74.3599167}, {103.400002,74.3574448}}} {{103.400002,74.3574448}} wnTs[0]=0 {{{103.400002,74.3574448}, {103.499123,74.3565369}, {103.598244,74.3556519}, {103.697365,74.3547668}}}
+debugShowCubicIntersection wtTs[0]=1 {{{103.400002,74.3574448}, {103.499123,74.3565369}, {103.598244,74.3556519}, {103.697365,74.3547668}}} {{103.697365,74.3547668}} wnTs[0]=0 {{{103.697365,74.3547668}, {104.331573,74.3491211}, {104.96579,74.3434753}, {105.599998,74.3318787}}}
+debugShowCubicIntersection wtTs[0]=1 {{{103.697365,74.3547668}, {104.331573,74.3491211}, {104.96579,74.3434753}, {105.599998,74.3318787}}} {{105.599998,74.3318787}} wnTs[0]=0 {{{105.599998,74.3318787}, {105.951164,74.3254547}, {106.30233,74.3153458}, {106.653496,74.3052368}}}
+debugShowCubicIntersection wtTs[0]=1 {{{105.599998,74.3318787}, {105.951164,74.3254547}, {106.30233,74.3153458}, {106.653496,74.3052368}}} {{106.653496,74.3052368}} wnTs[0]=0 {{{106.653496,74.3052368}, {107.035667,74.2942352}, {107.417831,74.2832336}, {107.800003,74.2769852}}}
+debugShowCubicIntersection wtTs[0]=1 {{{106.653496,74.3052368}, {107.035667,74.2942352}, {107.417831,74.2832336}, {107.800003,74.2769852}}} {{107.800003,74.2769852}} wnTs[0]=0 {{{107.800003,74.2769852}, {107.904305,74.2752838}, {108.008606,74.2734833}, {108.112907,74.2716827}}}
+debugShowCubicIntersection wtTs[0]=1 {{{107.800003,74.2769852}, {107.904305,74.2752838}, {108.008606,74.2734833}, {108.112907,74.2716827}}} {{108.112907,74.2716827}} wnTs[0]=0 {{{108.112907,74.2716827}, {108.741936,74.2608261}, {109.370972,74.2499771}, {110,74.2599411}}}
+debugShowCubicIntersection wtTs[0]=1 {{{108.112907,74.2716827}, {108.741936,74.2608261}, {109.370972,74.2499771}, {110,74.2599411}}} {{110,74.2599411}} wnTs[0]=0 {{{110,74.2599411}, {110.73333,74.2715607}, {111.466667,74.3080215}, {112.199997,74.3467178}}}
+debugShowCubicIntersection wtTs[0]=1 {{{110,74.2599411}, {110.73333,74.2715607}, {111.466667,74.3080215}, {112.199997,74.3467178}}} {{112.199997,74.3467178}} wnTs[0]=0 {{{112.199997,74.3467178}, {112.524193,74.3638306}, {112.848389,74.3887024}, {113.172585,74.4135742}}}
+debugShowCubicIntersection wtTs[0]=1 {{{112.199997,74.3467178}, {112.524193,74.3638306}, {112.848389,74.3887024}, {113.172585,74.4135742}}} {{113.172585,74.4135742}} wnTs[0]=0 {{{113.172585,74.4135742}, {113.581726,74.4449615}, {113.99086,74.4763565}, {114.400002,74.4921417}}}
+debugShowCubicIntersection wtTs[0]=1 {{{113.172585,74.4135742}, {113.581726,74.4449615}, {113.99086,74.4763565}, {114.400002,74.4921417}}} {{114.400002,74.4921417}} wnTs[0]=0 {{{114.400002,74.4921417}, {115.133331,74.5204391}, {115.866669,74.5265198}, {116.599998,74.5165176}}}
+debugShowCubicIntersection wtTs[0]=1 {{{114.400002,74.4921417}, {115.133331,74.5204391}, {115.866669,74.5265198}, {116.599998,74.5165176}}} {{116.599998,74.5165176}} wnTs[0]=0 {{{116.599998,74.5165176}, {117.013039,74.5108871}, {117.426071,74.4911652}, {117.839111,74.4714432}}}
+debugShowCubicIntersection wtTs[0]=1 {{{116.599998,74.5165176}, {117.013039,74.5108871}, {117.426071,74.4911652}, {117.839111,74.4714432}}} {{117.839111,74.4714432}} wnTs[0]=0 {{{117.839111,74.4714432}, {118.159409,74.4561462}, {118.479706,74.4408493}, {118.800003,74.4321289}}}
+debugShowCubicIntersection wtTs[0]=1 {{{117.839111,74.4714432}, {118.159409,74.4561462}, {118.479706,74.4408493}, {118.800003,74.4321289}}} {{118.800003,74.4321289}} wnTs[0]=0 {{{118.800003,74.4321289}, {118.915619,74.428978}, {119.031235,74.4256439}, {119.146851,74.4223099}}}
+debugShowCubicIntersection wtTs[0]=1 {{{118.800003,74.4321289}, {118.915619,74.428978}, {119.031235,74.4256439}, {119.146851,74.4223099}}} {{119.146851,74.4223099}} wnTs[0]=0 {{{119.146851,74.4223099}, {119.764565,74.4045105}, {120.382286,74.3867035}, {121,74.3966675}}}
+debugShowCubicIntersection wtTs[0]=1 {{{119.146851,74.4223099}, {119.764565,74.4045105}, {120.382286,74.3867035}, {121,74.3966675}}} {{121,74.3966675}} wnTs[0]=0 {{{121,74.3966675}, {121.552635,74.405571}, {122.105278,74.4385529}, {122.657913,74.4715347}}}
+debugShowCubicIntersection wtTs[0]=1 {{{121,74.3966675}, {121.552635,74.405571}, {122.105278,74.4385529}, {122.657913,74.4715347}}} {{122.657913,74.4715347}} wnTs[0]=0 {{{122.657913,74.4715347}, {122.838608,74.4823227}, {123.019302,74.4931107}, {123.199997,74.5030518}}}
+debugShowCubicIntersection wtTs[0]=1 {{{122.657913,74.4715347}, {122.838608,74.4823227}, {123.019302,74.4931107}, {123.199997,74.5030518}}} {{123.199997,74.5030518}} wnTs[0]=0 {{{123.199997,74.5030518}, {123.480431,74.5184784}, {123.760864,74.5394821}, {124.041298,74.5604858}}}
+debugShowCubicIntersection wtTs[0]=1 {{{123.199997,74.5030518}, {123.480431,74.5184784}, {123.760864,74.5394821}, {124.041298,74.5604858}}} {{124.041298,74.5604858}} wnTs[0]=0 {{{124.041298,74.5604858}, {124.494202,74.5943985}, {124.947098,74.6283112}, {125.400002,74.6387558}}}
+debugShowCubicIntersection wtTs[0]=1 {{{124.041298,74.5604858}, {124.494202,74.5943985}, {124.947098,74.6283112}, {125.400002,74.6387558}}} {{125.400002,74.6387558}} wnTs[0]=0 {{{125.400002,74.6387558}, {126.133331,74.6556625}, {126.866669,74.6397476}, {127.599998,74.6044846}}}
+debugShowCubicIntersection wtTs[0]=1 {{{125.400002,74.6387558}, {126.133331,74.6556625}, {126.866669,74.6397476}, {127.599998,74.6044846}}} {{127.599998,74.6044846}} wnTs[0]=0 {{{127.599998,74.6044846}, {128.17691,74.5767441}, {128.75383,74.5233994}, {129.33075,74.4700623}}}
+debugShowCubicIntersection wtTs[0]=1 {{{127.599998,74.6044846}, {128.17691,74.5767441}, {128.75383,74.5233994}, {129.33075,74.4700623}}} {{129.33075,74.4700623}} wnTs[0]=0 {{{129.33075,74.4700623}, {129.487167,74.4555969}, {129.643585,74.4411316}, {129.800003,74.4271774}}}
+debugShowCubicIntersection wtTs[0]=1 {{{129.33075,74.4700623}, {129.487167,74.4555969}, {129.643585,74.4411316}, {129.800003,74.4271774}}} {{129.800003,74.4271774}} wnTs[0]=0 {{{129.800003,74.4271774}, {130.08493,74.4017639}, {130.369843,74.3696442}, {130.65477,74.3375244}}}
+debugShowCubicIntersection wtTs[0]=1 {{{129.800003,74.4271774}, {130.08493,74.4017639}, {130.369843,74.3696442}, {130.65477,74.3375244}}} {{130.65477,74.3375244}} wnTs[0]=0 {{{130.65477,74.3375244}, {131.10318,74.2869797}, {131.55159,74.2364349}, {132,74.2120285}}}
+debugShowCubicIntersection wtTs[0]=1 {{{130.65477,74.3375244}, {131.10318,74.2869797}, {131.55159,74.2364349}, {132,74.2120285}}} {{132,74.2120285}} wnTs[0]=0 {{{132,74.2120285}, {132.682098,74.1748962}, {133.364182,74.1806335}, {134.04628,74.1863708}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{132,74.2120285}, {132.682098,74.1748962}, {133.364182,74.1806335}, {134.04628,74.1863708}}} {{134.04628,74.1863708}} wnTs[0]=0 {{{134.04628,74.1863708}, {134.199997,74.1876526}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{134.199997,74.1876526}, {134.740479,74.1920013}, {135.28096,74.2102814}, {135.821426,74.2285538}}} {{134.199997,74.1876526}} wnTs[0]=1 {{{134.04628,74.1863708}, {134.199997,74.1876526}}}
+debugShowCubicIntersection wtTs[0]=1 {{{134.199997,74.1876526}, {134.740479,74.1920013}, {135.28096,74.2102814}, {135.821426,74.2285538}}} {{135.821426,74.2285538}} wnTs[0]=0 {{{135.821426,74.2285538}, {136.014282,74.2350769}, {136.207138,74.2416}, {136.399994,74.2474899}}}
+debugShowCubicIntersection wtTs[0]=1 {{{135.821426,74.2285538}, {136.014282,74.2350769}, {136.207138,74.2416}, {136.399994,74.2474899}}} {{136.399994,74.2474899}} wnTs[0]=0 {{{136.399994,74.2474899}, {136.723831,74.2573776}, {137.047668,74.2692032}, {137.371506,74.2810364}}}
+debugShowCubicIntersection wtTs[0]=1 {{{136.399994,74.2474899}, {136.723831,74.2573776}, {137.047668,74.2692032}, {137.371506,74.2810364}}} {{137.371506,74.2810364}} wnTs[0]=0 {{{137.371506,74.2810364}, {137.781006,74.2959976}, {138.190506,74.3109589}, {138.600006,74.3219833}}}
+debugShowCubicIntersection wtTs[0]=1 {{{137.371506,74.2810364}, {137.781006,74.2959976}, {138.190506,74.3109589}, {138.600006,74.3219833}}} {{138.600006,74.3219833}} wnTs[0]=0 {{{138.600006,74.3219833}, {138.775055,74.3266983}, {138.950119,74.3321457}, {139.125183,74.3375931}}}
+debugShowCubicIntersection wtTs[0]=1 {{{138.600006,74.3219833}, {138.775055,74.3266983}, {138.950119,74.3321457}, {139.125183,74.3375931}}} {{139.125183,74.3375931}} wnTs[0]=0 {{{139.125183,74.3375931}, {139.683456,74.3549652}, {140.24173,74.3723373}, {140.800003,74.3659744}}}
+debugShowCubicIntersection wtTs[0]=1 {{{139.125183,74.3375931}, {139.683456,74.3549652}, {140.24173,74.3723373}, {140.800003,74.3659744}}} {{140.800003,74.3659744}} wnTs[0]=0 {{{140.800003,74.3659744}, {141.173523,74.3617172}, {141.547028,74.3401871}, {141.920547,74.3186493}}}
+debugShowCubicIntersection wtTs[0]=1 {{{140.800003,74.3659744}, {141.173523,74.3617172}, {141.547028,74.3401871}, {141.920547,74.3186493}}} {{141.920547,74.3186493}} wnTs[0]=0 {{{141.920547,74.3186493}, {142.280365,74.297905}, {142.640182,74.2771606}, {143,74.2718658}}}
+debugShowCubicIntersection wtTs[0]=1 {{{141.920547,74.3186493}, {142.280365,74.297905}, {142.640182,74.2771606}, {143,74.2718658}}} {{143,74.2718658}} wnTs[0]=0 {{{143,74.2718658}, {143.733337,74.2610703}, {144.46666,74.2575989}, {145.199997,74.3011856}}}
+debugShowCubicIntersection wtTs[0]=1 {{{143,74.2718658}, {143.733337,74.2610703}, {144.46666,74.2575989}, {145.199997,74.3011856}}} {{145.199997,74.3011856}} wnTs[0]=0 {{{145.199997,74.3011856}, {145.574966,74.3234711}, {145.949936,74.3711548}, {146.32489,74.4188385}}}
+debugShowCubicIntersection wtTs[0]=1 {{{145.199997,74.3011856}, {145.574966,74.3234711}, {145.949936,74.3711548}, {146.32489,74.4188385}}} {{146.32489,74.4188385}} wnTs[0]=0 {{{146.32489,74.4188385}, {146.683258,74.4644165}, {147.041626,74.5099869}, {147.399994,74.5333862}}}
+debugShowCubicIntersection wtTs[0]=1 {{{146.32489,74.4188385}, {146.683258,74.4644165}, {147.041626,74.5099869}, {147.399994,74.5333862}}} {{147.399994,74.5333862}} wnTs[0]=0 {{{147.399994,74.5333862}, {148.133331,74.5812607}, {148.866669,74.6018982}, {149.600006,74.5884552}}}
+debugShowCubicIntersection wtTs[0]=1 {{{147.399994,74.5333862}, {148.133331,74.5812607}, {148.866669,74.6018982}, {149.600006,74.5884552}}} {{149.600006,74.5884552}} wnTs[0]=0 {{{149.600006,74.5884552}, {149.982086,74.5814514}, {150.364182,74.5504913}, {150.746277,74.5195313}}}
+debugShowCubicIntersection wtTs[0]=1 {{{149.600006,74.5884552}, {149.982086,74.5814514}, {150.364182,74.5504913}, {150.746277,74.5195313}}} {{150.746277,74.5195313}} wnTs[0]=0 {{{150.746277,74.5195313}, {151.097519,74.491066}, {151.448761,74.4626007}, {151.800003,74.4527512}}}
+debugShowCubicIntersection wtTs[0]=1 {{{150.746277,74.5195313}, {151.097519,74.491066}, {151.448761,74.4626007}, {151.800003,74.4527512}}} {{151.800003,74.4527512}} wnTs[0]=0 {{{151.800003,74.4527512}, {152.343307,74.4375076}, {152.886597,74.4468765}, {153.429901,74.4562454}}}
+debugShowCubicIntersection wtTs[0]=1 {{{151.800003,74.4527512}, {152.343307,74.4375076}, {152.886597,74.4468765}, {153.429901,74.4562454}}} {{153.429901,74.4562454}} wnTs[0]=0 {{{153.429901,74.4562454}, {153.619934,74.4595261}, {153.809967,74.4627991}, {154,74.4650269}}}
+debugShowCubicIntersection wtTs[0]=1 {{{153.429901,74.4562454}, {153.619934,74.4595261}, {153.809967,74.4627991}, {154,74.4650269}}} {{154,74.4650269}} wnTs[0]=0 {{{154,74.4650269}, {154.295746,74.4684906}, {154.591476,74.4761581}, {154.887222,74.4838257}}}
+debugShowCubicIntersection wtTs[0]=1 {{{154,74.4650269}, {154.295746,74.4684906}, {154.591476,74.4761581}, {154.887222,74.4838257}}} {{154.887222,74.4838257}} wnTs[0]=0 {{{154.887222,74.4838257}, {155.324814,74.4951782}, {155.762405,74.5065308}, {156.199997,74.5042419}}}
+debugShowCubicIntersection wtTs[0]=1 {{{154.887222,74.4838257}, {155.324814,74.4951782}, {155.762405,74.5065308}, {156.199997,74.5042419}}} {{156.199997,74.5042419}} wnTs[0]=0 {{{156.199997,74.5042419}, {156.559143,74.5023651}, {156.918289,74.487793}, {157.27742,74.4732208}}}
+debugShowCubicIntersection wtTs[0]=1 {{{156.199997,74.5042419}, {156.559143,74.5023651}, {156.918289,74.487793}, {157.27742,74.4732208}}} {{157.27742,74.4732208}} wnTs[0]=0 {{{157.27742,74.4732208}, {157.651611,74.4580307}, {158.025803,74.4428406}, {158.399994,74.4420166}}}
+debugShowCubicIntersection wtTs[0]=1 {{{157.27742,74.4732208}, {157.651611,74.4580307}, {158.025803,74.4428406}, {158.399994,74.4420166}}} {{158.399994,74.4420166}} wnTs[0]=0 {{{158.399994,74.4420166}, {159.133331,74.4403992}, {159.866669,74.448494}, {160.600006,74.4945221}}}
+debugShowCubicIntersection wtTs[0]=1 {{{158.399994,74.4420166}, {159.133331,74.4403992}, {159.866669,74.448494}, {160.600006,74.4945221}}} {{160.600006,74.4945221}} wnTs[0]=0 {{{160.600006,74.4945221}, {160.952393,74.5166473}, {161.304794,74.5602722}, {161.657196,74.6038971}}}
+debugShowCubicIntersection wtTs[0]=1 {{{160.600006,74.4945221}, {160.952393,74.5166473}, {161.304794,74.5602722}, {161.657196,74.6038971}}} {{161.657196,74.6038971}} wnTs[0]=0 {{{161.657196,74.6038971}, {162.038132,74.6510468}, {162.419067,74.698204}, {162.800003,74.7182007}}}
+debugShowCubicIntersection wtTs[0]=1 {{{161.657196,74.6038971}, {162.038132,74.6510468}, {162.419067,74.698204}, {162.800003,74.7182007}}} {{162.800003,74.7182007}} wnTs[0]=0 {{{162.800003,74.7182007}, {163.53334,74.7566986}, {164.266663,74.7636337}, {165,74.7255325}}}
+debugShowCubicIntersection wtTs[0]=1 {{{162.800003,74.7182007}, {163.53334,74.7566986}, {164.266663,74.7636337}, {165,74.7255325}}} {{165,74.7255325}} wnTs[0]=0 {{{165,74.7255325}, {165.356293,74.7070236}, {165.712585,74.65802}, {166.068878,74.6090164}}}
+debugShowCubicIntersection wtTs[0]=1 {{{165,74.7255325}, {165.356293,74.7070236}, {165.712585,74.65802}, {166.068878,74.6090164}}} {{166.068878,74.6090164}} wnTs[0]=0 {{{166.068878,74.6090164}, {166.445923,74.5571594}, {166.822952,74.5053024}, {167.199997,74.4895782}}}
+debugShowCubicIntersection wtTs[0]=1 {{{166.068878,74.6090164}, {166.445923,74.5571594}, {166.822952,74.5053024}, {167.199997,74.4895782}}} {{167.199997,74.4895782}} wnTs[0]=0 {{{167.199997,74.4895782}, {167.933334,74.4590073}, {168.666672,74.4903488}, {169.399994,74.5420837}}}
+debugShowCubicIntersection wtTs[0]=1 {{{167.199997,74.4895782}, {167.933334,74.4590073}, {168.666672,74.4903488}, {169.399994,74.5420837}}} {{169.399994,74.5420837}} wnTs[0]=0 {{{169.399994,74.5420837}, {169.752808,74.5669785}, {170.105621,74.6176682}, {170.458435,74.6683578}}}
+debugShowCubicIntersection wtTs[0]=1 {{{169.399994,74.5420837}, {169.752808,74.5669785}, {170.105621,74.6176682}, {170.458435,74.6683578}}} {{170.458435,74.6683578}} wnTs[0]=0 {{{170.458435,74.6683578}, {170.838959,74.7230225}, {171.219482,74.7776947}, {171.600006,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{170.458435,74.6683578}, {170.838959,74.7230225}, {171.219482,74.7776947}, {171.600006,74.8000031}}} {{171.600006,74.8000031}} wnTs[0]=0 {{{171.600006,74.8000031}, {172.333328,74.8429871}, {173.066666,74.8376999}, {173.800003,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{170.458435,74.6683578}, {170.838959,74.7230225}, {171.219482,74.7776947}, {171.600006,74.8000031}}} {{171.600006,74.8000031}} wnTs[0]=0.440476 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.44047615 segID=150 spanID=608
+debugShowCubicIntersection wtTs[0]=1 {{{171.600006,74.8000031}, {172.333328,74.8429871}, {173.066666,74.8376999}, {173.800003,74.8000031}}} {{173.800003,74.8000031}} wnTs[0]=0 {{{173.800003,74.8000031}, {174.500809,74.7639694}, {175.201599,74.6750717}, {175.902405,74.5861664}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{171.600006,74.8000031}, {172.333328,74.8429871}, {173.066666,74.8376999}, {173.800003,74.8000031}}} {{171.600006,74.8000031}} wtTs[1]=1 {{173.800003,74.8000031}} wnTs[0]=0.440476 {{{253,74.8000031}, {68.1999969,74.8000031}}} wnTs[1]=0.428571405
+SkOpSegment::addT insert t=0.428571405 segID=150 spanID=609
+debugShowCubicLineIntersection wtTs[0]=1 {{{173.800003,74.8000031}, {174.500809,74.7639694}, {175.201599,74.6750717}, {175.902405,74.5861664}}} {{175.902405,74.5861664}} wnTs[0]=0 {{{175.902405,74.5861664}, {176,74.5737915}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{173.800003,74.8000031}, {174.500809,74.7639694}, {175.201599,74.6750717}, {175.902405,74.5861664}}} {{173.800003,74.8000031}} wnTs[0]=0.428571 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{176,74.5737915}, {176.306427,74.5349655}, {176.612839,74.4810333}, {176.919266,74.4270935}}} {{176,74.5737915}} wnTs[0]=1 {{{175.902405,74.5861664}, {176,74.5737915}}}
+debugShowCubicIntersection wtTs[0]=1 {{{176,74.5737915}, {176.306427,74.5349655}, {176.612839,74.4810333}, {176.919266,74.4270935}}} {{176.919266,74.4270935}} wnTs[0]=0 {{{176.919266,74.4270935}, {177.346176,74.3519516}, {177.773087,74.2768097}, {178.199997,74.2425385}}}
+debugShowCubicIntersection wtTs[0]=1 {{{176.919266,74.4270935}, {177.346176,74.3519516}, {177.773087,74.2768097}, {178.199997,74.2425385}}} {{178.199997,74.2425385}} wnTs[0]=0 {{{178.199997,74.2425385}, {178.933334,74.18367}, {179.666672,74.1942673}, {180.399994,74.2205505}}}
+debugShowCubicIntersection wtTs[0]=1 {{{178.199997,74.2425385}, {178.933334,74.18367}, {179.666672,74.1942673}, {180.399994,74.2205505}}} {{180.399994,74.2205505}} wnTs[0]=0 {{{180.399994,74.2205505}, {180.822174,74.2356796}, {181.244354,74.2772751}, {181.666534,74.3188705}}}
+debugShowCubicIntersection wtTs[0]=1 {{{180.399994,74.2205505}, {180.822174,74.2356796}, {181.244354,74.2772751}, {181.666534,74.3188705}}} {{181.666534,74.3188705}} wnTs[0]=0 {{{181.666534,74.3188705}, {181.977692,74.3495255}, {182.288849,74.3801804}, {182.600006,74.400238}}}
+debugShowCubicIntersection wtTs[0]=1 {{{181.666534,74.3188705}, {181.977692,74.3495255}, {182.288849,74.3801804}, {182.600006,74.400238}}} {{182.600006,74.400238}} wnTs[0]=0 {{{182.600006,74.400238}, {183.153549,74.435936}, {183.707108,74.4587555}, {184.260666,74.481575}}}
+debugShowCubicIntersection wtTs[0]=1 {{{182.600006,74.400238}, {183.153549,74.435936}, {183.707108,74.4587555}, {184.260666,74.481575}}} {{184.260666,74.481575}} wnTs[0]=0 {{{184.260666,74.481575}, {184.440445,74.4889832}, {184.620224,74.4963913}, {184.800003,74.5042419}}}
+debugShowCubicIntersection wtTs[0]=1 {{{184.260666,74.481575}, {184.440445,74.4889832}, {184.620224,74.4963913}, {184.800003,74.5042419}}} {{184.800003,74.5042419}} wnTs[0]=0 {{{184.800003,74.5042419}, {185.53334,74.5362625}, {186.266663,74.5669022}, {187,74.5923843}}}
+debugShowCubicIntersection wtTs[0]=1 {{{184.800003,74.5042419}, {185.53334,74.5362625}, {186.266663,74.5669022}, {187,74.5923843}}} {{187,74.5923843}} wnTs[0]=0 {{{187,74.5923843}, {187.218643,74.5999832}, {187.437286,74.6105118}, {187.65593,74.6210403}}}
+debugShowCubicIntersection wtTs[0]=1 {{{187,74.5923843}, {187.218643,74.5999832}, {187.437286,74.6105118}, {187.65593,74.6210403}}} {{187.65593,74.6210403}} wnTs[0]=0 {{{187.65593,74.6210403}, {188.170624,74.6458359}, {188.685303,74.6706314}, {189.199997,74.6571732}}}
+debugShowCubicIntersection wtTs[0]=1 {{{187.65593,74.6210403}, {188.170624,74.6458359}, {188.685303,74.6706314}, {189.199997,74.6571732}}} {{189.199997,74.6571732}} wnTs[0]=0 {{{189.199997,74.6571732}, {189.560562,74.6477432}, {189.921127,74.6077042}, {190.281693,74.5676651}}}
+debugShowCubicIntersection wtTs[0]=1 {{{189.199997,74.6571732}, {189.560562,74.6477432}, {189.921127,74.6077042}, {190.281693,74.5676651}}} {{190.281693,74.5676651}} wnTs[0]=0 {{{190.281693,74.5676651}, {190.654465,74.526268}, {191.027237,74.4848709}, {191.399994,74.4773026}}}
+debugShowCubicIntersection wtTs[0]=1 {{{190.281693,74.5676651}, {190.654465,74.526268}, {191.027237,74.4848709}, {191.399994,74.4773026}}} {{191.399994,74.4773026}} wnTs[0]=0 {{{191.399994,74.4773026}, {191.942627,74.4662857}, {192.48526,74.5001678}, {193.027893,74.5340576}}}
+debugShowCubicIntersection wtTs[0]=1 {{{191.399994,74.4773026}, {191.942627,74.4662857}, {192.48526,74.5001678}, {193.027893,74.5340576}}} {{193.027893,74.5340576}} wnTs[0]=0 {{{193.027893,74.5340576}, {193.218597,74.5459671}, {193.409302,74.5578766}, {193.600006,74.5678329}}}
+debugShowCubicIntersection wtTs[0]=1 {{{193.027893,74.5340576}, {193.218597,74.5459671}, {193.409302,74.5578766}, {193.600006,74.5678329}}} {{193.600006,74.5678329}} wnTs[0]=0 {{{193.600006,74.5678329}, {193.897125,74.5833511}, {194.19426,74.6063919}, {194.491394,74.6294403}}}
+debugShowCubicIntersection wtTs[0]=1 {{{193.600006,74.5678329}, {193.897125,74.5833511}, {194.19426,74.6063919}, {194.491394,74.6294403}}} {{194.491394,74.6294403}} wnTs[0]=0 {{{194.491394,74.6294403}, {194.927597,74.663269}, {195.3638,74.6970978}, {195.800003,74.7071152}}}
+debugShowCubicIntersection wtTs[0]=1 {{{194.491394,74.6294403}, {194.927597,74.663269}, {195.3638,74.6970978}, {195.800003,74.7071152}}} {{195.800003,74.7071152}} wnTs[0]=0 {{{195.800003,74.7071152}, {196.363327,74.7200546}, {196.926636,74.7021637}, {197.48996,74.6842728}}}
+debugShowCubicIntersection wtTs[0]=1 {{{195.800003,74.7071152}, {196.363327,74.7200546}, {196.926636,74.7021637}, {197.48996,74.6842728}}} {{197.48996,74.6842728}} wnTs[0]=0 {{{197.48996,74.6842728}, {197.659973,74.6788712}, {197.829987,74.6734695}, {198,74.6689148}}}
+debugShowCubicIntersection wtTs[0]=1 {{{197.48996,74.6842728}, {197.659973,74.6788712}, {197.829987,74.6734695}, {198,74.6689148}}} {{198,74.6689148}} wnTs[0]=0 {{{198,74.6689148}, {198.539948,74.6544571}, {199.07988,74.6331635}, {199.619827,74.6118698}}}
+debugShowCubicIntersection wtTs[0]=1 {{{198,74.6689148}, {198.539948,74.6544571}, {199.07988,74.6331635}, {199.619827,74.6118698}}} {{199.619827,74.6118698}} wnTs[0]=0 {{{199.619827,74.6118698}, {199.813217,74.6042404}, {200.006607,74.596611}, {200.199997,74.5892944}}}
+debugShowCubicIntersection wtTs[0]=1 {{{199.619827,74.6118698}, {199.813217,74.6042404}, {200.006607,74.596611}, {200.199997,74.5892944}}} {{200.199997,74.5892944}} wnTs[0]=0 {{{200.199997,74.5892944}, {200.468765,74.5791321}, {200.737534,74.5671921}, {201.006287,74.5552597}}}
+debugShowCubicIntersection wtTs[0]=1 {{{200.199997,74.5892944}, {200.468765,74.5791321}, {200.737534,74.5671921}, {201.006287,74.5552597}}} {{201.006287,74.5552597}} wnTs[0]=0 {{{201.006287,74.5552597}, {201.470856,74.5346298}, {201.935425,74.5139999}, {202.399994,74.5025177}}}
+debugShowCubicIntersection wtTs[0]=1 {{{201.006287,74.5552597}, {201.470856,74.5346298}, {201.935425,74.5139999}, {202.399994,74.5025177}}} {{202.399994,74.5025177}} wnTs[0]=0 {{{202.399994,74.5025177}, {202.888885,74.4904327}, {203.377777,74.4879456}, {203.866669,74.4854507}}}
+debugShowCubicIntersection wtTs[0]=1 {{{202.399994,74.5025177}, {202.888885,74.4904327}, {203.377777,74.4879456}, {203.866669,74.4854507}}} {{203.866669,74.4854507}} wnTs[0]=0 {{{203.866669,74.4854507}, {204.111115,74.4842072}, {204.35556,74.4829636}, {204.600006,74.4805222}}}
+debugShowCubicIntersection wtTs[0]=1 {{{203.866669,74.4854507}, {204.111115,74.4842072}, {204.35556,74.4829636}, {204.600006,74.4805222}}} {{204.600006,74.4805222}} wnTs[0]=0 {{{204.600006,74.4805222}, {204.84462,74.4780731}, {205.089233,74.476059}, {205.333847,74.4740448}}}
+debugShowCubicIntersection wtTs[0]=1 {{{204.600006,74.4805222}, {204.84462,74.4780731}, {205.089233,74.476059}, {205.333847,74.4740448}}} {{205.333847,74.4740448}} wnTs[0]=0 {{{205.333847,74.4740448}, {205.822556,74.4700241}, {206.311279,74.4659958}, {206.800003,74.4585342}}}
+debugShowCubicIntersection wtTs[0]=1 {{{205.333847,74.4740448}, {205.822556,74.4700241}, {206.311279,74.4659958}, {206.800003,74.4585342}}} {{206.800003,74.4585342}} wnTs[0]=0 {{{206.800003,74.4585342}, {207.53334,74.4473343}, {208.266663,74.4324722}, {209,74.413353}}}
+debugShowCubicIntersection wtTs[0]=1 {{{206.800003,74.4585342}, {207.53334,74.4473343}, {208.266663,74.4324722}, {209,74.413353}}} {{209,74.413353}} wnTs[0]=0 {{{209,74.413353}, {209.350708,74.404213}, {209.701416,74.3919983}, {210.052124,74.3797836}}}
+debugShowCubicIntersection wtTs[0]=1 {{{209,74.413353}, {209.350708,74.404213}, {209.701416,74.3919983}, {210.052124,74.3797836}}} {{210.052124,74.3797836}} wnTs[0]=0 {{{210.052124,74.3797836}, {210.434753,74.3664627}, {210.817368,74.3531418}, {211.199997,74.3438034}}}
+debugShowCubicIntersection wtTs[0]=1 {{{210.052124,74.3797836}, {210.434753,74.3664627}, {210.817368,74.3531418}, {211.199997,74.3438034}}} {{211.199997,74.3438034}} wnTs[0]=0 {{{211.199997,74.3438034}, {211.933334,74.3259048}, {212.666672,74.3128586}, {213.399994,74.305954}}}
+debugShowCubicIntersection wtTs[0]=1 {{{211.199997,74.3438034}, {211.933334,74.3259048}, {212.666672,74.3128586}, {213.399994,74.305954}}} {{213.399994,74.305954}} wnTs[0]=0 {{{213.399994,74.305954}, {214.133331,74.2990494}, {214.866669,74.2958145}, {215.600006,74.3023758}}}
+debugShowCubicIntersection wtTs[0]=1 {{{213.399994,74.305954}, {214.133331,74.2990494}, {214.866669,74.2958145}, {215.600006,74.3023758}}} {{215.600006,74.3023758}} wnTs[0]=0 {{{215.600006,74.3023758}, {216.076187,74.3066406}, {216.552383,74.317627}, {217.02858,74.3286133}}}
+debugShowCubicIntersection wtTs[0]=1 {{{215.600006,74.3023758}, {216.076187,74.3066406}, {216.552383,74.317627}, {217.02858,74.3286133}}} {{217.02858,74.3286133}} wnTs[0]=0 {{{217.02858,74.3286133}, {217.285721,74.334549}, {217.542862,74.340477}, {217.800003,74.3453522}}}
+debugShowCubicIntersection wtTs[0]=1 {{{217.02858,74.3286133}, {217.285721,74.334549}, {217.542862,74.340477}, {217.800003,74.3453522}}} {{217.800003,74.3453522}} wnTs[0]=0 {{{217.800003,74.3453522}, {218.041779,74.3499298}, {218.283554,74.358696}, {218.52533,74.3674622}}}
+debugShowCubicIntersection wtTs[0]=1 {{{217.800003,74.3453522}, {218.041779,74.3499298}, {218.283554,74.358696}, {218.52533,74.3674622}}} {{218.52533,74.3674622}} wnTs[0]=0 {{{218.52533,74.3674622}, {219.016891,74.3852844}, {219.508438,74.4031067}, {220,74.3857574}}}
+debugShowCubicIntersection wtTs[0]=1 {{{218.52533,74.3674622}, {219.016891,74.3852844}, {219.508438,74.4031067}, {220,74.3857574}}} {{220,74.3857574}} wnTs[0]=0 {{{220,74.3857574}, {220.409988,74.3712845}, {220.819977,74.3263474}, {221.229965,74.2814102}}}
+debugShowCubicIntersection wtTs[0]=1 {{{220,74.3857574}, {220.409988,74.3712845}, {220.819977,74.3263474}, {221.229965,74.2814102}}} {{221.229965,74.2814102}} wnTs[0]=0 {{{221.229965,74.2814102}, {221.553314,74.2459717}, {221.876648,74.2105331}, {222.199997,74.190033}}}
+debugShowCubicIntersection wtTs[0]=1 {{{221.229965,74.2814102}, {221.553314,74.2459717}, {221.876648,74.2105331}, {222.199997,74.190033}}} {{222.199997,74.190033}} wnTs[0]=0 {{{222.199997,74.190033}, {222.933334,74.1435471}, {223.666672,74.1037674}, {224.399994,74.1068344}}}
+debugShowCubicIntersection wtTs[0]=1 {{{222.199997,74.190033}, {222.933334,74.1435471}, {223.666672,74.1037674}, {224.399994,74.1068344}}} {{224.399994,74.1068344}} wnTs[0]=0 {{{224.399994,74.1068344}, {224.885803,74.1088638}, {225.371613,74.1381073}, {225.857422,74.1673508}}}
+debugShowCubicIntersection wtTs[0]=1 {{{224.399994,74.1068344}, {224.885803,74.1088638}, {225.371613,74.1381073}, {225.857422,74.1673508}}} {{225.857422,74.1673508}} wnTs[0]=0 {{{225.857422,74.1673508}, {226.10495,74.182251}, {226.352478,74.1971512}, {226.600006,74.2084503}}}
+debugShowCubicIntersection wtTs[0]=1 {{{225.857422,74.1673508}, {226.10495,74.182251}, {226.352478,74.1971512}, {226.600006,74.2084503}}} {{226.600006,74.2084503}} wnTs[0]=0 {{{226.600006,74.2084503}, {226.839722,74.2193909}, {227.079437,74.2324677}, {227.319153,74.2455521}}}
+debugShowCubicIntersection wtTs[0]=1 {{{226.600006,74.2084503}, {226.839722,74.2193909}, {227.079437,74.2324677}, {227.319153,74.2455521}}} {{227.319153,74.2455521}} wnTs[0]=0 {{{227.319153,74.2455521}, {227.812759,74.2724838}, {228.306381,74.2994156}, {228.800003,74.3076782}}}
+debugShowCubicIntersection wtTs[0]=1 {{{227.319153,74.2455521}, {227.812759,74.2724838}, {228.306381,74.2994156}, {228.800003,74.3076782}}} {{228.800003,74.3076782}} wnTs[0]=0 {{{228.800003,74.3076782}, {229.309921,74.3162155}, {229.819824,74.3054352}, {230.329742,74.2946548}}}
+debugShowCubicIntersection wtTs[0]=1 {{{228.800003,74.3076782}, {229.309921,74.3162155}, {229.819824,74.3054352}, {230.329742,74.2946548}}} {{230.329742,74.2946548}} wnTs[0]=0 {{{230.329742,74.2946548}, {230.553162,74.2899323}, {230.776581,74.2852097}, {231,74.2821121}}}
+debugShowCubicIntersection wtTs[0]=1 {{{230.329742,74.2946548}, {230.553162,74.2899323}, {230.776581,74.2852097}, {231,74.2821121}}} {{231,74.2821121}} wnTs[0]=0 {{{231,74.2821121}, {231.290405,74.2780838}, {231.580811,74.2724228}, {231.871216,74.2667542}}}
+debugShowCubicIntersection wtTs[0]=1 {{{231,74.2821121}, {231.290405,74.2780838}, {231.580811,74.2724228}, {231.871216,74.2667542}}} {{231.871216,74.2667542}} wnTs[0]=0 {{{231.871216,74.2667542}, {232.314148,74.2581177}, {232.757065,74.2494812}, {233.199997,74.2466507}}}
+debugShowCubicIntersection wtTs[0]=1 {{{231.871216,74.2667542}, {232.314148,74.2581177}, {232.757065,74.2494812}, {233.199997,74.2466507}}} {{233.199997,74.2466507}} wnTs[0]=0 {{{233.199997,74.2466507}, {233.343719,74.2457352}, {233.487442,74.2448959}, {233.631165,74.2442474}}}
+debugShowCubicIntersection wtTs[0]=1 {{{233.199997,74.2466507}, {233.343719,74.2457352}, {233.487442,74.2448959}, {233.631165,74.2442474}}} {{233.631165,74.2442474}} wnTs[0]=0 {{{233.631165,74.2442474}, {234.220779,74.2415924}, {234.810394,74.2421951}, {235.399994,74.2539825}}}
+debugShowCubicIntersection wtTs[0]=1 {{{233.631165,74.2442474}, {234.220779,74.2415924}, {234.810394,74.2421951}, {235.399994,74.2539825}}} {{235.399994,74.2539825}} wnTs[0]=0 {{{235.399994,74.2539825}, {235.845032,74.2628784}, {236.29007,74.2817612}, {236.735107,74.3006439}}}
+debugShowCubicIntersection wtTs[0]=1 {{{235.399994,74.2539825}, {235.845032,74.2628784}, {236.29007,74.2817612}, {236.735107,74.3006439}}} {{236.735107,74.3006439}} wnTs[0]=0 {{{236.735107,74.3006439}, {237.023407,74.3128738}, {237.311707,74.3251038}, {237.600006,74.3346176}}}
+debugShowCubicIntersection wtTs[0]=1 {{{236.735107,74.3006439}, {237.023407,74.3128738}, {237.311707,74.3251038}, {237.600006,74.3346176}}} {{237.600006,74.3346176}} wnTs[0]=0 {{{237.600006,74.3346176}, {238.333328,74.3588257}, {239.066666,74.3803329}, {239.800003,74.3992233}}}
+debugShowCubicIntersection wtTs[0]=1 {{{237.600006,74.3346176}, {238.333328,74.3588257}, {239.066666,74.3803329}, {239.800003,74.3992233}}} {{239.800003,74.3992233}} wnTs[0]=0 {{{239.800003,74.3992233}, {240.082169,74.4064941}, {240.364334,74.4108353}, {240.6465,74.4151764}}}
+debugShowCubicIntersection wtTs[0]=1 {{{239.800003,74.3992233}, {240.082169,74.4064941}, {240.364334,74.4108353}, {240.6465,74.4151764}}} {{240.6465,74.4151764}} wnTs[0]=0 {{{240.6465,74.4151764}, {241.097672,74.4221191}, {241.548828,74.4290695}, {242,74.4479828}}}
+debugShowCubicIntersection wtTs[0]=1 {{{240.6465,74.4151764}, {241.097672,74.4221191}, {241.548828,74.4290695}, {242,74.4479828}}} {{242,74.4479828}} wnTs[0]=0 {{{242,74.4479828}, {242.416458,74.4654388}, {242.832916,74.4942093}, {243.249374,74.5229874}}}
+debugShowCubicIntersection wtTs[0]=1 {{{242,74.4479828}, {242.416458,74.4654388}, {242.832916,74.4942093}, {243.249374,74.5229874}}} {{243.249374,74.5229874}} wnTs[0]=0 {{{243.249374,74.5229874}, {243.566254,74.5448837}, {243.883118,74.5667801}, {244.199997,74.5836868}}}
+debugShowCubicIntersection wtTs[0]=1 {{{243.249374,74.5229874}, {243.566254,74.5448837}, {243.883118,74.5667801}, {244.199997,74.5836868}}} {{244.199997,74.5836868}} wnTs[0]=0 {{{244.199997,74.5836868}, {244.933334,74.6228104}, {245.666672,74.6546402}, {246.399994,74.6827393}}}
+debugShowCubicIntersection wtTs[0]=1 {{{244.199997,74.5836868}, {244.933334,74.6228104}, {245.666672,74.6546402}, {246.399994,74.6827393}}} {{246.399994,74.6827393}} wnTs[0]=0 {{{246.399994,74.6827393}, {247.133331,74.7108383}, {247.866669,74.7327499}, {248.600006,74.7522964}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{246.399994,74.6827393}, {247.133331,74.7108383}, {247.866669,74.7327499}, {248.600006,74.7522964}}} {{248.600006,74.7522964}} wnTs[0]=0 {{{248.600006,74.7522964}, {248.714218,74.7553406}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{248.714218,74.7553406}, {249.40947,74.7739029}, {250.104736,74.7924576}, {250.800003,74.8000031}}} {{248.714218,74.7553406}} wnTs[0]=1 {{{248.600006,74.7522964}, {248.714218,74.7553406}}}
+debugShowCubicIntersection wtTs[0]=1 {{{248.714218,74.7553406}, {249.40947,74.7739029}, {250.104736,74.7924576}, {250.800003,74.8000031}}} {{250.800003,74.8000031}} wnTs[0]=0 {{{250.800003,74.8000031}, {251.288895,74.8053055}, {251.777771,74.8035355}, {252.266663,74.8017731}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{248.714218,74.7553406}, {249.40947,74.7739029}, {250.104736,74.7924576}, {250.800003,74.8000031}}} {{250.800003,74.8000031}} wnTs[0]=0.0119047 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.0119047452 segID=150 spanID=610
+debugShowCubicIntersection wtTs[0]=1 {{{250.800003,74.8000031}, {251.288895,74.8053055}, {251.777771,74.8035355}, {252.266663,74.8017731}}} {{252.266663,74.8017731}} wnTs[0]=0 {{{252.266663,74.8017731}, {252.511108,74.8008881}, {252.755554,74.8000031}, {253,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{250.800003,74.8000031}, {251.288895,74.8053055}, {251.777771,74.8035355}, {252.266663,74.8017731}}} {{250.800003,74.8000031}} wnTs[0]=0.0119047 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{252.266663,74.8017731}, {252.511108,74.8008881}, {252.755554,74.8000031}, {253,74.8000031}}} {{253,74.8000031}} wnTs[0]=0 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=0 {{{68.1999969,74.8000031}, {68.4444427,74.8000031}, {68.6888885,74.8039017}, {68.9333344,74.8078003}}} {{68.1999969,74.8000031}} wnTs[0]=0 {{{68.1999969,74.8000031}, {68.4444427,74.8000031}, {68.6888885,74.7961044}, {68.9333344,74.7922058}}}
+debugShowCubicIntersection no intersect {{{68.1999969,74.8000031}, {68.4444427,74.8000031}, {68.6888885,74.8039017}, {68.9333344,74.8078003}}} {{{68.9333344,74.7922058}, {69.422226,74.7844086}, {69.9111099,74.7766113}, {70.4000015,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{68.1999969,74.8000031}, {68.4444427,74.8000031}, {68.6888885,74.8039017}, {68.9333344,74.8078003}}} {{68.1999969,74.8000031}} wnTs[0]=1 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection no intersect {{{68.9333344,74.8078003}, {69.422226,74.8155975}, {69.9111099,74.8233948}, {70.4000015,74.8000031}}} {{{68.1999969,74.8000031}, {68.4444427,74.8000031}, {68.6888885,74.7961044}, {68.9333344,74.7922058}}}
+debugShowCubicIntersection wtTs[0]=1 {{{68.9333344,74.8078003}, {69.422226,74.8155975}, {69.9111099,74.8233948}, {70.4000015,74.8000031}}} {{70.4000015,74.8000031}} wnTs[0]=1 {{{68.9333344,74.7922058}, {69.422226,74.7844086}, {69.9111099,74.7766113}, {70.4000015,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{68.9333344,74.8078003}, {69.422226,74.8155975}, {69.9111099,74.8233948}, {70.4000015,74.8000031}}} {{70.4000015,74.8000031}} wnTs[0]=0 {{{70.4000015,74.8000031}, {70.7796326,74.8181686}, {71.1592636,74.8627396}, {71.5388947,74.9073105}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{68.9333344,74.8078003}, {69.422226,74.8155975}, {69.9111099,74.8233948}, {70.4000015,74.8000031}}} {{70.4000015,74.8000031}} wnTs[0]=0.988095 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.988095214 segID=299 spanID=611
+debugShowCubicIntersection wtTs[0]=0 {{{70.4000015,74.8000031}, {70.7796326,74.7818375}, {71.1592636,74.7372665}, {71.5388947,74.6926956}}} {{70.4000015,74.8000031}} wnTs[0]=1 {{{68.9333344,74.7922058}, {69.422226,74.7844086}, {69.9111099,74.7766113}, {70.4000015,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=0 {{{70.4000015,74.8000031}, {70.7796326,74.7818375}, {71.1592636,74.7372665}, {71.5388947,74.6926956}}} {{70.4000015,74.8000031}} wnTs[0]=0 {{{70.4000015,74.8000031}, {70.7796326,74.8181686}, {71.1592636,74.8627396}, {71.5388947,74.9073105}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{70.4000015,74.8000031}, {70.7796326,74.7818375}, {71.1592636,74.7372665}, {71.5388947,74.6926956}}} {{70.4000015,74.8000031}} wnTs[0]=0.988095 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection no intersect {{{78.1107025,74.66716}, {78.4738007,74.722496}, {78.8368988,74.777832}, {79.1999969,74.7998505}}} {{{79.1999969,74.8001556}, {79.689003,74.7705002}, {80.1780014,74.7803421}, {80.6670074,74.790184}}}
+debugShowCubicIntersection no intersect {{{79.1999969,74.7998505}, {79.689003,74.8295059}, {80.1780014,74.819664}, {80.6670074,74.8098221}}} {{{78.1107025,74.9328461}, {78.4738007,74.8775101}, {78.8368988,74.8221741}, {79.1999969,74.8001556}}}
+debugShowCubicIntersection wtTs[0]=0.00172605823 {{{79.1999969,74.7998505}, {79.689003,74.8295059}, {80.1780014,74.819664}, {80.6670074,74.8098221}}} {{79.2025299,74.8000031}} wnTs[0]=0.00172606 {{{79.1999969,74.8001556}, {79.689003,74.7705002}, {80.1780014,74.7803421}, {80.6670074,74.790184}}}
+SkOpSegment::addT alias t=0.00172605823 segID=11 spanID=600
+SkOpSegment::addT insert t=0.00172605823 segID=161 spanID=612
+debugShowCubicIntersection no intersect {{{79.1999969,74.7998505}, {79.689003,74.8295059}, {80.1780014,74.819664}, {80.6670074,74.8098221}}} {{{80.6670074,74.790184}, {80.9113388,74.7950974}, {81.1556702,74.8000107}, {81.4000015,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0.0017190524 {{{79.1999969,74.7998505}, {79.689003,74.8295059}, {80.1780014,74.819664}, {80.6670074,74.8098221}}} {{79.2025223,74.8000031}} wnTs[0]=0.940463 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.940462545 segID=299 spanID=613
+debugShowCubicIntersection no intersect {{{80.6670074,74.8098221}, {80.9113388,74.8049088}, {81.1556702,74.7999954}, {81.4000015,74.8000031}}} {{{79.1999969,74.8001556}, {79.689003,74.7705002}, {80.1780014,74.7803421}, {80.6670074,74.790184}}}
+debugShowCubicIntersection wtTs[0]=1 {{{80.6670074,74.8098221}, {80.9113388,74.8049088}, {81.1556702,74.7999954}, {81.4000015,74.8000031}}} {{81.4000015,74.8000031}} wnTs[0]=1 {{{80.6670074,74.790184}, {80.9113388,74.7950974}, {81.1556702,74.8000107}, {81.4000015,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{80.6670074,74.8098221}, {80.9113388,74.8049088}, {81.1556702,74.7999954}, {81.4000015,74.8000031}}} {{81.4000015,74.8000031}} wnTs[0]=0 {{{81.4000015,74.8000031}, {81.6438522,74.7999954}, {81.8877106,74.7989426}, {82.1315613,74.7978897}}}
+debugShowCubicLineIntersection wtTs[0]=0.99844881 {{{80.6670074,74.8098221}, {80.9113388,74.8049088}, {81.1556702,74.7999954}, {81.4000015,74.8000031}}} {{81.3988647,74.8000031}} wtTs[1]=1 {{81.4000015,74.8000031}} wnTs[0]=0.928578 {{{253,74.8000031}, {68.1999969,74.8000031}}} wnTs[1]=0.928571405
+SkOpSegment::addT insert t=0.928577558 segID=299 spanID=614
+SkOpSegment::addT insert t=0.928571405 segID=299 spanID=615
+debugShowCubicIntersection wtTs[0]=0 {{{81.4000015,74.8000031}, {81.6438522,74.8000107}, {81.8877106,74.8010635}, {82.1315613,74.8021164}}} {{81.4000015,74.8000031}} wnTs[0]=1 {{{80.6670074,74.790184}, {80.9113388,74.7950974}, {81.1556702,74.8000107}, {81.4000015,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=0 {{{81.4000015,74.8000031}, {81.6438522,74.8000107}, {81.8877106,74.8010635}, {82.1315613,74.8021164}}} {{81.4000015,74.8000031}} wnTs[0]=0 {{{81.4000015,74.8000031}, {81.6438522,74.7999954}, {81.8877106,74.7989426}, {82.1315613,74.7978897}}}
+debugShowCubicIntersection no intersect {{{81.4000015,74.8000031}, {81.6438522,74.8000107}, {81.8877106,74.8010635}, {82.1315613,74.8021164}}} {{{82.1315613,74.7978897}, {82.6210403,74.7957764}, {83.1105194,74.7936554}, {83.5999985,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{81.4000015,74.8000031}, {81.6438522,74.8000107}, {81.8877106,74.8010635}, {82.1315613,74.8021164}}} {{81.4000015,74.8000031}} wnTs[0]=0.928571 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection no intersect {{{82.1315613,74.8021164}, {82.6210403,74.8042297}, {83.1105194,74.8063507}, {83.5999985,74.8000031}}} {{{81.4000015,74.8000031}, {81.6438522,74.7999954}, {81.8877106,74.7989426}, {82.1315613,74.7978897}}}
+debugShowCubicIntersection wtTs[0]=1 {{{82.1315613,74.8021164}, {82.6210403,74.8042297}, {83.1105194,74.8063507}, {83.5999985,74.8000031}}} {{83.5999985,74.8000031}} wnTs[0]=1 {{{82.1315613,74.7978897}, {82.6210403,74.7957764}, {83.1105194,74.7936554}, {83.5999985,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{82.1315613,74.8021164}, {82.6210403,74.8042297}, {83.1105194,74.8063507}, {83.5999985,74.8000031}}} {{83.5999985,74.8000031}} wnTs[0]=0 {{{83.5999985,74.8000031}, {83.9259109,74.8042297}, {84.2518234,74.8159714}, {84.5777359,74.827713}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{82.1315613,74.8021164}, {82.6210403,74.8042297}, {83.1105194,74.8063507}, {83.5999985,74.8000031}}} {{83.5999985,74.8000031}} wnTs[0]=0.916667 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.91666666 segID=299 spanID=616
+debugShowCubicIntersection wtTs[0]=0 {{{83.5999985,74.8000031}, {83.9259109,74.7957764}, {84.2518234,74.7840347}, {84.5777359,74.7722931}}} {{83.5999985,74.8000031}} wnTs[0]=1 {{{82.1315613,74.7978897}, {82.6210403,74.7957764}, {83.1105194,74.7936554}, {83.5999985,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=0 {{{83.5999985,74.8000031}, {83.9259109,74.7957764}, {84.2518234,74.7840347}, {84.5777359,74.7722931}}} {{83.5999985,74.8000031}} wnTs[0]=0 {{{83.5999985,74.8000031}, {83.9259109,74.8042297}, {84.2518234,74.8159714}, {84.5777359,74.827713}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{83.5999985,74.8000031}, {83.9259109,74.7957764}, {84.2518234,74.7840347}, {84.5777359,74.7722931}}} {{83.5999985,74.8000031}} wnTs[0]=0.916667 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{87.0222626,74.7722931}, {87.348175,74.7840347}, {87.6740875,74.7957764}, {88,74.8000031}}} {{88,74.8000031}} wnTs[0]=1 {{{87.0222626,74.827713}, {87.348175,74.8159714}, {87.6740875,74.8042297}, {88,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{87.0222626,74.7722931}, {87.348175,74.7840347}, {87.6740875,74.7957764}, {88,74.8000031}}} {{88,74.8000031}} wnTs[0]=0 {{{88,74.8000031}, {88.1576462,74.7979584}, {88.3152924,74.7951202}, {88.4729385,74.7922745}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{87.0222626,74.7722931}, {87.348175,74.7840347}, {87.6740875,74.7957764}, {88,74.8000031}}} {{88,74.8000031}} wnTs[0]=0.892857 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.892857128 segID=299 spanID=617
+debugShowCubicIntersection wtTs[0]=0 {{{88,74.8000031}, {88.1576462,74.8020477}, {88.3152924,74.8048859}, {88.4729385,74.8077316}}} {{88,74.8000031}} wnTs[0]=1 {{{87.0222626,74.827713}, {87.348175,74.8159714}, {87.6740875,74.8042297}, {88,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=0 {{{88,74.8000031}, {88.1576462,74.8020477}, {88.3152924,74.8048859}, {88.4729385,74.8077316}}} {{88,74.8000031}} wnTs[0]=0 {{{88,74.8000031}, {88.1576462,74.7979584}, {88.3152924,74.7951202}, {88.4729385,74.7922745}}}
+debugShowCubicIntersection no intersect {{{88,74.8000031}, {88.1576462,74.8020477}, {88.3152924,74.8048859}, {88.4729385,74.8077316}}} {{{88.4729385,74.7922745}, {89.0486221,74.7818985}, {89.6243134,74.7715225}, {90.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{88,74.8000031}, {88.1576462,74.8020477}, {88.3152924,74.8048859}, {88.4729385,74.8077316}}} {{88,74.8000031}} wnTs[0]=0.892857 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection no intersect {{{88.4729385,74.8077316}, {89.0486221,74.8181076}, {89.6243134,74.8284836}, {90.1999969,74.8000031}}} {{{88,74.8000031}, {88.1576462,74.7979584}, {88.3152924,74.7951202}, {88.4729385,74.7922745}}}
+debugShowCubicIntersection wtTs[0]=1 {{{88.4729385,74.8077316}, {89.0486221,74.8181076}, {89.6243134,74.8284836}, {90.1999969,74.8000031}}} {{90.1999969,74.8000031}} wnTs[0]=1 {{{88.4729385,74.7922745}, {89.0486221,74.7818985}, {89.6243134,74.7715225}, {90.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{88.4729385,74.8077316}, {89.0486221,74.8181076}, {89.6243134,74.8284836}, {90.1999969,74.8000031}}} {{90.1999969,74.8000031}} wnTs[0]=0 {{{90.1999969,74.8000031}, {90.5620499,74.8179169}, {90.9241028,74.8630524}, {91.2861557,74.9081879}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{88.4729385,74.8077316}, {89.0486221,74.8181076}, {89.6243134,74.8284836}, {90.1999969,74.8000031}}} {{90.1999969,74.8000031}} wnTs[0]=0.880952 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.880952383 segID=299 spanID=618
+debugShowCubicIntersection wtTs[0]=0 {{{90.1999969,74.8000031}, {90.5620499,74.7820892}, {90.9241028,74.7369537}, {91.2861557,74.6918182}}} {{90.1999969,74.8000031}} wnTs[0]=1 {{{88.4729385,74.7922745}, {89.0486221,74.7818985}, {89.6243134,74.7715225}, {90.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=0 {{{90.1999969,74.8000031}, {90.5620499,74.7820892}, {90.9241028,74.7369537}, {91.2861557,74.6918182}}} {{90.1999969,74.8000031}} wnTs[0]=0 {{{90.1999969,74.8000031}, {90.5620499,74.8179169}, {90.9241028,74.8630524}, {91.2861557,74.9081879}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{90.1999969,74.8000031}, {90.5620499,74.7820892}, {90.9241028,74.7369537}, {91.2861557,74.6918182}}} {{90.1999969,74.8000031}} wnTs[0]=0.880952 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{170.458435,74.6683578}, {170.838959,74.7230225}, {171.219482,74.7776947}, {171.600006,74.8000031}}} {{171.600006,74.8000031}} wnTs[0]=1 {{{170.458435,74.9316483}, {170.838959,74.8769836}, {171.219482,74.8223114}, {171.600006,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{170.458435,74.6683578}, {170.838959,74.7230225}, {171.219482,74.7776947}, {171.600006,74.8000031}}} {{171.600006,74.8000031}} wnTs[0]=0 {{{171.600006,74.8000031}, {172.333328,74.757019}, {173.066666,74.7623062}, {173.800003,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{170.458435,74.6683578}, {170.838959,74.7230225}, {171.219482,74.7776947}, {171.600006,74.8000031}}} {{171.600006,74.8000031}} wnTs[0]=0.440476 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.44047615 segID=299 spanID=619
+debugShowCubicIntersection wtTs[0]=0 {{{171.600006,74.8000031}, {172.333328,74.8429871}, {173.066666,74.8376999}, {173.800003,74.8000031}}} {{171.600006,74.8000031}} wnTs[0]=1 {{{170.458435,74.9316483}, {170.838959,74.8769836}, {171.219482,74.8223114}, {171.600006,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=0 {{{171.600006,74.8000031}, {172.333328,74.8429871}, {173.066666,74.8376999}, {173.800003,74.8000031}}} {{171.600006,74.8000031}} wtTs[1]=1 {{173.800003,74.8000031}} wnTs[0]=0 {{{171.600006,74.8000031}, {172.333328,74.757019}, {173.066666,74.7623062}, {173.800003,74.8000031}}} wnTs[1]=1
+debugShowCubicIntersection wtTs[0]=1 {{{171.600006,74.8000031}, {172.333328,74.8429871}, {173.066666,74.8376999}, {173.800003,74.8000031}}} {{173.800003,74.8000031}} wnTs[0]=0 {{{173.800003,74.8000031}, {174.500809,74.8360367}, {175.201599,74.9249344}, {175.902405,75.0138397}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{171.600006,74.8000031}, {172.333328,74.8429871}, {173.066666,74.8376999}, {173.800003,74.8000031}}} {{171.600006,74.8000031}} wtTs[1]=1 {{173.800003,74.8000031}} wnTs[0]=0.440476 {{{253,74.8000031}, {68.1999969,74.8000031}}} wnTs[1]=0.428571405
+SkOpSegment::addT insert t=0.428571405 segID=299 spanID=620
+debugShowCubicIntersection wtTs[0]=0 {{{173.800003,74.8000031}, {174.500809,74.7639694}, {175.201599,74.6750717}, {175.902405,74.5861664}}} {{173.800003,74.8000031}} wnTs[0]=1 {{{171.600006,74.8000031}, {172.333328,74.757019}, {173.066666,74.7623062}, {173.800003,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=0 {{{173.800003,74.8000031}, {174.500809,74.7639694}, {175.201599,74.6750717}, {175.902405,74.5861664}}} {{173.800003,74.8000031}} wnTs[0]=0 {{{173.800003,74.8000031}, {174.500809,74.8360367}, {175.201599,74.9249344}, {175.902405,75.0138397}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{173.800003,74.8000031}, {174.500809,74.7639694}, {175.201599,74.6750717}, {175.902405,74.5861664}}} {{173.800003,74.8000031}} wnTs[0]=0.428571 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{248.714218,74.7553406}, {249.40947,74.7739029}, {250.104736,74.7924576}, {250.800003,74.8000031}}} {{250.800003,74.8000031}} wnTs[0]=1 {{{248.714218,74.8446655}, {249.40947,74.8261032}, {250.104736,74.8075485}, {250.800003,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{248.714218,74.7553406}, {249.40947,74.7739029}, {250.104736,74.7924576}, {250.800003,74.8000031}}} {{250.800003,74.8000031}} wnTs[0]=0 {{{250.800003,74.8000031}, {251.288895,74.7947006}, {251.777771,74.7964706}, {252.266663,74.798233}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{248.714218,74.7553406}, {249.40947,74.7739029}, {250.104736,74.7924576}, {250.800003,74.8000031}}} {{250.800003,74.8000031}} wnTs[0]=0.0119047 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.0119047452 segID=299 spanID=621
+debugShowCubicIntersection wtTs[0]=0 {{{250.800003,74.8000031}, {251.288895,74.8053055}, {251.777771,74.8035355}, {252.266663,74.8017731}}} {{250.800003,74.8000031}} wnTs[0]=1 {{{248.714218,74.8446655}, {249.40947,74.8261032}, {250.104736,74.8075485}, {250.800003,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=0 {{{250.800003,74.8000031}, {251.288895,74.8053055}, {251.777771,74.8035355}, {252.266663,74.8017731}}} {{250.800003,74.8000031}} wnTs[0]=0 {{{250.800003,74.8000031}, {251.288895,74.7947006}, {251.777771,74.7964706}, {252.266663,74.798233}}}
+debugShowCubicIntersection no intersect {{{250.800003,74.8000031}, {251.288895,74.8053055}, {251.777771,74.8035355}, {252.266663,74.8017731}}} {{{252.266663,74.798233}, {252.511108,74.799118}, {252.755554,74.8000031}, {253,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{250.800003,74.8000031}, {251.288895,74.8053055}, {251.777771,74.8035355}, {252.266663,74.8017731}}} {{250.800003,74.8000031}} wnTs[0]=0.0119047 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection no intersect {{{252.266663,74.8017731}, {252.511108,74.8008881}, {252.755554,74.8000031}, {253,74.8000031}}} {{{250.800003,74.8000031}, {251.288895,74.7947006}, {251.777771,74.7964706}, {252.266663,74.798233}}}
+debugShowCubicIntersection wtTs[0]=1 {{{252.266663,74.8017731}, {252.511108,74.8008881}, {252.755554,74.8000031}, {253,74.8000031}}} {{253,74.8000031}} wnTs[0]=1 {{{252.266663,74.798233}, {252.511108,74.799118}, {252.755554,74.8000031}, {253,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{252.266663,74.8017731}, {252.511108,74.8008881}, {252.755554,74.8000031}, {253,74.8000031}}} {{253,74.8000031}} wnTs[0]=0 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{68.1999969,74.8000031}, {68.4444427,74.8000031}, {68.6888885,74.7961044}, {68.9333344,74.7922058}}} {{68.1999969,74.8000031}} wnTs[0]=1 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{68.9333344,74.7922058}, {69.422226,74.7844086}, {69.9111099,74.7766113}, {70.4000015,74.8000031}}} {{70.4000015,74.8000031}} wnTs[0]=0.988095 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{70.4000015,74.8000031}, {70.7796326,74.8181686}, {71.1592636,74.8627396}, {71.5388947,74.9073105}}} {{70.4000015,74.8000031}} wnTs[0]=0.988095 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0.0017190524 {{{79.1999969,74.8001556}, {79.689003,74.7705002}, {80.1780014,74.7803421}, {80.6670074,74.790184}}} {{79.2025223,74.8000031}} wnTs[0]=0.940463 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT alias t=0.0017190524 segID=161 spanID=612
+debugShowCubicLineIntersection wtTs[0]=0.99844881 {{{80.6670074,74.790184}, {80.9113388,74.7950974}, {81.1556702,74.8000107}, {81.4000015,74.8000031}}} {{81.3988647,74.8000031}} wtTs[1]=1 {{81.4000015,74.8000031}} wnTs[0]=0.928578 {{{253,74.8000031}, {68.1999969,74.8000031}}} wnTs[1]=0.928571405
+SkOpSegment::addT insert t=0.99844881 segID=162 spanID=622
+debugShowCubicLineIntersection wtTs[0]=0 {{{81.4000015,74.8000031}, {81.6438522,74.7999954}, {81.8877106,74.7989426}, {82.1315613,74.7978897}}} {{81.4000015,74.8000031}} wnTs[0]=0.928571 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{82.1315613,74.7978897}, {82.6210403,74.7957764}, {83.1105194,74.7936554}, {83.5999985,74.8000031}}} {{83.5999985,74.8000031}} wnTs[0]=0.916667 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{83.5999985,74.8000031}, {83.9259109,74.8042297}, {84.2518234,74.8159714}, {84.5777359,74.827713}}} {{83.5999985,74.8000031}} wnTs[0]=0.916667 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{87.0222626,74.827713}, {87.348175,74.8159714}, {87.6740875,74.8042297}, {88,74.8000031}}} {{88,74.8000031}} wnTs[0]=0.892857 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{88,74.8000031}, {88.1576462,74.7979584}, {88.3152924,74.7951202}, {88.4729385,74.7922745}}} {{88,74.8000031}} wnTs[0]=0.892857 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{88.4729385,74.7922745}, {89.0486221,74.7818985}, {89.6243134,74.7715225}, {90.1999969,74.8000031}}} {{90.1999969,74.8000031}} wnTs[0]=0.880952 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{90.1999969,74.8000031}, {90.5620499,74.8179169}, {90.9241028,74.8630524}, {91.2861557,74.9081879}}} {{90.1999969,74.8000031}} wnTs[0]=0.880952 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{170.458435,74.9316483}, {170.838959,74.8769836}, {171.219482,74.8223114}, {171.600006,74.8000031}}} {{171.600006,74.8000031}} wnTs[0]=0.440476 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{171.600006,74.8000031}, {172.333328,74.757019}, {173.066666,74.7623062}, {173.800003,74.8000031}}} {{171.600006,74.8000031}} wtTs[1]=1 {{173.800003,74.8000031}} wnTs[0]=0.440476 {{{253,74.8000031}, {68.1999969,74.8000031}}} wnTs[1]=0.428571405
+debugShowCubicLineIntersection wtTs[0]=0 {{{173.800003,74.8000031}, {174.500809,74.8360367}, {175.201599,74.9249344}, {175.902405,75.0138397}}} {{173.800003,74.8000031}} wnTs[0]=0.428571 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{248.714218,74.8446655}, {249.40947,74.8261032}, {250.104736,74.8075485}, {250.800003,74.8000031}}} {{250.800003,74.8000031}} wnTs[0]=0.0119047 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{250.800003,74.8000031}, {251.288895,74.7947006}, {251.777771,74.7964706}, {252.266663,74.798233}}} {{250.800003,74.8000031}} wnTs[0]=0.0119047 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{252.266663,74.798233}, {252.511108,74.799118}, {252.755554,74.8000031}, {253,74.8000031}}} {{253,74.8000031}} wnTs[0]=0 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowLineIntersection wtTs[0]=0 {{{253,74.8000031}, {68.1999969,74.8000031}}} {{253,74.8000031}} wtTs[1]=1 {{68.1999969,74.8000031}} wnTs[0]=0 {{{253,74.8000031}, {68.1999969,74.8000031}}} wnTs[1]=1
+debugShowCubicIntersection wtTs[0]=1 {{{68.1999969,74.8000031}, {68.4444427,74.8000031}, {68.6888885,74.7961044}, {68.9333344,74.7922058}}} {{68.9333344,74.7922058}} wnTs[0]=0 {{{68.9333344,74.7922058}, {69.422226,74.7844086}, {69.9111099,74.7766113}, {70.4000015,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{68.1999969,74.8000031}, {68.4444427,74.8000031}, {68.6888885,74.7961044}, {68.9333344,74.7922058}}} {{68.1999969,74.8000031}} wnTs[0]=1 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{68.9333344,74.7922058}, {69.422226,74.7844086}, {69.9111099,74.7766113}, {70.4000015,74.8000031}}} {{70.4000015,74.8000031}} wnTs[0]=0 {{{70.4000015,74.8000031}, {70.7796326,74.8181686}, {71.1592636,74.8627396}, {71.5388947,74.9073105}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{68.9333344,74.7922058}, {69.422226,74.7844086}, {69.9111099,74.7766113}, {70.4000015,74.8000031}}} {{70.4000015,74.8000031}} wnTs[0]=0.988095 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{70.4000015,74.8000031}, {70.7796326,74.8181686}, {71.1592636,74.8627396}, {71.5388947,74.9073105}}} {{71.5388947,74.9073105}} wnTs[0]=0 {{{71.5388947,74.9073105}, {71.8925934,74.9488373}, {72.2462997,74.9903641}, {72.5999985,75.0105362}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{70.4000015,74.8000031}, {70.7796326,74.8181686}, {71.1592636,74.8627396}, {71.5388947,74.9073105}}} {{70.4000015,74.8000031}} wnTs[0]=0.988095 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{71.5388947,74.9073105}, {71.8925934,74.9488373}, {72.2462997,74.9903641}, {72.5999985,75.0105362}}} {{72.5999985,75.0105362}} wnTs[0]=0 {{{72.5999985,75.0105362}, {73.1301117,75.0407715}, {73.6602249,75.0435104}, {74.1903381,75.0462494}}}
+debugShowCubicIntersection wtTs[0]=1 {{{72.5999985,75.0105362}, {73.1301117,75.0407715}, {73.6602249,75.0435104}, {74.1903381,75.0462494}}} {{74.1903381,75.0462494}} wnTs[0]=0 {{{74.1903381,75.0462494}, {74.3935623,75.0472946}, {74.5967789,75.0483398}, {74.8000031,75.0509415}}}
+debugShowCubicIntersection wtTs[0]=1 {{{74.1903381,75.0462494}, {74.3935623,75.0472946}, {74.5967789,75.0483398}, {74.8000031,75.0509415}}} {{74.8000031,75.0509415}} wnTs[0]=0 {{{74.8000031,75.0509415}, {75.0211792,75.053772}, {75.242363,75.0601425}, {75.4635391,75.0665054}}}
+debugShowCubicIntersection wtTs[0]=1 {{{74.8000031,75.0509415}, {75.0211792,75.053772}, {75.242363,75.0601425}, {75.4635391,75.0665054}}} {{75.4635391,75.0665054}} wnTs[0]=0 {{{75.4635391,75.0665054}, {75.9756927,75.0812454}, {76.4878464,75.0959854}, {77,75.0667953}}}
+debugShowCubicIntersection wtTs[0]=1 {{{75.4635391,75.0665054}, {75.9756927,75.0812454}, {76.4878464,75.0959854}, {77,75.0667953}}} {{77,75.0667953}} wnTs[0]=0 {{{77,75.0667953}, {77.3702316,75.0456924}, {77.7404709,74.9892731}, {78.1107025,74.9328461}}}
+debugShowCubicIntersection wtTs[0]=1 {{{77,75.0667953}, {77.3702316,75.0456924}, {77.7404709,74.9892731}, {78.1107025,74.9328461}}} {{78.1107025,74.9328461}} wnTs[0]=0 {{{78.1107025,74.9328461}, {78.4738007,74.8775101}, {78.8368988,74.8221741}, {79.1999969,74.8001556}}}
+debugShowCubicIntersection wtTs[0]=1 {{{78.1107025,74.9328461}, {78.4738007,74.8775101}, {78.8368988,74.8221741}, {79.1999969,74.8001556}}} {{79.1999969,74.8001556}} wnTs[0]=0 {{{79.1999969,74.8001556}, {79.689003,74.7705002}, {80.1780014,74.7803421}, {80.6670074,74.790184}}}
+debugShowCubicIntersection wtTs[0]=1 {{{79.1999969,74.8001556}, {79.689003,74.7705002}, {80.1780014,74.7803421}, {80.6670074,74.790184}}} {{80.6670074,74.790184}} wnTs[0]=0 {{{80.6670074,74.790184}, {80.9113388,74.7950974}, {81.1556702,74.8000107}, {81.4000015,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0.0017190524 {{{79.1999969,74.8001556}, {79.689003,74.7705002}, {80.1780014,74.7803421}, {80.6670074,74.790184}}} {{79.2025223,74.8000031}} wnTs[0]=0.940463 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{80.6670074,74.790184}, {80.9113388,74.7950974}, {81.1556702,74.8000107}, {81.4000015,74.8000031}}} {{81.4000015,74.8000031}} wnTs[0]=0 {{{81.4000015,74.8000031}, {81.6438522,74.7999954}, {81.8877106,74.7989426}, {82.1315613,74.7978897}}}
+debugShowCubicLineIntersection wtTs[0]=0.99844881 {{{80.6670074,74.790184}, {80.9113388,74.7950974}, {81.1556702,74.8000107}, {81.4000015,74.8000031}}} {{81.3988647,74.8000031}} wtTs[1]=1 {{81.4000015,74.8000031}} wnTs[0]=0.928578 {{{253,74.8000031}, {68.1999969,74.8000031}}} wnTs[1]=0.928571405
+debugShowCubicIntersection wtTs[0]=1 {{{81.4000015,74.8000031}, {81.6438522,74.7999954}, {81.8877106,74.7989426}, {82.1315613,74.7978897}}} {{82.1315613,74.7978897}} wnTs[0]=0 {{{82.1315613,74.7978897}, {82.6210403,74.7957764}, {83.1105194,74.7936554}, {83.5999985,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{81.4000015,74.8000031}, {81.6438522,74.7999954}, {81.8877106,74.7989426}, {82.1315613,74.7978897}}} {{81.4000015,74.8000031}} wnTs[0]=0.928571 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{82.1315613,74.7978897}, {82.6210403,74.7957764}, {83.1105194,74.7936554}, {83.5999985,74.8000031}}} {{83.5999985,74.8000031}} wnTs[0]=0 {{{83.5999985,74.8000031}, {83.9259109,74.8042297}, {84.2518234,74.8159714}, {84.5777359,74.827713}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{82.1315613,74.7978897}, {82.6210403,74.7957764}, {83.1105194,74.7936554}, {83.5999985,74.8000031}}} {{83.5999985,74.8000031}} wnTs[0]=0.916667 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{83.5999985,74.8000031}, {83.9259109,74.8042297}, {84.2518234,74.8159714}, {84.5777359,74.827713}}} {{84.5777359,74.827713}} wnTs[0]=0 {{{84.5777359,74.827713}, {84.9851608,74.842392}, {85.3925781,74.8570709}, {85.8000031,74.8570709}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{83.5999985,74.8000031}, {83.9259109,74.8042297}, {84.2518234,74.8159714}, {84.5777359,74.827713}}} {{83.5999985,74.8000031}} wnTs[0]=0.916667 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{84.5777359,74.827713}, {84.9851608,74.842392}, {85.3925781,74.8570709}, {85.8000031,74.8570709}}} {{85.8000031,74.8570709}} wnTs[0]=0 {{{85.8000031,74.8570709}, {86.2074203,74.8570709}, {86.6148453,74.842392}, {87.0222626,74.827713}}}
+debugShowCubicIntersection wtTs[0]=1 {{{85.8000031,74.8570709}, {86.2074203,74.8570709}, {86.6148453,74.842392}, {87.0222626,74.827713}}} {{87.0222626,74.827713}} wnTs[0]=0 {{{87.0222626,74.827713}, {87.348175,74.8159714}, {87.6740875,74.8042297}, {88,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{87.0222626,74.827713}, {87.348175,74.8159714}, {87.6740875,74.8042297}, {88,74.8000031}}} {{88,74.8000031}} wnTs[0]=0 {{{88,74.8000031}, {88.1576462,74.7979584}, {88.3152924,74.7951202}, {88.4729385,74.7922745}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{87.0222626,74.827713}, {87.348175,74.8159714}, {87.6740875,74.8042297}, {88,74.8000031}}} {{88,74.8000031}} wnTs[0]=0.892857 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{88,74.8000031}, {88.1576462,74.7979584}, {88.3152924,74.7951202}, {88.4729385,74.7922745}}} {{88.4729385,74.7922745}} wnTs[0]=0 {{{88.4729385,74.7922745}, {89.0486221,74.7818985}, {89.6243134,74.7715225}, {90.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{88,74.8000031}, {88.1576462,74.7979584}, {88.3152924,74.7951202}, {88.4729385,74.7922745}}} {{88,74.8000031}} wnTs[0]=0.892857 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{88.4729385,74.7922745}, {89.0486221,74.7818985}, {89.6243134,74.7715225}, {90.1999969,74.8000031}}} {{90.1999969,74.8000031}} wnTs[0]=0 {{{90.1999969,74.8000031}, {90.5620499,74.8179169}, {90.9241028,74.8630524}, {91.2861557,74.9081879}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{88.4729385,74.7922745}, {89.0486221,74.7818985}, {89.6243134,74.7715225}, {90.1999969,74.8000031}}} {{90.1999969,74.8000031}} wnTs[0]=0.880952 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{90.1999969,74.8000031}, {90.5620499,74.8179169}, {90.9241028,74.8630524}, {91.2861557,74.9081879}}} {{91.2861557,74.9081879}} wnTs[0]=0 {{{91.2861557,74.9081879}, {91.6574402,74.9544754}, {92.028717,75.0007629}, {92.4000015,75.0176926}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{90.1999969,74.8000031}, {90.5620499,74.8179169}, {90.9241028,74.8630524}, {91.2861557,74.9081879}}} {{90.1999969,74.8000031}} wnTs[0]=0.880952 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{91.2861557,74.9081879}, {91.6574402,74.9544754}, {92.028717,75.0007629}, {92.4000015,75.0176926}}} {{92.4000015,75.0176926}} wnTs[0]=0 {{{92.4000015,75.0176926}, {92.9111481,75.0410004}, {93.4223022,75.0278778}, {93.9334488,75.0147552}}}
+debugShowCubicIntersection wtTs[0]=1 {{{92.4000015,75.0176926}, {92.9111481,75.0410004}, {93.4223022,75.0278778}, {93.9334488,75.0147552}}} {{93.9334488,75.0147552}} wnTs[0]=0 {{{93.9334488,75.0147552}, {94.155632,75.0090561}, {94.3778152,75.0033493}, {94.5999985,75.0006409}}}
+debugShowCubicIntersection wtTs[0]=1 {{{93.9334488,75.0147552}, {94.155632,75.0090561}, {94.3778152,75.0033493}, {94.5999985,75.0006409}}} {{94.5999985,75.0006409}} wnTs[0]=0 {{{94.5999985,75.0006409}, {94.8669891,74.9973831}, {95.1339798,74.9889908}, {95.4009705,74.9806061}}}
+debugShowCubicIntersection wtTs[0]=1 {{{94.5999985,75.0006409}, {94.8669891,74.9973831}, {95.1339798,74.9889908}, {95.4009705,74.9806061}}} {{95.4009705,74.9806061}} wnTs[0]=0 {{{95.4009705,74.9806061}, {95.8673172,74.9659576}, {96.3336563,74.9513092}, {96.8000031,74.9639893}}}
+debugShowCubicIntersection wtTs[0]=1 {{{95.4009705,74.9806061}, {95.8673172,74.9659576}, {96.3336563,74.9513092}, {96.8000031,74.9639893}}} {{96.8000031,74.9639893}} wnTs[0]=0 {{{96.8000031,74.9639893}, {97.2396164,74.9759521}, {97.6792297,75.0139465}, {98.1188431,75.0519409}}}
+debugShowCubicIntersection wtTs[0]=1 {{{96.8000031,74.9639893}, {97.2396164,74.9759521}, {97.6792297,75.0139465}, {98.1188431,75.0519409}}} {{98.1188431,75.0519409}} wnTs[0]=0 {{{98.1188431,75.0519409}, {98.4125595,75.0773239}, {98.7062836,75.1027069}, {99,75.1203232}}}
+debugShowCubicIntersection wtTs[0]=1 {{{98.1188431,75.0519409}, {98.4125595,75.0773239}, {98.7062836,75.1027069}, {99,75.1203232}}} {{99,75.1203232}} wnTs[0]=0 {{{99,75.1203232}, {99.7333298,75.1643066}, {100.466667,75.2075195}, {101.199997,75.2278976}}}
+debugShowCubicIntersection wtTs[0]=1 {{{99,75.1203232}, {99.7333298,75.1643066}, {100.466667,75.2075195}, {101.199997,75.2278976}}} {{101.199997,75.2278976}} wnTs[0]=0 {{{101.199997,75.2278976}, {101.6632,75.2407608}, {102.126396,75.2405472}, {102.5896,75.2403336}}}
+debugShowCubicIntersection wtTs[0]=1 {{{101.199997,75.2278976}, {101.6632,75.2407608}, {102.126396,75.2405472}, {102.5896,75.2403336}}} {{102.5896,75.2403336}} wnTs[0]=0 {{{102.5896,75.2403336}, {102.859734,75.2402115}, {103.129868,75.2400894}, {103.400002,75.2425613}}}
+debugShowCubicIntersection wtTs[0]=1 {{{102.5896,75.2403336}, {102.859734,75.2402115}, {103.129868,75.2400894}, {103.400002,75.2425613}}} {{103.400002,75.2425613}} wnTs[0]=0 {{{103.400002,75.2425613}, {103.499123,75.2434692}, {103.598244,75.2443542}, {103.697365,75.2452393}}}
+debugShowCubicIntersection wtTs[0]=1 {{{103.400002,75.2425613}, {103.499123,75.2434692}, {103.598244,75.2443542}, {103.697365,75.2452393}}} {{103.697365,75.2452393}} wnTs[0]=0 {{{103.697365,75.2452393}, {104.331573,75.250885}, {104.96579,75.2565308}, {105.599998,75.2681274}}}
+debugShowCubicIntersection wtTs[0]=1 {{{103.697365,75.2452393}, {104.331573,75.250885}, {104.96579,75.2565308}, {105.599998,75.2681274}}} {{105.599998,75.2681274}} wnTs[0]=0 {{{105.599998,75.2681274}, {105.951164,75.2745514}, {106.30233,75.2846603}, {106.653496,75.2947693}}}
+debugShowCubicIntersection wtTs[0]=1 {{{105.599998,75.2681274}, {105.951164,75.2745514}, {106.30233,75.2846603}, {106.653496,75.2947693}}} {{106.653496,75.2947693}} wnTs[0]=0 {{{106.653496,75.2947693}, {107.035667,75.3057709}, {107.417831,75.3167725}, {107.800003,75.3230209}}}
+debugShowCubicIntersection wtTs[0]=1 {{{106.653496,75.2947693}, {107.035667,75.3057709}, {107.417831,75.3167725}, {107.800003,75.3230209}}} {{107.800003,75.3230209}} wnTs[0]=0 {{{107.800003,75.3230209}, {107.904305,75.3247223}, {108.008606,75.3265228}, {108.112907,75.3283234}}}
+debugShowCubicIntersection wtTs[0]=1 {{{107.800003,75.3230209}, {107.904305,75.3247223}, {108.008606,75.3265228}, {108.112907,75.3283234}}} {{108.112907,75.3283234}} wnTs[0]=0 {{{108.112907,75.3283234}, {108.741936,75.33918}, {109.370972,75.350029}, {110,75.340065}}}
+debugShowCubicIntersection wtTs[0]=1 {{{108.112907,75.3283234}, {108.741936,75.33918}, {109.370972,75.350029}, {110,75.340065}}} {{110,75.340065}} wnTs[0]=0 {{{110,75.340065}, {110.73333,75.3284454}, {111.466667,75.2919846}, {112.199997,75.2532883}}}
+debugShowCubicIntersection wtTs[0]=1 {{{110,75.340065}, {110.73333,75.3284454}, {111.466667,75.2919846}, {112.199997,75.2532883}}} {{112.199997,75.2532883}} wnTs[0]=0 {{{112.199997,75.2532883}, {112.524193,75.2361755}, {112.848389,75.2113037}, {113.172585,75.1864319}}}
+debugShowCubicIntersection wtTs[0]=1 {{{112.199997,75.2532883}, {112.524193,75.2361755}, {112.848389,75.2113037}, {113.172585,75.1864319}}} {{113.172585,75.1864319}} wnTs[0]=0 {{{113.172585,75.1864319}, {113.581726,75.1550446}, {113.99086,75.1236496}, {114.400002,75.1078644}}}
+debugShowCubicIntersection wtTs[0]=1 {{{113.172585,75.1864319}, {113.581726,75.1550446}, {113.99086,75.1236496}, {114.400002,75.1078644}}} {{114.400002,75.1078644}} wnTs[0]=0 {{{114.400002,75.1078644}, {115.133331,75.079567}, {115.866669,75.0734863}, {116.599998,75.0834885}}}
+debugShowCubicIntersection wtTs[0]=1 {{{114.400002,75.1078644}, {115.133331,75.079567}, {115.866669,75.0734863}, {116.599998,75.0834885}}} {{116.599998,75.0834885}} wnTs[0]=0 {{{116.599998,75.0834885}, {117.013039,75.089119}, {117.426071,75.1088409}, {117.839111,75.1285629}}}
+debugShowCubicIntersection wtTs[0]=1 {{{116.599998,75.0834885}, {117.013039,75.089119}, {117.426071,75.1088409}, {117.839111,75.1285629}}} {{117.839111,75.1285629}} wnTs[0]=0 {{{117.839111,75.1285629}, {118.159409,75.1438599}, {118.479706,75.1591568}, {118.800003,75.1678772}}}
+debugShowCubicIntersection wtTs[0]=1 {{{117.839111,75.1285629}, {118.159409,75.1438599}, {118.479706,75.1591568}, {118.800003,75.1678772}}} {{118.800003,75.1678772}} wnTs[0]=0 {{{118.800003,75.1678772}, {118.915619,75.1710281}, {119.031235,75.1743622}, {119.146851,75.1776962}}}
+debugShowCubicIntersection wtTs[0]=1 {{{118.800003,75.1678772}, {118.915619,75.1710281}, {119.031235,75.1743622}, {119.146851,75.1776962}}} {{119.146851,75.1776962}} wnTs[0]=0 {{{119.146851,75.1776962}, {119.764565,75.1954956}, {120.382286,75.2133026}, {121,75.2033386}}}
+debugShowCubicIntersection wtTs[0]=1 {{{119.146851,75.1776962}, {119.764565,75.1954956}, {120.382286,75.2133026}, {121,75.2033386}}} {{121,75.2033386}} wnTs[0]=0 {{{121,75.2033386}, {121.552635,75.1944351}, {122.105278,75.1614532}, {122.657913,75.1284714}}}
+debugShowCubicIntersection wtTs[0]=1 {{{121,75.2033386}, {121.552635,75.1944351}, {122.105278,75.1614532}, {122.657913,75.1284714}}} {{122.657913,75.1284714}} wnTs[0]=0 {{{122.657913,75.1284714}, {122.838608,75.1176834}, {123.019302,75.1068954}, {123.199997,75.0969543}}}
+debugShowCubicIntersection wtTs[0]=1 {{{122.657913,75.1284714}, {122.838608,75.1176834}, {123.019302,75.1068954}, {123.199997,75.0969543}}} {{123.199997,75.0969543}} wnTs[0]=0 {{{123.199997,75.0969543}, {123.480431,75.0815277}, {123.760864,75.060524}, {124.041298,75.0395203}}}
+debugShowCubicIntersection wtTs[0]=1 {{{123.199997,75.0969543}, {123.480431,75.0815277}, {123.760864,75.060524}, {124.041298,75.0395203}}} {{124.041298,75.0395203}} wnTs[0]=0 {{{124.041298,75.0395203}, {124.494202,75.0056076}, {124.947098,74.9716949}, {125.400002,74.9612503}}}
+debugShowCubicIntersection wtTs[0]=1 {{{124.041298,75.0395203}, {124.494202,75.0056076}, {124.947098,74.9716949}, {125.400002,74.9612503}}} {{125.400002,74.9612503}} wnTs[0]=0 {{{125.400002,74.9612503}, {126.133331,74.9443436}, {126.866669,74.9602585}, {127.599998,74.9955215}}}
+debugShowCubicIntersection wtTs[0]=1 {{{125.400002,74.9612503}, {126.133331,74.9443436}, {126.866669,74.9602585}, {127.599998,74.9955215}}} {{127.599998,74.9955215}} wnTs[0]=0 {{{127.599998,74.9955215}, {128.17691,75.023262}, {128.75383,75.0766068}, {129.33075,75.1299438}}}
+debugShowCubicIntersection wtTs[0]=1 {{{127.599998,74.9955215}, {128.17691,75.023262}, {128.75383,75.0766068}, {129.33075,75.1299438}}} {{129.33075,75.1299438}} wnTs[0]=0 {{{129.33075,75.1299438}, {129.487167,75.1444092}, {129.643585,75.1588745}, {129.800003,75.1728287}}}
+debugShowCubicIntersection wtTs[0]=1 {{{129.33075,75.1299438}, {129.487167,75.1444092}, {129.643585,75.1588745}, {129.800003,75.1728287}}} {{129.800003,75.1728287}} wnTs[0]=0 {{{129.800003,75.1728287}, {130.08493,75.1982422}, {130.369843,75.2303619}, {130.65477,75.2624817}}}
+debugShowCubicIntersection wtTs[0]=1 {{{129.800003,75.1728287}, {130.08493,75.1982422}, {130.369843,75.2303619}, {130.65477,75.2624817}}} {{130.65477,75.2624817}} wnTs[0]=0 {{{130.65477,75.2624817}, {131.10318,75.3130264}, {131.55159,75.3635712}, {132,75.3879776}}}
+debugShowCubicIntersection wtTs[0]=1 {{{130.65477,75.2624817}, {131.10318,75.3130264}, {131.55159,75.3635712}, {132,75.3879776}}} {{132,75.3879776}} wnTs[0]=0 {{{132,75.3879776}, {132.682098,75.4251099}, {133.364182,75.4193726}, {134.04628,75.4136353}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{132,75.3879776}, {132.682098,75.4251099}, {133.364182,75.4193726}, {134.04628,75.4136353}}} {{134.04628,75.4136353}} wnTs[0]=0 {{{134.04628,75.4136353}, {134.199997,75.4123535}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{134.199997,75.4123535}, {134.740479,75.4080048}, {135.28096,75.3897247}, {135.821426,75.3714523}}} {{134.199997,75.4123535}} wnTs[0]=1 {{{134.04628,75.4136353}, {134.199997,75.4123535}}}
+debugShowCubicIntersection wtTs[0]=1 {{{134.199997,75.4123535}, {134.740479,75.4080048}, {135.28096,75.3897247}, {135.821426,75.3714523}}} {{135.821426,75.3714523}} wnTs[0]=0 {{{135.821426,75.3714523}, {136.014282,75.3649292}, {136.207138,75.3584061}, {136.399994,75.3525162}}}
+debugShowCubicIntersection wtTs[0]=1 {{{135.821426,75.3714523}, {136.014282,75.3649292}, {136.207138,75.3584061}, {136.399994,75.3525162}}} {{136.399994,75.3525162}} wnTs[0]=0 {{{136.399994,75.3525162}, {136.723831,75.3426285}, {137.047668,75.3308029}, {137.371506,75.3189697}}}
+debugShowCubicIntersection wtTs[0]=1 {{{136.399994,75.3525162}, {136.723831,75.3426285}, {137.047668,75.3308029}, {137.371506,75.3189697}}} {{137.371506,75.3189697}} wnTs[0]=0 {{{137.371506,75.3189697}, {137.781006,75.3040085}, {138.190506,75.2890472}, {138.600006,75.2780228}}}
+debugShowCubicIntersection wtTs[0]=1 {{{137.371506,75.3189697}, {137.781006,75.3040085}, {138.190506,75.2890472}, {138.600006,75.2780228}}} {{138.600006,75.2780228}} wnTs[0]=0 {{{138.600006,75.2780228}, {138.775055,75.2733078}, {138.950119,75.2678604}, {139.125183,75.262413}}}
+debugShowCubicIntersection wtTs[0]=1 {{{138.600006,75.2780228}, {138.775055,75.2733078}, {138.950119,75.2678604}, {139.125183,75.262413}}} {{139.125183,75.262413}} wnTs[0]=0 {{{139.125183,75.262413}, {139.683456,75.2450409}, {140.24173,75.2276688}, {140.800003,75.2340317}}}
+debugShowCubicIntersection wtTs[0]=1 {{{139.125183,75.262413}, {139.683456,75.2450409}, {140.24173,75.2276688}, {140.800003,75.2340317}}} {{140.800003,75.2340317}} wnTs[0]=0 {{{140.800003,75.2340317}, {141.173523,75.2382889}, {141.547028,75.259819}, {141.920547,75.2813568}}}
+debugShowCubicIntersection wtTs[0]=1 {{{140.800003,75.2340317}, {141.173523,75.2382889}, {141.547028,75.259819}, {141.920547,75.2813568}}} {{141.920547,75.2813568}} wnTs[0]=0 {{{141.920547,75.2813568}, {142.280365,75.3021011}, {142.640182,75.3228455}, {143,75.3281403}}}
+debugShowCubicIntersection wtTs[0]=1 {{{141.920547,75.2813568}, {142.280365,75.3021011}, {142.640182,75.3228455}, {143,75.3281403}}} {{143,75.3281403}} wnTs[0]=0 {{{143,75.3281403}, {143.733337,75.3389359}, {144.46666,75.3424072}, {145.199997,75.2988205}}}
+debugShowCubicIntersection wtTs[0]=1 {{{143,75.3281403}, {143.733337,75.3389359}, {144.46666,75.3424072}, {145.199997,75.2988205}}} {{145.199997,75.2988205}} wnTs[0]=0 {{{145.199997,75.2988205}, {145.574966,75.276535}, {145.949936,75.2288513}, {146.32489,75.1811676}}}
+debugShowCubicIntersection wtTs[0]=1 {{{145.199997,75.2988205}, {145.574966,75.276535}, {145.949936,75.2288513}, {146.32489,75.1811676}}} {{146.32489,75.1811676}} wnTs[0]=0 {{{146.32489,75.1811676}, {146.683258,75.1355896}, {147.041626,75.0900192}, {147.399994,75.0666199}}}
+debugShowCubicIntersection wtTs[0]=1 {{{146.32489,75.1811676}, {146.683258,75.1355896}, {147.041626,75.0900192}, {147.399994,75.0666199}}} {{147.399994,75.0666199}} wnTs[0]=0 {{{147.399994,75.0666199}, {148.133331,75.0187454}, {148.866669,74.9981079}, {149.600006,75.0115509}}}
+debugShowCubicIntersection wtTs[0]=1 {{{147.399994,75.0666199}, {148.133331,75.0187454}, {148.866669,74.9981079}, {149.600006,75.0115509}}} {{149.600006,75.0115509}} wnTs[0]=0 {{{149.600006,75.0115509}, {149.982086,75.0185547}, {150.364182,75.0495148}, {150.746277,75.0804749}}}
+debugShowCubicIntersection wtTs[0]=1 {{{149.600006,75.0115509}, {149.982086,75.0185547}, {150.364182,75.0495148}, {150.746277,75.0804749}}} {{150.746277,75.0804749}} wnTs[0]=0 {{{150.746277,75.0804749}, {151.097519,75.1089401}, {151.448761,75.1374054}, {151.800003,75.1472549}}}
+debugShowCubicIntersection wtTs[0]=1 {{{150.746277,75.0804749}, {151.097519,75.1089401}, {151.448761,75.1374054}, {151.800003,75.1472549}}} {{151.800003,75.1472549}} wnTs[0]=0 {{{151.800003,75.1472549}, {152.343307,75.1624985}, {152.886597,75.1531296}, {153.429901,75.1437607}}}
+debugShowCubicIntersection wtTs[0]=1 {{{151.800003,75.1472549}, {152.343307,75.1624985}, {152.886597,75.1531296}, {153.429901,75.1437607}}} {{153.429901,75.1437607}} wnTs[0]=0 {{{153.429901,75.1437607}, {153.619934,75.14048}, {153.809967,75.137207}, {154,75.1349792}}}
+debugShowCubicIntersection wtTs[0]=1 {{{153.429901,75.1437607}, {153.619934,75.14048}, {153.809967,75.137207}, {154,75.1349792}}} {{154,75.1349792}} wnTs[0]=0 {{{154,75.1349792}, {154.295746,75.1315155}, {154.591476,75.123848}, {154.887222,75.1161804}}}
+debugShowCubicIntersection wtTs[0]=1 {{{154,75.1349792}, {154.295746,75.1315155}, {154.591476,75.123848}, {154.887222,75.1161804}}} {{154.887222,75.1161804}} wnTs[0]=0 {{{154.887222,75.1161804}, {155.324814,75.1048279}, {155.762405,75.0934753}, {156.199997,75.0957642}}}
+debugShowCubicIntersection wtTs[0]=1 {{{154.887222,75.1161804}, {155.324814,75.1048279}, {155.762405,75.0934753}, {156.199997,75.0957642}}} {{156.199997,75.0957642}} wnTs[0]=0 {{{156.199997,75.0957642}, {156.559143,75.097641}, {156.918289,75.1122131}, {157.27742,75.1267853}}}
+debugShowCubicIntersection wtTs[0]=1 {{{156.199997,75.0957642}, {156.559143,75.097641}, {156.918289,75.1122131}, {157.27742,75.1267853}}} {{157.27742,75.1267853}} wnTs[0]=0 {{{157.27742,75.1267853}, {157.651611,75.1419754}, {158.025803,75.1571655}, {158.399994,75.1579895}}}
+debugShowCubicIntersection wtTs[0]=1 {{{157.27742,75.1267853}, {157.651611,75.1419754}, {158.025803,75.1571655}, {158.399994,75.1579895}}} {{158.399994,75.1579895}} wnTs[0]=0 {{{158.399994,75.1579895}, {159.133331,75.1596069}, {159.866669,75.1515121}, {160.600006,75.105484}}}
+debugShowCubicIntersection wtTs[0]=1 {{{158.399994,75.1579895}, {159.133331,75.1596069}, {159.866669,75.1515121}, {160.600006,75.105484}}} {{160.600006,75.105484}} wnTs[0]=0 {{{160.600006,75.105484}, {160.952393,75.0833588}, {161.304794,75.0397339}, {161.657196,74.996109}}}
+debugShowCubicIntersection wtTs[0]=1 {{{160.600006,75.105484}, {160.952393,75.0833588}, {161.304794,75.0397339}, {161.657196,74.996109}}} {{161.657196,74.996109}} wnTs[0]=0 {{{161.657196,74.996109}, {162.038132,74.9489594}, {162.419067,74.9018021}, {162.800003,74.8818054}}}
+debugShowCubicIntersection wtTs[0]=1 {{{161.657196,74.996109}, {162.038132,74.9489594}, {162.419067,74.9018021}, {162.800003,74.8818054}}} {{162.800003,74.8818054}} wnTs[0]=0 {{{162.800003,74.8818054}, {163.53334,74.8433075}, {164.266663,74.8363724}, {165,74.8744736}}}
+debugShowCubicIntersection wtTs[0]=1 {{{162.800003,74.8818054}, {163.53334,74.8433075}, {164.266663,74.8363724}, {165,74.8744736}}} {{165,74.8744736}} wnTs[0]=0 {{{165,74.8744736}, {165.356293,74.8929825}, {165.712585,74.9419861}, {166.068878,74.9909897}}}
+debugShowCubicIntersection wtTs[0]=1 {{{165,74.8744736}, {165.356293,74.8929825}, {165.712585,74.9419861}, {166.068878,74.9909897}}} {{166.068878,74.9909897}} wnTs[0]=0 {{{166.068878,74.9909897}, {166.445923,75.0428467}, {166.822952,75.0947037}, {167.199997,75.1104279}}}
+debugShowCubicIntersection wtTs[0]=1 {{{166.068878,74.9909897}, {166.445923,75.0428467}, {166.822952,75.0947037}, {167.199997,75.1104279}}} {{167.199997,75.1104279}} wnTs[0]=0 {{{167.199997,75.1104279}, {167.933334,75.1409988}, {168.666672,75.1096573}, {169.399994,75.0579224}}}
+debugShowCubicIntersection wtTs[0]=1 {{{167.199997,75.1104279}, {167.933334,75.1409988}, {168.666672,75.1096573}, {169.399994,75.0579224}}} {{169.399994,75.0579224}} wnTs[0]=0 {{{169.399994,75.0579224}, {169.752808,75.0330276}, {170.105621,74.982338}, {170.458435,74.9316483}}}
+debugShowCubicIntersection wtTs[0]=1 {{{169.399994,75.0579224}, {169.752808,75.0330276}, {170.105621,74.982338}, {170.458435,74.9316483}}} {{170.458435,74.9316483}} wnTs[0]=0 {{{170.458435,74.9316483}, {170.838959,74.8769836}, {171.219482,74.8223114}, {171.600006,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{170.458435,74.9316483}, {170.838959,74.8769836}, {171.219482,74.8223114}, {171.600006,74.8000031}}} {{171.600006,74.8000031}} wnTs[0]=0 {{{171.600006,74.8000031}, {172.333328,74.757019}, {173.066666,74.7623062}, {173.800003,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{170.458435,74.9316483}, {170.838959,74.8769836}, {171.219482,74.8223114}, {171.600006,74.8000031}}} {{171.600006,74.8000031}} wnTs[0]=0.440476 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{171.600006,74.8000031}, {172.333328,74.757019}, {173.066666,74.7623062}, {173.800003,74.8000031}}} {{173.800003,74.8000031}} wnTs[0]=0 {{{173.800003,74.8000031}, {174.500809,74.8360367}, {175.201599,74.9249344}, {175.902405,75.0138397}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{171.600006,74.8000031}, {172.333328,74.757019}, {173.066666,74.7623062}, {173.800003,74.8000031}}} {{171.600006,74.8000031}} wtTs[1]=1 {{173.800003,74.8000031}} wnTs[0]=0.440476 {{{253,74.8000031}, {68.1999969,74.8000031}}} wnTs[1]=0.428571405
+debugShowCubicLineIntersection wtTs[0]=1 {{{173.800003,74.8000031}, {174.500809,74.8360367}, {175.201599,74.9249344}, {175.902405,75.0138397}}} {{175.902405,75.0138397}} wnTs[0]=0 {{{175.902405,75.0138397}, {176,75.0262146}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{173.800003,74.8000031}, {174.500809,74.8360367}, {175.201599,74.9249344}, {175.902405,75.0138397}}} {{173.800003,74.8000031}} wnTs[0]=0.428571 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{176,75.0262146}, {176.306427,75.0650406}, {176.612839,75.1189728}, {176.919266,75.1729126}}} {{176,75.0262146}} wnTs[0]=1 {{{175.902405,75.0138397}, {176,75.0262146}}}
+debugShowCubicIntersection wtTs[0]=1 {{{176,75.0262146}, {176.306427,75.0650406}, {176.612839,75.1189728}, {176.919266,75.1729126}}} {{176.919266,75.1729126}} wnTs[0]=0 {{{176.919266,75.1729126}, {177.346176,75.2480545}, {177.773087,75.3231964}, {178.199997,75.3574677}}}
+debugShowCubicIntersection wtTs[0]=1 {{{176.919266,75.1729126}, {177.346176,75.2480545}, {177.773087,75.3231964}, {178.199997,75.3574677}}} {{178.199997,75.3574677}} wnTs[0]=0 {{{178.199997,75.3574677}, {178.933334,75.4163361}, {179.666672,75.4057388}, {180.399994,75.3794556}}}
+debugShowCubicIntersection wtTs[0]=1 {{{178.199997,75.3574677}, {178.933334,75.4163361}, {179.666672,75.4057388}, {180.399994,75.3794556}}} {{180.399994,75.3794556}} wnTs[0]=0 {{{180.399994,75.3794556}, {180.822174,75.3643265}, {181.244354,75.322731}, {181.666534,75.2811356}}}
+debugShowCubicIntersection wtTs[0]=1 {{{180.399994,75.3794556}, {180.822174,75.3643265}, {181.244354,75.322731}, {181.666534,75.2811356}}} {{181.666534,75.2811356}} wnTs[0]=0 {{{181.666534,75.2811356}, {181.977692,75.2504807}, {182.288849,75.2198257}, {182.600006,75.1997681}}}
+debugShowCubicIntersection wtTs[0]=1 {{{181.666534,75.2811356}, {181.977692,75.2504807}, {182.288849,75.2198257}, {182.600006,75.1997681}}} {{182.600006,75.1997681}} wnTs[0]=0 {{{182.600006,75.1997681}, {183.153549,75.1640701}, {183.707108,75.1412506}, {184.260666,75.1184311}}}
+debugShowCubicIntersection wtTs[0]=1 {{{182.600006,75.1997681}, {183.153549,75.1640701}, {183.707108,75.1412506}, {184.260666,75.1184311}}} {{184.260666,75.1184311}} wnTs[0]=0 {{{184.260666,75.1184311}, {184.440445,75.1110229}, {184.620224,75.1036148}, {184.800003,75.0957642}}}
+debugShowCubicIntersection wtTs[0]=1 {{{184.260666,75.1184311}, {184.440445,75.1110229}, {184.620224,75.1036148}, {184.800003,75.0957642}}} {{184.800003,75.0957642}} wnTs[0]=0 {{{184.800003,75.0957642}, {185.53334,75.0637436}, {186.266663,75.0331039}, {187,75.0076218}}}
+debugShowCubicIntersection wtTs[0]=1 {{{184.800003,75.0957642}, {185.53334,75.0637436}, {186.266663,75.0331039}, {187,75.0076218}}} {{187,75.0076218}} wnTs[0]=0 {{{187,75.0076218}, {187.218643,75.0000229}, {187.437286,74.9894943}, {187.65593,74.9789658}}}
+debugShowCubicIntersection wtTs[0]=1 {{{187,75.0076218}, {187.218643,75.0000229}, {187.437286,74.9894943}, {187.65593,74.9789658}}} {{187.65593,74.9789658}} wnTs[0]=0 {{{187.65593,74.9789658}, {188.170624,74.9541702}, {188.685303,74.9293747}, {189.199997,74.9428329}}}
+debugShowCubicIntersection wtTs[0]=1 {{{187.65593,74.9789658}, {188.170624,74.9541702}, {188.685303,74.9293747}, {189.199997,74.9428329}}} {{189.199997,74.9428329}} wnTs[0]=0 {{{189.199997,74.9428329}, {189.560562,74.9522629}, {189.921127,74.9923019}, {190.281693,75.032341}}}
+debugShowCubicIntersection wtTs[0]=1 {{{189.199997,74.9428329}, {189.560562,74.9522629}, {189.921127,74.9923019}, {190.281693,75.032341}}} {{190.281693,75.032341}} wnTs[0]=0 {{{190.281693,75.032341}, {190.654465,75.0737381}, {191.027237,75.1151352}, {191.399994,75.1227036}}}
+debugShowCubicIntersection wtTs[0]=1 {{{190.281693,75.032341}, {190.654465,75.0737381}, {191.027237,75.1151352}, {191.399994,75.1227036}}} {{191.399994,75.1227036}} wnTs[0]=0 {{{191.399994,75.1227036}, {191.942627,75.1337204}, {192.48526,75.0998383}, {193.027893,75.0659485}}}
+debugShowCubicIntersection wtTs[0]=1 {{{191.399994,75.1227036}, {191.942627,75.1337204}, {192.48526,75.0998383}, {193.027893,75.0659485}}} {{193.027893,75.0659485}} wnTs[0]=0 {{{193.027893,75.0659485}, {193.218597,75.054039}, {193.409302,75.0421295}, {193.600006,75.0321732}}}
+debugShowCubicIntersection wtTs[0]=1 {{{193.027893,75.0659485}, {193.218597,75.054039}, {193.409302,75.0421295}, {193.600006,75.0321732}}} {{193.600006,75.0321732}} wnTs[0]=0 {{{193.600006,75.0321732}, {193.897125,75.016655}, {194.19426,74.9936142}, {194.491394,74.9705658}}}
+debugShowCubicIntersection wtTs[0]=1 {{{193.600006,75.0321732}, {193.897125,75.016655}, {194.19426,74.9936142}, {194.491394,74.9705658}}} {{194.491394,74.9705658}} wnTs[0]=0 {{{194.491394,74.9705658}, {194.927597,74.9367371}, {195.3638,74.9029083}, {195.800003,74.8928909}}}
+debugShowCubicIntersection wtTs[0]=1 {{{194.491394,74.9705658}, {194.927597,74.9367371}, {195.3638,74.9029083}, {195.800003,74.8928909}}} {{195.800003,74.8928909}} wnTs[0]=0 {{{195.800003,74.8928909}, {196.363327,74.8799515}, {196.926636,74.8978424}, {197.48996,74.9157333}}}
+debugShowCubicIntersection wtTs[0]=1 {{{195.800003,74.8928909}, {196.363327,74.8799515}, {196.926636,74.8978424}, {197.48996,74.9157333}}} {{197.48996,74.9157333}} wnTs[0]=0 {{{197.48996,74.9157333}, {197.659973,74.9211349}, {197.829987,74.9265366}, {198,74.9310913}}}
+debugShowCubicIntersection wtTs[0]=1 {{{197.48996,74.9157333}, {197.659973,74.9211349}, {197.829987,74.9265366}, {198,74.9310913}}} {{198,74.9310913}} wnTs[0]=0 {{{198,74.9310913}, {198.539948,74.945549}, {199.07988,74.9668427}, {199.619827,74.9881363}}}
+debugShowCubicIntersection wtTs[0]=1 {{{198,74.9310913}, {198.539948,74.945549}, {199.07988,74.9668427}, {199.619827,74.9881363}}} {{199.619827,74.9881363}} wnTs[0]=0 {{{199.619827,74.9881363}, {199.813217,74.9957657}, {200.006607,75.0033951}, {200.199997,75.0107117}}}
+debugShowCubicIntersection wtTs[0]=1 {{{199.619827,74.9881363}, {199.813217,74.9957657}, {200.006607,75.0033951}, {200.199997,75.0107117}}} {{200.199997,75.0107117}} wnTs[0]=0 {{{200.199997,75.0107117}, {200.468765,75.020874}, {200.737534,75.032814}, {201.006287,75.0447464}}}
+debugShowCubicIntersection wtTs[0]=1 {{{200.199997,75.0107117}, {200.468765,75.020874}, {200.737534,75.032814}, {201.006287,75.0447464}}} {{201.006287,75.0447464}} wnTs[0]=0 {{{201.006287,75.0447464}, {201.470856,75.0653763}, {201.935425,75.0860062}, {202.399994,75.0974884}}}
+debugShowCubicIntersection wtTs[0]=1 {{{201.006287,75.0447464}, {201.470856,75.0653763}, {201.935425,75.0860062}, {202.399994,75.0974884}}} {{202.399994,75.0974884}} wnTs[0]=0 {{{202.399994,75.0974884}, {202.888885,75.1095734}, {203.377777,75.1120605}, {203.866669,75.1145554}}}
+debugShowCubicIntersection wtTs[0]=1 {{{202.399994,75.0974884}, {202.888885,75.1095734}, {203.377777,75.1120605}, {203.866669,75.1145554}}} {{203.866669,75.1145554}} wnTs[0]=0 {{{203.866669,75.1145554}, {204.111115,75.115799}, {204.35556,75.1170425}, {204.600006,75.1194839}}}
+debugShowCubicIntersection wtTs[0]=1 {{{203.866669,75.1145554}, {204.111115,75.115799}, {204.35556,75.1170425}, {204.600006,75.1194839}}} {{204.600006,75.1194839}} wnTs[0]=0 {{{204.600006,75.1194839}, {204.84462,75.121933}, {205.089233,75.1239471}, {205.333847,75.1259613}}}
+debugShowCubicIntersection wtTs[0]=1 {{{204.600006,75.1194839}, {204.84462,75.121933}, {205.089233,75.1239471}, {205.333847,75.1259613}}} {{205.333847,75.1259613}} wnTs[0]=0 {{{205.333847,75.1259613}, {205.822556,75.129982}, {206.311279,75.1340103}, {206.800003,75.1414719}}}
+debugShowCubicIntersection wtTs[0]=1 {{{205.333847,75.1259613}, {205.822556,75.129982}, {206.311279,75.1340103}, {206.800003,75.1414719}}} {{206.800003,75.1414719}} wnTs[0]=0 {{{206.800003,75.1414719}, {207.53334,75.1526718}, {208.266663,75.1675339}, {209,75.1866531}}}
+debugShowCubicIntersection wtTs[0]=1 {{{206.800003,75.1414719}, {207.53334,75.1526718}, {208.266663,75.1675339}, {209,75.1866531}}} {{209,75.1866531}} wnTs[0]=0 {{{209,75.1866531}, {209.350708,75.1957932}, {209.701416,75.2080078}, {210.052124,75.2202225}}}
+debugShowCubicIntersection wtTs[0]=1 {{{209,75.1866531}, {209.350708,75.1957932}, {209.701416,75.2080078}, {210.052124,75.2202225}}} {{210.052124,75.2202225}} wnTs[0]=0 {{{210.052124,75.2202225}, {210.434753,75.2335434}, {210.817368,75.2468643}, {211.199997,75.2562027}}}
+debugShowCubicIntersection wtTs[0]=1 {{{210.052124,75.2202225}, {210.434753,75.2335434}, {210.817368,75.2468643}, {211.199997,75.2562027}}} {{211.199997,75.2562027}} wnTs[0]=0 {{{211.199997,75.2562027}, {211.933334,75.2741013}, {212.666672,75.2871475}, {213.399994,75.2940521}}}
+debugShowCubicIntersection wtTs[0]=1 {{{211.199997,75.2562027}, {211.933334,75.2741013}, {212.666672,75.2871475}, {213.399994,75.2940521}}} {{213.399994,75.2940521}} wnTs[0]=0 {{{213.399994,75.2940521}, {214.133331,75.3009567}, {214.866669,75.3041916}, {215.600006,75.2976303}}}
+debugShowCubicIntersection wtTs[0]=1 {{{213.399994,75.2940521}, {214.133331,75.3009567}, {214.866669,75.3041916}, {215.600006,75.2976303}}} {{215.600006,75.2976303}} wnTs[0]=0 {{{215.600006,75.2976303}, {216.076187,75.2933655}, {216.552383,75.2823792}, {217.02858,75.2713928}}}
+debugShowCubicIntersection wtTs[0]=1 {{{215.600006,75.2976303}, {216.076187,75.2933655}, {216.552383,75.2823792}, {217.02858,75.2713928}}} {{217.02858,75.2713928}} wnTs[0]=0 {{{217.02858,75.2713928}, {217.285721,75.2654572}, {217.542862,75.2595291}, {217.800003,75.2546539}}}
+debugShowCubicIntersection wtTs[0]=1 {{{217.02858,75.2713928}, {217.285721,75.2654572}, {217.542862,75.2595291}, {217.800003,75.2546539}}} {{217.800003,75.2546539}} wnTs[0]=0 {{{217.800003,75.2546539}, {218.041779,75.2500763}, {218.283554,75.2413101}, {218.52533,75.2325439}}}
+debugShowCubicIntersection wtTs[0]=1 {{{217.800003,75.2546539}, {218.041779,75.2500763}, {218.283554,75.2413101}, {218.52533,75.2325439}}} {{218.52533,75.2325439}} wnTs[0]=0 {{{218.52533,75.2325439}, {219.016891,75.2147217}, {219.508438,75.1968994}, {220,75.2142487}}}
+debugShowCubicIntersection wtTs[0]=1 {{{218.52533,75.2325439}, {219.016891,75.2147217}, {219.508438,75.1968994}, {220,75.2142487}}} {{220,75.2142487}} wnTs[0]=0 {{{220,75.2142487}, {220.409988,75.2287216}, {220.819977,75.2736588}, {221.229965,75.3185959}}}
+debugShowCubicIntersection wtTs[0]=1 {{{220,75.2142487}, {220.409988,75.2287216}, {220.819977,75.2736588}, {221.229965,75.3185959}}} {{221.229965,75.3185959}} wnTs[0]=0 {{{221.229965,75.3185959}, {221.553314,75.3540344}, {221.876648,75.389473}, {222.199997,75.4099731}}}
+debugShowCubicIntersection wtTs[0]=1 {{{221.229965,75.3185959}, {221.553314,75.3540344}, {221.876648,75.389473}, {222.199997,75.4099731}}} {{222.199997,75.4099731}} wnTs[0]=0 {{{222.199997,75.4099731}, {222.933334,75.456459}, {223.666672,75.4962387}, {224.399994,75.4931717}}}
+debugShowCubicIntersection wtTs[0]=1 {{{222.199997,75.4099731}, {222.933334,75.456459}, {223.666672,75.4962387}, {224.399994,75.4931717}}} {{224.399994,75.4931717}} wnTs[0]=0 {{{224.399994,75.4931717}, {224.885803,75.4911423}, {225.371613,75.4618988}, {225.857422,75.4326553}}}
+debugShowCubicIntersection wtTs[0]=1 {{{224.399994,75.4931717}, {224.885803,75.4911423}, {225.371613,75.4618988}, {225.857422,75.4326553}}} {{225.857422,75.4326553}} wnTs[0]=0 {{{225.857422,75.4326553}, {226.10495,75.4177551}, {226.352478,75.4028549}, {226.600006,75.3915558}}}
+debugShowCubicIntersection wtTs[0]=1 {{{225.857422,75.4326553}, {226.10495,75.4177551}, {226.352478,75.4028549}, {226.600006,75.3915558}}} {{226.600006,75.3915558}} wnTs[0]=0 {{{226.600006,75.3915558}, {226.839722,75.3806152}, {227.079437,75.3675385}, {227.319153,75.354454}}}
+debugShowCubicIntersection wtTs[0]=1 {{{226.600006,75.3915558}, {226.839722,75.3806152}, {227.079437,75.3675385}, {227.319153,75.354454}}} {{227.319153,75.354454}} wnTs[0]=0 {{{227.319153,75.354454}, {227.812759,75.3275223}, {228.306381,75.3005905}, {228.800003,75.2923279}}}
+debugShowCubicIntersection wtTs[0]=1 {{{227.319153,75.354454}, {227.812759,75.3275223}, {228.306381,75.3005905}, {228.800003,75.2923279}}} {{228.800003,75.2923279}} wnTs[0]=0 {{{228.800003,75.2923279}, {229.309921,75.2837906}, {229.819824,75.2945709}, {230.329742,75.3053513}}}
+debugShowCubicIntersection wtTs[0]=1 {{{228.800003,75.2923279}, {229.309921,75.2837906}, {229.819824,75.2945709}, {230.329742,75.3053513}}} {{230.329742,75.3053513}} wnTs[0]=0 {{{230.329742,75.3053513}, {230.553162,75.3100739}, {230.776581,75.3147964}, {231,75.317894}}}
+debugShowCubicIntersection wtTs[0]=1 {{{230.329742,75.3053513}, {230.553162,75.3100739}, {230.776581,75.3147964}, {231,75.317894}}} {{231,75.317894}} wnTs[0]=0 {{{231,75.317894}, {231.290405,75.3219223}, {231.580811,75.3275833}, {231.871216,75.333252}}}
+debugShowCubicIntersection wtTs[0]=1 {{{231,75.317894}, {231.290405,75.3219223}, {231.580811,75.3275833}, {231.871216,75.333252}}} {{231.871216,75.333252}} wnTs[0]=0 {{{231.871216,75.333252}, {232.314148,75.3418884}, {232.757065,75.3505249}, {233.199997,75.3533554}}}
+debugShowCubicIntersection wtTs[0]=1 {{{231.871216,75.333252}, {232.314148,75.3418884}, {232.757065,75.3505249}, {233.199997,75.3533554}}} {{233.199997,75.3533554}} wnTs[0]=0 {{{233.199997,75.3533554}, {233.933334,75.3580399}, {234.666672,75.3606873}, {235.399994,75.3460236}}}
+debugShowCubicIntersection wtTs[0]=1 {{{233.199997,75.3533554}, {233.933334,75.3580399}, {234.666672,75.3606873}, {235.399994,75.3460236}}} {{235.399994,75.3460236}} wnTs[0]=0 {{{235.399994,75.3460236}, {235.845032,75.3371277}, {236.29007,75.3182449}, {236.735107,75.2993622}}}
+debugShowCubicIntersection wtTs[0]=1 {{{235.399994,75.3460236}, {235.845032,75.3371277}, {236.29007,75.3182449}, {236.735107,75.2993622}}} {{236.735107,75.2993622}} wnTs[0]=0 {{{236.735107,75.2993622}, {237.023407,75.2871323}, {237.311707,75.2749023}, {237.600006,75.2653885}}}
+debugShowCubicIntersection wtTs[0]=1 {{{236.735107,75.2993622}, {237.023407,75.2871323}, {237.311707,75.2749023}, {237.600006,75.2653885}}} {{237.600006,75.2653885}} wnTs[0]=0 {{{237.600006,75.2653885}, {238.333328,75.2411804}, {239.066666,75.2196732}, {239.800003,75.2007828}}}
+debugShowCubicIntersection wtTs[0]=1 {{{237.600006,75.2653885}, {238.333328,75.2411804}, {239.066666,75.2196732}, {239.800003,75.2007828}}} {{239.800003,75.2007828}} wnTs[0]=0 {{{239.800003,75.2007828}, {240.082169,75.193512}, {240.364334,75.1891708}, {240.6465,75.1848297}}}
+debugShowCubicIntersection wtTs[0]=1 {{{239.800003,75.2007828}, {240.082169,75.193512}, {240.364334,75.1891708}, {240.6465,75.1848297}}} {{240.6465,75.1848297}} wnTs[0]=0 {{{240.6465,75.1848297}, {241.097672,75.177887}, {241.548828,75.1709366}, {242,75.1520233}}}
+debugShowCubicIntersection wtTs[0]=1 {{{240.6465,75.1848297}, {241.097672,75.177887}, {241.548828,75.1709366}, {242,75.1520233}}} {{242,75.1520233}} wnTs[0]=0 {{{242,75.1520233}, {242.416458,75.1345673}, {242.832916,75.1057968}, {243.249374,75.0770187}}}
+debugShowCubicIntersection wtTs[0]=1 {{{242,75.1520233}, {242.416458,75.1345673}, {242.832916,75.1057968}, {243.249374,75.0770187}}} {{243.249374,75.0770187}} wnTs[0]=0 {{{243.249374,75.0770187}, {243.566254,75.0551224}, {243.883118,75.033226}, {244.199997,75.0163193}}}
+debugShowCubicIntersection wtTs[0]=1 {{{243.249374,75.0770187}, {243.566254,75.0551224}, {243.883118,75.033226}, {244.199997,75.0163193}}} {{244.199997,75.0163193}} wnTs[0]=0 {{{244.199997,75.0163193}, {244.933334,74.9771957}, {245.666672,74.9453659}, {246.399994,74.9172668}}}
+debugShowCubicIntersection wtTs[0]=1 {{{244.199997,75.0163193}, {244.933334,74.9771957}, {245.666672,74.9453659}, {246.399994,74.9172668}}} {{246.399994,74.9172668}} wnTs[0]=0 {{{246.399994,74.9172668}, {247.133331,74.8891678}, {247.866669,74.8672562}, {248.600006,74.8477097}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{246.399994,74.9172668}, {247.133331,74.8891678}, {247.866669,74.8672562}, {248.600006,74.8477097}}} {{248.600006,74.8477097}} wnTs[0]=0 {{{248.600006,74.8477097}, {248.714218,74.8446655}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{248.714218,74.8446655}, {249.40947,74.8261032}, {250.104736,74.8075485}, {250.800003,74.8000031}}} {{248.714218,74.8446655}} wnTs[0]=1 {{{248.600006,74.8477097}, {248.714218,74.8446655}}}
+debugShowCubicIntersection wtTs[0]=1 {{{248.714218,74.8446655}, {249.40947,74.8261032}, {250.104736,74.8075485}, {250.800003,74.8000031}}} {{250.800003,74.8000031}} wnTs[0]=0 {{{250.800003,74.8000031}, {251.288895,74.7947006}, {251.777771,74.7964706}, {252.266663,74.798233}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{248.714218,74.8446655}, {249.40947,74.8261032}, {250.104736,74.8075485}, {250.800003,74.8000031}}} {{250.800003,74.8000031}} wnTs[0]=0.0119047 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicIntersection wtTs[0]=1 {{{250.800003,74.8000031}, {251.288895,74.7947006}, {251.777771,74.7964706}, {252.266663,74.798233}}} {{252.266663,74.798233}} wnTs[0]=0 {{{252.266663,74.798233}, {252.511108,74.799118}, {252.755554,74.8000031}, {253,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=0 {{{250.800003,74.8000031}, {251.288895,74.7947006}, {251.777771,74.7964706}, {252.266663,74.798233}}} {{250.800003,74.8000031}} wnTs[0]=0.0119047 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+debugShowCubicLineIntersection wtTs[0]=1 {{{252.266663,74.798233}, {252.511108,74.799118}, {252.755554,74.8000031}, {253,74.8000031}}} {{253,74.8000031}} wnTs[0]=0 {{{253,74.8000031}, {68.1999969,74.8000031}}}
+SkOpSegment::addT insert t=0.928577558 segID=299 spanID=623
+SkOpSegment::addT insert t=0.99844881 segID=12 spanID=624
 </div>
+
 </div>
 
 <script type="text/javascript">
 
 var testDivs = [
-    fuzz763_1026368,
+    issue3651_7,
+
 ];
 
 var decimal_places = 3; // make this 3 to show more precision
@@ -482,7 +770,7 @@
 var mouseX, mouseY;
 var srcLeft, srcTop;
 var screenWidth, screenHeight;
-var drawnPts, drawnLines, drawnQuads, drawnCubics;
+var drawnPts, drawnLines, drawnQuads, drawnConics, drawnCubics;
 var curveT = 0;
 
 var pt_labels = 2;
@@ -496,9 +784,9 @@
 var draw_active = false;
 var draw_add = false;
 var draw_angle = 0;
+var draw_coincidence = false;
 var draw_deriviatives = 0;
 var draw_hints = false;
-var draw_hodo = 0;
 var draw_id = false;
 var draw_intersection = 0;
 var draw_intersectT = false;
@@ -509,6 +797,7 @@
 var draw_op = 0;
 var draw_sequence = false;
 var draw_sort = 0;
+var draw_top = false;
 var draw_path = 3;
 var draw_computed = 0;
 var retina_scale = !!window.devicePixelRatio;
@@ -516,22 +805,28 @@
 var activeCount = 0;
 var addCount = 0;
 var angleCount = 0;
+var coinCount = 0;
 var opCount = 0;
 var sectCount = 0;
 var sortCount = 0;
+var topCount = 0;
 var markCount = 0;
 var activeMax = 0;
 var addMax = 0;
 var angleMax = 0;
+var coinMax = 0;
 var sectMax = 0;
 var sectMax2 = 0;
 var sortMax = 0;
+var topMax = 0;
 var markMax = 0;
 var opMax = 0;
 var stepMax = 0;
 var lastIndex = 0;
 var hasPath = false;
 var hasComputedPath = false;
+var angleBetween = false;
+var afterIndex = 0;
 
 var firstActiveSpan = -1;
 var logStart = -1;
@@ -542,6 +837,7 @@
 var SPAN_Y1 = SPAN_X1 + 1;
 var SPAN_X2 = SPAN_Y1 + 1;
 var SPAN_Y2 = SPAN_X2 + 1;
+
 var SPAN_L_T = SPAN_Y2 + 1;
 var SPAN_L_TX = SPAN_L_T + 1;
 var SPAN_L_TY = SPAN_L_TX + 1;
@@ -555,6 +851,7 @@
 
 var SPAN_X3 = SPAN_Y2 + 1;
 var SPAN_Y3 = SPAN_X3 + 1;
+
 var SPAN_Q_T = SPAN_Y3 + 1;
 var SPAN_Q_TX = SPAN_Q_T + 1;
 var SPAN_Q_TY = SPAN_Q_TX + 1;
@@ -566,8 +863,21 @@
 var SPAN_Q_VAL = SPAN_Q_SUM + 1;
 var SPAN_Q_OPP = SPAN_Q_VAL + 1;
 
+var SPAN_K_W = SPAN_Y3 + 1;
+var SPAN_K_T = SPAN_K_W + 1;
+var SPAN_K_TX = SPAN_K_T + 1;
+var SPAN_K_TY = SPAN_K_TX + 1;
+var SPAN_K_TEND = SPAN_K_TY + 1;
+var SPAN_K_OTHER = SPAN_K_TEND + 1;
+var SPAN_K_OTHERT = SPAN_K_OTHER + 1;
+var SPAN_K_OTHERI = SPAN_K_OTHERT + 1;
+var SPAN_K_SUM = SPAN_K_OTHERI + 1;
+var SPAN_K_VAL = SPAN_K_SUM + 1;
+var SPAN_K_OPP = SPAN_K_VAL + 1;
+
 var SPAN_X4 = SPAN_Y3 + 1;
 var SPAN_Y4 = SPAN_X4 + 1;
+
 var SPAN_C_T = SPAN_Y4 + 1;
 var SPAN_C_TX = SPAN_C_T + 1;
 var SPAN_C_TY = SPAN_C_TX + 1;
@@ -581,18 +891,21 @@
 
 var ACTIVE_LINE_SPAN =        1;
 var ACTIVE_QUAD_SPAN =        ACTIVE_LINE_SPAN + 1;
-var ACTIVE_CUBIC_SPAN =       ACTIVE_QUAD_SPAN + 1;
+var ACTIVE_CONIC_SPAN =       ACTIVE_QUAD_SPAN + 1;
+var ACTIVE_CUBIC_SPAN =       ACTIVE_CONIC_SPAN + 1;
 
 var ADD_MOVETO =              ACTIVE_CUBIC_SPAN + 1;
 var ADD_LINETO =              ADD_MOVETO + 1;
 var ADD_QUADTO =              ADD_LINETO + 1;
-var ADD_CUBICTO =             ADD_QUADTO + 1;
+var ADD_CONICTO =             ADD_QUADTO + 1;
+var ADD_CUBICTO =             ADD_CONICTO + 1;
 var ADD_CLOSE =               ADD_CUBICTO + 1;
 var ADD_FILL =                ADD_CLOSE + 1;
 
 var PATH_LINE =               ADD_FILL + 1;
 var PATH_QUAD =               PATH_LINE + 1;
-var PATH_CUBIC =              PATH_QUAD + 1;
+var PATH_CONIC =              PATH_QUAD + 1;
+var PATH_CUBIC =              PATH_CONIC + 1;
 
 var INTERSECT_LINE =          PATH_CUBIC + 1;
 var INTERSECT_LINE_2 =        INTERSECT_LINE + 1;
@@ -603,7 +916,13 @@
 var INTERSECT_QUAD =          INTERSECT_QUAD_LINE_NO + 1;
 var INTERSECT_QUAD_2 =        INTERSECT_QUAD + 1;
 var INTERSECT_QUAD_NO =       INTERSECT_QUAD_2 + 1;
-var INTERSECT_SELF_CUBIC =    INTERSECT_QUAD_NO + 1;
+var INTERSECT_CONIC_LINE =    INTERSECT_QUAD_NO + 1;
+var INTERSECT_CONIC_LINE_2 =  INTERSECT_CONIC_LINE + 1;
+var INTERSECT_CONIC_LINE_NO = INTERSECT_CONIC_LINE_2 + 1;
+var INTERSECT_CONIC =         INTERSECT_CONIC_LINE_NO + 1;
+var INTERSECT_CONIC_2 =       INTERSECT_CONIC + 1;
+var INTERSECT_CONIC_NO =      INTERSECT_CONIC_2 + 1;
+var INTERSECT_SELF_CUBIC =    INTERSECT_CONIC_NO + 1;
 var INTERSECT_SELF_CUBIC_NO = INTERSECT_SELF_CUBIC + 1;
 var INTERSECT_CUBIC_LINE =    INTERSECT_SELF_CUBIC_NO + 1;
 var INTERSECT_CUBIC_LINE_2 =  INTERSECT_CUBIC_LINE + 1;
@@ -631,47 +950,60 @@
 
 var MARK_LINE =               OP_XOR + 1;
 var MARK_QUAD =               MARK_LINE + 1;
-var MARK_CUBIC =              MARK_QUAD + 1;
+var MARK_CONIC =              MARK_QUAD + 1;
+var MARK_CUBIC =              MARK_CONIC + 1;
 var MARK_DONE_LINE =          MARK_CUBIC + 1;
 var MARK_DONE_QUAD =          MARK_DONE_LINE + 1;
-var MARK_DONE_CUBIC =         MARK_DONE_QUAD + 1;
+var MARK_DONE_CONIC =         MARK_DONE_QUAD + 1;
+var MARK_DONE_CUBIC =         MARK_DONE_CONIC + 1;
 var MARK_UNSORTABLE_LINE =    MARK_DONE_CUBIC + 1;
 var MARK_UNSORTABLE_QUAD =    MARK_UNSORTABLE_LINE + 1;
-var MARK_UNSORTABLE_CUBIC =   MARK_UNSORTABLE_QUAD + 1;
+var MARK_UNSORTABLE_CONIC =   MARK_UNSORTABLE_QUAD + 1;
+var MARK_UNSORTABLE_CUBIC =   MARK_UNSORTABLE_CONIC + 1;
 var MARK_SIMPLE_LINE =        MARK_UNSORTABLE_CUBIC + 1;
 var MARK_SIMPLE_QUAD =        MARK_SIMPLE_LINE + 1;
-var MARK_SIMPLE_CUBIC =       MARK_SIMPLE_QUAD + 1;
+var MARK_SIMPLE_CONIC =       MARK_SIMPLE_QUAD + 1;
+var MARK_SIMPLE_CUBIC =       MARK_SIMPLE_CONIC + 1;
 var MARK_SIMPLE_DONE_LINE =   MARK_SIMPLE_CUBIC + 1;
 var MARK_SIMPLE_DONE_QUAD =   MARK_SIMPLE_DONE_LINE + 1;
-var MARK_SIMPLE_DONE_CUBIC =  MARK_SIMPLE_DONE_QUAD + 1;
+var MARK_SIMPLE_DONE_CONIC =  MARK_SIMPLE_DONE_QUAD + 1;
+var MARK_SIMPLE_DONE_CUBIC =  MARK_SIMPLE_DONE_CONIC + 1;
 var MARK_DONE_UNARY_LINE =    MARK_SIMPLE_DONE_CUBIC + 1;
 var MARK_DONE_UNARY_QUAD =    MARK_DONE_UNARY_LINE + 1;
-var MARK_DONE_UNARY_CUBIC =   MARK_DONE_UNARY_QUAD + 1;
+var MARK_DONE_UNARY_CONIC =   MARK_DONE_UNARY_QUAD + 1;
+var MARK_DONE_UNARY_CUBIC =   MARK_DONE_UNARY_CONIC + 1;
 var MARK_ANGLE_LAST =         MARK_DONE_UNARY_CUBIC + 1;
 
 var COMPUTED_SET_1 =          MARK_ANGLE_LAST + 1;
 var COMPUTED_SET_2 =          COMPUTED_SET_1 + 1;
 
-var ANGLE_AFTER =             COMPUTED_SET_2;
-var ANGLE_AFTER2 =            ANGLE_AFTER + 1;
+var ANGLE_AFTER =             COMPUTED_SET_2 + 1;
+var ANGLE_AFTERPART =         ANGLE_AFTER + 1;
 
-var ACTIVE_OP =               ANGLE_AFTER2 + 1;
+var ACTIVE_OP =               ANGLE_AFTERPART + 1;
 
-var FRAG_TYPE_LAST =          ACTIVE_OP;
+var COIN_MAIN_SPAN =          ACTIVE_OP + 1;
+var COIN_OPP_SPAN =           COIN_MAIN_SPAN + 1;
+
+var FRAG_TYPE_LAST =          COIN_OPP_SPAN;
 
 var REC_TYPE_UNKNOWN = -1;
 var REC_TYPE_PATH = 0;
-var REC_TYPE_SECT = 1;
-var REC_TYPE_ACTIVE = 2;
-var REC_TYPE_ADD = 3;
-var REC_TYPE_SORT = 4;
-var REC_TYPE_OP = 5;
-var REC_TYPE_MARK = 6;
-var REC_TYPE_COMPUTED = 7;
-var REC_TYPE_COIN = 8;
-var REC_TYPE_ANGLE = 9;
-var REC_TYPE_ACTIVE_OP = 10;
-var REC_TYPE_LAST = REC_TYPE_ACTIVE_OP;
+var REC_TYPE_PATH2 = 1;
+var REC_TYPE_SECT = 2;
+var REC_TYPE_ACTIVE = 3;
+var REC_TYPE_ADD = 4;
+var REC_TYPE_SORT = 5;
+var REC_TYPE_OP = 6;
+var REC_TYPE_MARK = 7;
+var REC_TYPE_COMPUTED = 8;
+var REC_TYPE_COIN = 9;
+var REC_TYPE_ANGLE = 10;
+var REC_TYPE_ACTIVE_OP = 11;
+var REC_TYPE_AFTERPART = 12;
+var REC_TYPE_TOP = 13;
+var REC_TYPE_COINCIDENCE = 14;
+var REC_TYPE_LAST = REC_TYPE_COINCIDENCE;
 
 function strs_to_nums(strs) {
     var result = [];
@@ -702,14 +1034,17 @@
     var escape = pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
     escape = escape.replace(/UNSORTABLE/g, "\\*\\*\\* UNSORTABLE \\*\\*\\*");
     escape = escape.replace(/CUBIC_VAL/g, "\\(P_VAL P_VAL P_VAL P_VAL\\)");
+    escape = escape.replace(/CONIC_VAL/g, "\\(P_VAL P_VAL P_VAL W_VAL\\)");
     escape = escape.replace(/QUAD_VAL/g, "\\(P_VAL P_VAL P_VAL\\)");
     escape = escape.replace(/LINE_VAL/g, "\\(P_VAL P_VAL\\)");
     escape = escape.replace(/FILL_TYPE/g, "SkPath::k[a-zA-Z]+_FillType");
+    escape = escape.replace(/PTR_VAL/g, "0x[0-9A-F]+");
     escape = escape.replace(/PT_VAL/g, "\\(P_VAL\\)");
     escape = escape.replace(/P_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?, ?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
     escape = escape.replace(/T_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)");
+    escape = escape.replace(/W_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
     escape = escape.replace(/PATH/g, "pathB?");
-    escape = escape.replace(/IDX/g, "(\\d+)");
+    escape = escape.replace(/IDX/g, "(-?\\d+)");
     escape = escape.replace(/NUM/g, "(-?\\d+)");
     escape = escape.replace(/OPT/g, "(\\?|-?\\d+)");
     return new RegExp(escape, 'i');
@@ -718,17 +1053,20 @@
 function construct_regexp2c(pattern) {
     var escape = pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
     escape = escape.replace(/UNSORTABLE/g, "\\*\\*\\* UNSORTABLE \\*\\*\\*");
-    escape = escape.replace(/CUBIC_VAL/g, "(?:\\$\\d = )?\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}");
-    escape = escape.replace(/QUAD_VAL/g, "(?:\\$\\d = )?\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}");
-    escape = escape.replace(/LINE_VAL/g, "(?:\\$\\d = )?\\{\\{P_VAL\\}, \\{P_VAL\\}\\}");
+    escape = escape.replace(/CUBIC_VAL/g, "(?:\\$\\d = )?\\{\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}\\}");
+    escape = escape.replace(/CONIC_VAL/g, "(?:\\$\\d = )?\\{\\{\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}\\}, W_VAL\\}");
+    escape = escape.replace(/QUAD_VAL/g, "(?:\\$\\d = )?\\{\\{\\{P_VAL\\}, \\{P_VAL\\}, \\{P_VAL\\}\\}\\}");
+    escape = escape.replace(/LINE_VAL/g, "(?:\\$\\d = )?\\{\\{\\{P_VAL\\}, \\{P_VAL\\}\\}\\}");
     escape = escape.replace(/FILL_TYPE/g, "SkPath::k[a-zA-Z]+_FillType");
+    escape = escape.replace(/PTR_VAL/g, "0x[0-9A-F]+");
     escape = escape.replace(/PT_VAL/g, "\\{\\{P_VAL\\}\\}");
-    escape = escape.replace(/P_VAL/g, "(?:f?[xX] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?,(?: f?[yY] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
+    escape = escape.replace(/P_VAL/g, "(?:f?[xX] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?, *(?: f?[yY] = )?(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
     escape = escape.replace(/T_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)");
+    escape = escape.replace(/W_VAL/g, "(-?\\d+\\.?\\d*(?:e-?\\d+)?)[Ff]?");
     escape = escape.replace(/OPER/g, "[a-z]+");
     escape = escape.replace(/PATH/g, "pathB?");
     escape = escape.replace(/T_F/g, "([TF])");
-    escape = escape.replace(/IDX/g, "(\\d+)");
+    escape = escape.replace(/IDX/g, "(-?\\d+)");
     escape = escape.replace(/NUM/g, "(-?\\d+)");
     escape = escape.replace(/OPT/g, "(\\?|-?\\d+)");
     return new RegExp(escape, 'i');
@@ -767,19 +1105,27 @@
         if (line.lastIndexOf(angleStart, 0) === 0) {
             line = line.substr(angleStart.length);
         }
-        var type = line.lastIndexOf("debugShowActiveSpans", 0) === 0 ? REC_TYPE_ACTIVE 
+        var coinStart = "SkOpCoincidence::";
+        if (line.lastIndexOf(coinStart, 0) === 0) {
+            line = line.substr(coinStart.length);
+        }
+        var type = line.lastIndexOf("debugShowActiveSpans", 0) === 0 ? REC_TYPE_ACTIVE
+                : line.lastIndexOf("debugShowCoincidence", 0) === 0 ? REC_TYPE_COINCIDENCE
+                : line.lastIndexOf("((SkOpSegment*)", 0) === 0 ? REC_TYPE_PATH2
                 : line.lastIndexOf("debugShowTs", 0) === 0 ? REC_TYPE_COIN 
+                : line.lastIndexOf("afterPart", 0) === 0 ? REC_TYPE_AFTERPART
                 : line.lastIndexOf("debugShow", 0) === 0 ? REC_TYPE_SECT
                 : line.lastIndexOf("activeOp", 0) === 0 ? REC_TYPE_ACTIVE_OP
                 : line.lastIndexOf("computed", 0) === 0 ? REC_TYPE_COMPUTED
                 : line.lastIndexOf("debugOne", 0) === 0 ? REC_TYPE_SORT
                 : line.lastIndexOf("dumpOne", 0) === 0 ? REC_TYPE_SORT
+                : line.lastIndexOf("findTop", 0) === 0 ? REC_TYPE_TOP
                 : line.lastIndexOf("pathB.", 0) === 0 ? REC_TYPE_ADD
                 : line.lastIndexOf("path.", 0) === 0 ? REC_TYPE_ADD
                 : line.lastIndexOf("after", 0) === 0 ? REC_TYPE_ANGLE
                 : line.lastIndexOf("mark", 0) === 0 ? REC_TYPE_MARK
                 : line.lastIndexOf("  {{", 0) === 0 ? REC_TYPE_COMPUTED
-                : line.lastIndexOf("{{", 0) === 0 ? REC_TYPE_PATH
+                : line.lastIndexOf("seg=", 0) === 0 ? REC_TYPE_PATH
                 : line.lastIndexOf("op", 0) === 0 ? REC_TYPE_OP
                 : line.lastIndexOf("$", 0) === 0 ? REC_TYPE_PATH
                 : REC_TYPE_UNKNOWN;
@@ -798,11 +1144,21 @@
         switch (recType) {
             case REC_TYPE_ACTIVE:
                 found = match_regexp(line, lineNo, record, ACTIVE_LINE_SPAN, "debugShowActiveSpans" +
-" id=IDX LINE_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=NUM"
+" id=IDX LINE_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, ACTIVE_QUAD_SPAN, "debugShowActiveSpans" +
-" id=IDX QUAD_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=NUM"
+" id=IDX QUAD_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, ACTIVE_CONIC_SPAN, "debugShowActiveSpans" +
+" id=IDX CONIC_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, ACTIVE_CUBIC_SPAN, "debugShowActiveSpans" +
-" id=IDX CUBIC_VAL t=T_VAL PT_VAL tEnd=T_VAL other=IDX otherT=T_VAL otherIndex=IDX windSum=OPT windValue=IDX oppValue=NUM"
+" id=IDX CUBIC_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, ACTIVE_LINE_SPAN, "debugShowActiveSpans" +
+" id=IDX LINE_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
+                ) || match_regexp(line, lineNo, record, ACTIVE_QUAD_SPAN, "debugShowActiveSpans" +
+" id=IDX QUAD_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
+                ) || match_regexp(line, lineNo, record, ACTIVE_CONIC_SPAN, "debugShowActiveSpans" +
+" id=IDX CONIC_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
+                ) || match_regexp(line, lineNo, record, ACTIVE_CUBIC_SPAN, "debugShowActiveSpans" +
+" id=IDX CUBIC_VAL t=T_VAL PT_VAL tEnd=T_VAL windSum=OPT oppSum=OPT windValue=IDX oppValue=NUM"
                 );
                 break;
             case REC_TYPE_ACTIVE_OP:
@@ -827,6 +1183,12 @@
                     moveX = record[1][4];
                     moveY = record[1][5];
                     found = true;
+                } else if (match_regexp(line, lineNo, record, ADD_CONICTO, "PATH.conicTo(P_VAL, P_VAL, T_VAL);")) {
+                    record[1].unshift(moveY);
+                    record[1].unshift(moveX);
+                    moveX = record[1][4];
+                    moveY = record[1][5];
+                    found = true;
                 } else if (match_regexp(line, lineNo, record, ADD_CUBICTO, "PATH.cubicTo(P_VAL, P_VAL, P_VAL);")) {
                     record[1].unshift(moveY);
                     record[1].unshift(moveX);
@@ -839,30 +1201,47 @@
                     found = match_regexp(line, lineNo, record, ADD_CLOSE, "PATH.close();");
                 }
                 break;
+            case REC_TYPE_AFTERPART:
+                found = match_regexp(line, lineNo, record, PATH_LINE, "afterPart LINE_VAL")
+                    || match_regexp(line, lineNo, record, PATH_QUAD, "afterPart QUAD_VAL")
+                    || match_regexp(line, lineNo, record, PATH_CONIC, "afterPart CONIC_VAL")
+                    || match_regexp(line, lineNo, record, PATH_CUBIC, "afterPart CUBIC_VAL")
+                break;
             case REC_TYPE_ANGLE:
                 found = match_regexp(line, lineNo, record, ANGLE_AFTER, "after " +
-"id=IDX IDX/IDX tStart=T_VAL tEnd=T_VAL < id=IDX IDX/IDX tStart=T_VAL tEnd=T_VAL < id=IDX IDX/IDX tStart=T_VAL tEnd=T_VAL  T_F IDX");
-                if (found) {
-                    break;
-                }
-                found = match_regexp(line, lineNo, record, ANGLE_AFTER2, "after " +
 "[IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL < [IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL < [IDX/IDX] NUM/NUM tStart=T_VAL tEnd=T_VAL  T_F IDX");
                 break;
             case REC_TYPE_COIN:
                 found = true;
                 break;
+            case REC_TYPE_COINCIDENCE:
+                found = match_regexp(line, lineNo, record, COIN_MAIN_SPAN, "debugShowCoincidence" +
+" + id=IDX t=T_VAL tEnd=T_VAL"
+                ) || match_regexp(line, lineNo, record, COIN_OPP_SPAN, "debugShowCoincidence" +
+" - id=IDX t=T_VAL tEnd=T_VAL"
+                );
+                break;
             case REC_TYPE_COMPUTED:
                 found = line ==  "computed quadratics given"
                   || match_regexp(line, lineNo, record, COMPUTED_SET_1, "computed quadratics set 1"
                 ) || match_regexp(line, lineNo, record, COMPUTED_SET_2, "computed quadratics set 2"
                 ) || match_regexp(line, lineNo, record, PATH_QUAD, "  QUAD_VAL,"
+                ) || match_regexp(line, lineNo, record, PATH_CONIC, "  CONIC_VAL,"
                 ) || match_regexp(line, lineNo, record, PATH_CUBIC, "  CUBIC_VAL,"
                 );
                 break;
             case REC_TYPE_PATH:
-                found = match_regexp(line, lineNo, record, PATH_LINE, "LINE_VAL"
-                ) || match_regexp(line, lineNo, record, PATH_QUAD, "QUAD_VAL"
-                ) || match_regexp(line, lineNo, record, PATH_CUBIC, "CUBIC_VAL"
+                found = match_regexp(line, lineNo, record, PATH_LINE, "seg=IDX LINE_VAL"
+                ) || match_regexp(line, lineNo, record, PATH_QUAD, "seg=IDX QUAD_VAL"
+                ) || match_regexp(line, lineNo, record, PATH_CONIC, "seg=IDX CONIC_VAL"
+                ) || match_regexp(line, lineNo, record, PATH_CUBIC, "seg=IDX CUBIC_VAL"
+                );
+                break;
+            case REC_TYPE_PATH2:
+                found = match_regexp(line, lineNo, record, PATH_LINE, "((SkOpSegment*) PTR_VAL) [IDX] {LINE_VAL}"
+                ) || match_regexp(line, lineNo, record, PATH_QUAD, "((SkOpSegment*) PTR_VAL) [IDX] {QUAD_VAL}"
+                ) || match_regexp(line, lineNo, record, PATH_CONIC, "((SkOpSegment*) PTR_VAL) [IDX] {CONIC_VAL}"
+                ) || match_regexp(line, lineNo, record, PATH_CUBIC, "((SkOpSegment*) PTR_VAL) [IDX] {CUBIC_VAL}"
                 );
                 break;
             case REC_TYPE_SECT:
@@ -884,6 +1263,18 @@
 " wtTs[0]=T_VAL QUAD_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL QUAD_VAL wnTs[1]=T_VAL"
                 ) || match_regexp(line, lineNo, record, INTERSECT_QUAD_NO, "debugShowQuadIntersection" +
 " no intersect QUAD_VAL QUAD_VAL"
+                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_LINE, "debugShowConicLineIntersection" +
+" wtTs[0]=T_VAL CONIC_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL"
+                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_LINE_2, "debugShowConicLineIntersection" +
+" wtTs[0]=T_VAL CONIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL wnTs[1]=T_VAL"
+                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_LINE_NO, "debugShowConicLineIntersection" +
+" no intersect CONIC_VAL LINE_VAL"
+                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC, "debugShowConicIntersection" +
+" wtTs[0]=T_VAL CONIC_VAL PT_VAL wnTs[0]=T_VAL CONIC_VAL"
+                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_2, "debugShowConicIntersection" +
+" wtTs[0]=T_VAL CONIC_VAL PT_VAL wtTs[1]=T_VAL PT_VAL wnTs[0]=T_VAL CONIC_VAL wnTs[1]=T_VAL"
+                ) || match_regexp(line, lineNo, record, INTERSECT_CONIC_NO, "debugShowConicIntersection" +
+" no intersect CONIC_VAL CONIC_VAL"
                 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE, "debugShowCubicLineIntersection" +
 " wtTs[0]=T_VAL CUBIC_VAL PT_VAL wnTs[0]=T_VAL LINE_VAL"
                 ) || match_regexp(line, lineNo, record, INTERSECT_CUBIC_LINE_2, "debugShowCubicLineIntersection" +
@@ -944,45 +1335,44 @@
                     record[1].push(hasStop);
                 }
                 break;
+            case REC_TYPE_TOP:
+                found = match_regexp(line, lineNo, record, ACTIVE_OP, "findTop" +
+" id=IDX s=T_VAL e=T_VAL cw=NUM swap=NUM inflections=NUM monotonic=NUM"
+                ) || match_regexp(line, lineNo, record, ACTIVE_OP, "findTop" +
+" id=IDX s=T_VAL e=T_VAL (-) cw=NUM swap=NUM inflections=NUM monotonic=NUM"
+                ) || match_regexp(line, lineNo, record, ACTIVE_OP, "findTop" +
+" id=IDX s=T_VAL e=T_VAL (+) cw=NUM swap=NUM inflections=NUM monotonic=NUM"
+                );
+                break;
             case REC_TYPE_MARK:
                 found = match_regexp(line, lineNo, record, MARK_LINE, "markWinding" +
-" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
+" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, MARK_QUAD, "markWinding" +
-" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
+" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_CONIC, "markWinding" +
+" id=IDX CONIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, MARK_CUBIC, "markWinding" +
-" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_DONE_LINE, "markDoneBinary" +
-" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_DONE_QUAD, "markDoneBinary" +
-" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_DONE_CUBIC, "markDoneBinary" +
-" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=NUM oppSum=OPT windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_UNSORTABLE_LINE, "markUnsortable" +
-" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_UNSORTABLE_QUAD, "markUnsortable" +
-" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_UNSORTABLE_CUBIC, "markUnsortable" +
-" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
+" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_DONE_LINE, "markDone" +
+" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=OPT newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX oppValue=OPT"
+                ) || match_regexp(line, lineNo, record, MARK_DONE_QUAD, "markDone" +
+" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=OPT newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX oppValue=OPT"
+                ) || match_regexp(line, lineNo, record, MARK_DONE_CONIC, "markDone" +
+" id=IDX CONIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=OPT newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX oppValue=OPT"
+                ) || match_regexp(line, lineNo, record, MARK_DONE_CUBIC, "markDone" +
+" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=OPT newOppSum=OPT oppSum=OPT windSum=OPT windValue=IDX oppValue=OPT"
                 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_LINE, "markWinding" +
 " id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_QUAD, "markWinding" +
 " id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_SIMPLE_CONIC, "markWinding" +
+" id=IDX CONIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, MARK_SIMPLE_CUBIC, "markWinding" +
 " id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_SIMPLE_DONE_LINE, "markDone" +
-" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_SIMPLE_DONE_QUAD, "markDone" +
-" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_SIMPLE_DONE_CUBIC, "markDone" +
-" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_DONE_UNARY_LINE, "markDoneUnary" +
-" id=IDX LINE_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_DONE_UNARY_QUAD, "markDoneUnary" +
-" id=IDX QUAD_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
-                ) || match_regexp(line, lineNo, record, MARK_DONE_UNARY_CUBIC, "markDoneUnary" +
-" id=IDX CUBIC_VAL t=T_VAL [IDX] PT_VAL tEnd=T_VAL newWindSum=NUM windSum=OPT windValue=IDX"
                 ) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
-" last id=IDX windSum=OPT small=IDX");
+" last segment=IDX span=IDX"
+                ) || match_regexp(line, lineNo, record, MARK_ANGLE_LAST, "markAngle" +
+" last segment=IDX span=IDX windSum=OPT");
                 break;
             case REC_TYPE_OP:
                 if (line.lastIndexOf("oppSign oppSign=", 0) === 0
@@ -990,8 +1380,9 @@
                     found = true;
                     break;
                 }
-                found = match_regexp(line, lineNo, record, OP_DIFFERENCE, "op difference"
+                found = match_regexp(line, lineNo, record, OP_DIFFERENCE, "op diff"
                 ) || match_regexp(line, lineNo, record, OP_INTERSECT, "op intersect"
+                ) || match_regexp(line, lineNo, record, OP_INTERSECT, "op sect"
                 ) || match_regexp(line, lineNo, record, OP_UNION, "op union"
                 ) || match_regexp(line, lineNo, record, OP_XOR, "op xor"
                 );
@@ -1062,15 +1453,17 @@
                     }
                     hasComputedPath = true;
                 case REC_TYPE_PATH:
+                    first = 1;
                     switch (fragType) {
                         case PATH_LINE:
-                            last = 4;
+                            last = 5;
                             break;
+                        case PATH_CONIC:
                         case PATH_QUAD:
-                            last = 6;
+                            last = 7;
                             break;
                         case PATH_CUBIC:
-                            last = 8;
+                            last = 9;
                             break;
                         default:
                             console.log("unknown " + (recType == REC_TYPE_PATH ? "REC_TYPE_PATH" 
@@ -1081,6 +1474,28 @@
                         hasPath = true;
                     }
                     break;
+                case REC_TYPE_PATH2:
+                    first = 1;
+                    switch (fragType) {
+                        case PATH_LINE:
+                            last = 5;
+                            break;
+                        case PATH_CONIC:
+                        case PATH_QUAD:
+                            last = 7;
+                            break;
+                        case PATH_CUBIC:
+                            last = 9;
+                            break;
+                        default:
+                            console.log("unknown " + (recType == REC_TYPE_PATH2 ? "REC_TYPE_PATH2" 
+                                    : "REC_TYPE_COMPUTED") + " frag type:" + fragType);
+                            throw "stop execution";
+                    }
+                    if (recType == REC_TYPE_PATH2) {
+                        hasPath = true;
+                    }
+                    break;
                 case REC_TYPE_ACTIVE:
                     if (firstActiveSpan < 0) {
                         firstActiveSpan = tIndex;
@@ -1090,6 +1505,7 @@
                         case ACTIVE_LINE_SPAN:
                             last = 5;
                             break;
+                        case ACTIVE_CONIC_SPAN:
                         case ACTIVE_QUAD_SPAN:
                             last = 7;
                             break;
@@ -1108,6 +1524,7 @@
                         case ADD_LINETO:
                             last = 4;
                             break;
+                        case ADD_CONICTO:
                         case ADD_QUADTO:
                             last = 6;
                             break;
@@ -1122,6 +1539,23 @@
                             throw "stop execution";
                     }
                     break;
+                case REC_TYPE_AFTERPART:
+                    switch (fragType) {
+                        case PATH_LINE:
+                            last = 4;
+                            break;
+                        case PATH_CONIC:
+                        case PATH_QUAD:
+                            last = 6;
+                            break;
+                        case PATH_CUBIC:
+                            last = 8;
+                            break;
+                        default:
+                            console.log("unknown REC_TYPE_ACTIVEPART frag type: " + fragType);
+                            throw "stop execution";
+                    }
+                    break;
                 case REC_TYPE_SECT:
                     switch (fragType) {
                         case INTERSECT_LINE:
@@ -1133,21 +1567,39 @@
                         case INTERSECT_LINE_NO:
                             first = 0; last = 4; first2 = 4; last2 = 8;
                             break;
+                        case INTERSECT_CONIC_LINE:
+                            first = 1; last = 7; first2 = 11; last2 = 15;
+                            break;
                         case INTERSECT_QUAD_LINE:
                             first = 1; last = 7; first2 = 10; last2 = 14;
                             break;
+                        case INTERSECT_CONIC_LINE_2:
+                            first = 1; last = 7; first2 = 14; last2 = 18;
+                            break;
                         case INTERSECT_QUAD_LINE_2:
                             first = 1; last = 7; first2 = 13; last2 = 17;
                             break;
+                        case INTERSECT_CONIC_LINE_NO:
+                            first = 0; last = 6; first2 = 7; last2 = 11;
+                            break;
                         case INTERSECT_QUAD_LINE_NO:
                             first = 0; last = 6; first2 = 6; last2 = 10;
                             break;
+                        case INTERSECT_CONIC:
+                            first = 1; last = 7; first2 = 11; last2 = 17;
+                            break;
                         case INTERSECT_QUAD:
                             first = 1; last = 7; first2 = 10; last2 = 16;
                             break;
+                        case INTERSECT_CONIC_2:
+                            first = 1; last = 7; first2 = 14; last2 = 20;
+                            break;
                         case INTERSECT_QUAD_2:
                             first = 1; last = 7; first2 = 13; last2 = 19;
                             break;
+                        case INTERSECT_CONIC_NO:
+                            first = 0; last = 6; first2 = 7; last2 = 13;
+                            break;
                         case INTERSECT_QUAD_NO:
                             first = 0; last = 6; first2 = 6; last2 = 12;
                             break;
@@ -1242,13 +1694,7 @@
                     if (!draw_angle) {
                         break;
                     }
-                    if (fragType == ANGLE_AFTER) {
-                        var curve = curvePartialByID(test, frags[0], frags[3], frags[4]);
-                        curve_extremes(curve, angleBounds);
-                        curve = curvePartialByID(test, frags[5], frags[8], frags[9]);
-                        curve_extremes(curve, angleBounds);
-                        curve = curvePartialByID(test, frags[10], frags[13], frags[14]);
-                    } else if (fragType == ANGLE_AFTER2) {
+                    {
                         var curve = curvePartialByID(test, frags[0], frags[4], frags[5]);
                         curve_extremes(curve, angleBounds);
                         curve = curvePartialByID(test, frags[6], frags[10], frags[11]);
@@ -1256,6 +1702,15 @@
                         curve = curvePartialByID(test, frags[12], frags[16], frags[17]);
                     }
                     break;
+                case REC_TYPE_COINCIDENCE:
+                    if (!draw_coincidence) {
+                        break;
+                    }
+                    {
+                        var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
+                        curve_extremes(curve, angleBounds);
+                    }
+                    break;
                 case REC_TYPE_SORT:
                     if (!draw_sort) {
                         break;
@@ -1265,6 +1720,15 @@
                         curve_extremes(curve, angleBounds);
                     }
                     break;
+                case REC_TYPE_TOP:
+                    if (!draw_top) {
+                        break;
+                    }
+                    {
+                        var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
+                        curve_extremes(curve, angleBounds);
+                    }
+                    break;
             }
         }
     }
@@ -1274,7 +1738,7 @@
     ymax = Math.max(ymax, angleBounds[3]);
     setScale(xmin, xmax, ymin, ymax);
     if (hasPath == false && hasComputedPath == true && !draw_computed) {
-        draw_computed = 3; // show both quadratics and cubics
+        draw_computed = 7; // show quadratics, conics, and cubics
     }
     if (hasPath == true && hasComputedPath == false && draw_computed) {
         draw_computed = 0;
@@ -1282,13 +1746,13 @@
 }
 
 function curveByID(test, id) {
-    var tIndex = firstActiveSpan;
-    if (tIndex < 0) {
-        return [];
-    }
-    while (tIndex < test.length) {
+    var tIndex = -3;
+    while ((tIndex += 3) < test.length) {
         var recType = test[tIndex];
-        if (recType != REC_TYPE_ACTIVE) {
+        if (recType == REC_TYPE_OP) {
+            continue;
+        }
+        if (recType != REC_TYPE_PATH) {
             return [];
         }
         var records = test[tIndex + 2];
@@ -1297,30 +1761,32 @@
             var frags = records[recordIndex + 1];
             if (frags[0] == id) {
                 switch (fragType) {
-                    case ACTIVE_LINE_SPAN:
+                    case PATH_LINE:
                         return [frags[1], frags[2], frags[3], frags[4]];
-                    case ACTIVE_QUAD_SPAN:
+                    case PATH_QUAD:
                         return [frags[1], frags[2], frags[3], frags[4],
                                 frags[5], frags[6]];
-                    case ACTIVE_CUBIC_SPAN:
+                    case PATH_CONIC:
+                        return [frags[1], frags[2], frags[3], frags[4],
+                                frags[5], frags[6], frags[7]];
+                    case PATH_CUBIC:
                         return [frags[1], frags[2], frags[3], frags[4],
                                 frags[5], frags[6], frags[7], frags[8]];
                 }
             }
         }
-        tIndex += 3;
     }
     return [];
 }
 
 function curvePartialByID(test, id, t0, t1) {
-    var tIndex = firstActiveSpan;
-    if (tIndex < 0) {
-        return [];
-    }
-    while (tIndex < test.length) {
+    var tIndex = -3;
+    while ((tIndex += 3) < test.length) {
         var recType = test[tIndex];
-        if (recType != REC_TYPE_ACTIVE) {
+        if (recType == REC_TYPE_OP) {
+            continue;
+        }
+        if (recType != REC_TYPE_PATH) {
             return [];
         }
         var records = test[tIndex + 2];
@@ -1329,60 +1795,62 @@
             var frags = records[recordIndex + 1];
             if (frags[0] == id) {
                 switch (fragType) {
-                    case ACTIVE_LINE_SPAN:
+                    case PATH_LINE:
                         return linePartial(frags[1], frags[2], frags[3], frags[4], t0, t1);
-                    case ACTIVE_QUAD_SPAN:
+                    case PATH_QUAD:
                         return quadPartial(frags[1], frags[2], frags[3], frags[4],
                                 frags[5], frags[6], t0, t1);
-                    case ACTIVE_CUBIC_SPAN:
+                    case PATH_CONIC:
+                        return conicPartial(frags[1], frags[2], frags[3], frags[4],
+                                frags[5], frags[6], frags[7], t0, t1);
+                    case PATH_CUBIC:
                         return cubicPartial(frags[1], frags[2], frags[3], frags[4],
                                 frags[5], frags[6], frags[7], frags[8], t0, t1);
                 }
             }
         }
-        tIndex += 3;
     }
     return [];
 }
 
 function idByCurve(test, frag, type) {
-    var tIndex = firstActiveSpan;
-    if (tIndex < 0) {
-        return -1;
-    }
+    var tIndex = 0;
     while (tIndex < test.length) {
         var recType = test[tIndex];
-        if (recType != REC_TYPE_ACTIVE) {
-            return -1;
+        if (recType != REC_TYPE_PATH) {
+            ++tIndex;
+            continue;
         }
         var records = test[tIndex + 2];
         for (var recordIndex = 0; recordIndex < records.length; recordIndex += 2) {
             var fragType = records[recordIndex];
             var frags = records[recordIndex + 1];
+            if (frag.length != frags.length - 1) {
+                continue;
+            }
             switch (fragType) {
-                case ACTIVE_LINE_SPAN:
-                    if (type != PATH_LINE) {
-                        continue;
-                    }
+                case PATH_LINE:
                     if (frag[0] != frags[1] || frag[1] != frags[2]
                             || frag[2] != frags[3] || frag[3] != frags[4]) {
                         continue;
                     }
                     return frags[0];
-                case ACTIVE_QUAD_SPAN:
-                    if (type != PATH_QUAD) {
-                        continue;
-                    }
+                case PATH_QUAD:
                     if (frag[0] != frags[1] || frag[1] != frags[2]
                             || frag[2] != frags[3] || frag[3] != frags[4]
                             || frag[4] != frags[5] || frag[5] != frags[6]) {
                         continue;
                     }
                     return frags[0];
-                case ACTIVE_CUBIC_SPAN:
-                    if (type != PATH_CUBIC) {
+                case PATH_CONIC:
+                    if (frag[0] != frags[1] || frag[1] != frags[2]
+                            || frag[2] != frags[3] || frag[3] != frags[4]
+                            || frag[4] != frags[5] || frag[5] != frags[6]
+                            || frag[6] != frags[7]) {
                         continue;
                     }
+                    return frags[0];
+                case PATH_CUBIC:
                     if (frag[0] != frags[1] || frag[1] != frags[2]
                             || frag[2] != frags[3] || frag[3] != frags[4]
                             || frag[4] != frags[5] || frag[5] != frags[6]
@@ -1398,6 +1866,7 @@
 }
 
 function curve_extremes(curve, bounds) {
+    var length = curve.length == 7 ? 6 : curve.length;
     for (var index = 0; index < curve.length; index += 2) {
         var x = curve[index];
         var y = curve[index + 1];
@@ -1494,7 +1963,8 @@
 }
 
 function drawPoint(px, py, end) {
-    for (var pts = 0; pts < drawnPts.length; pts += 2) {
+    var length = drawnPts.length == 7 ? 6 : drawnPts.length;
+    for (var pts = 0; pts < length; pts += 2) {
         var x = drawnPts[pts];
         var y = drawnPts[pts + 1];
         if (px == x && py == y) {
@@ -1520,8 +1990,22 @@
     }
 }
 
+function coordCount(curveType) {
+    switch (curveType) {
+        case PATH_LINE:
+            return 4;
+        case PATH_QUAD:
+            return 6;
+        case PATH_CONIC:
+            return 6;
+        case PATH_CUBIC:
+            return 8;
+    }
+    return -1;
+}
+
 function drawPoints(ptArray, curveType, drawControls) {
-    var count = (curveType - PATH_LINE + 2) * 2;
+    var count = coordCount(curveType);
     for (var idx = 0; idx < count; idx += 2) {
         if (!drawControls && idx != 0 && idx != count - 2) {
             continue;
@@ -1568,6 +2052,17 @@
             xy.x = a * curve[0] + b * curve[2] + c * curve[4];
             xy.y = a * curve[1] + b * curve[3] + c * curve[5];
             break;
+        case PATH_CONIC:
+            var one_t = 1 - t;
+            var a = one_t * one_t;
+            var b = 2 * one_t * t;
+            var c = t * t;
+            xy.x = a * curve[0] + b * curve[2] * curve[6] + c * curve[4];
+            xy.y = a * curve[1] + b * curve[3] * curve[6] + c * curve[5];
+            var d = a + b * curve[6] + c;
+            xy.x /= d;
+            xy.y /= d;
+            break;
         case PATH_CUBIC:
             var one_t = 1 - t;
             var one_t2 = one_t * one_t;
@@ -1760,6 +2255,164 @@
     ctx.stroke();
 }
 
+function alreadyDrawnConic(x1, y1, x2, y2, x3, y3, w) {
+    if (collect_bounds) {
+        if (focus_enabled) {
+            focusXmin = Math.min(focusXmin, x1, x2, x3);
+            focusYmin = Math.min(focusYmin, y1, y2, y3);
+            focusXmax = Math.max(focusXmax, x1, x2, x3);
+            focusYmax = Math.max(focusYmax, y1, y2, y3);
+        }
+        return true;
+    }
+    for (var pts = 0; pts < drawnConics.length; pts += 8) {
+        if (x1 == drawnConics[pts] && y1 == drawnCubics[pts + 1]
+                && x2 == drawnCubics[pts + 2] && y2 == drawnCubics[pts + 3] 
+                && x3 == drawnCubics[pts + 4] && y3 == drawnCubics[pts + 5] 
+                && w == drawnCubics[pts + 6]) {
+            return true;
+        }
+    }
+    drawnConics.push(x1);
+    drawnConics.push(y1);
+    drawnConics.push(x2);
+    drawnConics.push(y2);
+    drawnConics.push(x3);
+    drawnConics.push(y3);
+    drawnCubics.push(w);
+    return false;
+}
+
+var kMaxConicToQuadPOW2 = 5;
+
+function computeQuadPOW2(curve, tol) {
+    var a = curve[6] - 1;
+    var k = a / (4 * (2 + a));
+    var x = k * (curve[0] - 2 * curve[2] + curve[4]);
+    var y = k * (curve[1] - 2 * curve[3] + curve[5]);
+
+    var error = Math.sqrt(x * x + y * y);
+    var pow2;
+    for (pow2 = 0; pow2 < kMaxConicToQuadPOW2; ++pow2) {
+        if (error <= tol) {
+            break;
+        }
+        error *= 0.25;
+    }
+    return pow2;
+}
+
+function subdivide_w_value(w) {
+    return Math.sqrt(0.5 + w * 0.5);
+}
+
+function chop(curve, part1, part2) {
+    var w = curve[6];
+    var scale = 1 / (1 + w);
+    part1[0] = curve[0];
+    part1[1] = curve[1];
+    part1[2] = (curve[0] + curve[2] * w) * scale;
+    part1[3] = (curve[1] + curve[3] * w) * scale;
+    part1[4] = part2[0] = (curve[0] + (curve[2] * w) * 2 + curve[4]) * scale * 0.5;
+    part1[5] = part2[1] = (curve[1] + (curve[3] * w) * 2 + curve[5]) * scale * 0.5;
+    part2[2] = (curve[2] * w + curve[4]) * scale;
+    part2[3] = (curve[3] * w + curve[5]) * scale;
+    part2[4] = curve[4];
+    part2[5] = curve[5];
+    part1[6] = part2[6] = subdivide_w_value(w);
+}
+
+function subdivide(curve, level, pts) {
+    if (0 == level) {
+        pts.push(curve[2]);
+        pts.push(curve[3]);
+        pts.push(curve[4]);
+        pts.push(curve[5]);
+    } else {
+        var part1 = [], part2 = [];
+        chop(curve, part1, part2);
+        --level;
+        subdivide(part1, level, pts);
+        subdivide(part2, level, pts);
+    }
+}
+
+function chopIntoQuadsPOW2(curve, pow2, pts) {
+    subdivide(curve, pow2, pts);
+    return 1 << pow2;
+}
+
+function drawConicWithQuads(x1, y1, x2, y2, x3, y3, w) {
+    if (alreadyDrawnConic(x1, y1, x2, y2, x3, y3, w)) {
+        return;
+    }
+    ctx.beginPath();
+    ctx.moveTo((x1 - srcLeft) * scale,
+            (y1 - srcTop) * scale);
+    var tol = 1 / scale;
+    var curve = [x1, y1, x2, y2, x3, y3, w];
+    var pow2 = computeQuadPOW2(curve, tol);
+    var pts = [];
+    chopIntoQuadsPOW2(curve, pow2, pts);
+    for (var i = 0; i < pts.length; i += 4) {
+        ctx.quadraticCurveTo(
+            (pts[i + 0] - srcLeft) * scale, (pts[i + 1] - srcTop) * scale,
+            (pts[i + 2] - srcLeft) * scale, (pts[i + 3] - srcTop) * scale);
+    }
+    ctx.stroke();
+}
+
+function conic_eval_numerator(x1, x2, x3, w, t) {
+    var src2w = x2 * w;
+    var C = x1;
+    var A = x3 - 2 * src2w + C;
+    var B = 2 * (src2w - C);
+    return (A * t + B) * t + C;
+}
+
+
+function conic_eval_denominator(w, t) {
+    var B = 2 * (w - 1);
+    var C = 1;
+    var A = -B;
+    return (A * t + B) * t + C;
+}
+
+function conicPartial(x1, y1, x2, y2, x3, y3, w, t1, t2) {
+    var ax = conic_eval_numerator(x1, x2, x3, w, t1);
+    var ay = conic_eval_numerator(y1, y2, y3, w, t1);
+    var az = conic_eval_denominator(w, t1);
+    var midT = (t1 + t2) / 2;
+    var dx = conic_eval_numerator(x1, x2, x3, w, midT);
+    var dy = conic_eval_numerator(y1, y2, y3, w, midT);
+    var dz = conic_eval_denominator(w, midT);
+    var cx = conic_eval_numerator(x1, x2, x3, w, t2);
+    var cy = conic_eval_numerator(y1, y2, y3, w, t2);
+    var cz = conic_eval_denominator(w, t2);
+    var bx = 2 * dx - (ax + cx) / 2;
+    var by = 2 * dy - (ay + cy) / 2;
+    var bz = 2 * dz - (az + cz) / 2;
+    var dt = t2 - t1;
+    var dt_1 = 1 - dt;
+    var partW = (1 + dt * (w - 1)) / Math.sqrt(dt * dt + 2 * dt * dt_1 * w + dt_1 * dt_1);
+    var array = [
+        ax / az, ay / az, bx / bz, by / bz, cx / cz, cy / cz, partW
+    ];
+    return array;
+}
+    
+function drawConicPartial(x1, y1, x2, y2, x3, y3, w, t1, t2) {
+    var a = conicPartial(x1, y1, x2, y2, x3, y3, w, t1, t2);
+    var ax = a[0];
+    var ay = a[1];
+    var bx = a[2];
+    var by = a[3];
+    var cx = a[4];
+    var cy = a[5];
+    var w_ = a[6];
+    drawConicWithQuads(ax, ay, bx, by, cx, cy, w_);
+}
+
 function alreadyDrawnCubic(x1, y1, x2, y2, x3, y3, x4, y4) {
     if (collect_bounds) {
         if (focus_enabled) {
@@ -1872,6 +2525,9 @@
         case 6:
             drawQuad(c[0], c[1], c[2], c[3], c[4], c[5]);
             break;
+        case 7:
+            drawConicWithQuads(c[0], c[1], c[2], c[3], c[4], c[5], c[6]);
+            break;
         case 8:
             drawCubic(c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
             break;
@@ -1881,7 +2537,8 @@
 function boundsWidth(pts) {
     var min = pts[0];
     var max = pts[0];
-    for (var idx = 2; idx < pts.length; idx += 2) {
+    var length = pts.length == 7 ? 6 : pts.length;
+    for (var idx = 2; idx < length; idx += 2) {
         min = Math.min(min, pts[idx]);
         max = Math.max(max, pts[idx]);
     }
@@ -1891,7 +2548,8 @@
 function boundsHeight(pts) {
     var min = pts[1];
     var max = pts[1];
-    for (var idx = 3; idx < pts.length; idx += 2) {
+    var length = pts.length == 7 ? 6 : pts.length;
+    for (var idx = 3; idx < length; idx += 2) {
         min = Math.min(min, pts[idx]);
         max = Math.max(max, pts[idx]);
     }
@@ -1904,7 +2562,7 @@
     if (dx == 0 && dy == 0 && pts.length > 4) {
         dx = pts[4] - pts[0];
         dy = pts[5] - pts[1];
-        if (dx == 0 && dy == 0 && pts.length > 6) {
+        if (dx == 0 && dy == 0 && pts.length == 8) {
             dx = pts[6] - pts[0];
             dy = pts[7] - pts[1];
         }
@@ -1999,6 +2657,17 @@
         var c = t;
         dxy.x = a * curve[0] + b * curve[2] + c * curve[4];
         dxy.y = a * curve[1] + b * curve[3] + c * curve[5];
+    } else if (type == PATH_CONIC) {
+        var p20x = curve[4] - curve[0];
+        var p20y = curve[5] - curve[1];
+        var p10xw = (curve[2] - curve[0]) * curve[6];
+        var p10yw = (curve[3] - curve[1]) * curve[6];
+        var coeff0x = curve[6] * p20x - p20x;
+        var coeff0y = curve[6] * p20y - p20y;
+        var coeff1x = p20x - 2 * p10xw;
+        var coeff1y = p20y - 2 * p10yw;
+        dxy.x = t * (t * coeff0x + coeff1x) + p10xw;
+        dxy.y = t * (t * coeff0y + coeff1y) + p10yw;
     } else if (type == PATH_CUBIC) {
         var one_t = 1 - t;
         var a = curve[0];
@@ -2069,8 +2738,8 @@
     return mag / length;
 }
 
-function drawArrow(x, y, dx, dy) {
-    var dscale = scalexy(dx, dy, 1 / scale * 100);
+function drawArrow(x, y, dx, dy, s) {
+    var dscale = scalexy(dx, dy, 1 / scale * 100 * s);
     dx *= dscale;
     dy *= dscale;
     ctx.beginPath();
@@ -2098,6 +2767,10 @@
     if (curve.length == 6) {
         return one_t2 * curve[0] + 2 * one_t * t * curve[2] + t2 * curve[4];
     }
+    if (curve.length == 7) {
+        return (one_t2 * curve[0] + 2 * one_t * t * curve[2] * curve[6] + t2 * curve[4])
+                / (one_t2 +2 * one_t * t * curve[6] + t2);
+    }
     var a = one_t2 * one_t;
     var b = 3 * one_t2 * t;
     var c = 3 * one_t * t2;
@@ -2115,6 +2788,10 @@
     if (curve.length == 6) {
         return one_t2 * curve[1] + 2 * one_t * t * curve[3] + t2 * curve[5];
     }
+    if (curve.length == 7) {
+        return (one_t2 * curve[1] + 2 * one_t * t * curve[3] * curve[6] + t2 * curve[5])
+                / (one_t2 +2 * one_t * t * curve[6] + t2);
+    }
     var a = one_t2 * one_t;
     var b = 3 * one_t2 * t;
     var c = 3 * one_t * t2;
@@ -2179,6 +2856,11 @@
     drawCurvePartialID(id, curve, t1, t2);
 }
 
+function drawConicPartialID(id, x1, y1, x2, y2, x3, y3, w, t1, t2) {
+    var curve = [x1, y1, x2, y2, x3, y3, w];
+    drawCurvePartialID(id, curve, t1, t2);
+}
+
 function drawCubicPartialID(id, x1, y1, x2, y2, x3, y3, x4, y4, t1, t2) {
     var curve = [x1, y1, x2, y2, x3, y3, x4, y4];
     drawCurvePartialID(id, curve, t1, t2);
@@ -2217,64 +2899,24 @@
     }
     if (draw_deriviatives > 0) {
         var d = dxy_at_t(curve, type, 0);
-        drawArrow(curve[0], curve[1], d.x, d.y);
+        drawArrow(curve[0], curve[1], d.x, d.y, 1);
         if (draw_deriviatives == 2) {
             d = dxy_at_t(curve, type, 1);
             if (type == PATH_CUBIC) {
-                drawArrow(curve[6], curve[7], d.x, d.y);
+                drawArrow(curve[6], curve[7], d.x, d.y, 1);
             } else {
-                drawArrow(curve[4], curve[5], d.x, d.y);
+                drawArrow(curve[4], curve[5], d.x, d.y, 1);
             }
         }
         if (draw_midpoint) {
             var mid = pointAtT(curve, type, 0.5);
             d = dxy_at_t(curve, type, 0.5);
-            drawArrow(mid.x, mid.y, d.x, d.y);
+            drawArrow(mid.x, mid.y, d.x, d.y, 1);
         }
     }
     if (type != PATH_CUBIC) {
         return;
     }
-    if (draw_hodo == 1 || draw_hodo == 2) {
-        var hodo = hodograph(curve);
-        var hMinX = Math.min(0, hodo[0], hodo[2], hodo[4]);
-        var hMinY = Math.min(0, hodo[1], hodo[3], hodo[5]);
-        var hMaxX = Math.max(0, hodo[0], hodo[2], hodo[4]);
-        var hMaxY = Math.max(0, hodo[1], hodo[3], hodo[5]);
-        var hScaleX = hMaxX - hMinX > 0 ? screenWidth / (hMaxX - hMinX) : 1;
-        var hScaleY = hMaxY - hMinY > 0 ? screenHeight / (hMaxY - hMinY) : 1;
-        var hUnit = Math.min(hScaleX, hScaleY);
-        hUnit /= 2;
-        var hx = xoffset - hMinX * hUnit;
-        var hy = yoffset - hMinY * hUnit;
-        ctx.moveTo(hx + hodo[0] * hUnit, hy + hodo[1] * hUnit);
-        ctx.quadraticCurveTo(
-            hx + hodo[2] * hUnit, hy + hodo[3] * hUnit,
-            hx + hodo[4] * hUnit, hy + hodo[5] * hUnit);
-        ctx.strokeStyle = "red";
-        ctx.stroke();
-        if (draw_hodo == 1) {
-            drawHodoOrigin(hx, hy, hMinX, hMinY, hMaxX, hMaxY);
-        }
-    }
-    if (draw_hodo == 3) {
-        var hodo = hodograph2(curve);
-        var hMinX = Math.min(0, hodo[0], hodo[2]);
-        var hMinY = Math.min(0, hodo[1], hodo[3]);
-        var hMaxX = Math.max(0, hodo[0], hodo[2]);
-        var hMaxY = Math.max(0, hodo[1], hodo[3]);
-        var hScaleX = hMaxX - hMinX > 0 ? screenWidth / (hMaxX - hMinX) : 1;
-        var hScaleY = hMaxY - hMinY > 0 ? screenHeight / (hMaxY - hMinY) : 1;
-        var hUnit = Math.min(hScaleX, hScaleY);
-        hUnit /= 2;
-        var hx = xoffset - hMinX * hUnit;
-        var hy = yoffset - hMinY * hUnit;
-        ctx.moveTo(hx + hodo[0] * hUnit, hy + hodo[1] * hUnit);
-        ctx.lineTo(hx + hodo[2] * hUnit, hy + hodo[3] * hUnit);
-        ctx.strokeStyle = "red";
-        ctx.stroke();
-        drawHodoOrigin(hx, hy, hMinX, hMinY, hMaxX, hMaxY);
-    }
     if (draw_sequence) {
         var ymin = Math.min(curve[1], curve[3], curve[5], curve[7]);
         for (var i = 0; i < 8; i+= 2) {
@@ -2296,13 +2938,21 @@
 
 function curveToString(curve) {
     var str = "{{";
-    for (i = 0; i < curve.length; i += 2) {
+    var length = curve.length == 7 ? 6 : curve.length;
+    if (curve.length == 7) {
+        str += "{";
+    }
+    for (i = 0; i < length; i += 2) {
         str += curve[i].toFixed(decimal_places) + "," + curve[i + 1].toFixed(decimal_places);
         if (i < curve.length - 2) {
             str += "}, {";
         }
     }
-    str += "}}";
+    str += "}";
+    if (curve.length == 7) {
+        str += "}, " + curve[6].toFixed(decimal_places);
+    }
+    str += "}";
     return str;
 }
 
@@ -2327,23 +2977,28 @@
     var hasOp = false;
     var lastActive = 0;
     var lastAdd = 0;
+    var lastCoin = 0;
     var lastSect = 0;
     var lastSort = 0;
     var lastMark = 0;
+    var lastTop = 0;
     activeCount = 0;
     addCount = 0;
     angleCount = 0;
     opCount = 0;
     sectCount = 0;
     sortCount = 0;
+    topCount = 0;
     markCount = 0;
     activeMax = 0;
     addMax = 0;
     angleMax = 0;
+    coinMax = 0;
     opMax = 0;
     sectMax = 0;
     sectMax2 = 0;
     sortMax = 0;
+    topMax = 0;
     markMax = 0;
     lastIndex = test.length - 3;
     for (var tIndex = 0; tIndex < test.length; tIndex += 3) {
@@ -2387,6 +3042,9 @@
         if (recType == REC_TYPE_PATH && hasOp) {
             secondPath = tIndex;
         }
+        if (recType == REC_TYPE_PATH2 && hasOp) {
+            secondPath = tIndex;
+        }
         if (recType == REC_TYPE_ACTIVE) {
             ++activeMax;
             if (!draw_active || !inStepRange) {
@@ -2405,15 +3063,32 @@
             ++opCount;
             bumpStep = true;
         }
-        if (recType == REC_TYPE_ANGLE) {
-            ++angleMax;
-            if (!draw_angle || !inStepRange) {
+        if (recType == REC_TYPE_AFTERPART) {
+            if (draw_angle != 3 || !inStepRange) {
                 continue;
             }
             lastAngle = tIndex;
             ++angleCount;
             bumpStep = true;
         }
+        if (recType == REC_TYPE_ANGLE) {
+            ++angleMax;
+            if (draw_angle == 0 || draw_angle == 3 || !inStepRange) {
+                continue;
+            }
+            lastAngle = tIndex;
+            ++angleCount;
+            bumpStep = true;
+        }
+        if (recType == REC_TYPE_COINCIDENCE) {
+            ++coinMax;
+            if (!draw_coincidence || !inStepRange) {
+                continue;
+            }
+            lastCoin = tIndex;
+            ++coinCount;
+            bumpStep = true;
+        }
         if (recType == REC_TYPE_SECT) {
             if (records.length != 2) {
                 console.log("expect only two elements: " + records.length);
@@ -2425,6 +3100,8 @@
                 case INTERSECT_LINE:
                 case INTERSECT_QUAD_LINE:
                 case INTERSECT_QUAD:
+                case INTERSECT_CONIC_LINE:
+                case INTERSECT_CONIC:
                 case INTERSECT_SELF_CUBIC:
                 case INTERSECT_CUBIC_LINE:
                 case INTERSECT_CUBIC_QUAD:
@@ -2434,6 +3111,8 @@
                 case INTERSECT_LINE_2:
                 case INTERSECT_QUAD_LINE_2:
                 case INTERSECT_QUAD_2:
+                case INTERSECT_CONIC_LINE_2:
+                case INTERSECT_CONIC_2:
                 case INTERSECT_CUBIC_LINE_2:
                 case INTERSECT_CUBIC_QUAD_2:
                 case INTERSECT_CUBIC_2:
@@ -2442,6 +3121,8 @@
                 case INTERSECT_LINE_NO:
                 case INTERSECT_QUAD_LINE_NO:
                 case INTERSECT_QUAD_NO:
+                case INTERSECT_CONIC_LINE_NO:
+                case INTERSECT_CONIC_NO:
                 case INTERSECT_SELF_CUBIC_NO:
                 case INTERSECT_CUBIC_LINE_NO:
                 case INTERSECT_CUBIC_QUAD_NO:
@@ -2478,6 +3159,15 @@
             ++sortCount;
             bumpStep = true;
         }
+        if (recType == REC_TYPE_TOP) {
+            ++topMax;
+            if (!draw_top || !inStepRange) {
+                continue;
+            }
+            lastTop = tIndex;
+            ++topCount;
+            bumpStep = true;
+        }
         if (recType == REC_TYPE_MARK) {
             ++markMax;
             if (!draw_mark || !inStepRange) {
@@ -2496,17 +3186,20 @@
     }
     stepMax = (draw_add ? addMax : 0)
             + (draw_active ? activeMax : 0)
-            + (draw_op ? opMax : 0)
             + (draw_angle ? angleMax : 0)
+            + (draw_coincidence ? coinMax : 0)
+            + (draw_op ? opMax : 0)
             + (draw_sort ? sortMax : 0)
+            + (draw_top ? topMax : 0)
             + (draw_mark ? markMax : 0)
             + (draw_intersection == 2 ? sectMax : draw_intersection == 3 ? sectMax2 : 0);
     if (stepMax == 0) {
-        stepMax = addMax + activeMax + angleMax + opMax + sortMax + markMax;
+        stepMax = addMax + activeMax + angleMax + coinMax + opMax + sortMax + topMax + markMax;
     }
     drawnPts = [];
     drawnLines = [];
     drawnQuads = [];
+    drawnConics = [];
     drawnCubics = [];
     focusXmin = focusYmin = Infinity;
     focusXmax = focusYmax = -Infinity;
@@ -2534,16 +3227,24 @@
                     var drawThis = false;
                     switch (fragType) {
                         case PATH_QUAD:
-                            if ((draw_computed & 5) == 1 || ((draw_computed & 4) != 0
-                                    && (draw_computed & 1) == pathIndex)) {
+                            if ((draw_computed & 0x9) == 1 || ((draw_computed & 8) != 0
+                                    && (draw_computed & 7) == pathIndex)) {
                                 drawQuad(frags[0], frags[1], frags[2], frags[3],
                                         frags[4], frags[5]);
                                 drawThis = true;
                             }
                             break;
+                        case PATH_CONIC:
+                            if ((draw_computed & 0xA) == 2 || ((draw_computed & 8) != 0
+                                    && (draw_computed & 7) == pathIndex)) {
+                                drawConicWithQuads(frags[0], frags[1], frags[2], frags[3],
+                                        frags[4], frags[5], frags[6]);
+                                drawThis = true;
+                            }
+                            break;
                         case PATH_CUBIC:
-                            if ((draw_computed & 6) == 2 || ((draw_computed & 4) != 0
-                                     && (draw_computed & 1) != pathIndex)) {
+                            if ((draw_computed & 0xC) == 4 || ((draw_computed & 8) != 0
+                                     && (draw_computed & 7) == pathIndex)) {
                                 drawCubic(frags[0], frags[1], frags[2], frags[3],
                                         frags[4], frags[5], frags[6], frags[7]);
                                 drawThis = true;
@@ -2566,6 +3267,7 @@
                     drawCurveSpecials(test, frags, fragType);
                     break;
                 case REC_TYPE_PATH:
+                case REC_TYPE_PATH2:
                     if (!draw_path) {
                         continue;
                     }
@@ -2576,26 +3278,35 @@
                     ctx.lineWidth = 1;
                     ctx.strokeStyle = firstPath ? "black" : "red";
                     ctx.fillStyle = "blue";
+                    var frags2 = []; 
                     switch (fragType) {
                         case PATH_LINE:
-                            drawLine(frags[0], frags[1], frags[2], frags[3]);
+                            for (var i = 0; i < 4; ++ i) { frags2[i] = frags[i + 1]; }
+                            drawLine(frags2[0], frags2[1], frags2[2], frags2[3]);
                             break;
                         case PATH_QUAD:
-                            drawQuad(frags[0], frags[1], frags[2], frags[3],
-                                    frags[4], frags[5]);
+                            for (var i = 0; i < 6; ++ i) { frags2[i] = frags[i + 1]; }
+                            drawQuad(frags2[0], frags2[1], frags2[2], frags2[3],
+                                    frags2[4], frags2[5]);
+                            break;
+                        case PATH_CONIC:
+                            for (var i = 0; i < 7; ++ i) { frags2[i] = frags[i + 1]; }
+                            drawConicWithQuads(frags2[0], frags2[1], frags2[2], frags2[3],
+                                    frags2[4], frags2[5], frags2[6]);
                             break;
                         case PATH_CUBIC:
-                            drawCubic(frags[0], frags[1], frags[2], frags[3],
-                                    frags[4], frags[5], frags[6], frags[7]);
+                            for (var i = 0; i < 8; ++ i) { frags2[i] = frags[i + 1]; }
+                            drawCubic(frags2[0], frags2[1], frags2[2], frags2[3],
+                                    frags2[4], frags2[5], frags2[6], frags2[7]);
                             break;
                         default:
-                            console.log("unknown REC_TYPE_PATH frag type: " + fragType);
+                            console.log("unknown REC_TYPE_PATH2 frag type: " + fragType);
                             throw "stop execution";
                     }
                     if (collect_bounds) {
                         break;
                     }
-                    drawCurveSpecials(test, frags, fragType);
+                    drawCurveSpecials(test, frags2, fragType);
                     break;
                 case REC_TYPE_OP:
                     switch (fragType) {
@@ -2616,7 +3327,7 @@
                     var y1 = frags[SPAN_Y1];
                     var x2 = frags[SPAN_X2];
                     var y2 = frags[SPAN_Y2];
-                    var x3, y3, x3, y4, t1, t2;
+                    var x3, y3, x3, y4, t1, t2, w;
                     ctx.lineWidth = 3;
                     ctx.strokeStyle = "rgba(0,0,255, 0.3)";
                     focus_enabled = true;
@@ -2639,6 +3350,17 @@
                                 drawQuadPartialID(frags[0], x1, y1, x2, y2, x3, y3, t1, t2);
                             }
                             break;
+                        case ACTIVE_CONIC_SPAN:
+                            x3 = frags[SPAN_X3];
+                            y3 = frags[SPAN_Y3];
+                            t1 = frags[SPAN_K_T];
+                            t2 = frags[SPAN_K_TEND];
+                            w = frags[SPAN_K_W];
+                            drawConicPartial(x1, y1, x2, y2, x3, y3, w, t1, t2);
+                            if (draw_id) {
+                                drawConicPartialID(frags[0], x1, y1, x2, y2, x3, y3, w, t1, t2);
+                            }
+                            break;
                         case ACTIVE_CUBIC_SPAN:
                             x3 = frags[SPAN_X3];
                             y3 = frags[SPAN_Y3];
@@ -2695,6 +3417,12 @@
                                 drawQuad(frags[0], frags[1], frags[2], frags[3], frags[4], frags[5]);
                             }
                             break;
+                        case ADD_CONICTO:
+                            if (step_limit == 0 || tIndex >= lastAdd) {
+                                drawConicWithQuads(frags[0], frags[1], frags[2], frags[3],
+                                        frags[4], frags[5], frags[6]);
+                            }
+                            break;
                         case ADD_CUBICTO:
                             if (step_limit == 0 || tIndex >= lastAdd) {
                                 drawCubic(frags[0], frags[1], frags[2], frags[3],
@@ -2712,35 +3440,65 @@
                     }
                     break;
                 case REC_TYPE_ANGLE:
-                    if (!draw_angle || (step_limit > 0 && tIndex < lastAngle)) {
-                        continue;
-                    }
-                    if (fragType != ANGLE_AFTER && fragType != ANGLE_AFTER2) {
+                    angleBetween = frags[18] == "T";
+                    afterIndex = 0;
+                    if (draw_angle == 0 || draw_angle == 3 || (step_limit > 0 && tIndex < lastAngle)) {
                         continue;
                     }
                     focus_enabled = true;
                     ctx.lineWidth = 3;
                     ctx.strokeStyle = "rgba(127,45,127, 0.3)";
-                    var leftCurve, midCurve, rightCurve;
-                    if (fragType == ANGLE_AFTER) {
-                        leftCurve = curvePartialByID(test, frags[0], frags[3], frags[4]);
-                        midCurve = curvePartialByID(test, frags[5], frags[8], frags[9]);
-                        rightCurve = curvePartialByID(test, frags[10], frags[13], frags[14]);
-                    } else {
-                        leftCurve = curvePartialByID(test, frags[0], frags[4], frags[5]);
-                        midCurve = curvePartialByID(test, frags[6], frags[10], frags[11]);
-                        rightCurve = curvePartialByID(test, frags[12], frags[16], frags[17]);
-                    }
+                    var leftCurve = curvePartialByID(test, frags[0], frags[4], frags[5]);
+                    var midCurve = curvePartialByID(test, frags[6], frags[10], frags[11]);
+                    var rightCurve = curvePartialByID(test, frags[12], frags[16], frags[17]);
                     drawCurve(leftCurve);
                     drawCurve(rightCurve);
-                    var inBetween = frags[fragType == ANGLE_AFTER ? 15 : 18] == "T";
-                    ctx.strokeStyle = inBetween ? "rgba(0,160,45, 0.3)" : "rgba(255,0,45, 0.5)";
+                    ctx.strokeStyle = angleBetween ? "rgba(0,160,45, 0.3)" : "rgba(255,0,45, 0.5)";
                     drawCurve(midCurve);
                     if (draw_angle > 1) {
                         drawOrder(leftCurve, 'L');
                         drawOrder(rightCurve, 'R');
                     }
                     break;
+                case REC_TYPE_AFTERPART:
+                    if (draw_angle != 3 || (step_limit > 0 && tIndex < lastAngle)) {
+                        continue;
+                    }
+                    ctx.strokeStyle = afterIndex == 0 ? "rgba(255,0,0, 1.0)"
+                            : (afterIndex == 1) == angleBetween ? "rgba(0,128,0, 1.0)"
+                            :  "rgba(0,0,255, 1.0)";
+                    switch (fragType) {
+                        case PATH_LINE:
+                            drawLine(frags[0], frags[1], frags[2], frags[3]);
+                            break;
+                        case PATH_QUAD:
+                            drawQuad(frags[0], frags[1], frags[2], frags[3],
+                                     frags[4], frags[5]);
+                            break;
+                        case PATH_CONIC:
+                            drawConicWithQuads(frags[0], frags[1], frags[2], frags[3],
+                                     frags[4], frags[5], frags[6]);
+                            break;
+                        case PATH_CUBIC:
+                            drawCubic(frags[0], frags[1], frags[2], frags[3],
+                                     frags[4], frags[5], frags[6], frags[7]);
+                            break;
+                        default:
+                            console.log("unknown REC_TYPE_AFTERPART frag type: " + fragType);
+                            throw "stop execution";
+                    }
+                    ++afterIndex;
+                    break;
+                case REC_TYPE_COINCIDENCE:
+                    if (!draw_coincidence || (step_limit > 0 && tIndex < lastCoin)) {
+                        continue;
+                    }
+                    focus_enabled = true;
+                    ctx.lineWidth = 3;
+                    ctx.strokeStyle = "rgba(127,45,63, 0.3)";
+                    var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
+                    drawCurve(curve);
+                    break;
                 case REC_TYPE_SECT:
                     if (!draw_intersection) {
                         continue;
@@ -2797,6 +3555,30 @@
                         case INTERSECT_QUAD_NO:
                             c1s = 0; c1l = 6; c2s = 6; c2l = 6;
                             break;
+                        case INTERSECT_CONIC_LINE:
+                            f.push(8, 9, 0, 10);
+                            c1s = 1; c1l = 7; c2s = 11; c2l = 4;
+                            break;
+                        case INTERSECT_CONIC_LINE_2:
+                            f.push(8, 9, 0, 12);
+                            f.push(11, 12, 10, 18);
+                            c1s = 1; c1l = 7; c2s = 14; c2l = 4;
+                            break;
+                        case INTERSECT_CONIC_LINE_NO:
+                            c1s = 0; c1l = 7; c2s = 7; c2l = 4;
+                            break;
+                        case INTERSECT_CONIC:
+                            f.push(8, 9, 0, 10);
+                            c1s = 1; c1l = 7; c2s = 11; c2l = 7;
+                            break;
+                        case INTERSECT_CONIC_2:
+                            f.push(8, 9, 0, 13);
+                            f.push(11, 12, 10, 21);
+                            c1s = 1; c1l = 7; c2s = 14; c2l = 7;
+                            break;
+                        case INTERSECT_CONIC_NO:
+                            c1s = 0; c1l = 7; c2s = 7; c2l = 7;
+                            break;
                         case INTERSECT_SELF_CUBIC:
                             f.push(9, 10, 0, 11);
                             c1s = 1; c1l = 8; c2s = 0; c2l = 0;
@@ -2896,6 +3678,15 @@
                                     id = idByCurve(test, curve, PATH_QUAD);
                                 }
                                 break;
+                            case 7:
+                                drawConicWithQuads(frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
+                                        frags[c1s + 4], frags[c1s + 5], frags[c1s + 6]);
+                                if (draw_id) {
+                                    curve = [frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
+                                            frags[c1s + 4], frags[c1s + 5], frags[c1s + 6]];
+                                    id = idByCurve(test, curve, PATH_CONIC);
+                                }
+                                break;
                             case 8:
                                 drawCubic(frags[c1s], frags[c1s + 1], frags[c1s + 2], frags[c1s + 3],
                                         frags[c1s + 4], frags[c1s + 5], frags[c1s + 6], frags[c1s + 7]);
@@ -2929,6 +3720,15 @@
                                     id = idByCurve(test, curve, PATH_QUAD);
                                 }
                                 break;
+                            case 7:
+                                drawConicWithQuads(frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
+                                        frags[c2s + 4], frags[c2s + 5], frags[c2s + 6]);
+                                if (draw_id) {
+                                    curve = [frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
+                                            frags[c2s + 4], frags[c2s + 5], frags[c2s + 6]];
+                                    id = idByCurve(test, curve, PATH_CONIC);
+                                }
+                                break;
                             case 8:
                                 drawCubic(frags[c2s], frags[c2s + 1], frags[c2s + 2], frags[c2s + 3],
                                         frags[c2s + 4], frags[c2s + 5], frags[c2s + 6], frags[c2s + 7]);
@@ -2946,8 +3746,8 @@
                     if (collect_bounds) {
                         break;
                     }
-                    for (var idx = 0; idx < f.length; idx += 4) {
-                        if (draw_intersection != 3 || idx == lastSect - tIndex) {
+                    if (draw_intersection != 3 || step_limit == 0 || tIndex >= lastSect) {
+                        for (var idx = 0; idx < f.length; idx += 4) {
                             drawPoint(frags[f[idx]], frags[f[idx + 1]], true);
                         }
                     }
@@ -2955,8 +3755,8 @@
                         break;
                     }
                     ctx.fillStyle = "red";
-                    for (var idx = 0; idx < f.length; idx += 4) {
-                        if (draw_intersection != 3 || idx == lastSect - tIndex) {
+                    if (draw_intersection != 3 || step_limit == 0 || tIndex >= lastSect) {
+                        for (var idx = 0; idx < f.length; idx += 4) {
                             drawTAtPointUp(frags[f[idx]], frags[f[idx + 1]], frags[f[idx + 2]]);
                             drawTAtPointDown(frags[f[idx]], frags[f[idx + 1]], frags[f[idx + 3]]);
                         }
@@ -2980,6 +3780,22 @@
                             throw "stop execution";
                     }
                     break;
+                case REC_TYPE_TOP:
+                    if (!draw_top || (step_limit > 0 && tIndex < lastTop)) {
+                        continue;
+                    }
+                    ctx.lineWidth = 3;
+                    ctx.strokeStyle = "rgba(127,127,0, 0.5)";
+                    focus_enabled = true;
+                    {
+                        var curve = curvePartialByID(test, frags[0], frags[1], frags[2]);
+                        drawCurve(curve);
+                        var type = PATH_LINE + (curve.length / 2 - 2);
+                        var mid = pointAtT(curve, type, 0.5);
+                        var d = dxy_at_t(curve, type, 0.5);
+                        drawArrow(mid.x, mid.y, d.x, d.y, 0.3);
+                    }
+                    break;
                 case REC_TYPE_MARK:
                     if (!draw_mark || (step_limit > 0 && tIndex < lastMark)) {
                         continue;
@@ -3137,14 +3953,16 @@
     }
     if (draw_legend) {
         var pos = 0;
-        var drawSomething = draw_add | draw_active | draw_sort | draw_mark;
+        var drawSomething = draw_add | draw_active | draw_angle | draw_coincidence | draw_sort | draw_mark;
    //     drawBox(pos++, "yellow", "black", opLetter, true, '');
         drawBox(pos++, "rgba(0,0,255, 0.3)", "black", draw_intersection > 1 ? sectCount : sectMax2, draw_intersection, intersectionKey);
         drawBox(pos++, "rgba(0,0,255, 0.3)", "black", draw_add ? addCount : addMax, draw_add, addKey);
         drawBox(pos++, "rgba(0,0,255, 0.3)", "black", draw_active ? activeCount : activeMax, draw_active, activeKey);
         drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_angle ? angleCount : angleMax, draw_angle, angleKey);
+        drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_coincidence ? coinCount : coinMax, draw_coincidence, coincidenceKey);
         drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_op ? opCount : opMax, draw_op, opKey);
         drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_sort ? sortCount : sortMax, draw_sort, sortKey);
+        drawBox(pos++, "rgba(127,127,0, 0.3)", "black", draw_top ? topCount : topMax, draw_top, topKey);
         drawBox(pos++, "rgba(127,0,127, 0.3)", "black", draw_mark ? markCount : markMax, draw_mark, markKey);
         drawBox(pos++, "black", "white", 
                 (new Array('P', 'P1', 'P2', 'P'))[draw_path], draw_path != 0, pathKey);
@@ -3172,7 +3990,6 @@
         ctx.fillText("curve t : " +  curveTKey, screenWidth - 10, pos * 50 + y++ * 10);
         ctx.fillText("deriviatives : " +  deriviativesKey, screenWidth - 10, pos * 50 + y++ * 10);
         ctx.fillText("intersect t : " +  intersectTKey, screenWidth - 10, pos * 50 + y++ * 10);
-        ctx.fillText("hodo : " +  hodoKey, screenWidth - 10, pos * 50 + y++ * 10);
         ctx.fillText("log : " +  logKey, screenWidth - 10, pos * 50 + y++ * 10);
         ctx.fillText("log curve : " +  logCurvesKey, screenWidth - 10, pos * 50 + y++ * 10);
         ctx.fillText("mid point : " +  midpointKey, screenWidth - 10, pos * 50 + y++ * 10);
@@ -3309,15 +4126,6 @@
             var fragType = records[recordIndex];
             var frags = records[recordIndex + 1];
             if (recType == REC_TYPE_ANGLE && fragType == ANGLE_AFTER) {
-                dumpCurvePartial(test, frags[0], frags[3], frags[4]);
-                dumpCurvePartial(test, frags[5], frags[8], frags[9]);
-                dumpCurvePartial(test, frags[10], frags[13], frags[14]);
-                console.log("\nstatic IntersectData intersectDataSet[] = {");
-                dumpAngleTest(test, frags[0], frags[3], frags[4]);
-                dumpAngleTest(test, frags[5], frags[8], frags[9]);
-                dumpAngleTest(test, frags[10], frags[13], frags[14]);
-                console.log("};");
-            } else if (recType == REC_TYPE_ANGLE && fragType == ANGLE_AFTER2) {
                 dumpCurvePartial(test, frags[0], frags[4], frags[5]);
                 dumpCurvePartial(test, frags[6], frags[10], frags[11]);
                 dumpCurvePartial(test, frags[12], frags[16], frags[17]);
@@ -3335,11 +4143,11 @@
 var pathKey = 'b';
 var pathBackKey = 'B';
 var centerKey = 'c';
+var coincidenceKey = 'C';
 var addKey = 'd';
 var deriviativesKey = 'f';
 var angleKey = 'g';
 var angleBackKey = 'G';
-var hodoKey = 'h';
 var intersectionKey = 'i';
 var intersectionBackKey = 'I';
 var sequenceKey = 'j';
@@ -3355,6 +4163,7 @@
 var stepKey = 's';
 var stepBackKey = 'S';
 var intersectTKey = 't';
+var topKey = 'T';
 var curveTKey = 'u';
 var controlLinesBackKey = 'V';
 var controlLinesKey = 'v';
@@ -3391,7 +4200,7 @@
         redraw(); 
         break;
     case angleKey:
-        draw_angle = (draw_angle + 1) % 3;
+        draw_angle = (draw_angle + 1) % 4;
         redraw();
         break;
     case angleBackKey:
@@ -3402,6 +4211,10 @@
         setScale(xmin, xmax, ymin, ymax);
         redraw(); 
         break;
+    case coincidenceKey:
+        draw_coincidence ^= true;
+        redraw();
+        break;
     case controlLinesBackKey:
         control_lines = (control_lines + 3) % 4;
         redraw(); 
@@ -3434,10 +4247,6 @@
         setScale(xmin, xmax, ymin, ymax);
         redraw();
         break;
-    case hodoKey:
-        draw_hodo = (draw_hodo + 1) % 4;
-        redraw();
-        break;
     case idKey:
         draw_id ^= true;
         redraw();
@@ -3520,6 +4329,10 @@
         }
         redraw();
         break;
+    case topKey:
+        draw_top ^= true;
+        redraw();
+        break;
     case xyKey:
         debug_xy = (debug_xy + 1) % 3;
         redraw();
diff --git a/tools/render_pdfs_main.cpp b/tools/render_pdfs_main.cpp
index 4820a33..4e13f7e 100644
--- a/tools/render_pdfs_main.cpp
+++ b/tools/render_pdfs_main.cpp
@@ -98,11 +98,11 @@
 class NullWStream : public SkWStream {
 public:
     NullWStream() : fBytesWritten(0) { }
-    bool write(const void*, size_t size) SK_OVERRIDE {
+    bool write(const void*, size_t size) override {
         fBytesWritten += size;
         return true;
     }
-    size_t bytesWritten() const SK_OVERRIDE { return fBytesWritten; }
+    size_t bytesWritten() const override { return fBytesWritten; }
     size_t fBytesWritten;
 };
 }  // namespace
diff --git a/tools/render_pictures_main.cpp b/tools/render_pictures_main.cpp
index 16963dd..2624a6e 100644
--- a/tools/render_pictures_main.cpp
+++ b/tools/render_pictures_main.cpp
@@ -478,9 +478,6 @@
     if (renderer->isUsingGpuDevice()) {
         GrContext* ctx = renderer->getGrContext();
         ctx->printCacheStats();
-#ifdef SK_DEVELOPER
-        ctx->dumpFontCache();
-#endif
     }
 #endif
 
diff --git a/tools/sk_tool_utils.cpp b/tools/sk_tool_utils.cpp
index c3e574a..1a88086 100644
--- a/tools/sk_tool_utils.cpp
+++ b/tools/sk_tool_utils.cpp
@@ -12,6 +12,7 @@
 #include "SkCanvas.h"
 #include "SkShader.h"
 #include "SkTestScalerContext.h"
+#include "SkTextBlob.h"
 
 DEFINE_bool(portableFonts, false, "Use portable fonts");
 DEFINE_bool(resourceFonts, false, "Use resource fonts");
@@ -77,4 +78,20 @@
     canvas->drawPaint(paint);
 }
 
+void add_to_text_blob(SkTextBlobBuilder* builder, const char* text, const SkPaint& origPaint,
+                      SkScalar x, SkScalar y) {
+    SkPaint paint(origPaint);
+    SkTDArray<uint16_t> glyphs;
+
+    size_t len = strlen(text);
+    glyphs.append(paint.textToGlyphs(text, len, NULL));
+    paint.textToGlyphs(text, len, glyphs.begin());
+
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    const SkTextBlobBuilder::RunBuffer& run = builder->allocRun(paint, glyphs.count(), x, y,
+                                                                NULL);
+    memcpy(run.glyphs, glyphs.begin(), glyphs.count() * sizeof(uint16_t));
+}
+
+
 }  // namespace sk_tool_utils
diff --git a/tools/sk_tool_utils.h b/tools/sk_tool_utils.h
index 48c71e7..d9345a6 100644
--- a/tools/sk_tool_utils.h
+++ b/tools/sk_tool_utils.h
@@ -9,7 +9,9 @@
 #define sk_tool_utils_DEFINED
 
 #include "SkColor.h"
+#include "SkImageEncoder.h"
 #include "SkImageInfo.h"
+#include "SkPixelSerializer.h"
 #include "SkTypeface.h"
 
 class SkBitmap;
@@ -17,6 +19,7 @@
 class SkPaint;
 class SkShader;
 class SkTestFont;
+class SkTextBlobBuilder;
 
 namespace sk_tool_utils {
 
@@ -55,6 +58,22 @@
         sk_tool_utils::draw_checkerboard(canvas, 0xFF999999, 0xFF666666, 8);
     }
 
+    // Encodes to PNG, unless there is already encoded data, in which case that gets
+    // used.
+    class PngPixelSerializer : public SkPixelSerializer {
+    public:
+        bool onUseEncodedData(const void*, size_t) override { return true; }
+        SkData* onEncodePixels(const SkImageInfo& info, const void* pixels,
+                               size_t rowBytes) override {
+            return SkImageEncoder::EncodeData(info, pixels, rowBytes,
+                                              SkImageEncoder::kPNG_Type, 100);
+        }
+    };
+
+    // A helper for inserting a drawtext call into a SkTextBlobBuilder
+    void add_to_text_blob(SkTextBlobBuilder* builder, const char* text, const SkPaint& origPaint,
+                          SkScalar x, SkScalar y);
+
 }  // namespace sk_tool_utils
 
 #endif  // sk_tool_utils_DEFINED
diff --git a/tools/skp/page_sets/skia_amazon_desktop.py b/tools/skp/page_sets/skia_amazon_desktop.py
deleted file mode 100644
index 3e1bd1e..0000000
--- a/tools/skp/page_sets/skia_amazon_desktop.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path = 'data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_amazon_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaAmazonDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaAmazonDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_amazon_desktop.json')
-
-    urls_list = [
-      # Why: #1 world commerce website by visits; #3 commerce in the US by time
-      # spent.
-      'http://www.amazon.com',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_androidpolice_nexus10.py b/tools/skp/page_sets/skia_androidpolice_nexus10.py
deleted file mode 100644
index c8daf01..0000000
--- a/tools/skp/page_sets/skia_androidpolice_nexus10.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_androidpolice_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaAndroidpoliceNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaAndroidpoliceNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_androidpolice_nexus10.json')
-
-    urls_list = [
-      # Why: for Clank CY.
-      ('http://www.androidpolice.com/2012/10/03/rumor-evidence-mounts-that-an-'
-       'lg-optimus-g-nexus-is-coming-along-with-a-nexus-phone-certification-'
-       'program/'),
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_baidu_desktop.py b/tools/skp/page_sets/skia_baidu_desktop.py
deleted file mode 100644
index 24a96d4..0000000
--- a/tools/skp/page_sets/skia_baidu_desktop.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_baidu_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(90)
-
-
-class SkiaBaiduDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaBaiduDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_baidu_desktop.json')
-
-    urls_list = [
-      # Why: #5 (Alexa) most visited worldwide.
-      ('http://www.baidu.com/s?wd=barack+obama&rsv_bp=0&rsv_spt=3&rsv_sug3=9&'
-       'rsv_sug=0&rsv_sug4=3824&rsv_sug1=3&inputT=4920'),
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_blogger_desktop.py b/tools/skp/page_sets/skia_blogger_desktop.py
deleted file mode 100644
index 4881fb4..0000000
--- a/tools/skp/page_sets/skia_blogger_desktop.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_blogger_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaBloggerDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaBloggerDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_blogger_desktop.json')
-
-    urls_list = [
-      # Why: #11 (Alexa global), google property; some blogger layouts have
-      # infinite scroll but more interesting
-      'http://googlewebmastercentral.blogspot.com/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_cnet_nexus10.py b/tools/skp/page_sets/skia_cnet_nexus10.py
deleted file mode 100644
index 6b4eafb..0000000
--- a/tools/skp/page_sets/skia_cnet_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_cnet_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaCnetNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaCnetNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_cnet_nexus10.json')
-
-    urls_list = [
-      # Why: from klobag.
-      'http://www.cnet.com/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_cnn_nexus10.py b/tools/skp/page_sets/skia_cnn_nexus10.py
deleted file mode 100644
index 695ef17..0000000
--- a/tools/skp/page_sets/skia_cnn_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_cnn_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaCnnNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaCnnNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_cnn_nexus10.json')
-
-    urls_list = [
-      # Why: #2 news worldwide.
-      'http://www.cnn.com/2012/10/03/politics/michelle-obama-debate/index.html',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_culturalsolutions_nexus10.py b/tools/skp/page_sets/skia_culturalsolutions_nexus10.py
deleted file mode 100644
index 4331a45..0000000
--- a/tools/skp/page_sets/skia_culturalsolutions_nexus10.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_culturalsolutions_nexus10.json'
-
-  def RunSmoothness(self, action_runner):
-    action_runner.ScrollElement()
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaCulturalsolutionsNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaCulturalsolutionsNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_culturalsolutions_nexus10.json')
-
-    urls_list = [
-      # Why: from parallax scrolling thread.
-      'http://culturalsolutions.co.uk/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_deviantart_nexus10.py b/tools/skp/page_sets/skia_deviantart_nexus10.py
deleted file mode 100644
index 421489c..0000000
--- a/tools/skp/page_sets/skia_deviantart_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_deviantart_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaDeviantartNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaDeviantartNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_deviantart_nexus10.json')
-
-    urls_list = [
-      # Why: from caryclark
-      'http://www.deviantart.com/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_ebay_desktop.py b/tools/skp/page_sets/skia_ebay_desktop.py
deleted file mode 100644
index 07c4c14..0000000
--- a/tools/skp/page_sets/skia_ebay_desktop.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_ebay_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaEbayDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaEbayDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_ebay_desktop.json')
-
-    urls_list = [
-      # Why: #1 commerce website by time spent by users in US.
-      ('http://www.ebay.com/ctg/Harry-Potter-and-Deathly-Hallows-Year-7-J-K-'
-       'Rowling-2007-Cassette-Unabridged-/123341182?_dmpt=US_Childrens_Books'
-       '&_pcategid=279&_pcatid=4&_refkw=harry+potter+and+the+deathly+hallows'),
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_engadget_nexus10.py b/tools/skp/page_sets/skia_engadget_nexus10.py
deleted file mode 100644
index 88d8577..0000000
--- a/tools/skp/page_sets/skia_engadget_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_engadget_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaEngadgetNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaEngadgetNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_engadget_nexus10.json')
-
-    urls_list = [
-      # Why: from klobag.
-      'http://www.engadget.com/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_facebook_desktop.py b/tools/skp/page_sets/skia_facebook_desktop.py
deleted file mode 100644
index 2ef3741..0000000
--- a/tools/skp/page_sets/skia_facebook_desktop.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json'
-    )
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_facebook_desktop.json'
-    self.credentials = 'facebook'
-
-  def RunSmoothness(self, action_runner):
-    action_runner.ScrollElement()
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaFacebookDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaFacebookDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_facebook_desktop.json')
-
-    urls_list = [
-      # Why: Top social, public profile.
-      'http://www.facebook.com/barackobama',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_fontwipe_desktop.py b/tools/skp/page_sets/skia_fontwipe_desktop.py
deleted file mode 100644
index 9288b61..0000000
--- a/tools/skp/page_sets/skia_fontwipe_desktop.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_fontwipe_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaFontwipeDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaFontwipeDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_fontwipe_desktop.json')
-
-    urls_list = [
-      # Why: Page from Chromium's silk test cases.
-      'http://jsfiddle.net/u324ffkq/embedded/result/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_forecastio_desktop.py b/tools/skp/page_sets/skia_forecastio_desktop.py
deleted file mode 100644
index e1356fe..0000000
--- a/tools/skp/page_sets/skia_forecastio_desktop.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_forecastio_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaForecastioDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaForecastioDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_forecastio_desktop.json')
-
-    urls_list = [
-      # Why: Page from Chromium's silk test cases
-      'http://forecast.io?tap',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_googleblog_nexus10.py b/tools/skp/page_sets/skia_googleblog_nexus10.py
deleted file mode 100644
index 2158027..0000000
--- a/tools/skp/page_sets/skia_googleblog_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_googleblog_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaGoogleblogNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaGoogleblogNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_googleblog_nexus10.json')
-
-    urls_list = [
-      # Why: for Clank CY
-      'http://googleblog.blogspot.co.uk/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_googlecalendar_nexus10.py b/tools/skp/page_sets/skia_googlecalendar_nexus10.py
deleted file mode 100644
index a905c48..0000000
--- a/tools/skp/page_sets/skia_googlecalendar_nexus10.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_googlecalendar_nexus10.json'
-    self.credentials = 'google'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaGooglecalendarNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaGooglecalendarNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_googlecalendar_nexus10.json')
-
-    urls_list = [
-      # Why: productivity, top google properties
-      'https://www.google.com/calendar/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_googleplus_desktop.py b/tools/skp/page_sets/skia_googleplus_desktop.py
deleted file mode 100644
index 6d48677..0000000
--- a/tools/skp/page_sets/skia_googleplus_desktop.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_googleplus_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaGoogleplusDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaGoogleplusDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_googleplus_desktop.json')
-
-    urls_list = [
-      # Why: social; top google property; Public profile; infinite scrolls
-      'https://plus.google.com/110031535020051778989/posts',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_googlespreadsheetdashed_desktop.py b/tools/skp/page_sets/skia_googlespreadsheetdashed_desktop.py
deleted file mode 100644
index 235ee44..0000000
--- a/tools/skp/page_sets/skia_googlespreadsheetdashed_desktop.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_googlespreadsheetdashed_desktop.json'
-    self.credentials = 'google'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaGooglespreadsheetdashedDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaGooglespreadsheetdashedDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_googlespreadsheetdashed_desktop.json')
-
-    urls_list = [
-      # Why: from Tom W's list.
-      ('https://docs.google.com/spreadsheet/ccc?key='
-       '0ApnDjttF0gt9dGpRM0labXFaT3ExSFhWZWstR3dQSWc#gid=0'),
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_gspro_nexus10.py b/tools/skp/page_sets/skia_gspro_nexus10.py
deleted file mode 100644
index 1bbe423..0000000
--- a/tools/skp/page_sets/skia_gspro_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_gspro_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaGsproNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaGsproNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_gspro_nexus10.json')
-
-    urls_list = [
-      # Why: for Clank CY
-      'http://www.gsp.ro/click-less-think-more',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_gws_desktop.py b/tools/skp/page_sets/skia_gws_desktop.py
deleted file mode 100644
index 47db8e9..0000000
--- a/tools/skp/page_sets/skia_gws_desktop.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_gws_desktop.json'
-
-  def RunSmoothness(self, action_runner):
-    action_runner.ScrollElement()
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaGwsDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaGwsDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_gws_desktop.json')
-
-    urls_list = [
-      # Why: top google property; a google tab is often open
-      'https://www.google.com/#hl=en&q=barack+obama',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_hsfi_nexus10.py b/tools/skp/page_sets/skia_hsfi_nexus10.py
deleted file mode 100644
index b8694cf..0000000
--- a/tools/skp/page_sets/skia_hsfi_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_hsfi_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaHsfiNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaHsfiNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_hsfi_nexus10.json')
-
-    urls_list = [
-      # Why: for Clank CY
-      'http://www.hs.fi/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_jsfiddlehumperclip_desktop.py b/tools/skp/page_sets/skia_jsfiddlehumperclip_desktop.py
deleted file mode 100644
index 9aab8e2..0000000
--- a/tools/skp/page_sets/skia_jsfiddlehumperclip_desktop.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_jsfiddlehumperclip_desktop.json'
-
-  def RunSmoothness(self, action_runner):
-    action_runner.ScrollElement()
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaJsfiddlehumperclipDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaJsfiddlehumperclipDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_jsfiddlehumperclip_desktop.json')
-
-    urls_list = [
-      # Why: Page from Chromium's silk test cases.
-      'http://jsfiddle.net/humper/cKB9D/3/embedded/result/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_linkedin_desktop.py b/tools/skp/page_sets/skia_linkedin_desktop.py
deleted file mode 100644
index 04bcf55..0000000
--- a/tools/skp/page_sets/skia_linkedin_desktop.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_linkedin_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaLinkedinDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaLinkedinDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_linkedin_desktop.json')
-
-    urls_list = [
-      # Why: #12 (Alexa global), public profile
-      'http://www.linkedin.com/in/linustorvalds',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_mlb_nexus10.py b/tools/skp/page_sets/skia_mlb_nexus10.py
deleted file mode 100644
index d43af5b..0000000
--- a/tools/skp/page_sets/skia_mlb_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_mlb_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaMlbNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaMlbNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_mlb_nexus10.json')
-
-    urls_list = [
-      # Why: from Arrow scrolling thread
-      'http://mlb.mlb.com/index.jsp',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_mobilenews_desktop.py b/tools/skp/page_sets/skia_mobilenews_desktop.py
deleted file mode 100644
index 435d5c8..0000000
--- a/tools/skp/page_sets/skia_mobilenews_desktop.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_mobilenews_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaMobilenewsDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaMobilenewsDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_mobilenews_desktop.json')
-
-    urls_list = [
-      # Why: Page from Chromium's silk test cases.
-      'http://mobile-news.sandbox.google.com/news',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_nofolo_nexus10.py b/tools/skp/page_sets/skia_nofolo_nexus10.py
deleted file mode 100644
index a7caa7a..0000000
--- a/tools/skp/page_sets/skia_nofolo_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_nofolo_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaNofoloNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaNofoloNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_nofolo_nexus10.json')
-
-    urls_list = [
-      # Why: http://code.google.com/p/chromium/issues/detail?id=136555
-      'http://nofolo.com/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_pinterest_desktop.py b/tools/skp/page_sets/skia_pinterest_desktop.py
deleted file mode 100644
index 110eacf..0000000
--- a/tools/skp/page_sets/skia_pinterest_desktop.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_pinterest_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaPinterestDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaPinterestDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_pinterest_desktop.json')
-
-    urls_list = [
-      # Why: #37 Alexa global
-      'http://pinterest.com',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_sahadan_nexus10.py b/tools/skp/page_sets/skia_sahadan_nexus10.py
deleted file mode 100644
index 681b903..0000000
--- a/tools/skp/page_sets/skia_sahadan_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_sahadan_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaSahadanNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaSahadanNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_sahadan_nexus10.json')
-
-    urls_list = [
-      # Why: from Tom W's list
-      'http://www.sahadan.com/canli_sonuclar/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_sfgate_desktop.py b/tools/skp/page_sets/skia_sfgate_desktop.py
deleted file mode 100644
index be209bf..0000000
--- a/tools/skp/page_sets/skia_sfgate_desktop.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_sfgate_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaSfgateDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaSfgateDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_sfgate_desktop.json')
-
-    urls_list = [
-      # Why: from Arrow scrolling thread
-      'http://www.sfgate.com/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_silkfinance_desktop.py b/tools/skp/page_sets/skia_silkfinance_desktop.py
deleted file mode 100644
index d6528f8..0000000
--- a/tools/skp/page_sets/skia_silkfinance_desktop.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_silkfinance_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaSilkfinanceDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaSilkfinanceDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_silkfinance_desktop.json')
-
-    urls_list = [
-      # Why: Page from Chromium's silk test cases
-      'http://jsfiddle.net/47ftse2x/embedded/result/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_slashdot_nexus10.py b/tools/skp/page_sets/skia_slashdot_nexus10.py
deleted file mode 100644
index db595b2..0000000
--- a/tools/skp/page_sets/skia_slashdot_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_slashdot_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaSlashdotNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaSlashdotNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_slashdot_nexus10.json')
-
-    urls_list = [
-      # Why:
-      'http://slashdot.org/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_techmeme_nexus10.py b/tools/skp/page_sets/skia_techmeme_nexus10.py
deleted file mode 100644
index 3d68a6b..0000000
--- a/tools/skp/page_sets/skia_techmeme_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_techmeme_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaTechmemeNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaTechmemeNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_techmeme_nexus10.json')
-
-    urls_list = [
-      # Why: from klobag
-      'http://techmeme.com/',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_twitter_desktop.py b/tools/skp/page_sets/skia_twitter_desktop.py
deleted file mode 100644
index c3eadf0..0000000
--- a/tools/skp/page_sets/skia_twitter_desktop.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_twitter_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaTwitterDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaTwitterDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_twitter_desktop.json')
-
-    urls_list = [
-      # Why: #8 (Alexa global), picked an interesting page
-      'http://twitter.com/katyperry',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_ukwsj_nexus10.py b/tools/skp/page_sets/skia_ukwsj_nexus10.py
deleted file mode 100644
index 02db044..0000000
--- a/tools/skp/page_sets/skia_ukwsj_nexus10.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'tablet'
-    self.archive_data_file = 'data/skia_ukwsj_nexus10.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaUkwsjNexus10PageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaUkwsjNexus10PageSet, self).__init__(
-      user_agent_type='tablet',
-      archive_data_file='data/skia_ukwsj_nexus10.json')
-
-    urls_list = [
-      # Why: for Clank CY
-      'http://uk.wsj.com/home-page',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_yahooanswers_desktop.py b/tools/skp/page_sets/skia_unicodetable_desktop.py
similarity index 64%
rename from tools/skp/page_sets/skia_yahooanswers_desktop.py
rename to tools/skp/page_sets/skia_unicodetable_desktop.py
index 820ca8a..27a97c1 100644
--- a/tools/skp/page_sets/skia_yahooanswers_desktop.py
+++ b/tools/skp/page_sets/skia_unicodetable_desktop.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2015 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.
 # pylint: disable=W0401,W0614
@@ -16,25 +16,26 @@
         page_set=page_set,
         credentials_path='data/credentials.json')
     self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_yahooanswers_desktop.json'
+    self.archive_data_file = 'data/skia_unicodetable_desktop.json'
 
   def RunNavigateSteps(self, action_runner):
     action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
+    action_runner.ScrollPage(distance=100000)
+    action_runner.Wait(20)
 
 
-class SkiaYahooanswersDesktopPageSet(page_set_module.PageSet):
+class SkiaUnicodetableDesktopPageSet(page_set_module.PageSet):
 
   """ Pages designed to represent the median, not highly optimized web """
 
   def __init__(self):
-    super(SkiaYahooanswersDesktopPageSet, self).__init__(
+    super(SkiaUnicodetableDesktopPageSet, self).__init__(
       user_agent_type='desktop',
-      archive_data_file='data/skia_yahooanswers_desktop.json')
+      archive_data_file='data/skia_unicodetable_desktop.json')
 
     urls_list = [
-      # Why: #1 Alexa reference
-      'http://answers.yahoo.com',
+      # Why: stress tests for fonts (from skia:3574).
+      'http://unicode-table.com/en/',
     ]
 
     for url in urls_list:
diff --git a/tools/skp/page_sets/skia_weather_desktop.py b/tools/skp/page_sets/skia_weather_desktop.py
deleted file mode 100644
index 7d91161..0000000
--- a/tools/skp/page_sets/skia_weather_desktop.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_weather_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(15)
-
-
-class SkiaWeatherDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaWeatherDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_weather_desktop.json')
-
-    urls_list = [
-      # Why: #7 Alexa news; #27 total time spent, picked interesting page
-      'http://www.weather.com/weather/right-now/Mountain+View+CA+94043',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_booking_desktop.py b/tools/skp/page_sets/skia_wikipedia_desktop.py
similarity index 61%
rename from tools/skp/page_sets/skia_booking_desktop.py
rename to tools/skp/page_sets/skia_wikipedia_desktop.py
index bef74d8..fa7b70c 100644
--- a/tools/skp/page_sets/skia_booking_desktop.py
+++ b/tools/skp/page_sets/skia_wikipedia_desktop.py
@@ -1,4 +1,4 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
+# Copyright 2015 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.
 # pylint: disable=W0401,W0614
@@ -16,25 +16,21 @@
         page_set=page_set,
         credentials_path='data/credentials.json')
     self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_booking_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
+    self.archive_data_file = 'data/skia_wikipedia_desktop.json'
 
 
-class SkiaBookingDesktopPageSet(page_set_module.PageSet):
+class SkiaWikipediaDesktopPageSet(page_set_module.PageSet):
 
   """ Pages designed to represent the median, not highly optimized web """
 
   def __init__(self):
-    super(SkiaBookingDesktopPageSet, self).__init__(
+    super(SkiaWikipediaDesktopPageSet, self).__init__(
       user_agent_type='desktop',
-      archive_data_file='data/skia_booking_desktop.json')
+      archive_data_file='data/skia_wikipedia_desktop.json')
 
     urls_list = [
-      # Why: #1 Alexa recreation.
-      'http://booking.com',
+      # Why: stress tests for fonts (from skia:3574).
+      'http://www.wikipedia.org/',
     ]
 
     for url in urls_list:
diff --git a/tools/skp/page_sets/skia_wordpress_desktop.py b/tools/skp/page_sets/skia_wordpress_desktop.py
deleted file mode 100644
index 3b48f12..0000000
--- a/tools/skp/page_sets/skia_wordpress_desktop.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_wordpress_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(5)
-
-
-class SkiaWordpressDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaWordpressDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_wordpress_desktop.json')
-
-    urls_list = [
-      # Why: #18 (Alexa global), Picked an interesting post.
-      ('http://en.blog.wordpress.com/2012/09/04/'
-       'freshly-pressed-editors-picks-for-august-2012/'),
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/page_sets/skia_youtube_desktop.py b/tools/skp/page_sets/skia_youtube_desktop.py
deleted file mode 100644
index 335dc68..0000000
--- a/tools/skp/page_sets/skia_youtube_desktop.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 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.
-# pylint: disable=W0401,W0614
-
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class SkiaBuildbotDesktopPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SkiaBuildbotDesktopPage, self).__init__(
-        url=url,
-        page_set=page_set,
-        credentials_path='data/credentials.json')
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/skia_youtube_desktop.json'
-
-  def RunNavigateSteps(self, action_runner):
-    action_runner.NavigateToPage(self)
-    action_runner.Wait(25)
-
-
-class SkiaYoutubeDesktopPageSet(page_set_module.PageSet):
-
-  """ Pages designed to represent the median, not highly optimized web """
-
-  def __init__(self):
-    super(SkiaYoutubeDesktopPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/skia_youtube_desktop.json')
-
-    urls_list = [
-      # Why: #3 (Alexa global)
-      'http://www.youtube.com/watch?v=PC57z-oDPLs',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(SkiaBuildbotDesktopPage(url, self))
diff --git a/tools/skp/recreate_skps.py b/tools/skp/recreate_skps.py
index bbec14a..f32741a 100644
--- a/tools/skp/recreate_skps.py
+++ b/tools/skp/recreate_skps.py
@@ -57,13 +57,41 @@
     '--page_sets', 'all',
     '--browser_executable', browser_executable,
     '--non-interactive',
-    '--upload_to_gs',
+    '--upload',
     '--alternate_upload_dir', upload_dir,
     '--chrome_src_path', chrome_src_path,
   ]
 
   try:
     shell_utils.run(webpages_playback_cmd)
+
+    # Temporary change to enable Slimming Paint runs. See skia:3763.
+    skia_page_sets_path = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)), 'page_sets')
+    sp_skia_page_sets = [
+        os.path.join(skia_page_sets_path, 'skia_carsvg_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_chalkboard_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_css3gradients_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_espn_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_gmailthread_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_googlehome_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_googlespreadsheet_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_jsfiddlebigcar_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_mapsvg_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_nytimes_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_samoasvg_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_tigersvg_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_ugamsolutions_desktop.py'),
+        os.path.join(skia_page_sets_path, 'skia_digg_nexus10.py'),
+        os.path.join(skia_page_sets_path, 'skia_gmail_nexus10.py'),
+        os.path.join(skia_page_sets_path, 'skia_pravda_nexus10.py'),
+    ]
+    webpages_playback_cmd.extend([
+        '--skp_prefix', 'sp_',
+        '--browser_extra_args', '--enable-slimming-paint',
+        '--page_sets', '%s' % ','.join(sp_skia_page_sets),
+    ])
+    shell_utils.run(webpages_playback_cmd)
   finally:
     # Clean up any leftover browser instances. This can happen if there are
     # telemetry crashes, processes are not always cleaned up appropriately by
diff --git a/tools/skp/webpages_playback.py b/tools/skp/webpages_playback.py
index 422edbc..1bd2163 100644
--- a/tools/skp/webpages_playback.py
+++ b/tools/skp/webpages_playback.py
@@ -8,16 +8,17 @@
 To archive webpages and store SKP files (archives should be rarely updated):
 
 cd skia
-python tools/skp/webpages_playback.py --dest_gsbase=gs://rmistry --record \
+python tools/skp/webpages_playback.py --data_store=gs://rmistry --record \
 --page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \
 --browser_executable=/tmp/chromium/out/Release/chrome
 
+The above command uses Google Storage bucket 'rmistry' to download needed files.
 
 To replay archived webpages and re-generate SKP files (should be run whenever
 SkPicture.PICTURE_VERSION changes):
 
 cd skia
-python tools/skp/webpages_playback.py --dest_gsbase=gs://rmistry \
+python tools/skp/webpages_playback.py --data_store=gs://rmistry \
 --page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \
 --browser_executable=/tmp/chromium/out/Release/chrome
 
@@ -32,8 +33,15 @@
 to capture archives and/or capture SKP files. Majority of the time it should be
 a newly built chrome binary.
 
-The --upload_to_gs flag controls whether generated artifacts will be uploaded
-to Google Storage (default value is False if not specified).
+The --data_store flag controls where the needed artifacts, such as
+credential files, are downloaded from. It also controls where the
+generated artifacts, such as recorded webpages and resulting skp renderings,
+are uploaded to. URLs with scheme 'gs://' use Google Storage. Otherwise
+use local filesystem.
+
+The --upload=True flag means generated artifacts will be
+uploaded or copied to the location specified by --data_store. (default value is
+False if not specified).
 
 The --non-interactive flag controls whether the script will prompt the user
 (default value is False if not specified).
@@ -107,11 +115,22 @@
    gs_utils.GSUtils.Permission.READ),
 ]
 
+# Path to Chromium's page sets.
+CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
+
+# Dictionary of supported Chromium page sets to their file prefixes.
+CHROMIUM_PAGE_SETS_TO_PREFIX = {
+    'key_mobile_sites_smooth.py': 'keymobi',
+    'top_25_smooth.py': 'top25desk',
+}
+
+
 def remove_prefix(s, prefix):
   if s.startswith(prefix):
     return s[len(prefix):]
   return s
 
+
 class SkPicturePlayback(object):
   """Class that archives or replays webpages and creates SKPs."""
 
@@ -119,17 +138,27 @@
     """Constructs a SkPicturePlayback BuildStep instance."""
     assert parse_options.browser_executable, 'Must specify --browser_executable'
     self._browser_executable = parse_options.browser_executable
+    self._browser_args = '--disable-setuid-sandbox'
+    if parse_options.browser_extra_args:
+      self._browser_args = '%s %s' % (
+          self._browser_args, parse_options.browser_extra_args)
 
+    self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
+                                               CHROMIUM_PAGE_SETS_PATH)
     self._all_page_sets_specified = parse_options.page_sets == 'all'
     self._page_sets = self._ParsePageSets(parse_options.page_sets)
 
-    self._dest_gsbase = parse_options.dest_gsbase
     self._record = parse_options.record
     self._skia_tools = parse_options.skia_tools
     self._non_interactive = parse_options.non_interactive
-    self._upload_to_gs = parse_options.upload_to_gs
+    self._upload = parse_options.upload
+    self._skp_prefix = parse_options.skp_prefix
+    data_store_location = parse_options.data_store
+    if data_store_location.startswith(gs_utils.GS_PREFIX):
+      self.gs = GoogleStorageDataStore(data_store_location)
+    else:
+      self.gs = LocalFileSystemDataStore(data_store_location)
     self._alternate_upload_dir = parse_options.alternate_upload_dir
-    self._skip_all_gs_access = parse_options.skip_all_gs_access
     self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
                                                 'tools', 'perf')
 
@@ -152,6 +181,10 @@
             for page_set in os.listdir(page_sets_dir)
             if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
                page_set.endswith('.py')]
+      chromium_ps = [
+          os.path.join(self._chrome_page_sets_path, cr_page_set)
+          for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
+      ps.extend(chromium_ps)
     elif '*' in page_sets:
       # Explode and return the glob.
       ps = glob.glob(page_sets)
@@ -160,12 +193,21 @@
     ps.sort()
     return ps
 
+  def _IsChromiumPageSet(self, page_set):
+    """Returns true if the specified page set is a Chromium page set."""
+    return page_set.startswith(self._chrome_page_sets_path)
+
   def Run(self):
     """Run the SkPicturePlayback BuildStep."""
 
     # Download the credentials file if it was not previously downloaded.
-    if self._skip_all_gs_access:
-      print """\n\nPlease create a %s file that contains:
+    if not os.path.isfile(CREDENTIALS_FILE_PATH):
+      # Download the credentials.json file from Google Storage.
+      self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
+
+    if not os.path.isfile(CREDENTIALS_FILE_PATH):
+      print """\n\nCould not locate credentials file in the storage.
+      Please create a %s file that contains:
       {
         "google": {
           "username": "google_testing_account_username",
@@ -177,11 +219,6 @@
         }
       }\n\n""" % CREDENTIALS_FILE_PATH
       raw_input("Please press a key when you are ready to proceed...")
-    elif not os.path.isfile(CREDENTIALS_FILE_PATH):
-      # Download the credentials.json file from Google Storage.
-      gs_bucket = remove_prefix(self._dest_gsbase.lstrip(), gs_utils.GS_PREFIX)
-      gs_utils.GSUtils().download_file(gs_bucket, CREDENTIALS_GS_PATH,
-                                       CREDENTIALS_FILE_PATH)
 
     # Delete any left over data files in the data directory.
     for archive_file in glob.glob(
@@ -206,14 +243,16 @@
       wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr'
       page_set_dir = os.path.dirname(page_set)
 
-      if self._record:
+      if self._IsChromiumPageSet(page_set):
+        print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
+      elif self._record:
         # Create an archive of the specified webpages if '--record=True' is
         # specified.
         record_wpr_cmd = (
           'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
           'DISPLAY=%s' % X11_DISPLAY,
           os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
-          '--extra-browser-args=--disable-setuid-sandbox',
+          '--extra-browser-args="%s"' % self._browser_args,
           '--browser=exact',
           '--browser-executable=%s' % self._browser_executable,
           '%s_page_set' % page_set_basename,
@@ -222,6 +261,17 @@
         for _ in range(RETRY_RECORD_WPR_COUNT):
           try:
             shell_utils.run(' '.join(record_wpr_cmd), shell=True)
+
+            # Move over the created archive into the local webpages archive
+            # directory.
+            shutil.move(
+              os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
+              self._local_record_webpages_archive_dir)
+            shutil.move(
+              os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
+                           page_set_json_name),
+              self._local_record_webpages_archive_dir)
+
             # Break out of the retry loop since there were no errors.
             break
           except Exception:
@@ -233,16 +283,15 @@
           raise Exception('record_wpr failed for page_set: %s' % page_set)
 
       else:
-        if not self._skip_all_gs_access:
-          # Get the webpages archive so that it can be replayed.
-          self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
+        # Get the webpages archive so that it can be replayed.
+        self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
 
       run_benchmark_cmd = (
           'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
           'DISPLAY=%s' % X11_DISPLAY,
           'timeout', '300',
           os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
-          '--extra-browser-args=--disable-setuid-sandbox',
+          '--extra-browser-args="%s"' % self._browser_args,
           '--browser=exact',
           '--browser-executable=%s' % self._browser_executable,
           SKP_BENCHMARK,
@@ -261,17 +310,6 @@
           # captured SKP is still valid. This is a known issue.
           pass
 
-        if self._record:
-          # Move over the created archive into the local webpages archive
-          # directory.
-          shutil.move(
-              os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
-              self._local_record_webpages_archive_dir)
-          shutil.move(
-              os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
-                           page_set_json_name),
-              self._local_record_webpages_archive_dir)
-
         # Rename generated SKP files into more descriptive names.
         try:
           self._RenameSkpFiles(page_set)
@@ -314,51 +352,71 @@
 
     print '\n\n'
 
-    if not self._skip_all_gs_access and self._upload_to_gs:
-      print '\n\n=======Uploading to Google Storage=======\n\n'
+    if self._upload:
+      print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
       # Copy the directory structure in the root directory into Google Storage.
       dest_dir_name = ROOT_PLAYBACK_DIR_NAME
       if self._alternate_upload_dir:
         dest_dir_name = self._alternate_upload_dir
 
-      gs_bucket = remove_prefix(self._dest_gsbase.lstrip(), gs_utils.GS_PREFIX)
-      gs_utils.GSUtils().upload_dir_contents(
-          LOCAL_PLAYBACK_ROOT_DIR, gs_bucket, dest_dir_name,
+      self.gs.upload_dir_contents(
+          LOCAL_PLAYBACK_ROOT_DIR, dest_dir=dest_dir_name,
           upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED,
           predefined_acl=GS_PREDEFINED_ACL,
           fine_grained_acl_list=GS_FINE_GRAINED_ACL_LIST)
 
       print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
-          posixpath.join(self._dest_gsbase, dest_dir_name, SKPICTURES_DIR_NAME))
+          posixpath.join(self.gs.target_name(), dest_dir_name,
+                         SKPICTURES_DIR_NAME))
     else:
-      print '\n\n=======Not Uploading to Google Storage=======\n\n'
+      print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
       print 'Generated resources are available in %s\n\n' % (
           LOCAL_PLAYBACK_ROOT_DIR)
 
     return 0
 
+  def _GetSkiaSkpFileName(self, page_set):
+    """Returns the SKP file name for Skia page sets."""
+    # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
+    ps_filename = os.path.basename(page_set)
+    # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
+    ps_basename, _ = os.path.splitext(ps_filename)
+    # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
+    _, page_name, device = ps_basename.split('_')
+    basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
+    return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
+
+  def _GetChromiumSkpFileName(self, page_set, site):
+    """Returns the SKP file name for Chromium page sets."""
+    # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
+    _, webpage = os.path.split(site)
+    # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
+    for prefix in ('http___', 'https___', 'www_'):
+      if webpage.startswith(prefix):
+        webpage = webpage[len(prefix):]
+    # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
+    ps_filename = os.path.basename(page_set)
+    # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
+    basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
+    return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
+
   def _RenameSkpFiles(self, page_set):
     """Rename generated SKP files into more descriptive names.
 
     Look into the subdirectory of TMP_SKP_DIR and find the most interesting
     .skp in there to be this page_set's representative .skp.
     """
-    # Here's where we're assuming there's one page per pageset.
-    # If there were more than one, we'd overwrite filename below.
-
-    # /path/to/skia_yahooanswers_desktop.json -> skia_yahooanswers_desktop.json
-    _, ps_filename = os.path.split(page_set)
-    # skia_yahooanswers_desktop.json -> skia_yahooanswers_desktop
-    ps_basename, _ = os.path.splitext(ps_filename)
-    # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
-    _, page_name, device = ps_basename.split('_')
-
-    basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
-    filename = basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
-
     subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
-    assert len(subdirs) == 1
     for site in subdirs:
+      if self._IsChromiumPageSet(page_set):
+        filename = self._GetChromiumSkpFileName(page_set, site)
+      else:
+        filename = self._GetSkiaSkpFileName(page_set)
+      filename = filename.lower()
+
+      if self._skp_prefix:
+        filename = '%s%s' % (self._skp_prefix, filename)
+
       # We choose the largest .skp as the most likely to be interesting.
       largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
                         key=lambda path: os.stat(path).st_size)
@@ -383,20 +441,73 @@
     page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
                                      'webpages_archive',
                                      page_set_json_name)
-    gs = gs_utils.GSUtils()
-    gs_bucket = remove_prefix(self._dest_gsbase.lstrip(), gs_utils.GS_PREFIX)
-    if (gs.does_storage_object_exist(gs_bucket, wpr_source) and
-        gs.does_storage_object_exist(gs_bucket, page_set_source)):
-      gs.download_file(gs_bucket, wpr_source,
+    gs = self.gs
+    if (gs.does_storage_object_exist(wpr_source) and
+        gs.does_storage_object_exist(page_set_source)):
+      gs.download_file(wpr_source,
                        os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
                                     wpr_data_file))
-      gs.download_file(gs_bucket, page_set_source,
+      gs.download_file(page_set_source,
                        os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
                                     page_set_json_name))
     else:
-      raise Exception('%s and %s do not exist in Google Storage!' % (
-          wpr_source, page_set_source))
+      raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
+        wpr_source, page_set_source))
 
+class DataStore:
+  """An abstract base class for uploading recordings to a data storage.
+  The interface emulates the google storage api."""
+  def target_name(self):
+    raise NotImplementedError()
+  def target_type(self):
+    raise NotImplementedError()
+  def does_storage_object_exist(self, *args):
+    raise NotImplementedError()
+  def download_file(self, *args):
+    raise NotImplementedError()
+  def upload_dir_contents(self, source_dir, **kwargs):
+    raise NotImplementedError()
+
+class GoogleStorageDataStore(DataStore):
+  def __init__(self, data_store_url):
+    self._data_store_url = data_store_url
+    self._bucket = remove_prefix(self._data_store_url.lstrip(),
+                                 gs_utils.GS_PREFIX)
+    self.gs = gs_utils.GSUtils()
+  def target_name(self):
+    return self._data_store_url
+  def target_type(self):
+    return 'Google Storage'
+  def does_storage_object_exist(self, *args):
+    return self.gs.does_storage_object_exist(self._bucket, *args)
+  def download_file(self, *args):
+    self.gs.download_file(self._bucket, *args)
+  def upload_dir_contents(self, source_dir, **kwargs):
+    self.gs.upload_dir_contents(source_dir, self._bucket, **kwargs)
+
+class LocalFileSystemDataStore(DataStore):
+  def __init__(self, data_store_location):
+    self._base_dir = data_store_location
+  def target_name(self):
+    return self._base_dir
+  def target_type(self):
+    return self._base_dir
+  def does_storage_object_exist(self, name, *args):
+    return os.path.isfile(os.path.join(self._base_dir, name))
+  def download_file(self, name, local_path, *args):
+    shutil.copyfile(os.path.join(self._base_dir, name), local_path)
+  def upload_dir_contents(self, source_dir, dest_dir, **kwargs):
+    def copytree(source_dir, dest_dir):
+      if not os.path.exists(dest_dir):
+        os.makedirs(dest_dir)
+      for item in os.listdir(source_dir):
+        source = os.path.join(source_dir, item)
+        dest = os.path.join(dest_dir, item)
+        if os.path.isdir(source):
+          copytree(source, dest)
+        else:
+          shutil.copy2(source, dest)
+    copytree(source_dir, os.path.join(self._base_dir, dest_dir))
 
 if '__main__' == __name__:
   option_parser = optparse.OptionParser()
@@ -405,21 +516,10 @@
       help='Specifies the page sets to use to archive. Supports globs.',
       default='all')
   option_parser.add_option(
-      '', '--skip_all_gs_access', action='store_true',
-      help='All Google Storage interactions will be skipped if this flag is '
-           'specified. This is useful for cases where the user does not have '
-           'the required .boto file but would like to generate webpage '
-           'archives and SKPs from the Skia page sets.',
-      default=False)
-  option_parser.add_option(
       '', '--record', action='store_true',
       help='Specifies whether a new website archive should be created.',
       default=False)
   option_parser.add_option(
-      '', '--dest_gsbase',
-      help='gs:// bucket_name, the bucket to upload the file to.',
-      default='gs://chromium-skia-gm')
-  option_parser.add_option(
       '', '--skia_tools',
       help=('Path to compiled Skia executable tools. '
             'render_pictures/render_pdfs is run on the set '
@@ -429,23 +529,35 @@
             'than Release builds.'),
       default=None)
   option_parser.add_option(
-      '', '--upload_to_gs', action='store_true',
-      help='Does not upload to Google Storage if this is False.',
+      '', '--upload', action='store_true',
+      help=('Uploads to Google Storage or copies to local filesystem storage '
+            ' if this is True.'),
       default=False)
   option_parser.add_option(
+      '', '--data_store',
+    help=('The location of the file storage to use to download and upload '
+          'files. Can be \'gs://<bucket>\' for Google Storage, or '
+          'a directory for local filesystem storage'),
+      default='gs://chromium-skia-gm')
+  option_parser.add_option(
       '', '--alternate_upload_dir',
-      help='Uploads to a different directory in Google Storage if this flag is '
-           'specified',
+      help= ('Uploads to a different directory in Google Storage or local '
+             'storage if this flag is specified'),
       default=None)
   option_parser.add_option(
       '', '--output_dir',
-      help='Directory where SKPs and webpage archives will be outputted to.',
+      help=('Temporary directory where SKPs and webpage archives will be '
+            'outputted to.'),
       default=tempfile.gettempdir())
   option_parser.add_option(
       '', '--browser_executable',
       help='The exact browser executable to run.',
       default=None)
   option_parser.add_option(
+      '', '--browser_extra_args',
+      help='Additional arguments to pass to the browser.',
+      default=None)
+  option_parser.add_option(
       '', '--chrome_src_path',
       help='Path to the chromium src directory.',
       default=None)
@@ -454,6 +566,10 @@
       help='Runs the script without any prompts. If this flag is specified and '
            '--skia_tools is specified then the debugger is not run.',
       default=False)
+  option_parser.add_option(
+      '', '--skp_prefix',
+      help='Prefix to add to the names of generated SKPs.',
+      default=None)
   options, unused_args = option_parser.parse_args()
 
   playback = SkPicturePlayback(options)
diff --git a/tools/skpdiff/SkCLImageDiffer.h b/tools/skpdiff/SkCLImageDiffer.h
index 177940d..cb9a8d6 100644
--- a/tools/skpdiff/SkCLImageDiffer.h
+++ b/tools/skpdiff/SkCLImageDiffer.h
@@ -26,7 +26,7 @@
 public:
     SkCLImageDiffer();
 
-    bool requiresOpenCL() const SK_OVERRIDE { return true; }
+    bool requiresOpenCL() const override { return true; }
 
     /**
      * Initializes the OpenCL resources this differ needs to work
diff --git a/tools/skpdiff/SkDiffContext.cpp b/tools/skpdiff/SkDiffContext.cpp
index c072c27..c422636 100644
--- a/tools/skpdiff/SkDiffContext.cpp
+++ b/tools/skpdiff/SkDiffContext.cpp
@@ -219,7 +219,7 @@
         fTestPath = testPath;
     }
 
-    void run() SK_OVERRIDE {
+    void run() override {
         fDiffContext->addDiff(fBaselinePath.c_str(), fTestPath.c_str());
     }
 
diff --git a/tools/skpdiff/SkDifferentPixelsMetric.h b/tools/skpdiff/SkDifferentPixelsMetric.h
index 0ef7337..1f39b35 100644
--- a/tools/skpdiff/SkDifferentPixelsMetric.h
+++ b/tools/skpdiff/SkDifferentPixelsMetric.h
@@ -27,14 +27,14 @@
     public SkImageDiffer {
 #endif
 public:
-    const char* getName() const SK_OVERRIDE;
+    const char* getName() const override;
     virtual bool diff(SkBitmap* baseline, SkBitmap* test,
                       const BitmapsToCreate& bitmapsToCreate,
-                      Result* result) const SK_OVERRIDE;
+                      Result* result) const override;
 
 protected:
 #if SK_SUPPORT_OPENCL
-    bool onInit() SK_OVERRIDE;
+    bool onInit() override;
 #endif
 
 private:
diff --git a/tools/skpdiff/SkPMetric.h b/tools/skpdiff/SkPMetric.h
index 8f7aa6a..0448708 100644
--- a/tools/skpdiff/SkPMetric.h
+++ b/tools/skpdiff/SkPMetric.h
@@ -18,9 +18,9 @@
  */
 class SkPMetric : public SkImageDiffer {
 public:
-    const char* getName() const SK_OVERRIDE { return "perceptual"; }
+    const char* getName() const override { return "perceptual"; }
     virtual bool diff(SkBitmap* baseline, SkBitmap* test, const BitmapsToCreate& bitmapsToCreate,
-                      Result* result) const SK_OVERRIDE;
+                      Result* result) const override;
 
 private:
     typedef SkImageDiffer INHERITED;
diff --git a/tools/skpdiff/skpdiff_main.cpp b/tools/skpdiff/skpdiff_main.cpp
index 92f1307..b5ba310 100644
--- a/tools/skpdiff/skpdiff_main.cpp
+++ b/tools/skpdiff/skpdiff_main.cpp
@@ -10,6 +10,8 @@
 // the execution of an arbitrary set of difference algorithms.
 // See http://skbug.com/2711 ('rename skpdiff')
 
+#include "SkTypes.h"
+
 #if SK_SUPPORT_OPENCL
 
 #define __NO_STD_VECTOR // Uses cl::vectpr instead of std::vectpr
@@ -259,7 +261,7 @@
     return 0;
 }
 
-#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
+#if !defined(SK_BUILD_FOR_IOS)
 int main(int argc, char * argv[]) {
     return tool_main(argc, (char**) argv);
 }
diff --git a/tools/timer/Timer.cpp b/tools/timer/Timer.cpp
index e430404..b59988c 100644
--- a/tools/timer/Timer.cpp
+++ b/tools/timer/Timer.cpp
@@ -53,12 +53,13 @@
 }
 
 SkString HumanizeMs(double ms) {
-    if (ms > 1e+3)     return SkStringPrintf("%.3gs",  ms/1e3);
-    if (ms < 1e-3)     return SkStringPrintf("%.3gns", ms*1e6);
+    if (ms > 60e+3)  return SkStringPrintf("%.3gm", ms/60e+3);
+    if (ms >  1e+3)  return SkStringPrintf("%.3gs",  ms/1e+3);
+    if (ms <  1e-3)  return SkStringPrintf("%.3gns", ms*1e+6);
 #ifdef SK_BUILD_FOR_WIN
-    if (ms < 1)        return SkStringPrintf("%.3gus", ms*1e3);
+    if (ms < 1)      return SkStringPrintf("%.3gus", ms*1e+3);
 #else
-    if (ms < 1)        return SkStringPrintf("%.3gµs", ms*1e3);
+    if (ms < 1)      return SkStringPrintf("%.3gµs", ms*1e+3);
 #endif
     return SkStringPrintf("%.3gms", ms);
 }
diff --git a/tools/valgrind.supp b/tools/valgrind.supp
index d919c86..ffb7108 100644
--- a/tools/valgrind.supp
+++ b/tools/valgrind.supp
@@ -1,5 +1,17 @@
 # Pass this file to Valgrind with "--suppressions=tools/valgrind.supp"
 
+# Intentional thread / memory leak in DM.
+{
+   dm_keepalive_thread_leak
+   Memcheck:Leak
+   match-leak-kinds: possible
+   ...
+   fun:_ZN8SkThreadC1EPFvPvES0_
+   ...
+   fun:_Z7dm_mainv
+   fun:main
+}
+
 # Third party lib, driver issues.
 {
     ati_driver_bug_1
diff --git a/whitespace.txt b/whitespace.txt
index 2d4bb17..b31ad66 100644
--- a/whitespace.txt
+++ b/whitespace.txt
@@ -1,256 +1,12 @@
 You can modify this file to create no-op changelists (like this one).
-
 I LOVE WHITESPACE!
-
-
-
-
-
-
 sclnafjkds
-
-
-
-
-
-
 Lithp.
 Use the Forth.
-
-
-
-
-
 Life is a mistry.  Everyone must WAKA WAKA WAKA
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 This file has gotten so boring.
-
-
-
-
-
-
-
-
-
-
-
-
 epoger added this line using a pure git checkout
-
-
-
-
-
-
-
-
-
-
-
-
 florin was here
-
-
-
-
-
 more whitespace
 whitespace returns 
+